diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2021-02-08 15:42:59 +0100 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2021-02-12 12:00:09 +0100 |
commit | f3281ca869420df83d618c255aa7d62e63a102d5 (patch) | |
tree | 4c990b4253313c3826dfb840cf427e8efa15b341 | |
parent | 9c282fe2e90eec05e160241069c219c7f09f4078 (diff) |
Support runtime functions evaluation by index through QQmlEngine
Add execution function that can evaluate runtime functions available
in the compilation unit. Private API for now as it's unclear what would
be a comprehensive solution to support all existing use cases
Task-number: QTBUG-84368
Task-number: QTBUG-91039
Change-Id: Icf755b53484587d7983eaae4821c1aa0111d5c05
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 28 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 24 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine_p.h | 3 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/data/runtimeFunctions.qml | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 35 |
6 files changed, 99 insertions, 0 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 7ae281fb1a..ee35ad2586 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -93,6 +93,7 @@ #include "qv4stackframe_p.h" #include "qv4atomics_p.h" #include "qv4urlobject_p.h" +#include "qv4jscall_p.h" #if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" @@ -124,6 +125,8 @@ #include <qmetatype.h> #include <qsequentialiterable.h> +#include <private/qqmlengine_p.h> + #if USE(PTHREADS) # include <pthread.h> #if !defined(Q_OS_INTEGRITY) @@ -2051,6 +2054,31 @@ bool ExecutionEngine::diskCacheEnabled() const return (!disableDiskCache() && !debugger()) || forceDiskCache(); } +ReturnedValue ExecutionEngine::callInContext(Function *function, QObject *self, + QQmlRefPointer<QQmlContextData> ctxtdata, void **args, + int *types) +{ + QV4::Scope scope(this); + ExecutionContext *ctx = currentStackFrame ? currentContext() : scriptContext(); + QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(ctx, ctxtdata, self)); + QV4::ScopedValue selfValue(scope, QV4::QObjectWrapper::wrap(this, self)); + + if (!args) + return function->call(selfValue, nullptr, 0, qmlContext); + + if (!types) // both args and types must be present + return Encode::undefined(); + + // use JSCallData to pass arguments into the function call + QV4::JSCallData jsCall(scope, types[0]); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_qmlEngine); + QV4::populateJSCallArguments(ep, this, jsCall, args, types); + + QV4::CallData *callData = jsCall->callData(); + return function->call(selfValue, callData->argValues<QV4::Value>(), callData->argc(), + qmlContext); +} + void ExecutionEngine::initQmlGlobalObject() { initializeGlobal(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 6d02ffd54b..4529d09c2d 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -741,6 +741,9 @@ public: bool diskCacheEnabled() const; + ReturnedValue callInContext(Function *function, QObject *self, + QQmlRefPointer<QQmlContextData> ctxtdata, void **args, int *types); + private: #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index aa30c08c19..5e108f6914 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2312,6 +2312,30 @@ bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const return typeLoader.isScriptLoaded(url); } +QJSValue QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex, + QObject *thisObject, void **args, int *types) +{ + Q_Q(QQmlEngine); + if (const auto unit = typeLoader.getType(url)->compilationUnit()) { + Q_ASSERT(functionIndex >= 0); + Q_ASSERT(thisObject); + + if (!unit->engine) + unit->linkToEngine(q->handle()); + + if (unit->runtimeFunctions.length() <= functionIndex) + return QJSValue(); + + QQmlContext *ctx = q->contextForObject(thisObject); + if (!ctx) + ctx = q->rootContext(); + return QJSValuePrivate::fromReturnedValue( + q->handle()->callInContext(unit->runtimeFunctions[functionIndex], thisObject, + QQmlContextData::get(ctx), args, types)); + } + return QJSValue(); +} + #if defined(Q_OS_WIN) // Normalize a file name using Shell API. As opposed to converting it // to a short 8.3 name and back, this also works for drives where 8.3 notation diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index b7073ffcfd..4f1c5f26dd 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -300,6 +300,9 @@ public: return nullptr; } + QJSValue executeRuntimeFunction(const QUrl &url, qsizetype functionIndex, QObject *thisObject, + void **args = nullptr, int *types = nullptr); + private: class SingletonInstances : private QHash<QQmlType, QJSValue> { diff --git a/tests/auto/qml/qqmlengine/data/runtimeFunctions.qml b/tests/auto/qml/qqmlengine/data/runtimeFunctions.qml new file mode 100644 index 0000000000..c14c13b540 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/runtimeFunctions.qml @@ -0,0 +1,6 @@ +import QtQml 2.15 +QtObject { + function getConstantValue() { return 42; } + function squareValue(x) { return x * x; } + function concatenate(str1, str2) { return str1 + str2; } +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 1abec1fa56..4e4292bac7 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -85,6 +85,7 @@ private slots: void cachedGetterLookup_qtbug_75335(); void createComponentOnSingletonDestruction(); void uiLanguage(); + void executeRuntimeFunction(); public slots: QObject *createAQObjectForOwnershipTest () @@ -1234,6 +1235,40 @@ void tst_qqmlengine::uiLanguage() } } +void tst_qqmlengine::executeRuntimeFunction() +{ + QQmlEngine engine; + QQmlEnginePrivate *priv = QQmlEnginePrivate::get(std::addressof(engine)); + + const QUrl url = testFileUrl("runtimeFunctions.qml"); + QQmlComponent component(&engine, url); + QScopedPointer<QObject> dummy(component.create()); + + // getConstantValue(): + const int constant = qjsvalue_cast<int>(priv->executeRuntimeFunction(url, 0, dummy.get())); + QCOMPARE(constant, 42); + + // squareValue(): + int x = 5; + void *a0[] = { nullptr, const_cast<void *>(reinterpret_cast<const void *>(std::addressof(x))) }; + int t0[] = { 1, QMetaTypeId2<int>::qt_metatype_id() }; + const int squared = + qjsvalue_cast<int>(priv->executeRuntimeFunction(url, 1, dummy.get(), a0, t0)); + QCOMPARE(squared, x * x); + + // concatenate(): + QString str1 = QStringLiteral("Hello"); // uses "raw data" storage + QString str2 = QLatin1String(", Qml"); // uses own QString storage + void *a1[] = { nullptr, + const_cast<void *>(reinterpret_cast<const void *>(std::addressof(str1))), + const_cast<void *>(reinterpret_cast<const void *>(std::addressof(str2))) }; + int t1[] = { 2, QMetaTypeId2<QString>::qt_metatype_id(), + QMetaTypeId2<QString>::qt_metatype_id() }; + QString concatenated = + qjsvalue_cast<QString>(priv->executeRuntimeFunction(url, 2, dummy.get(), a1, t1)); + QCOMPARE(concatenated, str1 + str2); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" |