aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--src/qml/common/qv4compileddata_p.h2
-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
-rw-r--r--src/qml/qml/qqml.cpp16
-rw-r--r--src/qml/qml/qqmlbinding.cpp2
-rw-r--r--src/qml/qml/qqmlprivate.h11
-rw-r--r--src/qml/qml/qqmltype_p_p.h24
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp150
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h3
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h2
-rw-r--r--src/qmlcompiler/qqmljscompiler.cpp21
-rw-r--r--src/qmlcompiler/qqmljscompiler_p.h4
-rw-r--r--src/qmlcompiler/qqmljscontextualtypes_p.h29
-rw-r--r--src/qmlcompiler/qqmljsfunctioninitializer.cpp13
-rw-r--r--src/qmlcompiler/qqmljsstoragegeneralizer.cpp8
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp2
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp21
24 files changed, 321 insertions, 141 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index caedd8928e..79df230872 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
-#define QV4_DATA_STRUCTURE_VERSION 0x41 // Change signature of AOT compiled functions
+#define QV4_DATA_STRUCTURE_VERSION 0x42 // Change metatype computation of AOT-compiled functions
class QIODevice;
class QQmlTypeNameCache;
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);
});
}
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index ff419b0b34..eb716671b1 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -176,6 +176,22 @@ void QQmlPrivate::qmlRegistrationWarning(
}
}
+QMetaType QQmlPrivate::compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .typeId();
+}
+
+QMetaType QQmlPrivate::compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .qListTypeId();
+}
+
int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
const char *uri, int versionMajor,
int versionMinor, const char *qmlName,
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index f1bedaf65f..47f8e5c429 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -675,7 +675,7 @@ void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::Write
auto canWrite = [&]() { return !watcher.wasDeleted() && isAddedToObject() && !hasError(); };
const QV4::Function *v4Function = function();
if (v4Function && v4Function->kind == QV4::Function::AotCompiled && !hasBoundFunction()) {
- const auto returnType = v4Function->aotCompiledFunction->returnType;
+ const auto returnType = v4Function->aotCompiledFunction.types[0];
if (returnType == QMetaType::fromType<QVariant>()) {
QVariant result;
const bool isUndefined = !evaluate(&result, returnType);
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 25dc36d33d..92c9765509 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -740,9 +740,9 @@ namespace QQmlPrivate
};
struct AOTCompiledFunction {
- qintptr extraData;
- QMetaType returnType;
- QList<QMetaType> argumentTypes;
+ int functionIndex;
+ int numArguments;
+ void (*signature)(QV4::ExecutableCompilationUnit *unit, QMetaType *argTypes);
void (*functionPtr)(const AOTCompiledContext *context, void **argv);
};
@@ -1152,6 +1152,11 @@ namespace QQmlPrivate
Q_QML_EXPORT void qmlRegistrationWarning(QmlRegistrationWarning warning, QMetaType type);
+ Q_QML_EXPORT QMetaType compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+ Q_QML_EXPORT QMetaType compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName);
+
} // namespace QQmlPrivate
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index 8f614eeac2..2bf83ddb8b 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -21,6 +21,9 @@
#include <private/qqmlrefcount_p.h>
#include <private/qqmlpropertycache_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qv4executablecompilationunit_p.h>
+#include <private/qv4engine_p.h>
#include <QAtomicInteger>
@@ -231,6 +234,27 @@ public:
return nullptr;
}
+ static QQmlType compositeQmlType(
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit,
+ QQmlTypeLoader *typeLoader, const QString &type)
+ {
+ Q_ASSERT(typeLoader);
+
+ const QQmlType qmltype
+ = unit->typeNameCache->query<QQmlImport::AllowRecursion>(type, typeLoader).type;
+ if (!qmltype.isValid())
+ return qmltype;
+
+ if (qmltype.isInlineComponentType()
+ && !QQmlMetaType::obtainCompilationUnit(qmltype.typeId())) {
+ // 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 QQmlType();
+ }
+
+ return qmltype;
+ }
+
private:
mutable QAtomicPointer<const ProxyMetaObjects> proxyMetaObjects;
mutable QAtomicPointer<const Enums> enums;
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index 7b0664f918..e6529a07d9 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -73,22 +73,54 @@ QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type)
+ u"\"); return t; }()"_s;
}
+QString QQmlJSCodeGenerator::compositeListMetaType(const QString &elementName) const
+{
+ return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeListMetaType("
+ "aotContext->compilationUnit, \""_s
+ + elementName
+ + u"\"); return t; }(aotContext)"_s;
+}
+
+QString QQmlJSCodeGenerator::compositeMetaType(const QString &elementName) const
+{
+ return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeMetaType("
+ "aotContext->compilationUnit, \""_s
+ + elementName
+ + u"\"); return t; }(aotContext)"_s;
+}
+
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
{
- if (!objectType->isComposite()) {
- if (objectType->internalName() == u"QObject"_s
- || objectType->internalName() == u"QQmlComponent"_s) {
- return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
+ if (objectType->isComposite()) {
+ const QString name = m_typeResolver->nameForType(objectType);
+ if (name.isEmpty()) {
+ reject(u"retrieving the metaObject of a composite type without an element name."_s);
+ return QString();
}
- return metaTypeFromName(objectType) + u".metaObject()"_s;
+ return compositeMetaType(name) + u".metaObject()"_s;
}
- reject(u"retrieving the metaObject of a composite type without using an instance."_s);
- return QString();
+ if (objectType->internalName() == u"QObject"_s
+ || objectType->internalName() == u"QQmlComponent"_s) {
+ return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
+ }
+ return metaTypeFromName(objectType) + u".metaObject()"_s;
}
QString QQmlJSCodeGenerator::metaType(const QQmlJSScope::ConstPtr &type)
{
+ if (type->isComposite()) {
+ const QString name = m_typeResolver->nameForType(type);
+ if (!name.isEmpty())
+ return compositeMetaType(name);
+ }
+
+ if (type->isListProperty() && type->valueType()->isComposite()) {
+ const QString name = m_typeResolver->nameForType(type->valueType());
+ if (!name.isEmpty())
+ return compositeListMetaType(name);
+ }
+
return m_typeResolver->equals(m_typeResolver->genericType(type), type)
? metaTypeFromType(type)
: metaTypeFromName(type);
@@ -223,24 +255,26 @@ QT_WARNING_POP
result.code += m_body;
- for (const QQmlJSRegisterContent &argType : std::as_const(function->argumentTypes)) {
- if (argType.isValid()) {
- result.argumentTypes.append(
- m_typeResolver->originalType(argType.storedType())
- ->augmentedInternalName());
- } else {
- result.argumentTypes.append(u"void"_s);
- }
- }
- if (function->returnType) {
- result.returnType = function->returnType->internalName();
- if (function->returnType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- result.returnType += u'*';
+ QString signature
+ = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { unit };\n"
+ " const auto *aotContext = &c;\n"
+ " Q_UNUSED(aotContext);\n"_s;
+
+ if (function->returnType.isValid()) {
+ signature += u" argTypes[0] = %1;\n"_s.arg(
+ metaType(m_typeResolver->containedType(function->returnType)));
} else {
- result.returnType = u"void"_s;
+ signature += u" argTypes[0] = QMetaType();\n"_s;
+ }
+ result.numArguments = function->argumentTypes.length();
+ for (qsizetype i = 0; i != result.numArguments; ++i) {
+ signature += u" argTypes[%1] = %2;\n"_s.arg(
+ QString::number(i + 1),
+ metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
}
+ result.signature = signature;
return result;
}
@@ -250,16 +284,27 @@ void QQmlJSCodeGenerator::generateReturnError()
m_body += u"aotContext->setReturnValueUndefined();\n"_s;
const auto ret = m_function->returnType;
- if (!ret || m_typeResolver->equals(m_function->returnType, m_typeResolver->voidType()))
+ if (!ret.isValid() || m_typeResolver->registerContains(ret, m_typeResolver->voidType()))
return;
- const QString value = ret->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
- ? convertStored(m_typeResolver->nullType(), ret, QString())
- : ret->internalName() + u"()"_s;
-
m_body += u"if (argv[0]) {\n"_s;
- m_body += u" *static_cast<"_s + m_function->returnType->augmentedInternalName()
- + u" *>(argv[0]) = "_s + value + u";\n"_s;
+
+ const auto contained = m_typeResolver->containedType(ret);
+ const auto stored = ret.storedType();
+ if (contained->isReferenceType() && stored->isReferenceType()) {
+ m_body += u" *static_cast<"_s
+ + stored->augmentedInternalName()
+ + u" *>(argv[0]) = nullptr;\n"_s;
+ } else if (m_typeResolver->equals(contained, stored)) {
+ m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
+ + stored->internalName() + u"();\n"_s;
+ } else {
+ m_body += u" const QMetaType returnType = "_s
+ + metaType(m_typeResolver->containedType(ret)) + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0]);\n "_s;
+ }
+
m_body += u"}\n"_s;
}
@@ -273,7 +318,7 @@ void QQmlJSCodeGenerator::generate_Ret()
resetState();
});
- if (!m_function->returnType)
+ if (!m_function->returnType.isValid())
return;
m_body += u"if (argv[0]) {\n"_s;
@@ -301,11 +346,36 @@ void QQmlJSCodeGenerator::generate_Ret()
m_body += u" "_s + signalUndefined;
}
- if (!m_typeResolver->equals(m_function->returnType, m_typeResolver->voidType())) {
- m_body += u" *static_cast<"_s + m_function->returnType->augmentedInternalName()
- +u" *>(argv[0]) = "_s + convertStored(
- m_state.accumulatorIn().storedType(), m_function->returnType, in)
+ if (m_typeResolver->registerContains(
+ m_function->returnType, m_typeResolver->voidType())) {
+ m_body += u"}\n"_s;
+ return;
+ }
+
+ const auto contained = m_typeResolver->containedType(m_function->returnType);
+ const auto stored = m_function->returnType.storedType();
+ if (m_typeResolver->equals(contained, stored)
+ || (contained->isReferenceType() && stored->isReferenceType())) {
+ m_body += u" *static_cast<"_s
+ + stored->augmentedInternalName()
+ + u" *>(argv[0]) = "_s
+ + conversion(m_state.accumulatorIn(), m_function->returnType, in)
+ + u";\n"_s;
+ } else if (m_typeResolver->registerContains(m_state.accumulatorIn(), contained)) {
+ m_body += u" const QMetaType returnType = "_s + contentType(m_state.accumulatorIn(), in)
+ u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0], "_s
+ + contentPointer(m_state.accumulatorIn(), in) + u");\n"_s;
+ } else {
+ m_body += u" const auto converted = "_s
+ + conversion(m_state.accumulatorIn(), m_function->returnType, in) + u";\n"_s;
+ m_body += u" const QMetaType returnType = "_s
+ + contentType(m_function->returnType, u"converted"_s)
+ + u";\n"_s;
+ m_body += u" returnType.destruct(argv[0]);\n"_s;
+ m_body += u" returnType.construct(argv[0], "_s
+ + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
}
m_body += u"}\n"_s;
@@ -2871,8 +2941,7 @@ QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content
QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, const QString &var)
{
const QQmlJSScope::ConstPtr stored = content.storedType();
- const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
- m_typeResolver->containedType(content));
+ const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
if (m_typeResolver->equals(contained, stored))
return metaTypeFromType(stored);
@@ -2882,13 +2951,14 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c
}
if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
- return metaTypeFromName(contained);
+ return metaType(contained);
- if (m_typeResolver->isNumeric(stored) && contained->scopeType() == QQmlSA::ScopeType::EnumScope)
- return metaTypeFromType(contained->baseType());
+ const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
+ if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
+ return metaTypeFromType(nonComposite->baseType());
- if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
- return metaTypeFromType(m_typeResolver->listPropertyType());
+ if (stored->isListProperty() && contained->isListProperty())
+ return metaType(contained);
reject(u"content type of unsupported wrapper type "_s + content.descriptiveName());
return QString();
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 79f6352551..2edccf31ae 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -247,6 +247,9 @@ protected:
QString metaTypeFromType(const QQmlJSScope::ConstPtr &type) const;
QString metaTypeFromName(const QQmlJSScope::ConstPtr &type) const;
+ QString compositeMetaType(const QString &elementName) const;
+ QString compositeListMetaType(const QString &elementName) const;
+
QString contentPointer(const QQmlJSRegisterContent &content, const QString &var);
QString contentType(const QQmlJSRegisterContent &content, const QString &var);
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index 7a56494744..a18b906d8d 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -94,7 +94,7 @@ public:
QQmlJSScopesById addressableScopes;
QList<QQmlJSRegisterContent> argumentTypes;
QList<QQmlJSRegisterContent> registerTypes;
- QQmlJSScope::ConstPtr returnType;
+ QQmlJSRegisterContent returnType;
QQmlJSScope::ConstPtr qmlScope;
QByteArray code;
const SourceLocationTable *sourceLocations = nullptr;
diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp
index cca05380c9..8ecc69d1c9 100644
--- a/src/qmlcompiler/qqmljscompiler.cpp
+++ b/src/qmlcompiler/qqmljscompiler.cpp
@@ -560,7 +560,7 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile
if (aotFunctions.size() <= 1) {
// FileScopeCodeIndex is always there, but it may be the only one.
writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
- "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, QMetaType::fromType<void>(), {}, nullptr } };\n");
+ "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, 0, nullptr, nullptr } };\n");
} else {
writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n"
"extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n");
@@ -574,25 +574,18 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile
if (func.key() == FileScopeCodeIndex)
continue;
- QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
+ const QString function = QString::fromUtf8(funcHeaderCode) + func.value().code + footer;
- QString argumentTypes = func.value().argumentTypes.join(
- QStringLiteral(">(), QMetaType::fromType<"));
- if (!argumentTypes.isEmpty()) {
- argumentTypes = QStringLiteral("QMetaType::fromType<")
- + argumentTypes + QStringLiteral(">()");
- }
-
- writeStr(QStringLiteral("{ %1, QMetaType::fromType<%2>(), { %3 }, %4 },")
+ writeStr(QStringLiteral("{ %1, %2, [](QV4::ExecutableCompilationUnit *unit, "
+ "QMetaType *argTypes) {\n%3}, %4 },")
.arg(func.key())
- .arg(func.value().returnType)
- .arg(argumentTypes)
- .arg(function)
+ .arg(func->numArguments)
+ .arg(func->signature, function)
.toUtf8().constData());
}
// Conclude the list with a nullptr
- writeStr("{ 0, QMetaType::fromType<void>(), {}, nullptr }");
+ writeStr("{ 0, 0, nullptr, nullptr }");
writeStr("};\n");
}
diff --git a/src/qmlcompiler/qqmljscompiler_p.h b/src/qmlcompiler/qqmljscompiler_p.h
index aace476cf1..e358f76fef 100644
--- a/src/qmlcompiler/qqmljscompiler_p.h
+++ b/src/qmlcompiler/qqmljscompiler_p.h
@@ -48,9 +48,9 @@ struct Q_QMLCOMPILER_EXPORT QQmlJSCompileError
struct Q_QMLCOMPILER_EXPORT QQmlJSAotFunction
{
QStringList includes;
- QStringList argumentTypes;
QString code;
- QString returnType;
+ QString signature;
+ int numArguments = 0;
};
class Q_QMLCOMPILER_EXPORT QQmlJSAotCompiler
diff --git a/src/qmlcompiler/qqmljscontextualtypes_p.h b/src/qmlcompiler/qqmljscontextualtypes_p.h
index 10bbeef3b7..cccd7fd1b0 100644
--- a/src/qmlcompiler/qqmljscontextualtypes_p.h
+++ b/src/qmlcompiler/qqmljscontextualtypes_p.h
@@ -42,14 +42,23 @@ struct ContextualTypes
QQmlJSScope::ConstPtr arrayType() const { return m_arrayType; }
bool hasType(const QString &name) const { return m_types.contains(name); }
+
ImportedScope<QQmlJSScope::ConstPtr> type(const QString &name) const { return m_types[name]; }
+ QString name(const QQmlJSScope::ConstPtr &type) const { return m_names[type]; }
+
void setType(const QString &name, const ImportedScope<QQmlJSScope::ConstPtr> &type)
{
+ if (!name.startsWith(u'$'))
+ m_names.insert(type.scope, name);
m_types.insert(name, type);
}
void clearType(const QString &name)
{
- m_types[name].scope = QQmlJSScope::ConstPtr();
+ auto &scope = m_types[name].scope;
+ auto it = m_names.constFind(scope);
+ while (it != m_names.constEnd() && it.key() == scope)
+ it = m_names.erase(it);
+ scope = QQmlJSScope::ConstPtr();
}
bool isNullType(const QString &name) const
@@ -61,21 +70,37 @@ struct ContextualTypes
void addTypes(ContextualTypes &&types)
{
Q_ASSERT(types.m_context == m_context);
+ insertNames(types);
m_types.insert(std::move(types.m_types));
}
void addTypes(const ContextualTypes &types)
{
Q_ASSERT(types.m_context == m_context);
+ insertNames(types);
m_types.insert(types.m_types);
}
const QHash<QString, ImportedScope<QQmlJSScope::ConstPtr>> &types() const { return m_types; }
- void clearTypes() { m_types.clear(); }
+ void clearTypes()
+ {
+ m_names.clear();
+ m_types.clear();
+ }
private:
+ void insertNames(const ContextualTypes &types) {
+ for (auto it = types.m_types.constBegin(), end = types.m_types.constEnd();
+ it != end; ++it) {
+ const QString &name = it.key();
+ if (!name.startsWith(u'$'))
+ m_names.insert(it->scope, name);
+ }
+ }
+
QHash<QString, ImportedScope<QQmlJSScope::ConstPtr>> m_types;
+ QMultiHash<QQmlJSScope::ConstPtr, QString> m_names;
CompileContext m_context;
// For resolving sequence types
diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
index 217b00c52c..09928364b1 100644
--- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp
+++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp
@@ -116,10 +116,11 @@ void QQmlJSFunctionInitializer::populateSignature(
}
}
- if (!function->returnType) {
+ if (!function->returnType.isValid()) {
if (ast->typeAnnotation) {
- function->returnType = m_typeResolver->typeFromAST(ast->typeAnnotation->type);
- if (!function->returnType)
+ function->returnType = m_typeResolver->globalType(
+ m_typeResolver->typeFromAST(ast->typeAnnotation->type));
+ if (!function->returnType.isValid())
signatureError(u"Cannot resolve return type %1"_s.arg(
QmlIR::IRBuilder::asString(ast->typeAnnotation->type->typeId)));
}
@@ -218,9 +219,9 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run(
const auto property = m_objectType->property(propertyName);
if (const QQmlJSScope::ConstPtr propertyType = property.type()) {
- function.returnType = propertyType->isListProperty()
- ? m_typeResolver->qObjectListType()
- : propertyType;
+ function.returnType = m_typeResolver->globalType(propertyType->isListProperty()
+ ? m_typeResolver->qObjectListType()
+ : QQmlJSScope::ConstPtr(property.type()));
} else {
QString message = u"Cannot resolve property type %1 for binding on %2."_s
.arg(property.typeName(), propertyName);
diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
index 077c1dcc01..937c35ddcd 100644
--- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
+++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp
@@ -24,13 +24,13 @@ QQmlJSStorageGeneralizer::run(Function *function, QQmlJS::DiagnosticMessage *err
{
m_error = error;
- if (QQmlJSScope::ConstPtr &returnType = function->returnType) {
+ if (QQmlJSRegisterContent &returnType = function->returnType; returnType.isValid()) {
if (QQmlJSScope::ConstPtr stored = m_typeResolver->genericType(
- returnType, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- returnType = stored;
+ returnType.storedType(), QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
+ returnType = returnType.storedIn(stored);
} else {
setError(QStringLiteral("Cannot store the return type %1.")
- .arg(returnType->internalName(), 0));
+ .arg(returnType.storedType()->internalName()));
return {};
}
}
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index baa6eab694..d7a7d68d9f 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -41,7 +41,7 @@ QQmlJSCompilePass::BlocksAndAnnotations QQmlJSTypePropagator::run(
{
m_function = function;
m_error = error;
- m_returnType = m_typeResolver->globalType(m_function->returnType);
+ m_returnType = m_function->returnType;
do {
// Reset the error if we need to do another pass
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 6a5ea8cf9b..9961c24842 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -96,6 +96,11 @@ public:
{
return m_imports.type(name).scope;
}
+ QString nameForType(const QQmlJSScope::ConstPtr &type) const
+ {
+ return m_imports.name(originalType(type));
+ }
+
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const;
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const;
QQmlJSRegisterContent typeForBinaryOperation(QSOperator::Op oper,
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index 4d9b6aea41..8c5449d192 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -125,6 +125,7 @@ set(qml_files
dialog.qml
dialogButtonBox.qml
dynamicscene.qml
+ enforceSignature.qml
enumConversion.qml
enumFromBadSingleton.qml
enumInvalid.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
new file mode 100644
index 0000000000..571a000199
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/enforceSignature.qml
@@ -0,0 +1,11 @@
+import QtQml
+
+QtObject {
+ id: mainItem
+
+ function arg(item: Binding) : QtObject { return item }
+ function ret(item: QtObject) : Binding { return item }
+
+ property QtObject a: arg(mainItem);
+ property QtObject b: ret(mainItem);
+}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index ae8ef49b22..9b66143f62 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -89,6 +89,7 @@ private slots:
void enumProblems();
void enumScope();
void enums();
+ void enforceSignature();
void enumsInOtherObject();
void equalityQObjects();
void equalityQUrl();
@@ -1652,6 +1653,23 @@ void tst_QmlCppCodegen::enums()
}
+void tst_QmlCppCodegen::enforceSignature()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/enforceSignature.qml"_s));
+ QVERIFY2(!component.isError(), component.errorString().toUtf8());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ const QVariant a = object->property("a");
+ QCOMPARE(a.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(a.value<QObject *>(), nullptr);
+
+ const QVariant b = object->property("b");
+ QCOMPARE(b.metaType(), QMetaType::fromType<QObject *>());
+ QCOMPARE(b.value<QObject *>(), nullptr);
+}
+
void tst_QmlCppCodegen::enumsInOtherObject()
{
QQmlEngine engine;
@@ -1865,9 +1883,8 @@ void tst_QmlCppCodegen::failures()
{
const auto &aotFailure
= QmlCacheGeneratedCode::_qt_qml_TestTypes_failures_qml::aotBuiltFunctions[0];
- QVERIFY(aotFailure.argumentTypes.isEmpty());
QVERIFY(!aotFailure.functionPtr);
- QCOMPARE(aotFailure.extraData, 0);
+ QCOMPARE(aotFailure.functionIndex, 0);
}
void tst_QmlCppCodegen::fallbackLookups()