diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-11-05 10:18:53 +0100 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-11-22 08:47:08 +0000 |
commit | 33c13efd91954fb50019e82f3ab8e8e1d8458332 (patch) | |
tree | 6d91724990f49fc4f04f012599cfa3241b98f4ec /src/qml/jsruntime | |
parent | 03f492f91a9ac6d33be05488f7ea6fb5decaf873 (diff) |
Ensure our builtin constructors are subclassable
Respect the newTarget passed into those constructors and make
sure we set up the proto chain correctly.
Change-Id: I3d12c7dbef4b33660a6715d73e9fb0f89105167a
Fixes: QTBUG-71138
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4arrayobject.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4arrayobject_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4booleanobject.cpp | 12 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4dateobject.cpp | 18 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4generatorobject.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4mapobject.cpp | 7 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4numberobject.cpp | 12 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4object.cpp | 15 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4object_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4promiseobject.cpp | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexpobject.cpp | 8 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4setobject.cpp | 7 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4stringobject.cpp | 10 |
14 files changed, 100 insertions, 24 deletions
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 2e7c994550..8637db3dfd 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -60,11 +60,13 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Array")); } -ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine(); Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); + if (newTarget) + a->setProtoFromNewTarget(newTarget); uint len; if (argc == 1 && argv[0].isNumber()) { bool ok; diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 04ec7e1607..c959b71bc6 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,7 +70,7 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 075ee1657e..3e5f51c302 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,10 +50,18 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Boolean")); } -ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) +ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *newTarget) { + auto v4 = that->engine(); bool n = argc ? argv[0].toBoolean() : false; - return Encode(that->engine()->newBooleanObject(n)); + + ReturnedValue o = Encode(v4->newBooleanObject(n)); + if (!newTarget) + return o; + Scope scope(v4); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue BooleanCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index df3bb37e9c..a13fb37a52 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -755,16 +755,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Date")); } -ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) +ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *newTarget) { - ExecutionEngine *e = that->engine(); + ExecutionEngine *v4 = that->engine(); double t = 0; if (argc == 0) t = currentTime(); else if (argc == 1) { - Scope scope(e); + Scope scope(v4); ScopedValue arg(scope, argv[0]); if (DateObject *d = arg->as<DateObject>()) { t = d->date(); @@ -772,7 +772,7 @@ ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, con arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT); if (String *s = arg->stringValue()) - t = ParseString(s->toQString(), e->localTZA); + t = ParseString(s->toQString(), v4->localTZA); else t = TimeClip(arg->toNumber()); } @@ -789,10 +789,16 @@ ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, con if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - t = TimeClip(UTC(t, e->localTZA)); + t = TimeClip(UTC(t, v4->localTZA)); } - return Encode(e->newDateObject(Value::fromDouble(t))); + ReturnedValue o = Encode(v4->newDateObject(Value::fromDouble(t))); + if (!newTarget) + return o; + Scope scope(v4); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue DateCtor::virtualCall(const FunctionObject *m, const Value *, const Value *, int) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 93cc55f8ad..dfe9d35194 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -276,7 +276,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngin return cg.generateCompilationUnit(); } -ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *engine = f->engine(); @@ -286,7 +286,14 @@ ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, co Function *vmf = compilationUnit->linkToEngine(engine); ExecutionContext *global = engine->scriptContext(); - return Encode(FunctionObject::createScriptFunction(global, vmf)); + ReturnedValue o = Encode(FunctionObject::createScriptFunction(global, vmf)); + + if (!newTarget) + return o; + Scope scope(engine); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } // 15.3.1: This is equivalent to new Function(...) diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index da87127e08..566db6fd4e 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -54,7 +54,7 @@ void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction")); } -ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *engine = f->engine(); @@ -64,7 +64,14 @@ ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObje Function *vmf = compilationUnit->linkToEngine(engine); ExecutionContext *global = engine->scriptContext(); - return Encode(GeneratorFunction::create(global, vmf)); + ReturnedValue o = Encode(GeneratorFunction::create(global, vmf)); + + if (!newTarget) + return o; + Scope scope(engine); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } // 15.3.1: This is equivalent to new Function(...) diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp index 7d53b36fcd..68741e7677 100644 --- a/src/qml/jsruntime/qv4mapobject.cpp +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -59,11 +59,14 @@ void Heap::MapCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Map")); } -ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakMap) +ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool weakMap) { Scope scope(f); Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>()); - if (weakMap) { + bool protoSet = false; + if (newTarget) + protoSet = a->setProtoFromNewTarget(newTarget); + if (!protoSet && weakMap) { a->setPrototypeOf(scope.engine->weakMapPrototype()); scope.engine->memoryManager->registerWeakMap(a->d()); } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 11ec53ced5..d26e888069 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -78,10 +78,18 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Number")); } -ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { + auto v4 = f->engine(); double dbl = argc ? argv[0].toNumber() : 0.; - return Encode(f->engine()->newNumberObject(dbl)); + + ReturnedValue o = Encode(f->engine()->newNumberObject(dbl)); + if (!newTarget) + return o; + Scope scope(v4); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue NumberCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index a7ede4627c..3d2d54f651 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -986,6 +986,21 @@ const FunctionObject *Object::speciesConstructor(Scope &scope, const FunctionObj return static_cast<const FunctionObject *>(f); } +bool Object::setProtoFromNewTarget(const Value *newTarget) +{ + if (!newTarget || newTarget->isUndefined()) + return false; + + Q_ASSERT(newTarget->isFunctionObject()); + Scope scope(this); + ScopedObject proto(scope, static_cast<const FunctionObject *>(newTarget)->protoProperty()); + if (proto) { + setPrototypeOf(proto); + return true; + } + return false; +} + DEFINE_OBJECT_VTABLE(ArrayObject); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 6753ebfcd4..ff47810994 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -373,6 +373,8 @@ public: bool isArray() const; const FunctionObject *speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const; + bool setProtoFromNewTarget(const Value *newTarget); + protected: 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); diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp index a955e5eb6a..8450655334 100644 --- a/src/qml/jsruntime/qv4promiseobject.cpp +++ b/src/qml/jsruntime/qv4promiseobject.cpp @@ -364,7 +364,7 @@ void Heap::RejectWrapper::init() } -ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { Scope scope(f); @@ -396,6 +396,9 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con a->d()->resolution.set(scope.engine, Value::fromReturnedValue(scope.engine->catchException())); } + if (newTarget) + a->setProtoFromNewTarget(newTarget); + return a->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 4ef4fa2c9e..9df286065d 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -332,7 +332,13 @@ ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, con return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); } - return Encode(scope.engine->newRegExpObject(regexp)); + ReturnedValue o = Encode(scope.engine->newRegExpObject(regexp)); + + if (!newTarget) + return o; + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp index 3c9b5031d1..088ecbe30d 100644 --- a/src/qml/jsruntime/qv4setobject.cpp +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -59,11 +59,14 @@ void Heap::SetCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Set")); } -ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool isWeak) +ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool isWeak) { Scope scope(f); Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>()); - if (isWeak) + bool protoSet = false; + if (newTarget) + protoSet = a->setProtoFromNewTarget(newTarget); + if (!protoSet && isWeak) a->setPrototypeOf(scope.engine->weakSetPrototype()); a->d()->isWeakSet = isWeak; diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 03f351b9e4..d0f6aff9d9 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -170,7 +170,7 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("String")); } -ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = static_cast<const Object *>(f)->engine(); Scope scope(v4); @@ -180,7 +180,13 @@ ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, cons else value = v4->newString(); CHECK_EXCEPTION(); - return Encode(v4->newStringObject(value)); + ReturnedValue o = Encode(v4->newStringObject(value)); + + if (!newTarget) + return o; + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue StringCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc) |