summaryrefslogtreecommitdiff
path: root/src/qml/jsruntime/qv4function.cpp
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-09-23 16:43:58 +0200
committerUlf Hermann <ulf.hermann@qt.io>2022-10-14 16:36:36 +0200
commitde2d7cba76bcfbe0a29271df1178c176d00bf9b4 (patch)
tree5d37fcfad0636975f8ed4292f07140fdecdd19f2 /src/qml/jsruntime/qv4function.cpp
parentd45925b0fdbd8055b60afbf87324325465d89568 (diff)
downloadqtdeclarative-de2d7cba76bcfbe0a29271df1178c176d00bf9b4.tar.gz
Add option to enforce function signatures
By default, the QML engine does not enforce signatures given as type annotations to functions. By passing different types than the function declares, you can get different behavior between the interpreter/JIT and the AOT-compiled code. In addition, in interpreted or JIT'ed mode, we pass all non-primitive value types as references. This means, if you modify them within the called function, the modifications are propagated back to the place where the value was loaded from. Enforcing the signature prevents all of this, at a run time cost. Since we have to coerce all arguments to the desired types, the function call overhead grows. This change introduces a pragma "FunctionSignatureBehavior" which you can set to "Ignored" or "Enforced" to choose one way or the other as universal way of handling type annotations. Fixes: QTBUG-106819 Change-Id: I50e9b2bd6702907da44974cd9e05b48a96bb609e Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4function.cpp')
-rw-r--r--src/qml/jsruntime/qv4function.cpp105
1 files changed, 91 insertions, 14 deletions
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index e03c85d382..34b548113d 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -14,10 +14,11 @@
#include <private/qv4vme_moth_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qv4jscall_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
QT_BEGIN_NAMESPACE
-using namespace QV4;
+namespace QV4 {
bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int argc,
ExecutionContext *context)
@@ -39,20 +40,13 @@ bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int a
return !frame.isReturnValueUndefined();
}
-ReturnedValue Function::call(
- const Value *thisObject, const Value *argv, int argc, ExecutionContext *context) {
- if (kind == AotCompiled) {
- return QV4::convertAndCall(
- context->engine(), typedFunction, thisObject, argv, argc,
- [this, context](QObject *thisObject,
- void **a, const QMetaType *types, int argc) {
- call(thisObject, a, types, argc, context);
- });
- }
-
+static ReturnedValue doCall(
+ Function *self, const Value *thisObject, const Value *argv, int argc,
+ ExecutionContext *context)
+{
ExecutionEngine *engine = context->engine();
JSTypesStackFrame frame;
- frame.init(this, argv, argc);
+ frame.init(self, argv, argc);
frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(),
thisObject ? *thisObject : Value::undefinedValue());
engine->jsStackTop += frame.requiredJSStackFrameSize();
@@ -62,6 +56,29 @@ ReturnedValue Function::call(
return result;
}
+ReturnedValue Function::call(
+ const Value *thisObject, const Value *argv, int argc, ExecutionContext *context) {
+ switch (kind) {
+ case AotCompiled:
+ return QV4::convertAndCall(
+ context->engine(), typedFunction, thisObject, argv, argc,
+ [this, context](
+ QObject *thisObject, void **a, const QMetaType *types, int argc) {
+ call(thisObject, a, types, argc, context);
+ });
+ case JsTyped:
+ return QV4::coerceAndCall(
+ context->engine(), typedFunction, thisObject, argv, argc,
+ [this, context](const Value *thisObject, const Value *argv, int argc) {
+ return doCall(this, thisObject, argv, argc, context);
+ });
+ default:
+ break;
+ }
+
+ return doCall(this, thisObject, argv, argc, context);
+}
+
Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
const CompiledData::Function *function,
const QQmlPrivate::TypedFunction *aotFunction)
@@ -94,11 +111,67 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable();
- for (quint32 i = 0; i < compiledFunction->nFormals; ++i)
+ const bool enforcesSignature = !aotFunction && unit->enforcesFunctionSignature();
+ bool hasTypes = false;
+ for (quint32 i = 0; i < compiledFunction->nFormals; ++i) {
ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable);
+ if (enforcesSignature
+ && !hasTypes
+ && formalsIndices[i].type.typeNameIndexOrBuiltinType()
+ != quint32(QV4::CompiledData::BuiltinType::InvalidBuiltin)) {
+ hasTypes = true;
+ }
+ }
internalClass = ic->d();
nFormals = compiledFunction->nFormals;
+
+ if (!enforcesSignature)
+ return;
+
+ if (!hasTypes
+ && compiledFunction->returnType.typeNameIndexOrBuiltinType()
+ == quint32(QV4::CompiledData::BuiltinType::InvalidBuiltin)) {
+ return;
+ }
+
+ QQmlPrivate::TypedFunction *synthesized = new QQmlPrivate::TypedFunction;
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine->qmlEngine());
+
+ auto findMetaType = [&](const CompiledData::ParameterType &param) {
+ if (param.indexIsBuiltinType()) {
+ return QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(
+ QV4::CompiledData::BuiltinType(param.typeNameIndexOrBuiltinType()));
+ }
+
+ const quint32 type = param.typeNameIndexOrBuiltinType();
+ if (type == 0)
+ return QMetaType();
+
+ const QQmlType qmltype = unit->typeNameCache->query(unit->stringAt(type)).type;
+ if (!qmltype.isValid())
+ return QMetaType();
+
+ const QMetaType metaType = qmltype.typeId();
+ if (metaType.isValid())
+ return metaType;
+
+ if (!qmltype.isComposite()) {
+ return qmltype.isInlineComponentType()
+ ? unit->typeIdsForComponent(qmltype.inlineComponentId()).id
+ : QMetaType();
+ }
+
+ return enginePrivate->typeLoader.getType(
+ qmltype.sourceUrl())->compilationUnit()->typeIds.id;
+ };
+
+ for (quint16 i = 0; i < nFormals; ++i)
+ synthesized->argumentTypes.append(findMetaType(formalsIndices[i].type));
+
+ synthesized->returnType = findMetaType(compiledFunction->returnType);
+ typedFunction = synthesized;
+ kind = JsTyped;
}
Function::~Function()
@@ -107,6 +180,8 @@ Function::~Function()
destroyFunctionTable(this, codeRef);
delete codeRef;
}
+ if (kind == JsTyped)
+ delete typedFunction;
}
void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters)
@@ -173,4 +248,6 @@ QQmlSourceLocation Function::sourceLocation() const
sourceFile(), compiledFunction->location.line(), compiledFunction->location.column());
}
+} // namespace QV4
+
QT_END_NAMESPACE