diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-08-30 13:16:28 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-08-31 06:08:48 +0000 |
commit | ac3a9fd8f352b1d717334d16111d22d6c155e4e2 (patch) | |
tree | 17c12dbc09143103d060da062a5d9102d7c50f57 /src | |
parent | d457b919885fdbcc07f3414de64b0b50ebe8b263 (diff) |
Fix super property access
Super properties work in a rather special way by
accessing a 'home object' on the function object,
and reading from it's prototype.
Change-Id: I666334c9c27048c6c2ba6770dd8c9f56aecbee14
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject.cpp | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject_p.h | 28 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4generatorobject.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4generatorobject_p.h | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4globalobject.cpp | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 64 |
7 files changed, 80 insertions, 43 deletions
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index d542ce752f..029dd7786b 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -81,6 +81,7 @@ struct Q_QML_EXPORT Function { uint nFormals; int interpreterCallCount = 0; bool hasQmlDependencies; + bool isEval = false; Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); ~Function(); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index f7bea9a5f0..fe1ebbd556 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -175,7 +175,7 @@ Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *sco return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function); } -Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor) +Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor) { if (!function) { Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope); @@ -183,13 +183,16 @@ Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext return c; } Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function); + c->homeObject.set(scope->engine(), homeObject->d()); c->isDerivedConstructor = isDerivedConstructor; return c; } -Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function, QV4::String *name) +Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, QV4::String *name) { - return scope->engine()->memoryManager->allocate<MemberFunction>(scope, function, name); + Heap::MemberFunction *m = scope->engine()->memoryManager->allocate<MemberFunction>(scope, function, name); + m->homeObject.set(scope->engine(), homeObject->d()); + return m; } Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 54964b9bbd..766960d2ac 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -122,13 +122,16 @@ DECLARE_HEAP_OBJECT(ScriptFunction, FunctionObject) { void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr); }; -struct ConstructorFunction : ScriptFunction -{ - bool isDerivedConstructor; +#define MemberFunctionMembers(class, Member) \ + Member(class, Pointer, Object *, homeObject) + +DECLARE_HEAP_OBJECT(MemberFunction, ScriptFunction) { + DECLARE_MARKOBJECTS(MemberFunction) }; -struct MemberFunction : ScriptFunction +struct ConstructorFunction : MemberFunction { + bool isDerivedConstructor; }; struct DefaultClassConstructorFunction : FunctionObject @@ -187,8 +190,8 @@ struct Q_QML_EXPORT FunctionObject: Object { static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); - static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor); - static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function, String *name); + static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor); + static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, String *name); static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount); bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } @@ -261,18 +264,17 @@ struct ScriptFunction : FunctionObject { Heap::InternalClass *classForConstructor() const; }; -struct ConstructorFunction : ScriptFunction { - V4_OBJECT2(ConstructorFunction, ScriptFunction) - V4_INTERNALCLASS(ConstructorFunction) - static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); -}; - struct MemberFunction : ScriptFunction { V4_OBJECT2(MemberFunction, ScriptFunction) V4_INTERNALCLASS(MemberFunction) }; +struct ConstructorFunction : MemberFunction { + V4_OBJECT2(ConstructorFunction, MemberFunction) + V4_INTERNALCLASS(ConstructorFunction) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; struct DefaultClassConstructorFunction : FunctionObject { V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject) diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index bab0cfbfb1..82d8d88882 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -230,13 +230,19 @@ ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) DEFINE_OBJECT_VTABLE(MemberGeneratorFunction); -Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, Function *function, String *name) +Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, Function *function, Object *homeObject, String *name) { Scope scope(context); - Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<MemberGeneratorFunction>(context, function, name)); + Scoped<MemberGeneratorFunction> g(scope, context->engine()->memoryManager->allocate<MemberGeneratorFunction>(context, function, name)); + g->d()->homeObject.set(scope.engine, homeObject->d()); ScopedObject proto(scope, scope.engine->newObject()); proto->setPrototypeOf(scope.engine->generatorPrototype()); g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable); g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); return g->d(); } + +ReturnedValue MemberGeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + return GeneratorFunction::virtualCall(f, thisObject, argv, argc); +} diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index f00f730344..55e6091ad5 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -75,7 +75,7 @@ struct GeneratorFunctionCtor : FunctionObject { struct GeneratorFunction : ScriptFunction { }; -struct MemberGeneratorFunction : ScriptFunction { +struct MemberGeneratorFunction : MemberFunction { }; struct GeneratorPrototype : FunctionObject { @@ -113,12 +113,14 @@ struct GeneratorFunction : ScriptFunction static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; -struct MemberGeneratorFunction : GeneratorFunction +struct MemberGeneratorFunction : MemberFunction { - V4_OBJECT2(MemberGeneratorFunction, GeneratorFunction) + V4_OBJECT2(MemberGeneratorFunction, MemberFunction) V4_INTERNALCLASS(MemberGeneratorFunction) - static Heap::FunctionObject *create(ExecutionContext *scope, Function *function, String *name); + static Heap::FunctionObject *create(ExecutionContext *scope, Function *function, Object *homeObject, String *name); + static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct GeneratorPrototype : Object diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 43895011f3..1f747b62d7 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -371,6 +371,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, Function *function = script.function(); if (!function) return Encode::undefined(); + function->isEval = true; if (function->isStrict() || isStrict) { ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function)); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index fbddcb6ee1..1a201a5d37 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -875,38 +875,60 @@ ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name); } +static Object *getSuperBase(Scope &scope) +{ + ScopedFunctionObject f(scope, scope.engine->currentStackFrame->jsFrame->function); + MemberFunction *m = f->as<MemberFunction>(); + if (!m) { + ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context)); + Q_ASSERT(ctx); + while (ctx) { + if (CallContext *c = ctx->asCallContext()) { + f = c->d()->function; + QV4::Function *fn = f->function(); + if (fn && !fn->isArrowFunction() && !fn->isEval) + break; + } + ctx = ctx->d()->outer; + } + m = f->as<MemberFunction>(); + } + if (!m) { + scope.engine->throwTypeError(); + return nullptr; + } + ScopedObject homeObject(scope, m->d()->homeObject); + Q_ASSERT(homeObject); + ScopedObject proto(scope, homeObject->getPrototypeOf()); + if (!proto) { + scope.engine->throwTypeError(); + return nullptr; + } + return proto; +} + ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property) { Scope scope(engine); - ScopedObject base(scope, engine->currentStackFrame->thisObject()); + Object *base = getSuperBase(scope); if (!base) - return engine->throwTypeError(); - ScopedObject proto(scope, base->getPrototypeOf()); - if (!proto) - return engine->throwTypeError(); + return Encode::undefined(); ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); - return proto->get(key, base); + return base->get(key, &engine->currentStackFrame->jsFrame->thisObject); } void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value) { Scope scope(engine); - ScopedObject base(scope, engine->currentStackFrame->thisObject()); - if (!base) { - engine->throwTypeError(); - return; - } - ScopedObject proto(scope, base->getPrototypeOf()); - if (!proto) { - engine->throwTypeError(); + Object *base = getSuperBase(scope); + if (!base) return; - } ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return; - bool result = proto->put(key, value, base); + bool result = base->put(key, value, &engine->currentStackFrame->jsFrame->thisObject); if (!result && engine->currentStackFrame->v4Function->isStrict()) engine->throwTypeError(); } @@ -1554,9 +1576,9 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); if (clos->isGenerator()) - value = MemberGeneratorFunction::create(current, clos, fnName)->asReturnedValue(); + value = MemberGeneratorFunction::create(current, clos, o, fnName)->asReturnedValue(); else - value = FunctionObject::createMemberFunction(current, clos, fnName)->asReturnedValue(); + value = FunctionObject::createMemberFunction(current, clos, o, fnName)->asReturnedValue(); } else if (args[2].isFunctionObject()) { fn = static_cast<const FunctionObject &>(args[2]); @@ -1612,7 +1634,7 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde ScopedFunctionObject constructor(scope); QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr; - constructor = FunctionObject::createConstructorFunction(current, f, !superClass.isEmpty())->asReturnedValue(); + constructor = FunctionObject::createConstructorFunction(current, f, proto, !superClass.isEmpty())->asReturnedValue(); constructor->setPrototypeUnchecked(constructorParent); Value argCount = Primitive::fromInt32(f ? f->nFormals : 0); constructor->defineReadonlyConfigurableProperty(scope.engine->id_length(), argCount); @@ -1655,9 +1677,9 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde name = propertyName->asFunctionName(engine, prefix); if (f->isGenerator()) - function = MemberGeneratorFunction::create(current, f, name); + function = MemberGeneratorFunction::create(current, f, receiver, name); else - function = FunctionObject::createMemberFunction(current, f, name); + function = FunctionObject::createMemberFunction(current, f, receiver, name); Q_ASSERT(function); PropertyAttributes attributes; switch (methods[i].type) { |