From f15cc9f1df8e17f049c111e3147d6c63c07eb756 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 18 Aug 2018 15:26:45 +0200 Subject: Implement IsConstructor for Function objects Use the jsConstruct member in the function object for this and set it to a nullptr for methods that are not a constructor. Change-Id: I63d2971b23b2596a8e3b6d2781f0d9ed3208693b Reviewed-by: Simon Hausmann --- src/qml/jit/qv4jithelpers.cpp | 4 +++- src/qml/jsruntime/qv4arrayobject.cpp | 16 +++------------- src/qml/jsruntime/qv4functionobject.cpp | 15 ++++----------- src/qml/jsruntime/qv4functionobject_p.h | 13 +++++++++++-- src/qml/jsruntime/qv4generatorobject.cpp | 10 ---------- src/qml/jsruntime/qv4generatorobject_p.h | 3 +-- src/qml/jsruntime/qv4jscall_p.h | 6 ++---- src/qml/jsruntime/qv4object.cpp | 16 ++++------------ src/qml/jsruntime/qv4object_p.h | 3 --- src/qml/jsruntime/qv4runtime.cpp | 3 ++- src/qml/jsruntime/qv4symbol.cpp | 5 +++++ src/qml/jsruntime/qv4symbol_p.h | 1 + src/qml/jsruntime/qv4typedarray.cpp | 12 +----------- src/qml/jsruntime/qv4typedarray_p.h | 3 +-- src/qml/jsruntime/qv4vme_moth.cpp | 4 ++-- tests/auto/qml/ecmascripttests/TestExpectations | 2 -- 16 files changed, 40 insertions(+), 76 deletions(-) diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp index 23e3095a85..9e057dd33d 100644 --- a/src/qml/jit/qv4jithelpers.cpp +++ b/src/qml/jit/qv4jithelpers.cpp @@ -42,6 +42,7 @@ #include "qv4function_p.h" #include "qv4value_p.h" #include "qv4object_p.h" +#include "qv4functionobject_p.h" #include "qv4lookup_p.h" #include @@ -71,7 +72,8 @@ ReturnedValue loadGlobalLookup(ExecutionEngine *engine, Function *f, int index) ReturnedValue loadSuperConstructor(ExecutionEngine *engine, const Value *t) { - if (!t->isObject()) { + const FunctionObject *f = t->as(); + if (!f || !f->isConstructor()) { engine->throwTypeError(); return Encode::undefined(); } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 9be744038c..c162a38e9d 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -165,23 +165,13 @@ ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor { ScopedObject a(scope, Primitive::undefinedValue()); - if (ctor) { - // ### the spec says that we should only call constructors if - // IsConstructor(that), but we have no way of knowing if a builtin is a - // constructor. so for the time being, just try call it, and silence any - // exceptions-- this is not ideal, as the spec also says that we should - // return on exception. - // - // this also isn't completely kosher. for instance: + if (ctor && ctor->isConstructor()) { + // this isn't completely kosher. for instance: // Array.from.call(Object, []).constructor == Object // is expected by the tests, but naturally, we get Number. ScopedValue argument(scope, useLen ? QV4::Encode(len) : Primitive::undefinedValue()); a = ctor->callAsConstructor(argument, useLen ? 1 : 0); - if (scope.engine->hasException) - scope.engine->catchException(); // probably not a constructor, then. - } - - if (!a) { + } else { a = scope.engine->newArrayObject(len); } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 35d8fe18e0..a5dc0ba567 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -73,7 +73,7 @@ DEFINE_OBJECT_VTABLE(FunctionObject); void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call) { jsCall = call; - jsConstruct = QV4::FunctionObject::virtualCallAsConstructor; + jsConstruct = nullptr; Object::init(); this->scope.set(scope->engine(), scope->d()); @@ -165,11 +165,6 @@ ReturnedValue FunctionObject::name() const return get(scope()->internalClass->engine->id_name()); } -ReturnedValue FunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(); -} - ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) { return Encode::undefined(); @@ -587,11 +582,6 @@ ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Va DEFINE_OBJECT_VTABLE(MemberFunction); -ReturnedValue MemberFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); -} - DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction); ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -652,6 +642,9 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : nullptr); this->boundThis.set(scope->engine(), boundThis); + if (!target->isConstructor()) + jsConstruct = nullptr; + ScopedObject f(s, this); ScopedValue l(s, target->get(s.engine->id_length())); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 8482189bb3..54964b9bbd 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -80,6 +80,10 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { Index_ProtoConstructor = 0 }; + bool isConstructor() const { + return jsConstruct != nullptr; + } + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call); void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr, bool createProto = false); void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); @@ -170,13 +174,16 @@ struct Q_QML_EXPORT FunctionObject: Object { inline ReturnedValue callAsConstructor(const JSCallData &data) const; ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const { + if (!d()->jsConstruct) + return engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this); } inline ReturnedValue call(const JSCallData &data) const; ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { + if (!d()->jsCall) + return engine()->throwTypeError(QStringLiteral("Function can only be called with |new|.")); return d()->jsCall(this, thisObject, argv, argc); } - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); @@ -187,6 +194,9 @@ struct Q_QML_EXPORT FunctionObject: Object { bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; bool isBoundFunction() const; + bool isConstructor() const { + return d()->isConstructor(); + } ReturnedValue protoProperty() const { return get(engine()->id_prototype()); } @@ -261,7 +271,6 @@ struct ConstructorFunction : ScriptFunction { struct MemberFunction : ScriptFunction { V4_OBJECT2(MemberFunction, ScriptFunction) V4_INTERNALCLASS(MemberFunction) - static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); }; diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 4a784bff35..bab0cfbfb1 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -84,11 +84,6 @@ Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Funct return g->d(); } -ReturnedValue GeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(); -} - ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { const GeneratorFunction *gf = static_cast(f); @@ -245,8 +240,3 @@ Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); return g->d(); } - -ReturnedValue MemberGeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); -} diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index 017580e1a1..f00f730344 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -109,7 +109,7 @@ struct GeneratorFunction : ScriptFunction V4_INTERNALCLASS(GeneratorFunction) static Heap::FunctionObject *create(ExecutionContext *scope, Function *function); - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; @@ -119,7 +119,6 @@ struct MemberGeneratorFunction : GeneratorFunction V4_INTERNALCLASS(MemberGeneratorFunction) static Heap::FunctionObject *create(ExecutionContext *scope, Function *function, String *name); - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct GeneratorPrototype : Object diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 307eec9111..55cedf50aa 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -102,15 +102,13 @@ struct JSCallData { inline ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const { - if (!d()->jsConstruct) - return engine()->throwTypeError(QStringLiteral("Object is not a constructor.")); - return d()->jsConstruct(this, data.args, data.argc, this); + return callAsConstructor(data.args, data.argc, this); } inline ReturnedValue FunctionObject::call(const JSCallData &data) const { - return d()->jsCall(this, data.thisObject, data.args, data.argc); + return call(data.thisObject, data.args, data.argc); } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 72f9f84bb2..eb4e392c0f 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -308,16 +308,6 @@ PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs return { nullptr, nullptr }; } -ReturnedValue Object::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(); -} - -ReturnedValue Object::virtualCall(const FunctionObject *f, const Value *, const Value *, int) -{ - return f->engine()->throwTypeError(); -} - ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { if (id.isArrayIndex()) @@ -973,11 +963,13 @@ const FunctionObject *Object::speciesConstructor(Scope &scope, const FunctionObj ScopedValue S(scope, c->get(scope.engine->symbol_species())); if (S->isNullOrUndefined()) return defaultConstructor; - if (!S->isFunctionObject()) { + const FunctionObject *f = S->as(); + if (!f || !f->isConstructor()) { scope.engine->throwTypeError(); return nullptr; } - return static_cast(S.ptr); + Q_ASSERT(f->isFunctionObject()); + return static_cast(f); } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 3dcee8addf..70fee128b6 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -363,10 +363,7 @@ public: bool isArray() const; const FunctionObject *speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const; - protected: - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); static bool virtualDeleteProperty(Managed *m, PropertyKey id); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 82e1284289..9180108a0c 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1550,8 +1550,9 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde if (superClass.isNull()) { protoParent = Encode::null(); } else { + const FunctionObject *superFunction = superClass.as(); // ### check that the heritage object is a constructor - if (!superClass.isFunctionObject()) + if (!superFunction || !superFunction->isConstructor()) return engine->throwTypeError(QStringLiteral("The superclass is not a function object.")); const FunctionObject *s = static_cast(&superClass); ScopedValue result(scope, s->get(scope.engine->id_prototype())); diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp index bdefe1eb9e..d5ae094e1f 100644 --- a/src/qml/jsruntime/qv4symbol.cpp +++ b/src/qml/jsruntime/qv4symbol.cpp @@ -80,6 +80,11 @@ ReturnedValue QV4::SymbolCtor::virtualCall(const QV4::FunctionObject *f, const Q return Symbol::create(scope.engine, desc)->asReturnedValue(); } +ReturnedValue SymbolCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(QStringLiteral("Symbol can't be used together with |new|.")); +} + ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h index cb60f748de..c7e12b512b 100644 --- a/src/qml/jsruntime/qv4symbol_p.h +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -83,6 +83,7 @@ struct SymbolCtor : FunctionObject V4_OBJECT2(SymbolCtor, FunctionObject) static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *newTarget); static ReturnedValue method_for(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_keyFor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 54c3b9cfc1..456a577b52 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -1440,16 +1440,6 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const Functio return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue(); } -ReturnedValue IntrinsicTypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(); -} - -ReturnedValue IntrinsicTypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) -{ - return f->engine()->throwTypeError(); -} - static bool validateTypedArray(const Object *o) { const TypedArray *a = o->as(); @@ -1466,7 +1456,7 @@ ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const int len = argc; const Value *items = argv; const FunctionObject *C = thisObject->as(); - if (!C) + if (!C || !C->isConstructor()) return scope.engine->throwTypeError(); Value lenValue = Primitive::fromInt32(len); diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 775ba61ddc..11b3a0dabf 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -148,8 +148,7 @@ struct IntrinsicTypedArrayCtor: FunctionObject { V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject) - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static constexpr VTable::Call virtualCall = nullptr; static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 575cad70e4..aed3fce6b1 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -949,8 +949,8 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(ConvertThisToObject) MOTH_BEGIN_INSTR(LoadSuperConstructor) - const Value *f = &stack[CallData::Function]; - if (!f->isFunctionObject()) { + const FunctionObject *f = stack[CallData::Function].as(); + if (!f || !f->isConstructor()) { engine->throwTypeError(); } else { acc = static_cast(f)->getPrototypeOf()->asReturnedValue(); diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 3d79b87652..1a29d0b206 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -26,7 +26,6 @@ language/module-code/namespace/internals/define-own-property.js strictFails language/module-code/namespace/internals/set.js strictFails # ----- test failures that should be fixed -built-ins/Array/from/iter-cstm-ctor-err.js fails built-ins/Array/from/proto-from-ctor-realm.js fails built-ins/Array/isArray/proxy-revoked.js fails built-ins/Array/isArray/proxy.js fails @@ -1491,7 +1490,6 @@ language/statements/class/subclass/builtin-objects/WeakSet/super-must-be-called. language/statements/class/subclass/builtins.js fails language/statements/class/subclass/class-definition-null-proto-super.js fails language/statements/class/subclass/class-definition-null-proto-this.js fails -language/statements/class/subclass/class-definition-superclass-generator.js fails language/statements/class/subclass/default-constructor-spread-override.js fails language/statements/class/super/in-methods.js fails language/statements/const/block-local-closure-get-before-initialization.js fails -- cgit v1.2.3