aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2024-04-16 17:03:01 +0200
committerUlf Hermann <ulf.hermann@qt.io>2024-04-26 12:18:15 +0000
commit8bf5aae19b77b618f3f7a55a59e87c8a319475a8 (patch)
treed331328f478ac13593524eaaeb3a874691ccadd2 /src/qml/jsruntime
parent23fc22e16022e355f2a1aff8705c09b807fbe024 (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.cpp2
-rw-r--r--src/qml/jsruntime/qv4function.cpp74
-rw-r--r--src/qml/jsruntime/qv4function_p.h27
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h14
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp16
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 &param) {
@@ -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> &parameters)
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);
});
}