diff options
-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" |