aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-08-18 15:26:45 +0200
committerLars Knoll <lars.knoll@qt.io>2018-08-23 19:18:15 +0000
commitf15cc9f1df8e17f049c111e3147d6c63c07eb756 (patch)
treeae4daaa5e1cde31c0e412b84e9521f3745911657
parent6e2fc84646987135c96755fbe1c2af20fc722e3d (diff)
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 <simon.hausmann@qt.io>
-rw-r--r--src/qml/jit/qv4jithelpers.cpp4
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp16
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp15
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h13
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp10
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h3
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h6
-rw-r--r--src/qml/jsruntime/qv4object.cpp16
-rw-r--r--src/qml/jsruntime/qv4object_p.h3
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp3
-rw-r--r--src/qml/jsruntime/qv4symbol.cpp5
-rw-r--r--src/qml/jsruntime/qv4symbol_p.h1
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp12
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h3
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp4
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations2
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 <QtCore/private/qnumeric_p.h>
@@ -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<FunctionObject>();
+ 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<const GeneratorFunction *>(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<FunctionObject>();
+ if (!f || !f->isConstructor()) {
scope.engine->throwTypeError();
return nullptr;
}
- return static_cast<const FunctionObject *>(S.ptr);
+ Q_ASSERT(f->isFunctionObject());
+ return static_cast<const FunctionObject *>(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<FunctionObject>();
// ### 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<const FunctionObject *>(&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<TypedArray>();
@@ -1466,7 +1456,7 @@ ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const
int len = argc;
const Value *items = argv;
const FunctionObject *C = thisObject->as<FunctionObject>();
- 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<FunctionObject>();
+ if (!f || !f->isConstructor()) {
engine->throwTypeError();
} else {
acc = static_cast<const Object *>(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