aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlengine.cpp
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2022-04-08 15:49:24 +0200
committerAndrei Golubev <andrei.golubev@qt.io>2022-04-27 15:29:24 +0200
commitb4d5b64359cc0e908fb68d4490df076475c899c3 (patch)
tree15045e1a90b00aef24488b466313b2d5b39ba5d6 /src/qml/qml/qqmlengine.cpp
parent52aaf0a4e78a197b1c12189db7d7bf5681946df5 (diff)
Make QQmlEngine resolve closures when executing runtime functions
QML can create a function which holds a closure in the cases like: onSignal: function() { ... } If the left-hand side is a signal handler (or similar), we want to execute the *inner* function when a signal is called, not the outer one. However, under certain conditions (e.g. we use `this`), the outer function must also be called beforehand to correctly setup the calling scope for the inner function Thus, make the QQmlEnginePrivate::executeRuntimeFunction() do that: always call an outer function first and then the inner one if present. This creates an overhead when dealing with certain signal handlers but we could optimize it later if needed Note that the case `property var prop: function() { return 42; }` where a property contains a callable function is no longer supported by the executeRuntimeFunction() routine (we always call the inner code now). This is fine since qmltc (the main beneficiary of the routine) does not rely on this functionality when dealing with property bindings Given the change, qmltc can be simplified to only work with absolute function indices, ignoring the nesting problem altogether Change-Id: I61f61587b6fe700cb695b3b7a213d9cfab0eb746 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/qml/qqmlengine.cpp')
-rw-r--r--src/qml/qml/qqmlengine.cpp39
1 files changed, 36 insertions, 3 deletions
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index dac2a5225e..3b3213e0d9 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -1807,9 +1807,42 @@ void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationU
QQmlData *ddata = QQmlData::get(thisObject);
Q_ASSERT(ddata && ddata->outerContext);
- // implicitly sets the return value, if it is present
- v4engine()->callInContext(unit->runtimeFunctions[functionIndex], thisObject,
- ddata->outerContext, argc, args, types);
+ QV4::Function *function = unit->runtimeFunctions[functionIndex];
+ Q_ASSERT(function);
+ Q_ASSERT(function->compiledFunction);
+
+ QV4::ExecutionEngine *v4 = v4engine();
+
+ // NB: always use scriptContext() by default as this method ignores whether
+ // there's already a stack frame (except when dealing with closures). the
+ // method is called from C++ (through QQmlEngine::executeRuntimeFunction())
+ // and thus the caller must ensure correct setup
+ QV4::Scope scope(v4);
+ QV4::ExecutionContext *ctx = v4->scriptContext();
+ QV4::Scoped<QV4::ExecutionContext> callContext(scope,
+ QV4::QmlContext::create(ctx, ddata->outerContext, thisObject));
+
+ if (auto nested = function->nestedFunction()) {
+ // if a nested function is already known, call the closure directly
+ function = nested;
+ } else if (function->isClosureWrapper()) {
+ // if there is a nested function, but we don't know it, we need to call
+ // an outer function first and then the inner function. we fetch the
+ // return value of a function call (that is a closure) by calling a
+ // different version of ExecutionEngine::callInContext() that returns a
+ // QV4::ReturnedValue with no arguments since they are not needed by the
+ // outer function anyhow
+ QV4::ScopedFunctionObject result(scope,
+ v4->callInContext(function, thisObject, callContext, 0, nullptr));
+ Q_ASSERT(result->function());
+ Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
+
+ // overwrite the function and its context
+ function = result->function();
+ callContext = QV4::Scoped<QV4::ExecutionContext>(scope, result->scope());
+ }
+
+ v4->callInContext(function, thisObject, callContext, argc, args, types);
}
QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url)