aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4engine.cpp28
-rw-r--r--src/qml/jsruntime/qv4engine_p.h3
-rw-r--r--src/qml/qml/qqmlengine.cpp24
-rw-r--r--src/qml/qml/qqmlengine_p.h3
-rw-r--r--tests/auto/qml/qqmlengine/data/runtimeFunctions.qml6
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp35
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"