From 910f98031fdd834a22af0da21c9ff6ae145f61c5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 3 Jul 2019 15:13:46 +0200 Subject: Add support for C++ accessible typed parameters and return types in qml functions These can be declared using the new typescript-like syntax and using type names that are also used for signal parameters and property types. This merely affects their signature on the C++ side and allows the corresponding invocation. Change-Id: Icaed4ee0dc7aa71330f99d96e073a2a63d409bbe Reviewed-by: Ulf Hermann --- src/qml/compiler/qqmlirbuilder.cpp | 33 +++++++++++---- src/qml/compiler/qqmlirbuilder_p.h | 11 +++-- src/qml/compiler/qv4compileddata_p.h | 59 ++++++++++++++------------- src/qml/compiler/qv4compiler.cpp | 18 +++++--- src/qml/compiler/qv4compilercontext_p.h | 1 + src/qml/compiler/qv4compilerscanfunctions.cpp | 11 ++--- 6 files changed, 79 insertions(+), 54 deletions(-) (limited to 'src/qml/compiler') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index a94a448ec0..6dc6f191ab 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -66,19 +66,31 @@ using namespace QmlIR; bool Parameter::init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString ¶meterName, const QString &typeName) { - nameIndex = stringGenerator->registerString(parameterName); - type.indexIsBuiltinType = false; - type.typeNameIndexOrBuiltinType = 0; + return init(this, stringGenerator, stringGenerator->registerString(parameterName), stringGenerator->registerString(typeName)); +} + +bool Parameter::init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator, + int parameterNameIndex, int typeNameIndex) +{ + param->nameIndex = parameterNameIndex; + return initType(¶m->type, stringGenerator, typeNameIndex); +} + +bool Parameter::initType(QV4::CompiledData::ParameterType *paramType, const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex) +{ + paramType->indexIsBuiltinType = false; + paramType->typeNameIndexOrBuiltinType = 0; + const QString typeName = stringGenerator->stringForIndex(typeNameIndex); auto builtinType = stringToBuiltinType(typeName); if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) { if (typeName.isEmpty() || !typeName.at(0).isUpper()) return false; - type.indexIsBuiltinType = false; - type.typeNameIndexOrBuiltinType = stringGenerator->registerString(typeName); - Q_ASSERT(quint32(stringGenerator->getStringId(typeName)) < (1u << 31)); + paramType->indexIsBuiltinType = false; + paramType->typeNameIndexOrBuiltinType = typeNameIndex; + Q_ASSERT(quint32(typeNameIndex) < (1u << 31)); } else { - type.indexIsBuiltinType = true; - type.typeNameIndexOrBuiltinType = static_cast(builtinType); + paramType->indexIsBuiltinType = true; + paramType->typeNameIndexOrBuiltinType = static_cast(builtinType); Q_ASSERT(quint32(builtinType) < (1u << 31)); } return true; @@ -909,13 +921,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) f->index = index; f->nameIndex = registerString(funDecl->name.toString()); + QString returnTypeName = funDecl->typeAnnotation ? funDecl->typeAnnotation->type->toString() : QString(); + Parameter::initType(&f->returnType, jsGenerator, registerString(returnTypeName)); + const QQmlJS::AST::BoundNames formals = funDecl->formals ? funDecl->formals->formals() : QQmlJS::AST::BoundNames(); int formalsCount = formals.size(); f->formals.allocate(pool, formalsCount); int i = 0; for (const auto &arg : formals) { - f->formals[i] = registerString(arg.id); + f->formals[i].init(jsGenerator, arg.id, arg.typeName()); ++i; } diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 44d6233d91..e04fb923c3 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -221,6 +221,10 @@ struct Parameter : public QV4::CompiledData::Parameter Parameter *next; bool init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString ¶meterName, const QString &typeName); + static bool init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator, + int parameterNameIndex, int typeNameIndex); + static bool initType(QV4::CompiledData::ParameterType *paramType, + const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex); static QV4::CompiledData::BuiltinType stringToBuiltinType(const QString &typeName); }; @@ -265,11 +269,12 @@ struct Function QV4::CompiledData::Location location; int nameIndex; quint32 index; // index in parsedQML::functions - FixedPoolArray formals; + FixedPoolArray formals; + QV4::CompiledData::ParameterType returnType; // --- QQmlPropertyCacheCreator interface - const int *formalsBegin() const { return formals.begin(); } - const int *formalsEnd() const { return formals.end(); } + const Parameter *formalsBegin() const { return formals.begin(); } + const Parameter *formalsEnd() const { return formals.end(); } // --- Function *next; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 8a40255062..b542d7f918 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -74,7 +74,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 0x23 // Remove trace slots +#define QV4_DATA_STRUCTURE_VERSION 0x24 // Collect function parameter types class QIODevice; class QQmlPropertyData; @@ -257,6 +257,29 @@ struct Block }; static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +enum class BuiltinType : unsigned int { + Var = 0, Variant, Int, Bool, Real, String, Url, Color, + Font, Time, Date, DateTime, Rect, Point, Size, + Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin +}; + +struct ParameterType +{ + union { + quint32 _dummy; + quint32_le_bitfield<0, 1> indexIsBuiltinType; + quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType; + }; +}; +static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Parameter +{ + quint32_le nameIndex; + ParameterType type; +}; +static_assert(sizeof(Parameter) == 8, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + // Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties // for unaligned access. The ordering of the fields is also from largest to smallest. struct Function @@ -275,6 +298,7 @@ struct Function quint16_le length; quint16_le nFormals; quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData. + ParameterType returnType; quint32_le localsOffset; quint16_le nLocals; quint16_le nLineNumbers; @@ -296,13 +320,13 @@ struct Function // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] - const quint32_le *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } + const Parameter *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast(reinterpret_cast(this) + lineNumberOffset()); } // --- QQmlPropertyCacheCreator interface - const quint32_le *formalsBegin() const { return formalsTable(); } - const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } + const Parameter *formalsBegin() const { return formalsTable(); } + const Parameter *formalsEnd() const { return formalsTable() + nFormals; } // --- const quint32_le *labelInfoTable() const { return reinterpret_cast(reinterpret_cast(this) + labelInfosOffset()); } @@ -310,7 +334,7 @@ struct Function const char *code() const { return reinterpret_cast(this) + codeOffset; } static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) { - int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) + int trailingData = nFormals * sizeof(Parameter) + (nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); Q_ASSERT(size < INT_MAX); @@ -321,7 +345,7 @@ struct Function return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 56, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Method { enum Type { @@ -604,29 +628,6 @@ struct Enum }; static_assert(sizeof(Enum) == 12, "Enum structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); -enum class BuiltinType : unsigned int { - Var = 0, Variant, Int, Bool, Real, String, Url, Color, - Font, Time, Date, DateTime, Rect, Point, Size, - Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin -}; - -struct ParameterType -{ - union { - quint32 _dummy; - quint32_le_bitfield<0, 1> indexIsBuiltinType; - quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType; - }; -}; -static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Parameter -{ - quint32_le nameIndex; - ParameterType type; -}; -static_assert(sizeof(Parameter) == 8, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - struct Signal { quint32_le nameIndex; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 7329d526c1..22f393eaec 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include // Efficient implementation that takes advantage of powers of two. @@ -268,8 +269,11 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(module->finalUrl); for (Context *f : qAsConst(module->functions)) { registerString(f->name); - for (int i = 0; i < f->arguments.size(); ++i) + registerString(f->returnType); + for (int i = 0; i < f->arguments.size(); ++i) { registerString(f->arguments.at(i).id); + registerString(f->arguments.at(i).typeName()); + } for (int i = 0; i < f->locals.size(); ++i) registerString(f->locals.at(i)); } @@ -436,7 +440,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->length = irFunction->formals ? irFunction->formals->length() : 0; function->nFormals = irFunction->arguments.size(); function->formalsOffset = currentOffset; - currentOffset += function->nFormals * sizeof(quint32); + currentOffset += function->nFormals * sizeof(CompiledData::Parameter); + + QmlIR::Parameter::initType(&function->returnType, this, getStringId(irFunction->returnType)); function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone; function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone; @@ -465,9 +471,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->codeSize = irFunction->code.size(); // write formals - quint32_le *formals = (quint32_le *)(f + function->formalsOffset); - for (int i = 0; i < irFunction->arguments.size(); ++i) - formals[i] = getStringId(irFunction->arguments.at(i).id); + CompiledData::Parameter *formals = (CompiledData::Parameter *)(f + function->formalsOffset); + for (int i = 0; i < irFunction->arguments.size(); ++i) { + QmlIR::Parameter::init(&formals[i], this, getStringId(irFunction->arguments.at(i).id), + getStringId(irFunction->arguments.at(i).typeName())); + } // write locals quint32_le *locals = (quint32_le *)(f + function->localsOffset); diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index e7ba13ec20..1ae7c8c149 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -190,6 +190,7 @@ struct Context { QSet usedVariables; QQmlJS::AST::FormalParameterList *formals = nullptr; QQmlJS::AST::BoundNames arguments; + QString returnType; QStringList locals; QStringList moduleRequests; QVector importEntries; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 9eaf0adf70..003c32a8fe 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -680,10 +680,8 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete else if (expr->isGenerator) _context->isGenerator = true; - if (expr->typeAnnotation) { - _cg->throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet).")); - return false; - } + if (expr->typeAnnotation) + _context->returnType = expr->typeAnnotation->type->toString(); } @@ -700,10 +698,6 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete const BoundNames boundNames = formals ? formals->boundNames() : BoundNames(); for (int i = 0; i < boundNames.size(); ++i) { - if (auto type = boundNames.at(i).typeAnnotation) { - _cg->throwSyntaxError(type->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet).")); - return false; - } const QString &arg = boundNames.at(i).id; if (_context->isStrict || !isSimpleParameterList) { bool duplicate = (boundNames.indexOf(arg, i + 1) != -1); @@ -721,6 +715,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete if (!_context->arguments.contains(arg)) _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var); } + return true; } -- cgit v1.2.3