diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2024-04-16 17:03:01 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-04-26 12:18:15 +0000 |
commit | 8bf5aae19b77b618f3f7a55a59e87c8a319475a8 (patch) | |
tree | d331328f478ac13593524eaaeb3a874691ccadd2 /src/qml/jsruntime | |
parent | 23fc22e16022e355f2a1aff8705c09b807fbe024 (diff) |
QtQml: Properly enforce signatures of AOT-compiled functions
Pass the metatypes of the contained types rather than the stored types.
[ChangeLog][QtQml][Important Behavior Changes] The AOT compiled code for
type-annotated JavaScript functions does not let you pass or return
values of the wrong type anymore.
Fixes: QTBUG-119885
Change-Id: I685d398c0745d32a999a3abd76c622a2c0d6651f
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 74 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 27 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4jscall_p.h | 14 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 16 |
6 files changed, 73 insertions, 64 deletions
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 9e10e437a8..34d737cdae 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -157,7 +157,7 @@ void ExecutableCompilationUnit::populate() auto advanceAotFunction = [&](int i) -> const QQmlPrivate::AOTCompiledFunction * { if (aotFunction) { if (aotFunction->functionPtr) { - if (aotFunction->extraData == i) + if (aotFunction->functionIndex == i) return aotFunction++; } else { aotFunction = nullptr; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index caba2b1a9a..ae36b563e0 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -61,14 +61,14 @@ ReturnedValue Function::call( switch (kind) { case AotCompiled: return QV4::convertAndCall( - context->engine(), aotCompiledFunction, thisObject, argv, argc, + context->engine(), &aotCompiledFunction, 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(), jsTypedFunction, compiledFunction, argv, argc, + context->engine(), &jsTypedFunction, compiledFunction, argv, argc, [this, context, thisObject](const Value *argv, int argc) { return doCall(this, thisObject, argv, argc, context); }); @@ -109,10 +109,6 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, : FunctionData(engine, unit) , compiledFunction(function) , codeData(function->code()) - , jittedCode(nullptr) - , codeRef(nullptr) - , aotCompiledFunction(aotFunction) - , kind(aotFunction ? AotCompiled : JsUntyped) { Scope scope(engine); Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); @@ -123,7 +119,7 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable(); - bool enforceJsTypes = !aotFunction && !unit->ignoresFunctionSignature(); + bool enforceJsTypes = !unit->ignoresFunctionSignature(); for (quint32 i = 0; i < compiledFunction->nFormals; ++i) { ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable); @@ -134,14 +130,24 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, nFormals = compiledFunction->nFormals; + if (!enforceJsTypes) + return; + + if (aotFunction) { + aotCompiledCode = aotFunction->functionPtr; + new (&aotCompiledFunction) AOTCompiledFunction; + kind = AotCompiled; + aotCompiledFunction.types.resize(aotFunction->numArguments + 1); + aotFunction->signature(unit, aotCompiledFunction.types.data()); + return; + } + // If a function has any typed arguments, but an untyped return value, the return value is void. // If it doesn't have any arguments at all and the return value is untyped, the function is // untyped. Users can specifically set the return type to "void" to have it enforced. - if (!enforceJsTypes || (nFormals == 0 && !isSpecificType(compiledFunction->returnType))) + if (nFormals == 0 && !isSpecificType(compiledFunction->returnType)) return; - JSTypedFunction *synthesized = new JSTypedFunction; - QQmlTypeLoader *typeLoader = engine->typeLoader(); auto findQmlType = [&](const CompiledData::ParameterType ¶m) { @@ -151,37 +157,21 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, QV4::CompiledData::CommonType(type))); } - if (type == 0) + if (type == 0 || !typeLoader) return QQmlType(); - const auto base = unit->baseCompilationUnit(); - const QQmlType qmltype = typeLoader - ? base->typeNameCache->query<QQmlImport::AllowRecursion>( - base->stringAt(type), typeLoader).type - : QQmlType(); - - if (!qmltype.isValid() || qmltype.isComposite()) - return qmltype; - - if (qmltype.isInlineComponentType()) { - Q_ASSERT(qmltype.typeId().isValid()); - - // If it seems to be an IC type, make sure there is an actual - // compilation unit for it. We create inline component types speculatively. - return QQmlMetaType::obtainCompilationUnit(qmltype.typeId()) - ? qmltype - : QQmlType(); - } - + const auto &base = unit->baseCompilationUnit(); + const QQmlType qmltype = QQmlTypePrivate::compositeQmlType( + base, typeLoader, base->stringAt(type)); return qmltype.typeId().isValid() ? qmltype : QQmlType(); }; - for (quint16 i = 0; i < nFormals; ++i) - synthesized->argumentTypes.append(findQmlType(formalsIndices[i].type)); - - synthesized->returnType = findQmlType(compiledFunction->returnType); - jsTypedFunction = synthesized; + new (&jsTypedFunction) JSTypedFunction; kind = JsTyped; + jsTypedFunction.types.reserve(nFormals + 1); + jsTypedFunction.types.append(findQmlType(compiledFunction->returnType)); + for (quint16 i = 0; i < nFormals; ++i) + jsTypedFunction.types.append(findQmlType(formalsIndices[i].type)); } Function::~Function() @@ -190,8 +180,18 @@ Function::~Function() destroyFunctionTable(this, codeRef); delete codeRef; } - if (kind == JsTyped) - delete jsTypedFunction; + + switch (kind) { + case JsTyped: + jsTypedFunction.~JSTypedFunction(); + break; + case AotCompiled: + aotCompiledFunction.~AOTCompiledFunction(); + break; + case JsUntyped: + case Eval: + break; + } } void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 3c9617f359..7543dd3c4b 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -51,11 +51,12 @@ protected: public: struct JSTypedFunction { - QList<QQmlType> argumentTypes; - QQmlType returnType; + QVarLengthArray<QQmlType, 4> types; }; - const CompiledData::Function *compiledFunction; + struct AOTCompiledFunction { + QVarLengthArray<QMetaType, 4> types; + }; QV4::ExecutableCompilationUnit *executableCompilationUnit() const { @@ -73,20 +74,28 @@ public: ReturnedValue call(const Value *thisObject, const Value *argv, int argc, ExecutionContext *context); - const char *codeData; + const CompiledData::Function *compiledFunction = nullptr; + const char *codeData = nullptr; + JSC::MacroAssemblerCodeRef *codeRef = nullptr; typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *); - JittedCode jittedCode; - JSC::MacroAssemblerCodeRef *codeRef; + typedef void (*AotCompiledCode)(const QQmlPrivate::AOTCompiledContext *context, void **argv); + + union { + void *noFunction = nullptr; + JSTypedFunction jsTypedFunction; + AOTCompiledFunction aotCompiledFunction; + }; + union { - const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction = nullptr; - const JSTypedFunction *jsTypedFunction; + JittedCode jittedCode = nullptr; + AotCompiledCode aotCompiledCode; }; // first nArguments names in internalClass are the actual arguments QV4::WriteBarrier::Pointer<Heap::InternalClass> internalClass; int interpreterCallCount = 0; - quint16 nFormals; + quint16 nFormals = 0; enum Kind : quint8 { JsUntyped, JsTyped, AotCompiled, Eval }; Kind kind = JsUntyped; bool detectedInjectedParameters = false; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index b5a3934528..ab6a34435f 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -539,13 +539,13 @@ ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Va switch (function->kind) { case Function::AotCompiled: return QV4::convertAndCall( - fo->engine(), function->aotCompiledFunction, thisObject, argv, argc, + fo->engine(), &function->aotCompiledFunction, thisObject, argv, argc, [fo](QObject *thisObject, void **a, const QMetaType *types, int argc) { ArrowFunction::virtualCallWithMetaTypes(fo, thisObject, a, types, argc); }); case Function::JsTyped: return QV4::coerceAndCall( - fo->engine(), function->jsTypedFunction, function->compiledFunction, argv, argc, + fo->engine(), &function->jsTypedFunction, function->compiledFunction, argv, argc, [fo, thisObject](const Value *argv, int argc) { return qfoDoCall(fo, thisObject, argv, argc); }); diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 59f594c939..ed1ca983ad 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -113,15 +113,15 @@ void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int a template<typename Callable> ReturnedValue convertAndCall( - ExecutionEngine *engine, const QQmlPrivate::AOTCompiledFunction *aotFunction, + ExecutionEngine *engine, const Function::AOTCompiledFunction *aotFunction, const Value *thisObject, const Value *argv, int argc, Callable call) { - const qsizetype numFunctionArguments = aotFunction->argumentTypes.size(); + const qsizetype numFunctionArguments = aotFunction->types.length() - 1; Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *)); Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType)); for (qsizetype i = 0; i < numFunctionArguments; ++i) { - const QMetaType argumentType = aotFunction->argumentTypes[i]; + const QMetaType argumentType = aotFunction->types[i + 1]; types[i + 1] = argumentType; if (const qsizetype argumentSize = argumentType.sizeOf()) { Q_ALLOCA_VAR(void, argument, argumentSize); @@ -144,7 +144,7 @@ ReturnedValue convertAndCall( } Q_ALLOCA_DECLARE(void, returnValue); - types[0] = aotFunction->returnType; + types[0] = aotFunction->types[0]; if (const qsizetype returnSize = types[0].sizeOf()) { Q_ALLOCA_ASSIGN(void, returnValue, returnSize); values[0] = returnValue; @@ -412,16 +412,16 @@ ReturnedValue coerceAndCall( { Scope scope(engine); - QV4::JSCallArguments jsCallData(scope, typedFunction->argumentTypes.size()); + QV4::JSCallArguments jsCallData(scope, typedFunction->types.size() - 1); const CompiledData::Parameter *formals = compiledFunction->formalsTable(); for (qsizetype i = 0; i < jsCallData.argc; ++i) { jsCallData.args[i] = coerce( engine, i < argc ? argv[i] : Encode::undefined(), - typedFunction->argumentTypes[i], formals[i].type.isList()); + typedFunction->types[i + 1], formals[i].type.isList()); } ScopedValue result(scope, call(jsCallData.args, jsCallData.argc)); - return coerce(engine, result, typedFunction->returnType, compiledFunction->returnType.isList()); + return coerce(engine, result, typedFunction->types[0], compiledFunction->returnType.isList()); } // Note: \a to is unininitialized here! This is in contrast to most other related functions. diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index db17a7141c..096b9a6299 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -398,16 +398,16 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) struct AOTCompiledMetaMethod { public: - AOTCompiledMetaMethod(const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction) + AOTCompiledMetaMethod(const Function::AOTCompiledFunction *aotCompiledFunction) : aotCompiledFunction(aotCompiledFunction) {} - int parameterCount() const { return aotCompiledFunction->argumentTypes.size(); } - QMetaType returnMetaType() const { return aotCompiledFunction->returnType; } - QMetaType parameterMetaType(int i) const { return aotCompiledFunction->argumentTypes[i]; } + int parameterCount() const { return aotCompiledFunction->types.size() - 1; } + QMetaType returnMetaType() const { return aotCompiledFunction->types[0]; } + QMetaType parameterMetaType(int i) const { return aotCompiledFunction->types[i + 1]; } private: - const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction = nullptr; + const Function::AOTCompiledFunction *aotCompiledFunction = nullptr; }; void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine) @@ -420,14 +420,14 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine) ExecutionEngineCallDepthRecorder executionEngineCallDepthRecorder(engine); Function *function = frame->v4Function; - Q_ASSERT(function->aotCompiledFunction); + Q_ASSERT(function->aotCompiledCode); Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(), function->executableCompilationUnit()->fileName(), function->compiledFunction->location.line(), function->compiledFunction->location.column()); Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling - const AOTCompiledMetaMethod method(function->aotCompiledFunction); + const AOTCompiledMetaMethod method(&function->aotCompiledFunction); QV4::coerceAndCall( engine, &method, frame->returnAndArgValues(), frame->returnAndArgTypes(), frame->argc(), @@ -443,7 +443,7 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine) aotContext.engine = engine->jsEngine(); aotContext.compilationUnit = function->executableCompilationUnit(); - function->aotCompiledFunction->functionPtr(&aotContext, argv); + function->aotCompiledCode(&aotContext, argv); }); } |