Skip to content

Commit

Permalink
Implement ValueRef::callConsturctor
Browse files Browse the repository at this point in the history
Signed-off-by: Seonghyun Kim <[email protected]>
  • Loading branch information
ksh8281 committed Sep 13, 2024
1 parent 0434ba9 commit 713b37d
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 54 deletions.
73 changes: 30 additions & 43 deletions src/api/EscargotPublic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2420,6 +2420,21 @@ void ObjectRef::setLength(ExecutionStateRef* pState, uint64_t newLength)
toImpl(this)->set(state, state.context()->staticStrings().length, Value(newLength), toImpl(this));
}

IteratorObjectRef* ObjectRef::values(ExecutionStateRef* state)
{
return toRef(toImpl(this)->values(*toImpl(state)));
}

IteratorObjectRef* ObjectRef::keys(ExecutionStateRef* state)
{
return toRef(toImpl(this)->keys(*toImpl(state)));
}

IteratorObjectRef* ObjectRef::entries(ExecutionStateRef* state)
{
return toRef(toImpl(this)->entries(*toImpl(state)));
}

bool ObjectRef::isExtensible(ExecutionStateRef* state)
{
return toImpl(this)->isExtensible(*toImpl(state));
Expand Down Expand Up @@ -3522,6 +3537,21 @@ ValueRef* ValueRef::construct(ExecutionStateRef* state, const size_t argc, Value
return toRef(Object::construct(*toImpl(state), o, argc, newArgv));
}

void ValueRef::callConstructor(ExecutionStateRef* state, ObjectRef* receiver, const size_t argc, ValueRef** argv)
{
auto impl = toImpl(this);
if (UNLIKELY(!impl.isObject())) {
ErrorObject::throwBuiltinError(*toImpl(state), ErrorCode::TypeError, ErrorObject::Messages::NOT_Callable);
}
Object* o = impl.asObject();
Value* newArgv = ALLOCA(sizeof(Value) * argc, Value);
for (size_t i = 0; i < argc; i++) {
newArgv[i] = toImpl(argv[i]);
}

Object::callConstructor(*toImpl(state), o, toImpl(receiver), argc, newArgv);
}

ValueRef* ValueRef::create(bool value)
{
return reinterpret_cast<ValueRef*>(EncodedValue(Value(value)).payload());
Expand Down Expand Up @@ -3725,19 +3755,6 @@ ArrayObjectRef* ArrayObjectRef::create(ExecutionStateRef* state, ValueVectorRef*
return toRef(ret);
}

IteratorObjectRef* ArrayObjectRef::values(ExecutionStateRef* state)
{
return toRef(toImpl(this)->values(*toImpl(state)));
}
IteratorObjectRef* ArrayObjectRef::keys(ExecutionStateRef* state)
{
return toRef(toImpl(this)->keys(*toImpl(state)));
}
IteratorObjectRef* ArrayObjectRef::entries(ExecutionStateRef* state)
{
return toRef(toImpl(this)->entries(*toImpl(state)));
}

COMPILE_ASSERT((int)ErrorCode::None == (int)ErrorObjectRef::Code::None, "");
COMPILE_ASSERT((int)ErrorCode::ReferenceError == (int)ErrorObjectRef::Code::ReferenceError, "");
COMPILE_ASSERT((int)ErrorCode::TypeError == (int)ErrorObjectRef::Code::TypeError, "");
Expand Down Expand Up @@ -4258,21 +4275,6 @@ size_t SetObjectRef::size(ExecutionStateRef* state)
return toImpl(this)->size(*toImpl(state));
}

IteratorObjectRef* SetObjectRef::values(ExecutionStateRef* state)
{
return toRef(toImpl(this)->values(*toImpl(state)));
}

IteratorObjectRef* SetObjectRef::keys(ExecutionStateRef* state)
{
return toRef(toImpl(this)->keys(*toImpl(state)));
}

IteratorObjectRef* SetObjectRef::entries(ExecutionStateRef* state)
{
return toRef(toImpl(this)->entries(*toImpl(state)));
}

WeakSetObjectRef* WeakSetObjectRef::create(ExecutionStateRef* state)
{
return toRef(new WeakSetObject(*toImpl(state)));
Expand Down Expand Up @@ -4338,21 +4340,6 @@ size_t MapObjectRef::size(ExecutionStateRef* state)
return toImpl(this)->size(*toImpl(state));
}

IteratorObjectRef* MapObjectRef::values(ExecutionStateRef* state)
{
return toRef(toImpl(this)->values(*toImpl(state)));
}

IteratorObjectRef* MapObjectRef::keys(ExecutionStateRef* state)
{
return toRef(toImpl(this)->keys(*toImpl(state)));
}

IteratorObjectRef* MapObjectRef::entries(ExecutionStateRef* state)
{
return toRef(toImpl(this)->entries(*toImpl(state)));
}

WeakMapObjectRef* WeakMapObjectRef::create(ExecutionStateRef* state)
{
return toRef(new WeakMapObject(*toImpl(state)));
Expand Down
15 changes: 6 additions & 9 deletions src/api/EscargotPublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,8 @@ class ESCARGOT_EXPORT ValueRef {
bool instanceOf(ExecutionStateRef* state, const ValueRef* other) const;
ValueRef* call(ExecutionStateRef* state, ValueRef* receiver, const size_t argc, ValueRef** argv);
ValueRef* construct(ExecutionStateRef* state, const size_t argc, ValueRef** argv); // same with new expression in js
// call constrictor with already created object
void callConstructor(ExecutionStateRef* state, ObjectRef* receiver, const size_t argc, ValueRef** argv);
};

class ESCARGOT_EXPORT ValueVectorRef {
Expand Down Expand Up @@ -1416,6 +1418,10 @@ class ESCARGOT_EXPORT ObjectRef : public PointerValueRef {
uint64_t length(ExecutionStateRef* state);
void setLength(ExecutionStateRef* state, uint64_t newLength);

IteratorObjectRef* values(ExecutionStateRef* state);
IteratorObjectRef* keys(ExecutionStateRef* state);
IteratorObjectRef* entries(ExecutionStateRef* state);

bool isExtensible(ExecutionStateRef* state);
bool preventExtensions(ExecutionStateRef* state);
bool setIntegrityLevel(ExecutionStateRef* state, bool isSealed);
Expand Down Expand Up @@ -1611,9 +1617,6 @@ class ESCARGOT_EXPORT ArrayObjectRef : public ObjectRef {
static ArrayObjectRef* create(ExecutionStateRef* state);
static ArrayObjectRef* create(ExecutionStateRef* state, const uint64_t size);
static ArrayObjectRef* create(ExecutionStateRef* state, ValueVectorRef* source);
IteratorObjectRef* values(ExecutionStateRef* state);
IteratorObjectRef* keys(ExecutionStateRef* state);
IteratorObjectRef* entries(ExecutionStateRef* state);
};

class ESCARGOT_EXPORT ErrorObjectRef : public ObjectRef {
Expand Down Expand Up @@ -1907,9 +1910,6 @@ class ESCARGOT_EXPORT SetObjectRef : public ObjectRef {
bool deleteOperation(ExecutionStateRef* state, ValueRef* key);
bool has(ExecutionStateRef* state, ValueRef* key);
size_t size(ExecutionStateRef* state);
IteratorObjectRef* values(ExecutionStateRef* state);
IteratorObjectRef* keys(ExecutionStateRef* state);
IteratorObjectRef* entries(ExecutionStateRef* state);
};

class ESCARGOT_EXPORT WeakSetObjectRef : public ObjectRef {
Expand All @@ -1929,9 +1929,6 @@ class ESCARGOT_EXPORT MapObjectRef : public ObjectRef {
bool has(ExecutionStateRef* state, ValueRef* key);
void set(ExecutionStateRef* state, ValueRef* key, ValueRef* value);
size_t size(ExecutionStateRef* state);
IteratorObjectRef* values(ExecutionStateRef* state);
IteratorObjectRef* keys(ExecutionStateRef* state);
IteratorObjectRef* entries(ExecutionStateRef* state);
};

class ESCARGOT_EXPORT WeakMapObjectRef : public ObjectRef {
Expand Down
14 changes: 13 additions & 1 deletion src/interpreter/ByteCodeInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4159,7 +4159,19 @@ NEVER_INLINE void InterpreterSlowPath::callFunctionComplexCase(ExecutionState& s
}

// Let result be Construct(func, argList, newTarget).
Value result = Object::construct(state, registerFile[code->m_calleeIndex], argc, argv, newTarget);
Value result;
Value* stackStorage = registerFile + byteCodeBlock->m_requiredOperandRegisterNumber;
// below if-statement is not spec
// using this value on stack is trick for callConstructor util
if (stackStorage[0].asPointerValue()) {
result = stackStorage[0].asObject();
Object* proto = Object::getPrototypeFromConstructor(state, newTarget, [](ExecutionState& state, Context* constructorRealm) -> Object* {
return constructorRealm->globalObject()->objectPrototype();
});
result.asObject()->setPrototype(state, proto);
} else {
result = Object::construct(state, registerFile[code->m_calleeIndex], argc, argv, newTarget);
}

// Let thisER be GetThisEnvironment( ).
// Return thisER.BindThisValue(result).
Expand Down
12 changes: 12 additions & 0 deletions src/runtime/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,18 @@ Value Object::construct(ExecutionState& state, const Value& constructor, const s
return constructor.asPointerValue()->construct(state, argc, argv, newTarget);
}

void Object::callConstructor(ExecutionState& state, const Value& constructor, Object* receiver, const size_t argc, Value* argv, Object* newTarget)
{
if (newTarget == nullptr) {
newTarget = constructor.asObject();
}

ASSERT(constructor.isConstructor());
ASSERT(newTarget->isConstructor());

return constructor.asPointerValue()->callConstructor(state, receiver, argc, argv, newTarget);
}

// https://www.ecma-international.org/ecma-262/#sec-setintegritylevel
bool Object::setIntegrityLevel(ExecutionState& state, Object* O, bool isSealed)
{
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,9 @@ class Object : public PointerValue {

static Value call(ExecutionState& state, const Value& callee, const Value& thisValue, const size_t argc, Value* argv);
static Value construct(ExecutionState& state, const Value& constructor, const size_t argc, Value* argv, Object* newTarget = nullptr);
// call constructor with already created object(some clients need this)
static void callConstructor(ExecutionState& state, const Value& constructor, Object* receiver, const size_t argc, Value* argv, Object* newTarget = nullptr);

static bool setIntegrityLevel(ExecutionState& state, Object* O, bool isSealed);
static bool testIntegrityLevel(ExecutionState& state, Object* O, bool isSealed);

Expand Down
5 changes: 5 additions & 0 deletions src/runtime/PointerValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,9 @@ Value PointerValue::construct(ExecutionState& state, const size_t argc, Value* a
// never get here. but I add return statement for removing compile warning
return Value();
}

void PointerValue::callConstructor(ExecutionState& state, Object* receiver, const size_t argc, Value* argv, Object* newTarget)
{
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::Not_Constructor);
}
} // namespace Escargot
1 change: 1 addition & 0 deletions src/runtime/PointerValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,7 @@ class PointerValue : public gc {

virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv);
virtual Value construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget);
virtual void callConstructor(ExecutionState& state, Object* receiver, const size_t argc, Value* argv, Object* newTarget);

// tag values for fast type check
// these values actually have unique virtual table address of each object class
Expand Down
13 changes: 13 additions & 0 deletions src/runtime/ScriptClassConstructorFunctionObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ Value ScriptClassConstructorFunctionObject::construct(ExecutionState& state, con
ScriptClassConstructorFunctionObjectNewTargetBinderWithConstruct, ScriptClassConstructorFunctionObjectReturnValueBinderWithConstruct>(state, this, thisArgument, argc, argv, newTarget);
}

void ScriptClassConstructorFunctionObject::callConstructor(ExecutionState& state, Object* receiver, const size_t argc, Value* argv, Object* newTarget)
{
ConstructorKind kind = constructorKind();
if (kind == ConstructorKind::Base) {
Object* proto = Object::getPrototypeFromConstructor(state, newTarget, [](ExecutionState& state, Context* constructorRealm) -> Object* {
return constructorRealm->globalObject()->objectPrototype();
});
receiver->setPrototype(state, proto);
}
FunctionObjectProcessCallGenerator::processCall<ScriptClassConstructorFunctionObject, true, true, true, ScriptClassConstructorFunctionObjectThisValueBinder,
ScriptClassConstructorFunctionObjectNewTargetBinderWithConstruct, ScriptClassConstructorFunctionObjectReturnValueBinderWithConstruct>(state, this, receiver, argc, argv, newTarget);
}

void ScriptClassConstructorFunctionObject::initInstanceFieldMembers(ExecutionState& state, Object* instance)
{
size_t s = m_instanceFieldInitData.size();
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/ScriptClassConstructorFunctionObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ScriptClassConstructorFunctionObject : public ScriptFunctionObject {
friend class FunctionObjectProcessCallGenerator;
virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv) override;
virtual Value construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget) override;

virtual void callConstructor(ExecutionState& state, Object* receiver, const size_t argc, Value* argv, Object* newTarget) override;
bool isConstructor() const override
{
return true;
Expand Down
9 changes: 9 additions & 0 deletions src/runtime/ScriptFunctionObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ Value ScriptFunctionObject::construct(ExecutionState& state, const size_t argc,
return FunctionObjectProcessCallGenerator::processCall<ScriptFunctionObject, true, true, false, ScriptFunctionObjectObjectThisValueBinderWithConstruct, ScriptFunctionObjectNewTargetBinderWithConstruct, ScriptFunctionObjectReturnValueBinderWithConstruct>(state, this, Value(thisArgument), argc, argv, newTarget).asObject();
}

void ScriptFunctionObject::callConstructor(ExecutionState& state, Object* receiver, const size_t argc, Value* argv, Object* newTarget)
{
Object* proto = Object::getPrototypeFromConstructor(state, newTarget, [](ExecutionState& state, Context* constructorRealm) -> Object* {
return constructorRealm->globalObject()->objectPrototype();
});
receiver->setPrototype(state, proto);
FunctionObjectProcessCallGenerator::processCall<ScriptFunctionObject, true, true, false, ScriptFunctionObjectObjectThisValueBinderWithConstruct, ScriptFunctionObjectNewTargetBinderWithConstruct, ScriptFunctionObjectReturnValueBinderWithConstruct>(state, this, receiver, argc, argv, newTarget);
}

void ScriptFunctionObject::generateArgumentsObject(ExecutionState& state, size_t argc, Value* argv, FunctionEnvironmentRecord* environmentRecordWillArgumentsObjectBeLocatedIn, Value* stackStorage, bool isMapped)
{
// arrow function should not create an ArgumentsObject
Expand Down
1 change: 1 addition & 0 deletions src/runtime/ScriptFunctionObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class ScriptFunctionObject : public FunctionObject {
virtual Value call(ExecutionState& state, const Value& thisValue, const size_t argc, Value* argv) override;
// https://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
virtual Value construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget) override;
virtual void callConstructor(ExecutionState& state, Object* receiver, const size_t argc, Value* argv, Object* newTarget) override;

LexicalEnvironment* outerEnvironment()
{
Expand Down
26 changes: 26 additions & 0 deletions test/cctest/testapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,32 @@ TEST(FunctionObject, Consturct)
fn);
}

TEST(FunctionObject, CallConsturctor)
{
eval(g_context.get(), StringRef::createFromASCII("function callconstructortestFunction() {} this.testFunction = callconstructortestFunction;"));
eval(g_context.get(), StringRef::createFromASCII("class callconstructortest{}; this.testClass = callconstructortest;"));
eval(g_context.get(), StringRef::createFromASCII("class callconstructortest2 extends callconstructortest{ #pri; constructor() { super(); this.#pri = 123; } read() { return this.#pri; } }; this.testClass2 = callconstructortest2;"));
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state) -> ValueRef* {
auto testClass = state->context()->globalObject()->get(state, StringRef::createFromASCII("testClass"));
ObjectRef* obj = ObjectRef::create(state);
testClass->callConstructor(state, obj, 0, nullptr);
EXPECT_TRUE(obj->instanceOf(state, testClass));

auto testClass2 = state->context()->globalObject()->get(state, StringRef::createFromASCII("testClass2"));
ObjectRef* obj2 = ObjectRef::create(state);
testClass2->callConstructor(state, obj2, 0, nullptr);
EXPECT_TRUE(obj2->instanceOf(state, testClass2));
EXPECT_TRUE(obj2->get(state, StringRef::createFromASCII("read"))->call(state, obj2, 0, nullptr)->asNumber() == 123);

auto testFunction = state->context()->globalObject()->get(state, StringRef::createFromASCII("testFunction"));
ObjectRef* obj3 = ObjectRef::create(state);
testFunction->callConstructor(state, obj3, 0, nullptr);
EXPECT_TRUE(obj3->instanceOf(state, testFunction));

return ValueRef::createUndefined();
});
}

TEST(ObjectTemplate, Basic1)
{
ObjectTemplateRef* tpl = ObjectTemplateRef::create();
Expand Down

0 comments on commit 713b37d

Please sign in to comment.