diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2022-04-08 15:49:24 +0200 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2022-04-27 15:29:24 +0200 |
commit | b4d5b64359cc0e908fb68d4490df076475c899c3 (patch) | |
tree | 15045e1a90b00aef24488b466313b2d5b39ba5d6 /src/qml/qml/qqmlengine.cpp | |
parent | 52aaf0a4e78a197b1c12189db7d7bf5681946df5 (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.cpp | 39 |
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) |