From 57428f94d443fc51c071a23e18026d83bf8f219b Mon Sep 17 00:00:00 2001 From: Seonghyun Kim Date: Tue, 18 Jul 2023 18:09:51 +0900 Subject: [PATCH] Update public API for using NewTarget from outside of escargot Signed-off-by: Seonghyun Kim --- src/api/EscargotPublic.cpp | 61 ++++++++++++++++++++++++++++++++++---- src/api/EscargotPublic.h | 21 ++++++++++++- test/cctest/testapi.cpp | 21 +++++++++++++ 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/api/EscargotPublic.cpp b/src/api/EscargotPublic.cpp index 1d2a58f0b..43dfb05d6 100644 --- a/src/api/EscargotPublic.cpp +++ b/src/api/EscargotPublic.cpp @@ -1957,6 +1957,17 @@ ObjectRef* ObjectRef::create(ExecutionStateRef* state) #endif } +ObjectRef* ObjectRef::create(ExecutionStateRef* state, ObjectRef* proto) +{ +#if defined(ESCARGOT_SMALL_CONFIG) + auto obj = new Object(*toImpl(state), toImpl(proto)); + obj->markThisObjectDontNeedStructureTransitionTable(); + return toRef(obj); +#else + return toRef(new Object(*toImpl(state), toImpl(proto))); +#endif +} + // can not redefine or delete virtual property class ExposableObject : public DerivedObject { @@ -2658,6 +2669,16 @@ class CallPublicFunctionData : public gc { FunctionObjectRef::NativeFunctionPointer m_publicFn; }; +class CallPublicFunctionWithNewTargetData : public gc { +public: + CallPublicFunctionWithNewTargetData(FunctionObjectRef::NativeFunctionWithNewTargetPointer publicFn) + : m_publicFn(publicFn) + { + } + + FunctionObjectRef::NativeFunctionWithNewTargetPointer m_publicFn; +}; + static Value publicFunctionBridge(ExecutionState& state, Value thisValue, size_t calledArgc, Value* calledArgv, Optional newTarget) { ExtendedNativeFunctionObject* func = state.resolveCallee()->asExtendedNativeFunctionObject(); @@ -2671,6 +2692,24 @@ static Value publicFunctionBridge(ExecutionState& state, Value thisValue, size_t return toImpl(code->m_publicFn(toRef(&state), toRef(thisValue), calledArgc, newArgv, newTarget.hasValue())); } +static Value publicFunctionBridgeWithNewTarget(ExecutionState& state, Value thisValue, size_t calledArgc, Value* calledArgv, Optional newTarget) +{ + ExtendedNativeFunctionObject* func = state.resolveCallee()->asExtendedNativeFunctionObject(); + CallPublicFunctionWithNewTargetData* code = func->internalSlotAsPointer(FunctionObjectRef::BuiltinFunctionSlot::PublicFunctionIndex); + + ValueRef** newArgv = ALLOCA(sizeof(ValueRef*) * calledArgc, ValueRef*); + for (size_t i = 0; i < calledArgc; i++) { + newArgv[i] = toRef(calledArgv[i]); + } + + OptionalRef newTargetRef; + if (newTarget) { + newTargetRef = toRef(newTarget.value()); + } + + return toImpl(code->m_publicFn(toRef(&state), toRef(thisValue), calledArgc, newArgv, newTargetRef)); +} + typedef void (*SecurityCheckCallback)(ExecutionStateRef* state, GlobalObjectProxyObjectRef* proxy, GlobalObjectRef* targetGlobalObject); GlobalObjectProxyObjectRef* GlobalObjectProxyObjectRef::create(ExecutionStateRef* state, GlobalObjectRef* target, SecurityCheckCallback callback) @@ -2693,16 +2732,28 @@ static FunctionObjectRef* createFunction(ExecutionStateRef* state, FunctionObjec int flags = 0; flags |= info.m_isStrict ? NativeFunctionInfo::Strict : 0; flags |= info.m_isConstructor ? NativeFunctionInfo::Constructor : 0; - NativeFunctionInfo nativeInfo(toImpl(info.m_name), publicFunctionBridge, info.m_argumentCount, flags); + NativeFunctionInfo nativeInfo(toImpl(info.m_name), nullptr, info.m_argumentCount, flags); + + if (info.m_hasWithNewTargetCallback) { + nativeInfo.m_nativeFunction = publicFunctionBridgeWithNewTarget; + } else { + nativeInfo.m_nativeFunction = publicFunctionBridge; + } ExtendedNativeFunctionObject* func; - if (isBuiltin) + if (isBuiltin) { func = new ExtendedNativeFunctionObjectImpl<1>(*toImpl(state), nativeInfo, NativeFunctionObject::__ForBuiltinConstructor__); - else + } else { func = new ExtendedNativeFunctionObjectImpl<1>(*toImpl(state), nativeInfo); + } - CallPublicFunctionData* data = new CallPublicFunctionData(info.m_nativeFunction); - func->setInternalSlotAsPointer(FunctionObjectRef::BuiltinFunctionSlot::PublicFunctionIndex, data); + if (info.m_hasWithNewTargetCallback) { + CallPublicFunctionWithNewTargetData* data = new CallPublicFunctionWithNewTargetData(info.m_nativeFunctionWithNewTarget); + func->setInternalSlotAsPointer(FunctionObjectRef::BuiltinFunctionSlot::PublicFunctionIndex, data); + } else { + CallPublicFunctionData* data = new CallPublicFunctionData(info.m_nativeFunction); + func->setInternalSlotAsPointer(FunctionObjectRef::BuiltinFunctionSlot::PublicFunctionIndex, data); + } return toRef(func); } diff --git a/src/api/EscargotPublic.h b/src/api/EscargotPublic.h index 6b7b81224..bedbc6574 100644 --- a/src/api/EscargotPublic.h +++ b/src/api/EscargotPublic.h @@ -1276,6 +1276,7 @@ typedef bool (*ExposableObjectDeleteOwnPropertyCallback)(ExecutionStateRef* stat class ESCARGOT_EXPORT ObjectRef : public PointerValueRef { public: static ObjectRef* create(ExecutionStateRef* state); + static ObjectRef* create(ExecutionStateRef* state, ObjectRef* proto); // can not redefine or delete virtual property // virtual property does not follow every rule of ECMAScript static ObjectRef* createExposableObject(ExecutionStateRef* state, @@ -1495,7 +1496,10 @@ class ESCARGOT_EXPORT FunctionObjectRef : public ObjectRef { public: // if newTarget is present, that means constructor call // in constructor call, function must return newly created object && thisValue is always undefined - typedef ValueRef* (*NativeFunctionPointer)(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructorCall); + typedef ValueRef* (*NativeFunctionPointer)(ExecutionStateRef* state, ValueRef* thisValue, + size_t argc, ValueRef** argv, bool isConstructorCall); + typedef ValueRef* (*NativeFunctionWithNewTargetPointer)(ExecutionStateRef* state, ValueRef* thisValue, + size_t argc, ValueRef** argv, OptionalRef newTarget); enum BuiltinFunctionSlot : size_t { PublicFunctionIndex = 0, @@ -1504,15 +1508,30 @@ class ESCARGOT_EXPORT FunctionObjectRef : public ObjectRef { struct ESCARGOT_EXPORT NativeFunctionInfo { bool m_isStrict; bool m_isConstructor; + bool m_hasWithNewTargetCallback; AtomicStringRef* m_name; NativeFunctionPointer m_nativeFunction; + NativeFunctionWithNewTargetPointer m_nativeFunctionWithNewTarget; size_t m_argumentCount; NativeFunctionInfo(AtomicStringRef* name, NativeFunctionPointer fn, size_t argc, bool isStrict = true, bool isConstructor = true) : m_isStrict(isStrict) , m_isConstructor(isConstructor) + , m_hasWithNewTargetCallback(false) , m_name(name) , m_nativeFunction(fn) + , m_nativeFunctionWithNewTarget(nullptr) + , m_argumentCount(argc) + { + } + + NativeFunctionInfo(AtomicStringRef* name, NativeFunctionWithNewTargetPointer fn, size_t argc, bool isStrict = true, bool isConstructor = true) + : m_isStrict(isStrict) + , m_isConstructor(isConstructor) + , m_hasWithNewTargetCallback(true) + , m_name(name) + , m_nativeFunction(nullptr) + , m_nativeFunctionWithNewTarget(fn) , m_argumentCount(argc) { } diff --git a/test/cctest/testapi.cpp b/test/cctest/testapi.cpp index 86bc60208..1cdf518f4 100644 --- a/test/cctest/testapi.cpp +++ b/test/cctest/testapi.cpp @@ -648,6 +648,27 @@ TEST(Object, ConstructorName) testObj); } +TEST(FunctionObject, Consturct) +{ + FunctionObjectRef* fn = Evaluator::execute(g_context.get(), [](ExecutionStateRef* state) -> ValueRef* { + FunctionObjectRef::NativeFunctionInfo nativeFunctionInfo(AtomicStringRef::create(g_context.get(), "test"), + [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, OptionalRef newTarget) -> ValueRef* { + EXPECT_TRUE(newTarget.hasValue()); + ObjectRef* obj = ObjectRef::create(state, newTarget->asFunctionObject()->getFunctionPrototype(state)->asObject()); + return obj; + }, + 0, true, true); + return FunctionObjectRef::create(state, nativeFunctionInfo); + }) + .result->asFunctionObject(); + + Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionObjectRef* fn) -> ValueRef* { + ObjectRef* obj = fn->construct(state, 0, nullptr)->asObject(); + EXPECT_TRUE(obj->instanceOf(state, fn)); + }, + fn); +} + TEST(ObjectTemplate, Basic1) { ObjectTemplateRef* tpl = ObjectTemplateRef::create();