diff options
author | Erik Verbruggen <erik.verbruggen@qt.io> | 2018-02-15 15:16:01 +0100 |
---|---|---|
committer | Erik Verbruggen <erik.verbruggen@qt.io> | 2018-02-20 08:44:07 +0000 |
commit | 2f784a544ec4dccaa70ee1bcec71c8e6c2bd5d99 (patch) | |
tree | febaa003be903df7dfc9af2c8c3a442a60ba2557 | |
parent | b8dbe476f01a38afc921f9693d89f3c72396651a (diff) |
Correctly set this object when calling scope/context functions
When a function is called that is in a QML scope or a QML context, set
the 'this' object to the QML scope. This is done by introducing two new
interpreter instructions, which get the context passed in.
Note: this patch is 5.11 specific. 5.9 had a similair issue, but the
implementation is quite different, so that was fixed separately.
Task-number: QTBUG-66432
Change-Id: Ie43150cdd26360025895df28d31264985abf1c15
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 18 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 8 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 4 | ||||
-rw-r--r-- | src/qml/jit/qv4jit.cpp | 32 | ||||
-rw-r--r-- | src/qml/jit/qv4jit_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 31 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 12 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml | 10 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 15 |
10 files changed, 133 insertions, 1 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index b2808784a0..f236683506 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1263,6 +1263,8 @@ bool Codegen::visit(CallExpression *ast) switch (base.type) { case Reference::Member: case Reference::Subscript: + case Reference::QmlScopeObject: + case Reference::QmlContextObject: base = base.asLValue(); break; case Reference::Name: @@ -1277,7 +1279,21 @@ bool Codegen::visit(CallExpression *ast) return false; //### Do we really need all these call instructions? can's we load the callee in a temp? - if (base.type == Reference::Member) { + if (base.type == Reference::QmlScopeObject) { + Instruction::CallScopeObjectProperty call; + call.base = base.qmlBase.stackSlot(); + call.name = base.qmlCoreIndex; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else if (base.type == Reference::QmlContextObject) { + Instruction::CallContextObjectProperty call; + call.base = base.qmlBase.stackSlot(); + call.name = base.qmlCoreIndex; + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else if (base.type == Reference::Member) { if (useFastLookups) { Instruction::CallPropertyLookup call; call.base = base.propertyBase.stackSlot(); diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 3a81aca7f6..34953d52ce 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -369,6 +369,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallGlobalLookup) + MOTH_BEGIN_INSTR(CallScopeObjectProperty) + d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallScopeObjectProperty) + + MOTH_BEGIN_INSTR(CallContextObjectProperty) + d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallContextObjectProperty) + MOTH_BEGIN_INSTR(SetExceptionHandler) if (offset) d << ABSOLUTE_OFFSET(); diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 4eeeed1fbc..2d1428bd19 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -105,6 +105,8 @@ QT_BEGIN_NAMESPACE #define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) #define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) #define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv) +#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv) #define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset) #define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0) #define INSTR_GetException(op) INSTRUCTION(op, GetException, 0) @@ -221,6 +223,8 @@ QT_BEGIN_NAMESPACE F(CallName) \ F(CallPossiblyDirectEval) \ F(CallGlobalLookup) \ + F(CallScopeObjectProperty) \ + F(CallContextObjectProperty) \ F(SetExceptionHandler) \ F(ThrowException) \ F(GetException) \ diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp index 1ef6cc4add..5dc98a591a 100644 --- a/src/qml/jit/qv4jit.cpp +++ b/src/qml/jit/qv4jit.cpp @@ -548,6 +548,32 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) as->checkException(); } +void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passInt32AsArg(propIdx, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlScopeObjectProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passInt32AsArg(propIdx, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlContextObjectProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + void BaselineJIT::generate_SetExceptionHandler(int offset) { if (offset) @@ -1087,6 +1113,12 @@ void BaselineJIT::collectLabelsInBytecode() MOTH_BEGIN_INSTR(CallGlobalLookup) MOTH_END_INSTR(CallGlobalLookup) + MOTH_BEGIN_INSTR(CallScopeObjectProperty) + MOTH_END_INSTR(CallScopeObjectProperty) + + MOTH_BEGIN_INSTR(CallContextObjectProperty) + MOTH_END_INSTR(CallContextObjectProperty) + MOTH_BEGIN_INSTR(SetExceptionHandler) addLabel(code - start + offset); MOTH_END_INSTR(SetExceptionHandler) diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4jit_p.h index c7ec1700c3..077c2e2177 100644 --- a/src/qml/jit/qv4jit_p.h +++ b/src/qml/jit/qv4jit_p.h @@ -168,6 +168,8 @@ public: void generate_CallName(int name, int argc, int argv) Q_DECL_OVERRIDE; void generate_CallPossiblyDirectEval(int argc, int argv) Q_DECL_OVERRIDE; void generate_CallGlobalLookup(int index, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) Q_DECL_OVERRIDE; void generate_SetExceptionHandler(int offset) Q_DECL_OVERRIDE; void generate_ThrowException() Q_DECL_OVERRIDE; void generate_GetException() Q_DECL_OVERRIDE; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index e7a104af66..eb4f6a21fc 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1098,6 +1098,37 @@ ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &fu return static_cast<const FunctionObject &>(func).call(nullptr, argv, argc); } +ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, Value *base, + int propertyIndex, Value *argv, int argc) +{ + Scope scope(engine); + ScopedFunctionObject fo(scope, method_loadQmlScopeObjectProperty(engine, *base, propertyIndex, + /*captureRequired*/true)); + if (!fo) { + QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex); + return engine->throwTypeError(error); + } + + QObject *qmlScopeObj = static_cast<QmlContext *>(base)->d()->qml()->scopeObject; + ScopedValue qmlScopeValue(scope, QObjectWrapper::wrap(engine, qmlScopeObj)); + return fo->call(qmlScopeValue, argv, argc); +} + +ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, Value *base, + int propertyIndex, Value *argv, int argc) +{ + Scope scope(engine); + ScopedFunctionObject fo(scope, method_loadQmlContextObjectProperty(engine, *base, propertyIndex, + /*captureRequired*/true)); + if (!fo) { + QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex); + return engine->throwTypeError(error); + } + + QObject *qmlContextObj = static_cast<QmlContext *>(base)->d()->qml()->context->contextData()->contextObject; + ScopedValue qmlContextValue(scope, QObjectWrapper::wrap(engine, qmlContextObj)); + return fo->call(qmlContextValue, argv, argc); +} ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc) { diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index ea31dfd08b..2956a4a463 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -188,6 +188,8 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(ReturnedValue, loadQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ F(ReturnedValue, loadQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ F(ReturnedValue, loadQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ + F(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, Value *base, int propertyIndex, Value *argv, int argc)) \ + F(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, Value *base, int propertyIndex, Value *argv, int argc)) \ \ F(void, storeQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ F(void, storeQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index e248d590f7..d44c219d18 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -840,6 +840,18 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, CHECK_EXCEPTION; MOTH_END_INSTR(CallGlobalLookup) + MOTH_BEGIN_INSTR(CallScopeObjectProperty) + STORE_IP(); + acc = Runtime::method_callQmlScopeObjectProperty(engine, stack + base, name, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallScopeObjectProperty) + + MOTH_BEGIN_INSTR(CallContextObjectProperty) + STORE_IP(); + acc = Runtime::method_callQmlContextObjectProperty(engine, stack + base, name, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallContextObjectProperty) + MOTH_BEGIN_INSTR(SetExceptionHandler) exceptionHandler = offset ? code + offset : nullptr; MOTH_END_INSTR(SetExceptionHandler) diff --git a/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml b/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml new file mode 100644 index 0000000000..e3c99e70e1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml @@ -0,0 +1,10 @@ +import QtQml 2.2 +QtObject { + property int x: 42 + property int y: 0 + function g(){ + y = this.x; + } + property var f: g + Component.onCompleted: f() +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 03d53b755d..195a9687a8 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -281,6 +281,8 @@ private slots: void lowercaseTypeNames(); + void thisInQmlScope(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -4911,6 +4913,19 @@ void tst_qqmllanguage::lowercaseTypeNames() QCOMPARE(qmlRegisterSingletonType<QObject>("Test", 1, 0, "lowerCaseTypeName", nullptr), -1); } +void tst_qqmllanguage::thisInQmlScope() +{ + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("thisInQmlScope.qml")); + QTRY_VERIFY(component.isReady()); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("x"), QVariant(42)); + QCOMPARE(o->property("y"), QVariant(42)); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" |