diff options
Diffstat (limited to 'src/qml/compiler')
31 files changed, 1143 insertions, 7408 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index da3c173545..4d6926d420 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -3,20 +3,19 @@ INCLUDEPATH += $$OUT_PWD HEADERS += \ $$PWD/qv4bytecodegenerator_p.h \ - $$PWD/qv4compileddata_p.h \ $$PWD/qv4compiler_p.h \ $$PWD/qv4compilercontext_p.h \ $$PWD/qv4compilercontrolflow_p.h \ + $$PWD/qv4compilerglobal_p.h \ $$PWD/qv4compilerscanfunctions_p.h \ $$PWD/qv4codegen_p.h \ $$PWD/qqmlirbuilder_p.h \ - $$PWD/qqmltypecompiler_p.h \ $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4bytecodehandler_p.h + $$PWD/qv4bytecodehandler_p.h \ + $$PWD/qv4util_p.h SOURCES += \ $$PWD/qv4bytecodegenerator.cpp \ - $$PWD/qv4compileddata.cpp \ $$PWD/qv4compiler.cpp \ $$PWD/qv4compilercontext.cpp \ $$PWD/qv4compilerscanfunctions.cpp \ @@ -25,25 +24,6 @@ SOURCES += \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4bytecodehandler.cpp -!qmldevtools_build { - -HEADERS += \ - $$PWD/qqmltypecompiler_p.h \ - $$PWD/qqmlpropertycachecreator_p.h \ - $$PWD/qqmlpropertyvalidator_p.h \ - $$PWD/qv4compilationunitmapper_p.h - - -SOURCES += \ - $$PWD/qqmltypecompiler.cpp \ - $$PWD/qqmlpropertycachecreator.cpp \ - $$PWD/qqmlpropertyvalidator.cpp \ - $$PWD/qv4compilationunitmapper.cpp - -unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp -else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -} - gcc { equals(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -fno-strict-aliasing } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 9218c4a652..665d2633a7 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -39,7 +39,7 @@ #include "qqmlirbuilder_p.h" -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> @@ -48,12 +48,6 @@ #include <QCryptographicHash> #include <cmath> -#ifndef V4_BOOTSTRAP -#include <private/qqmlglobal_p.h> -#include <private/qqmltypeloader_p.h> -#include <private/qqmlengine_p.h> -#endif - #ifdef CONST #undef CONST #endif @@ -61,12 +55,8 @@ QT_USE_NAMESPACE static const quint32 emptyStringIndex = 0; - -#if 0 //ndef V4_BOOTSTRAP -DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); -#endif // V4_BOOTSTRAP - using namespace QmlIR; +using namespace QQmlJS; #define COMPILE_EXCEPTION(location, desc) \ { \ @@ -74,6 +64,84 @@ using namespace QmlIR; return false; \ } +bool Parameter::init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString ¶meterName, + const QString &typeName) +{ + 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; + paramType->indexIsBuiltinType = false; + paramType->typeNameIndexOrBuiltinType = typeNameIndex; + Q_ASSERT(quint32(typeNameIndex) < (1u << 31)); + } else { + paramType->indexIsBuiltinType = true; + paramType->typeNameIndexOrBuiltinType = static_cast<quint32>(builtinType); + Q_ASSERT(quint32(builtinType) < (1u << 31)); + } + return true; +} + +QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typeName) +{ + static const struct TypeNameToType { + const char *name; + size_t nameLength; + QV4::CompiledData::BuiltinType type; + } propTypeNameToTypes[] = { + { "int", strlen("int"), QV4::CompiledData::BuiltinType::Int }, + { "bool", strlen("bool"), QV4::CompiledData::BuiltinType::Bool }, + { "double", strlen("double"), QV4::CompiledData::BuiltinType::Real }, + { "real", strlen("real"), QV4::CompiledData::BuiltinType::Real }, + { "string", strlen("string"), QV4::CompiledData::BuiltinType::String }, + { "url", strlen("url"), QV4::CompiledData::BuiltinType::Url }, + { "color", strlen("color"), QV4::CompiledData::BuiltinType::Color }, + // Internally QTime, QDate and QDateTime are all supported. + // To be more consistent with JavaScript we expose only + // QDateTime as it matches closely with the Date JS type. + // We also call it "date" to match. + // { "time", strlen("time"), Property::Time }, + // { "date", strlen("date"), Property::Date }, + { "date", strlen("date"), QV4::CompiledData::BuiltinType::DateTime }, + { "rect", strlen("rect"), QV4::CompiledData::BuiltinType::Rect }, + { "point", strlen("point"), QV4::CompiledData::BuiltinType::Point }, + { "size", strlen("size"), QV4::CompiledData::BuiltinType::Size }, + { "font", strlen("font"), QV4::CompiledData::BuiltinType::Font }, + { "vector2d", strlen("vector2d"), QV4::CompiledData::BuiltinType::Vector2D }, + { "vector3d", strlen("vector3d"), QV4::CompiledData::BuiltinType::Vector3D }, + { "vector4d", strlen("vector4d"), QV4::CompiledData::BuiltinType::Vector4D }, + { "quaternion", strlen("quaternion"), QV4::CompiledData::BuiltinType::Quaternion }, + { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::BuiltinType::Matrix4x4 }, + { "variant", strlen("variant"), QV4::CompiledData::BuiltinType::Variant }, + { "var", strlen("var"), QV4::CompiledData::BuiltinType::Var } + }; + static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / + sizeof(propTypeNameToTypes[0]); + + for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { + const TypeNameToType *t = propTypeNameToTypes + typeIndex; + if (typeName == QLatin1String(t->name, static_cast<int>(t->nameLength))) { + return t->type; + } + } + return QV4::CompiledData::BuiltinType::InvalidBuiltin; +} + void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc) { inheritedTypeNameIndex = typeNameIndex; @@ -258,64 +326,11 @@ QStringList Signal::parameterStringList(const QV4::Compiler::StringTableGenerato { QStringList result; result.reserve(parameters->count); - for (SignalParameter *param = parameters->first; param; param = param->next) + for (Parameter *param = parameters->first; param; param = param->next) result << stringPool->stringForIndex(param->nameIndex); return result; } -static void replaceWithSpace(QString &str, int idx, int n) -{ - QChar *data = str.data() + idx; - const QChar space(QLatin1Char(' ')); - for (int ii = 0; ii < n; ++ii) - *data++ = space; -} - -void Document::removeScriptPragmas(QString &script) -{ - const QLatin1String pragma("pragma"); - const QLatin1String library("library"); - - QQmlJS::Lexer l(nullptr); - l.setCode(script, 0); - - int token = l.lex(); - - while (true) { - if (token != QQmlJSGrammar::T_DOT) - return; - - int startOffset = l.tokenOffset(); - int startLine = l.tokenStartLine(); - - token = l.lex(); - - if (token != QQmlJSGrammar::T_PRAGMA || - l.tokenStartLine() != startLine || - script.midRef(l.tokenOffset(), l.tokenLength()) != pragma) - return; - - token = l.lex(); - - if (token != QQmlJSGrammar::T_IDENTIFIER || - l.tokenStartLine() != startLine) - return; - - const QStringRef pragmaValue = script.midRef(l.tokenOffset(), l.tokenLength()); - int endOffset = l.tokenLength() + l.tokenOffset(); - - token = l.lex(); - if (l.tokenStartLine() == startLine) - return; - - if (pragmaValue == library) { - replaceWithSpace(script, startOffset, endOffset - startOffset); - } else { - return; - } - } -} - Document::Document(bool debugMode) : jsModule(debugMode) , program(nullptr) @@ -385,13 +400,12 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen if (!parseResult || !diagnosticMessages.isEmpty()) { // Extract errors from the parser for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { - if (m.isWarning()) { - qWarning("%s:%d : %s", qPrintable(url), m.loc.startLine, qPrintable(m.message)); + qWarning("%s:%d : %s", qPrintable(url), m.line, qPrintable(m.message)); continue; } - recordError(m.loc, m.message); + errors << m; } return false; } @@ -661,11 +675,9 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) return false; } - if (node->versionToken.isValid()) { - int major, minor; - extractVersion(textRefAt(node->versionToken), &major, &minor); - import->majorVersion = major; - import->minorVersion = minor; + if (node->version) { + import->majorVersion = node->version->majorVersion; + import->minorVersion = node->version->minorVersion; } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version")); return false; @@ -778,40 +790,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) { - static const struct TypeNameToType { - const char *name; - size_t nameLength; - QV4::CompiledData::Property::Type type; - } propTypeNameToTypes[] = { - { "int", strlen("int"), QV4::CompiledData::Property::Int }, - { "bool", strlen("bool"), QV4::CompiledData::Property::Bool }, - { "double", strlen("double"), QV4::CompiledData::Property::Real }, - { "real", strlen("real"), QV4::CompiledData::Property::Real }, - { "string", strlen("string"), QV4::CompiledData::Property::String }, - { "url", strlen("url"), QV4::CompiledData::Property::Url }, - { "color", strlen("color"), QV4::CompiledData::Property::Color }, - // Internally QTime, QDate and QDateTime are all supported. - // To be more consistent with JavaScript we expose only - // QDateTime as it matches closely with the Date JS type. - // We also call it "date" to match. - // { "time", strlen("time"), Property::Time }, - // { "date", strlen("date"), Property::Date }, - { "date", strlen("date"), QV4::CompiledData::Property::DateTime }, - { "rect", strlen("rect"), QV4::CompiledData::Property::Rect }, - { "point", strlen("point"), QV4::CompiledData::Property::Point }, - { "size", strlen("size"), QV4::CompiledData::Property::Size }, - { "font", strlen("font"), QV4::CompiledData::Property::Font }, - { "vector2d", strlen("vector2d"), QV4::CompiledData::Property::Vector2D }, - { "vector3d", strlen("vector3d"), QV4::CompiledData::Property::Vector3D }, - { "vector4d", strlen("vector4d"), QV4::CompiledData::Property::Vector4D }, - { "quaternion", strlen("quaternion"), QV4::CompiledData::Property::Quaternion }, - { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::Property::Matrix4x4 }, - { "variant", strlen("variant"), QV4::CompiledData::Property::Variant }, - { "var", strlen("var"), QV4::CompiledData::Property::Var } - }; - static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / - sizeof(propTypeNameToTypes[0]); - if (node->type == QQmlJS::AST::UiPublicMember::Signal) { Signal *signal = New<Signal>(); QString signalName = node->name.toString(); @@ -821,7 +799,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) signal->location.line = loc.startLine; signal->location.column = loc.startColumn; - signal->parameters = New<PoolList<SignalParameter> >(); + signal->parameters = New<PoolList<Parameter> >(); QQmlJS::AST::UiParameterList *p = node->parameters; while (p) { @@ -832,38 +810,13 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) return false; } - const TypeNameToType *type = nullptr; - for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { - const TypeNameToType *t = propTypeNameToTypes + typeIndex; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - type = t; - break; - } - } - - SignalParameter *param = New<SignalParameter>(); - - if (!type) { - if (memberType.at(0).isUpper()) { - // Must be a QML object type. - // Lazily determine type during compilation. - param->type = QV4::CompiledData::Property::Custom; - param->customTypeNameIndex = registerString(memberType); - } else { - QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); - errStr.append(memberType); - recordError(node->typeToken, errStr); - return false; - } - } else { - // the parameter is a known basic type - param->type = type->type; - param->customTypeNameIndex = emptyStringIndex; + Parameter *param = New<Parameter>(); + if (!param->init(jsGenerator, p->name.toString(), memberType)) { + QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); + errStr.append(memberType); + recordError(node->typeToken, errStr); + return false; } - - param->nameIndex = registerString(p->name.toString()); - param->location.line = p->identifierToken.startLine; - param->location.column = p->identifierToken.startColumn; signal->parameters->append(param); p = p->next; } @@ -886,25 +839,21 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) } else { const QStringRef &name = node->name; - bool typeFound = false; - QV4::CompiledData::Property::Type type = QV4::CompiledData::Property::Var; + Property *property = New<Property>(); + property->isReadOnly = node->isReadonlyMember; - for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { - const TypeNameToType *t = propTypeNameToTypes + ii; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - type = t->type; - typeFound = true; - } - } + QV4::CompiledData::BuiltinType builtinPropertyType = Parameter::stringToBuiltinType(memberType); + bool typeFound = builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin; + if (typeFound) + property->setBuiltinType(builtinPropertyType); if (!typeFound && memberType.at(0).isUpper()) { const QStringRef &typeModifier = node->typeModifier; - if (typeModifier.isEmpty()) { - type = QV4::CompiledData::Property::Custom; - } else if (typeModifier == QLatin1String("list")) { - type = QV4::CompiledData::Property::CustomList; - } else { + property->setCustomType(registerString(memberType)); + if (typeModifier == QLatin1String("list")) { + property->isList = true; + } else if (!typeModifier.isEmpty()) { recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); return false; } @@ -919,16 +868,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) return false; } - Property *property = New<Property>(); - property->flags = 0; - if (node->isReadonlyMember) - property->flags |= QV4::CompiledData::Property::IsReadOnly; - property->type = type; - if (type >= QV4::CompiledData::Property::Custom) - property->customTypeNameIndex = registerString(memberType); - else - property->customTypeNameIndex = emptyStringIndex; - const QString propName = name.toString(); property->nameIndex = registerString(propName); @@ -969,7 +908,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) { - if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) { + if (QQmlJS::AST::FunctionExpression *funDecl = node->sourceElement->asFunctionDefinition()) { CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>(); foe->node = funDecl; foe->parentNode = funDecl; @@ -983,13 +922,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) f->index = index; f->nameIndex = registerString(funDecl->name.toString()); - const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList(); + 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 QString &arg : formals) { - f->formals[i] = registerString(arg); + for (const auto &arg : formals) { + f->formals[i].init(jsGenerator, arg.id, arg.typeName()); ++i; } @@ -1022,7 +964,7 @@ QStringRef IRBuilder::asStringRef(QQmlJS::AST::Node *node) return textRefAt(node->firstSourceLocation(), node->lastSourceLocation()); } -void IRBuilder::extractVersion(QStringRef string, int *maj, int *min) +void IRBuilder::extractVersion(const QStringRef &string, int *maj, int *min) { *maj = -1; *min = -1; @@ -1051,7 +993,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST binding->valueLocation.line = loc.startLine; binding->valueLocation.column = loc.startColumn; binding->type = QV4::CompiledData::Binding::Type_Invalid; - if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + if (_propertyDeclaration && _propertyDeclaration->isReadOnly) binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); @@ -1282,7 +1224,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo binding->flags = 0; - if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + if (_propertyDeclaration && _propertyDeclaration->isReadOnly) binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; // No type name on the initializer means it must be a group property @@ -1513,7 +1455,8 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O void IRBuilder::recordError(const QQmlJS::AST::SourceLocation &location, const QString &description) { QQmlJS::DiagnosticMessage error; - error.loc = location; + error.line = location.startLine; + error.column = location.startColumn; error.message = description; errors << error; } @@ -1545,7 +1488,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement) bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement) { - if (property->type != QV4::CompiledData::Property::Custom) + if (property->isBuiltinType || property->isList) return false; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); if (!exprStmt) @@ -1559,17 +1502,12 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen output.jsGenerator.stringTable.registerString(output.jsModule.fileName); output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit; - QV4::CompiledData::Unit *jsUnit = nullptr; - const bool finalize = !compilationUnit->data; // We may already have unit data if we're loading an ahead-of-time generated cache file. - if (!finalize) { - jsUnit = const_cast<QV4::CompiledData::Unit *>(compilationUnit->data); -#ifndef V4_BOOTSTRAP - output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings(); -#endif + if (output.javaScriptCompilationUnit.data) { + jsUnit = const_cast<QV4::CompiledData::Unit *>(output.javaScriptCompilationUnit.data); + output.javaScriptCompilationUnit.dynamicStrings = output.jsGenerator.stringTable.allStrings(); } else { QV4::CompiledData::Unit *createdUnit; jsUnit = createdUnit = output.jsGenerator.generateUnit(); @@ -1581,22 +1519,15 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen break; } } - // This unit's memory was allocated with malloc on the heap, so it's - // definitely not suitable for StaticData access. - createdUnit->flags &= ~QV4::CompiledData::Unit::StaticData; -#ifndef V4_BOOTSTRAP if (dependencyHasher) { - QCryptographicHash hash(QCryptographicHash::Md5); - if (dependencyHasher(&hash)) { - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)); - memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), sizeof(createdUnit->dependencyMD5Checksum)); + const QByteArray checksum = dependencyHasher(); + if (checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)) { + memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), + sizeof(createdUnit->dependencyMD5Checksum)); } } -#else - Q_UNUSED(dependencyHasher); -#endif + createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName); createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl); } @@ -1730,7 +1661,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen signalToWrite->nParameters = s->parameters->count; QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite)); - for (SignalParameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) + for (Parameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) *parameterToWrite = *param; int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count); @@ -1765,14 +1696,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen } } - if (finalize) { + if (!output.javaScriptCompilationUnit.data) { // Combine the qml data into the general unit data. jsUnit = static_cast<QV4::CompiledData::Unit *>(realloc(jsUnit, jsUnit->unitSize + totalSize)); jsUnit->offsetToQmlUnit = jsUnit->unitSize; jsUnit->unitSize += totalSize; memcpy(jsUnit->qmlUnit(), qmlUnit, totalSize); free(qmlUnit); - jsUnit->generateChecksum(); + QV4::Compiler::JSUnitGenerator::generateUnitChecksum(jsUnit); qmlUnit = jsUnit->qmlUnit(); } @@ -1798,7 +1729,8 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen qDebug() << " " << totalStringSize << "bytes total strings"; } - compilationUnit->setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl); + output.javaScriptCompilationUnit.setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, + output.jsModule.finalUrl); } char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const @@ -1815,19 +1747,11 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding return bindingPtr; } -JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, - QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, - QQmlJS::AST::UiProgram *qmlRoot, - const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames) - : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false) - , sourceCode(sourceCode) - , jsEngine(jsEngine) - , qmlRoot(qmlRoot) - , stringPool(stringPool) +JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames) + : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/false), document(document) { m_globalNames = globalNames; - - _module = jsModule; + _module = &document->jsModule; _fileNameIsUrl = true; } @@ -1835,18 +1759,18 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil { auto qmlName = [&](const CompiledFunctionOrExpression &c) { if (c.nameIndex != 0) - return stringPool->stringForIndex(c.nameIndex); + return document->stringAt(c.nameIndex); else return QStringLiteral("%qml-expression-entry"); }; QVector<int> runtimeFunctionIndices(functions.size()); - QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global); + QV4::Compiler::ScanFunctions scan(this, document->code, QV4::Compiler::ContextType::Global); scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding); for (const CompiledFunctionOrExpression &f : functions) { - Q_ASSERT(f.node != qmlRoot); - Q_ASSERT(f.parentNode && f.parentNode != qmlRoot); - QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node); + Q_ASSERT(f.node != document->program); + Q_ASSERT(f.parentNode && f.parentNode != document->program); + auto function = f.node->asFunctionDefinition(); if (function) { scan.enterQmlFunction(function); @@ -1860,7 +1784,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil } scan.leaveEnvironment(); - if (hasError) + if (hasError()) return QVector<int>(); _context = nullptr; @@ -1868,9 +1792,9 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil for (int i = 0; i < functions.count(); ++i) { const CompiledFunctionOrExpression &qmlFunction = functions.at(i); QQmlJS::AST::Node *node = qmlFunction.node; - Q_ASSERT(node != qmlRoot); + Q_ASSERT(node != document->program); - QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(node); + QQmlJS::AST::FunctionExpression *function = node->asFunctionDefinition(); QString name; if (function) @@ -1883,7 +1807,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil body = function->body; } else { // Synthesize source elements. - QQmlJS::MemoryPool *pool = jsEngine->pool(); + QQmlJS::MemoryPool *pool = document->jsParserEngine.pool(); QQmlJS::AST::Statement *stmt = node->statementCast(); if (!stmt) { @@ -1904,213 +1828,57 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil return runtimeFunctionIndices; } -#ifndef V4_BOOTSTRAP - -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - - // Find the first property - while (d && d->isFunction()) - d = cache->overrideData(d); - - if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else { - return d; - } -} - - -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const +bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots) { - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - if (notInRevision) *notInRevision = false; - - while (d && !(d->isFunction())) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else if (d && d->isSignal()) { - return d; - } - - if (name.endsWith(QLatin1String("Changed"))) { - QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); - - d = property(propName, notInRevision); - if (d) - return cache->signal(d->notifyIndex()); + for (int i = 0; i < componentRoots.count(); ++i) { + if (!compileComponent(componentRoots.at(i))) + return false; } - return nullptr; -} - -IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) - : unit(qmlData) - , output(output) -{ - pool = output->jsParserEngine.pool(); + return compileComponent(/*root object*/0); } -void IRLoader::load() +bool JSCodeGen::compileComponent(int contextObject) { - output->jsGenerator.stringTable.initializeFromBackingUnit(unit); - - const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); - - for (quint32 i = 0; i < qmlUnit->nImports; ++i) - output->imports << qmlUnit->importAt(i); - - if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { - QmlIR::Pragma *p = New<QmlIR::Pragma>(); - p->location = QV4::CompiledData::Location(); - p->type = QmlIR::Pragma::PragmaSingleton; - output->pragmas << p; + const QmlIR::Object *obj = document->objects.at(contextObject); + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->bindingCount() == 1); + const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); + Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + contextObject = componentBinding->value.objectIndex; } - for (uint i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); - QmlIR::Object *object = loadObject(serializedObject); - output->objects.append(object); - } + return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject); } -struct FakeExpression : public QQmlJS::AST::NullExpression -{ - FakeExpression(int start, int length) - : location(start, length) - {} - - virtual QQmlJS::AST::SourceLocation firstSourceLocation() const - { return location; } - - virtual QQmlJS::AST::SourceLocation lastSourceLocation() const - { return location; } - -private: - QQmlJS::AST::SourceLocation location; -}; - -QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) { - QmlIR::Object *object = pool->New<QmlIR::Object>(); - object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); - - object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; - object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - object->flags = serializedObject->flags; - object->id = serializedObject->id; - object->location = serializedObject->location; - object->locationOfIdProperty = serializedObject->locationOfIdProperty; - - QVector<int> functionIndices; - functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); - - for (uint i = 0; i < serializedObject->nBindings; ++i) { - QmlIR::Binding *b = pool->New<QmlIR::Binding>(); - *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; - object->bindings->append(b); - if (b->type == QV4::CompiledData::Binding::Type_Script) { - functionIndices.append(b->value.compiledScriptIndex); - b->value.compiledScriptIndex = functionIndices.count() - 1; - - QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); - foe->nameIndex = 0; - - QQmlJS::AST::ExpressionNode *expr; - - if (b->stringIndex != quint32(0)) { - const int start = output->code.length(); - const QString script = output->stringAt(b->stringIndex); - const int length = script.length(); - output->code.append(script); - expr = new (pool) FakeExpression(start, length); - } else - expr = new (pool) QQmlJS::AST::NullExpression(); - foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy - object->functionsAndExpressions->append(foe); - } - } - - Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + QmlIR::Object *object = document->objects.at(objectIndex); + if (object->flags & QV4::CompiledData::Object::IsComponent) + return true; - for (uint i = 0; i < serializedObject->nSignals; ++i) { - const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); - QmlIR::Signal *s = pool->New<QmlIR::Signal>(); - s->nameIndex = serializedSignal->nameIndex; - s->location = serializedSignal->location; - s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); - - for (uint i = 0; i < serializedSignal->nParameters; ++i) { - QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); - *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); - s->parameters->append(p); - } - - object->qmlSignals->append(s); - } - - for (uint i = 0; i < serializedObject->nEnums; ++i) { - const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); - QmlIR::Enum *e = pool->New<QmlIR::Enum>(); - e->nameIndex = serializedEnum->nameIndex; - e->location = serializedEnum->location; - e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >(); - - for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { - QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>(); - *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i); - e->enumValues->append(v); - } - - object->qmlEnums->append(e); - } - - const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); - for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { - QmlIR::Property *p = pool->New<QmlIR::Property>(); - *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; - object->properties->append(p); - } + if (object->functionsAndExpressions->count > 0) { + QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; + for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) + functionsToCompile << *foe; + const QVector<int> runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile); + if (hasError()) + return false; - { - const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); - for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { - QmlIR::Alias *a = pool->New<QmlIR::Alias>(); - *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias; - object->aliases->append(a); - } + object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(), + runtimeFunctionIndices); } - const quint32_le *functionIdx = serializedObject->functionOffsetTable(); - for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { - QmlIR::Function *f = pool->New<QmlIR::Function>(); - const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); - - functionIndices.append(*functionIdx); - f->index = functionIndices.count() - 1; - f->location = compiledFunction->location; - f->nameIndex = compiledFunction->nameIndex; + for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { + if (binding->type < QV4::CompiledData::Binding::Type_Object) + continue; - f->formals.allocate(pool, int(compiledFunction->nFormals)); - const quint32_le *formalNameIdx = compiledFunction->formalsTable(); - for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) - f->formals[i] = *formalNameIdx; + int target = binding->value.objectIndex; + int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; - object->functions->append(f); + if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope)) + return false; } - object->runtimeFunctionIndices.allocate(pool, functionIndices); - - return object; + return true; } - -#endif // V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 22bc2d2953..4279f5b768 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -55,25 +55,22 @@ #include <private/qv4compiler_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsmemorypool_p.h> +#include <private/qqmljsfixedpoolarray_p.h> #include <private/qv4codegen_p.h> #include <private/qv4compiler_p.h> #include <QTextStream> #include <QCoreApplication> -#ifndef V4_BOOTSTRAP -#include <private/qqmlpropertycache_p.h> -#endif - QT_BEGIN_NAMESPACE class QQmlPropertyCache; class QQmlContextData; class QQmlTypeNameCache; +struct QQmlIRLoader; namespace QmlIR { struct Document; -struct IRLoader; template <typename T> struct PoolList @@ -219,22 +216,30 @@ struct Enum }; -struct SignalParameter : public QV4::CompiledData::Parameter +struct Parameter : public QV4::CompiledData::Parameter { - SignalParameter *next; + 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); }; struct Signal { int nameIndex; QV4::CompiledData::Location location; - PoolList<SignalParameter> *parameters; + PoolList<Parameter> *parameters; QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const; int parameterCount() const { return parameters->count; } - PoolList<SignalParameter>::Iterator parametersBegin() const { return parameters->begin(); } - PoolList<SignalParameter>::Iterator parametersEnd() const { return parameters->end(); } + PoolList<Parameter>::Iterator parametersBegin() const { return parameters->begin(); } + PoolList<Parameter>::Iterator parametersEnd() const { return parameters->end(); } Signal *next; }; @@ -264,17 +269,18 @@ struct Function QV4::CompiledData::Location location; int nameIndex; quint32 index; // index in parsedQML::functions - FixedPoolArray<int> formals; + QQmlJS::FixedPoolArray<Parameter> 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; }; -struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression +struct Q_QMLCOMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression { CompiledFunctionOrExpression() {} @@ -285,7 +291,7 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression CompiledFunctionOrExpression *next = nullptr; }; -struct Q_QML_PRIVATE_EXPORT Object +struct Q_QMLCOMPILER_PRIVATE_EXPORT Object { Q_DECLARE_TR_FUNCTIONS(Object) public: @@ -344,14 +350,14 @@ public: QString bindingAsString(Document *doc, int scriptIndex) const; PoolList<CompiledFunctionOrExpression> *functionsAndExpressions; - FixedPoolArray<int> runtimeFunctionIndices; + QQmlJS::FixedPoolArray<int> runtimeFunctionIndices; - FixedPoolArray<quint32> namedObjectsInComponent; + QQmlJS::FixedPoolArray<quint32> namedObjectsInComponent; int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); } const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } private: - friend struct IRLoader; + friend struct ::QQmlIRLoader; PoolList<Property> *properties; PoolList<Alias> *aliases; @@ -361,7 +367,7 @@ private: PoolList<Function> *functions; }; -struct Q_QML_PRIVATE_EXPORT Pragma +struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma { enum PragmaType { PragmaSingleton = 0x1 @@ -371,7 +377,7 @@ struct Q_QML_PRIVATE_EXPORT Pragma QV4::CompiledData::Location location; }; -struct Q_QML_PRIVATE_EXPORT Document +struct Q_QMLCOMPILER_PRIVATE_EXPORT Document { Document(bool debugMode); QString code; @@ -383,15 +389,13 @@ struct Q_QML_PRIVATE_EXPORT Document QVector<Object*> objects; QV4::Compiler::JSUnitGenerator jsGenerator; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit; + QV4::CompiledData::CompilationUnit javaScriptCompilationUnit; int registerString(const QString &str) { return jsGenerator.registerString(str); } QString stringAt(int index) const { return jsGenerator.stringForIndex(index); } - - static void removeScriptPragmas(QString &script); }; -class Q_QML_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives +class Q_QMLCOMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives { QmlIR::Document *document; QQmlJS::Engine *engine; @@ -405,7 +409,7 @@ public: void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column) override; }; -struct Q_QML_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor +struct Q_QMLCOMPILER_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor { Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator) public: @@ -436,7 +440,7 @@ public: void throwRecursionDepthError() override { - recordError(AST::SourceLocation(), + recordError(QQmlJS::AST::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } @@ -449,19 +453,25 @@ public: static QString asString(QQmlJS::AST::UiQualifiedId *node); QStringRef asStringRef(QQmlJS::AST::Node *node); - static void extractVersion(QStringRef string, int *maj, int *min); + static void extractVersion(const QStringRef &string, int *maj, int *min); QStringRef textRefAt(const QQmlJS::AST::SourceLocation &loc) const { return QStringRef(&sourceCode, loc.offset, loc.length); } QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first, const QQmlJS::AST::SourceLocation &last) const; - void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode); + void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, + QQmlJS::AST::Node *parentNode); void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding); - void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, AST::Node *parentNode); + void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, + QQmlJS::AST::Node *parentNode); void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); - void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode); - void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); + void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, + const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, + QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode); + void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, + const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, + int objectIndex, bool isListItem = false, bool isOnAssignment = false); bool appendAlias(QQmlJS::AST::UiPublicMember *node); @@ -503,7 +513,7 @@ public: QV4::Compiler::JSUnitGenerator *jsGenerator; }; -struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator +struct Q_QMLCOMPILER_PRIVATE_EXPORT QmlUnitGenerator { void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher()); @@ -512,76 +522,23 @@ private: char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; -#ifndef V4_BOOTSTRAP -struct Q_QML_EXPORT PropertyResolver -{ - PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache) - : cache(cache) - {} - - QQmlPropertyData *property(int index) const - { - return cache->property(index); - } - - enum RevisionCheck { - CheckRevision, - IgnoreRevision - }; - - QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, RevisionCheck check = CheckRevision) const; - - // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; - - QQmlRefPointer<QQmlPropertyCache> cache; -}; -#endif - -struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen +struct Q_QMLCOMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen { - JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, - QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, - const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames); + JSCodeGen(Document *document, const QSet<QString> &globalNames); // Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); -private: - QString sourceCode; - QQmlJS::Engine *jsEngine; // needed for memory pool - QQmlJS::AST::UiProgram *qmlRoot; - const QV4::Compiler::StringTableGenerator *stringPool; -}; - -struct Q_QML_PRIVATE_EXPORT IRLoader { - IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); - - void load(); + bool generateCodeForComponents(const QVector<quint32> &componentRoots); + bool compileComponent(int contextObject); + bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); private: - QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); - - template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } - - const QV4::CompiledData::Unit *unit; - QmlIR::Document *output; - QQmlJS::MemoryPool *pool; + Document *document; }; } // namespace QmlIR -struct QQmlCompileError -{ - QQmlCompileError() {} - QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) - : location(location), description(description) {} - QV4::CompiledData::Location location; - QString description; - - bool isSet() const { return !description.isEmpty(); } -}; - QT_END_NAMESPACE #endif // QQMLIRBUILDER_P_H diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp deleted file mode 100644 index fb54da5b73..0000000000 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlpropertycachecreator_p.h" - -#include <private/qqmlengine_p.h> - -QT_BEGIN_NAMESPACE - -QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0); - -QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, - const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache) - : referencingObjectIndex(referencingObjectIndex) - , instantiatingBinding(instantiatingBinding) - , instantiatingPropertyName(instantiatingPropertyName) - , referencingObjectPropertyCache(referencingObjectPropertyCache) -{ -} - -bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() -{ - if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty) - return true; - - Q_ASSERT(referencingObjectIndex >= 0); - Q_ASSERT(referencingObjectPropertyCache); - Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); - - bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision, QmlIR::PropertyResolver::IgnoreRevision); - return instantiatingProperty != nullptr; -} - -QQmlRefPointer<QQmlPropertyCache> QQmlBindingInstantiationContext::instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const -{ - if (instantiatingProperty) { - if (instantiatingProperty->isQObject()) { - return enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType(), instantiatingProperty->typeMinorVersion()); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType())) { - return enginePrivate->cache(vtmo, instantiatingProperty->typeMinorVersion()); - } - } - return QQmlRefPointer<QQmlPropertyCache>(); -} - -void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const -{ - for (QQmlBindingInstantiationContext pendingBinding: *this) { - const int groupPropertyObjectIndex = pendingBinding.instantiatingBinding->value.objectIndex; - - if (propertyCaches->at(groupPropertyObjectIndex)) - continue; - - if (!pendingBinding.resolveInstantiatingProperty()) - continue; - - auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); - propertyCaches->set(groupPropertyObjectIndex, cache); - } -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h deleted file mode 100644 index 901602d17b..0000000000 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ /dev/null @@ -1,841 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QQMLPROPERTYCACHECREATOR_P_H -#define QQMLPROPERTYCACHECREATOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmlvaluetype_p.h> -#include <private/qqmlengine_p.h> - -QT_BEGIN_NAMESPACE - -struct QQmlBindingInstantiationContext { - QQmlBindingInstantiationContext() {} - QQmlBindingInstantiationContext(int referencingObjectIndex, - const QV4::CompiledData::Binding *instantiatingBinding, - const QString &instantiatingPropertyName, - QQmlPropertyCache *referencingObjectPropertyCache); - - bool resolveInstantiatingProperty(); - QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const; - - int referencingObjectIndex = -1; - const QV4::CompiledData::Binding *instantiatingBinding = nullptr; - QString instantiatingPropertyName; - QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache; - QQmlPropertyData *instantiatingProperty = nullptr; -}; - -struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext> -{ - void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const; -}; - -struct QQmlPropertyCacheCreatorBase -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) -public: - static QAtomicInt classIndexCounter; -}; - -template <typename ObjectContainer> -class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase -{ -public: - typedef typename ObjectContainer::CompiledObject CompiledObject; - - QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, - QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, - QQmlEnginePrivate *enginePrivate, - const ObjectContainer *objectContainer, const QQmlImports *imports); - - QQmlCompileError buildMetaObjects(); - -protected: - QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); - QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; - QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); - - QString stringAt(int index) const { return objectContainer->stringAt(index); } - - QQmlEnginePrivate * const enginePrivate; - const ObjectContainer * const objectContainer; - const QQmlImports * const imports; - QQmlPropertyCacheVector *propertyCaches; - QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings; -}; - -template <typename ObjectContainer> -inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, - QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, - QQmlEnginePrivate *enginePrivate, - const ObjectContainer *objectContainer, const QQmlImports *imports) - : enginePrivate(enginePrivate) - , objectContainer(objectContainer) - , imports(imports) - , propertyCaches(propertyCaches) - , pendingGroupPropertyBindings(pendingGroupPropertyBindings) -{ - propertyCaches->resize(objectContainer->objectCount()); -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() -{ - QQmlBindingInstantiationContext context; - return buildMetaObjectRecursively(/*root object*/0, context); -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) -{ - const CompiledObject *obj = objectContainer->objectAt(objectIndex); - - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0; - if (!needVMEMetaObject) { - auto binding = obj->bindingsBegin(); - auto end = obj->bindingsEnd(); - for ( ; binding != end; ++binding) { - if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { - // If the on assignment is inside a group property, we need to distinguish between QObject based - // group properties and value type group properties. For the former the base type is derived from - // the property that references us, for the latter we only need a meta-object on the referencing object - // because interceptors can't go to the shared value type instances. - if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) { - if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { - const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); - auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); - if (error.isSet()) - return error; - } - } else { - // On assignments are implemented using value interceptors, which require a VME meta object. - needVMEMetaObject = true; - } - break; - } - } - } - - QQmlRefPointer<QQmlPropertyCache> baseTypeCache; - { - QQmlCompileError error; - baseTypeCache = propertyCacheForObject(obj, context, &error); - if (error.isSet()) - return error; - } - - if (baseTypeCache) { - if (needVMEMetaObject) { - QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache); - if (error.isSet()) - return error; - } else { - propertyCaches->set(objectIndex, baseTypeCache); - } - } - - if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { - auto binding = obj->bindingsBegin(); - auto end = obj->bindingsEnd(); - for ( ; binding != end; ++binding) - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); - - // Binding to group property where we failed to look up the type of the - // property? Possibly a group property that is an alias that's not resolved yet. - // Let's attempt to resolve it after we're done with the aliases and fill in the - // propertyCaches entry then. - if (!context.resolveInstantiatingProperty()) - pendingGroupPropertyBindings->append(context); - - QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); - if (error.isSet()) - return error; - } - } - - QQmlCompileError noError; - return noError; -} - -template <typename ObjectContainer> -inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const -{ - if (context.instantiatingProperty) { - return context.instantiatingPropertyCache(enginePrivate); - } else if (obj->inheritedTypeNameIndex != 0) { - auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->isFullyDynamicType) { - if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); - return nullptr; - } - if (obj->signalCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); - return nullptr; - } - if (obj->functionCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); - return nullptr; - } - } - - return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { - auto *typeRef = objectContainer->resolvedType( - context.instantiatingBinding->propertyNameIndex); - Q_ASSERT(typeRef); - QQmlType qmltype = typeRef->type; - if (!qmltype.isValid()) { - QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); - if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) { - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } - } - - const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate); - if (!attachedMo) { - *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); - return nullptr; - } - return enginePrivate->cache(attachedMo); - } - return nullptr; -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache) -{ - QQmlRefPointer<QQmlPropertyCache> cache; - cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), - obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), - obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->enumCount())); - - propertyCaches->set(objectIndex, cache); - propertyCaches->setNeedsVMEMetaObject(objectIndex); - - struct TypeData { - QV4::CompiledData::Property::Type dtype; - int metaType; - } builtinTypes[] = { - { QV4::CompiledData::Property::Var, QMetaType::QVariant }, - { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, - { QV4::CompiledData::Property::Int, QMetaType::Int }, - { QV4::CompiledData::Property::Bool, QMetaType::Bool }, - { QV4::CompiledData::Property::Real, QMetaType::Double }, - { QV4::CompiledData::Property::String, QMetaType::QString }, - { QV4::CompiledData::Property::Url, QMetaType::QUrl }, - { QV4::CompiledData::Property::Color, QMetaType::QColor }, - { QV4::CompiledData::Property::Font, QMetaType::QFont }, - { QV4::CompiledData::Property::Time, QMetaType::QTime }, - { QV4::CompiledData::Property::Date, QMetaType::QDate }, - { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, - { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, - { QV4::CompiledData::Property::Point, QMetaType::QPointF }, - { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, - { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, - { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, - { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, - { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, - { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } -}; - static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - - QByteArray newClassName; - - if (objectIndex == /*root object*/0) { - const QString path = objectContainer->url().path(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (lastSlash > -1) { - const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); - if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) - newClassName = nameBase.toUtf8() + "_QMLTYPE_" + - QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); - } - } - if (newClassName.isEmpty()) { - newClassName = QQmlMetaObject(baseTypeCache.data()).className(); - newClassName.append("_QML_"); - newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); - } - - cache->_dynamicClassName = newClassName; - - int varPropCount = 0; - - QmlIR::PropertyResolver resolver(baseTypeCache); - - auto p = obj->propertiesBegin(); - auto pend = obj->propertiesEnd(); - for ( ; p != pend; ++p) { - if (p->type == QV4::CompiledData::Property::Var) - varPropCount++; - - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); - if (d && d->isFinal()) - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); - } - - auto a = obj->aliasesBegin(); - auto aend = obj->aliasesEnd(); - for ( ; a != aend; ++a) { - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); - if (d && d->isFinal()) - return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); - } - - int effectivePropertyIndex = cache->propertyIndexCacheStart; - int effectiveMethodIndex = cache->methodIndexCacheStart; - - // For property change signal override detection. - // We prepopulate a set of signal names which already exist in the object, - // and throw an error if there is a signal/method defined as an override. - QSet<QString> seenSignals; - seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); - QQmlPropertyCache *parentCache = cache.data(); - while ((parentCache = parentCache->parent())) { - if (int pSigCount = parentCache->signalCount()) { - int pSigOffset = parentCache->signalOffset(); - for (int i = pSigOffset; i < pSigCount; ++i) { - QQmlPropertyData *currPSig = parentCache->signal(i); - // XXX TODO: find a better way to get signal name from the property data :-/ - for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); - iter != parentCache->stringCache.end(); ++iter) { - if (currPSig == (*iter).second) { - seenSignals.insert(iter.key()); - break; - } - } - } - } - } - - // Set up notify signals for properties - first normal, then alias - p = obj->propertiesBegin(); - pend = obj->propertiesEnd(); - for ( ; p != pend; ++p) { - auto flags = QQmlPropertyData::defaultSignalFlags(); - - QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - a = obj->aliasesBegin(); - aend = obj->aliasesEnd(); - for ( ; a != aend; ++a) { - auto flags = QQmlPropertyData::defaultSignalFlags(); - - QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - auto e = obj->enumsBegin(); - auto eend = obj->enumsEnd(); - for ( ; e != eend; ++e) { - const int enumValueCount = e->enumValueCount(); - QVector<QQmlEnumValue> values; - values.reserve(enumValueCount); - - auto enumValue = e->enumValuesBegin(); - auto end = e->enumValuesEnd(); - for ( ; enumValue != end; ++enumValue) - values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value)); - - cache->appendEnum(stringAt(e->nameIndex), values); - } - - // Dynamic signals - auto s = obj->signalsBegin(); - auto send = obj->signalsEnd(); - for ( ; s != send; ++s) { - const int paramCount = s->parameterCount(); - - QList<QByteArray> names; - names.reserve(paramCount); - QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); - - if (paramCount) { - paramTypes[0] = paramCount; - - int i = 0; - auto param = s->parametersBegin(); - auto end = s->parametersEnd(); - for ( ; param != end; ++param, ++i) { - names.append(stringAt(param->nameIndex).toUtf8()); - if (param->type < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[param->type].metaType; - } else { - // lazily resolved type - Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); - const QString customTypeName = stringAt(param->customTypeNameIndex); - QQmlType qmltype; - if (!imports->resolveType(customTypeName, &qmltype, nullptr, nullptr, nullptr)) - return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); - - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - paramTypes[i + 1] = compilationUnit->metaTypeId; - } else { - paramTypes[i + 1] = qmltype.typeId(); - } - } - } - } - - auto flags = QQmlPropertyData::defaultSignalFlags(); - if (paramCount) - flags.hasArguments = true; - - QString signalName = stringAt(s->nameIndex); - if (seenSignals.contains(signalName)) - return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); - seenSignals.insert(signalName); - - cache->appendSignal(signalName, flags, effectiveMethodIndex++, - paramCount?paramTypes.constData():nullptr, names); - } - - - // Dynamic slots - auto function = objectContainer->objectFunctionsBegin(obj); - auto fend = objectContainer->objectFunctionsEnd(obj); - for ( ; function != fend; ++function) { - auto flags = QQmlPropertyData::defaultSlotFlags(); - - const QString slotName = stringAt(function->nameIndex); - if (seenSignals.contains(slotName)) - return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); - // Note: we don't append slotName to the seenSignals list, since we don't - // protect against overriding change signals or methods with properties. - - QList<QByteArray> parameterNames; - auto formal = function->formalsBegin(); - auto end = function->formalsEnd(); - for ( ; formal != end; ++formal) { - flags.hasArguments = true; - parameterNames << stringAt(*formal).toUtf8(); - } - - cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); - } - - - // Dynamic properties - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; - int propertyIdx = 0; - p = obj->propertiesBegin(); - pend = obj->propertiesEnd(); - for ( ; p != pend; ++p, ++propertyIdx) { - int propertyType = 0; - int propertTypeMinorVersion = 0; - QQmlPropertyData::Flags propertyFlags; - - if (p->type == QV4::CompiledData::Property::Var) { - propertyType = QMetaType::QVariant; - propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType; - } else if (p->type < builtinTypeCount) { - propertyType = builtinTypes[p->type].metaType; - - if (p->type == QV4::CompiledData::Property::Variant) - propertyFlags.type = QQmlPropertyData::Flags::QVariantType; - } else { - Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || - p->type == QV4::CompiledData::Property::Custom); - - QQmlType qmltype; - if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) { - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); - } - - Q_ASSERT(qmltype.isValid()); - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = compilationUnit->metaTypeId; - } else { - propertyType = compilationUnit->listMetaTypeId; - } - } else { - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = qmltype.typeId(); - propertTypeMinorVersion = qmltype.minorVersion(); - } else { - propertyType = qmltype.qListTypeId(); - } - } - - if (p->type == QV4::CompiledData::Property::Custom) - propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; - else - propertyFlags.type = QQmlPropertyData::Flags::QListType; - } - - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) - propertyFlags.isWritable = true; - - - QString propertyName = stringAt(p->nameIndex); - if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) - cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, propertTypeMinorVersion, effectiveSignalIndex); - - effectiveSignalIndex++; - } - - QQmlCompileError noError; - return noError; -} - -template <typename ObjectContainer> -class QQmlPropertyCacheAliasCreator -{ -public: - typedef typename ObjectContainer::CompiledObject CompiledObject; - - QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); - - void appendAliasPropertiesToMetaObjects(); - - QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); - -private: - void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); - QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags); - - void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const; - - int objectForId(const CompiledObject &component, int id) const; - - QQmlPropertyCacheVector *propertyCaches; - const ObjectContainer *objectContainer; -}; - -template <typename ObjectContainer> -inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer) - : propertyCaches(propertyCaches) - , objectContainer(objectContainer) -{ - -} - -template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects() -{ - // skip the root object (index 0) as that one does not have a first object index originating - // from a binding. - for (int i = 1; i < objectContainer->objectCount(); ++i) { - const CompiledObject &component = *objectContainer->objectAt(i); - if (!(component.flags & QV4::CompiledData::Object::IsComponent)) - continue; - - const auto rootBinding = component.bindingsBegin(); - appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex); - } - - const int rootObjectIndex = 0; - appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex); -} - -template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) -{ - QVector<int> objectsWithAliases; - collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases); - if (objectsWithAliases.isEmpty()) - return; - - const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) { - auto alias = object.aliasesBegin(); - auto end = object.aliasesEnd(); - for ( ; alias != end; ++alias) { - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); - - const int targetObjectIndex = objectForId(component, alias->targetObjectId); - Q_ASSERT(targetObjectIndex >= 0); - - if (alias->aliasToLocalAlias) - continue; - - if (alias->encodedMetaPropertyIndex == -1) - continue; - - const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); - Q_ASSERT(targetCache); - - int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex(); - QQmlPropertyData *targetProperty = targetCache->property(coreIndex); - if (!targetProperty) - return false; - } - return true; - }; - - do { - QVector<int> pendingObjects; - - for (int objectIndex: qAsConst(objectsWithAliases)) { - const CompiledObject &object = *objectContainer->objectAt(objectIndex); - - if (allAliasTargetsExist(object)) { - appendAliasesToPropertyCache(component, objectIndex); - } else { - pendingObjects.append(objectIndex); - } - - } - qSwap(objectsWithAliases, pendingObjects); - } while (!objectsWithAliases.isEmpty()); -} - -template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const -{ - const CompiledObject &object = *objectContainer->objectAt(objectIndex); - if (object.aliasCount() > 0) - objectsWithAliases->append(objectIndex); - - // Stop at Component boundary - if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) - return; - - auto binding = object.bindingsBegin(); - auto end = object.bindingsEnd(); - for (; binding != end; ++binding) { - if (binding->type != QV4::CompiledData::Binding::Type_Object - && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty - && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) - continue; - - collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases); - } -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias( - const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, - QQmlPropertyData::Flags *propertyFlags) -{ - *type = 0; - bool writable = false; - bool resettable = false; - - propertyFlags->isAlias = true; - - if (alias.aliasToLocalAlias) { - const QV4::CompiledData::Alias *lastAlias = &alias; - QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias}); - - do { - const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId); - Q_ASSERT(targetObjectIndex >= 0); - const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex); - Q_ASSERT(targetObject); - - auto nextAlias = targetObject->aliasesBegin(); - for (uint i = 0; i < lastAlias->localAliasIndex; ++i) - ++nextAlias; - - const QV4::CompiledData::Alias *targetAlias = &(*nextAlias); - if (seenAliases.contains(targetAlias)) { - return QQmlCompileError(targetAlias->location, - QQmlPropertyCacheCreatorBase::tr("Cyclic alias")); - } - - seenAliases.append(targetAlias); - lastAlias = targetAlias; - } while (lastAlias->aliasToLocalAlias); - - return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags); - } - - const int targetObjectIndex = objectForId(component, alias.targetObjectId); - Q_ASSERT(targetObjectIndex >= 0); - const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex); - - if (alias.encodedMetaPropertyIndex == -1) { - Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); - auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex); - if (!typeRef) { - // Can be caused by the alias target not being a valid id or property. E.g.: - // property alias dataValue: dataVal - // invalidAliasComponent { id: dataVal } - return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); - } - - if (typeRef->type.isValid()) - *type = typeRef->type.typeId(); - else - *type = typeRef->compilationUnit->metaTypeId; - - *minorVersion = typeRef->minorVersion; - - propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType; - } else { - int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex(); - int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex(); - - QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); - Q_ASSERT(targetCache); - QQmlPropertyData *targetProperty = targetCache->property(coreIndex); - Q_ASSERT(targetProperty); - - *type = targetProperty->propType(); - - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); - - if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - *type = QVariant::Int; - else - *type = valueTypeMetaObject->property(valueTypeIndex).userType(); - } else { - if (targetProperty->isEnum()) { - *type = QVariant::Int; - } else { - // Copy type flags - propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); - - if (targetProperty->isVarProperty()) - propertyFlags->type = QQmlPropertyData::Flags::QVariantType; - } - } - } - - propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable; - propertyFlags->isResettable = resettable; - return QQmlCompileError(); -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( - const CompiledObject &component, int objectIndex) -{ - const CompiledObject &object = *objectContainer->objectAt(objectIndex); - if (!object.aliasCount()) - return QQmlCompileError(); - - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); - Q_ASSERT(propertyCache); - - int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); - int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); - - int aliasIndex = 0; - auto alias = object.aliasesBegin(); - auto end = object.aliasesEnd(); - for ( ; alias != end; ++alias, ++aliasIndex) { - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); - - int type = 0; - int minorVersion = 0; - QQmlPropertyData::Flags propertyFlags; - QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags); - if (error.isSet()) - return error; - - const QString propertyName = objectContainer->stringAt(alias->nameIndex); - - if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) - propertyCache->_defaultPropertyName = propertyName; - - propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, minorVersion, effectiveSignalIndex++); - } - - return QQmlCompileError(); -} - -template <typename ObjectContainer> -inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const -{ - for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) { - const int candidateIndex = component.namedObjectsInComponentTable()[i]; - const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex); - if (candidate.id == id) - return candidateIndex; - } - return -1; -} - -QT_END_NAMESPACE - -#endif // QQMLPROPERTYCACHECREATOR_P_H diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp deleted file mode 100644 index d20efe616b..0000000000 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ /dev/null @@ -1,727 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlpropertyvalidator_p.h" - -#include <private/qqmlcustomparser_p.h> -#include <private/qqmlstringconverters_p.h> -#include <QtCore/qdatetime.h> - -QT_BEGIN_NAMESPACE - -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) - : enginePrivate(enginePrivate) - , compilationUnit(compilationUnit) - , imports(imports) - , qmlUnit(compilationUnit->unitData()) - , propertyCaches(compilationUnit->propertyCaches) - , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) -{ - bindingPropertyDataPerObject->resize(compilationUnit->objectCount()); -} - -QVector<QQmlCompileError> QQmlPropertyValidator::validate() -{ - return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr); -} - -typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector; - -struct BindingFinder -{ - bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const - { - return name < binding->propertyNameIndex; - } - bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const - { - return binding->propertyNameIndex < name; - } - bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const - { - return lhs->propertyNameIndex < rhs->propertyNameIndex; - } -}; - -QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const -{ - const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex); - - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->nBindings == 1); - const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - return validateObject(componentBinding->value.objectIndex, componentBinding); - } - - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); - if (!propertyCache) - return QVector<QQmlCompileError>(); - - QQmlCustomParser *customParser = nullptr; - if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) - customParser = typeRef->type.customParser(); - } - - QList<const QV4::CompiledData::Binding*> customBindings; - - // Collect group properties first for sanity checking - // vector values are sorted by property name string index. - GroupPropertyVector groupProperties; - const QV4::CompiledData::Binding *binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - if (!binding->isGroupProperty()) - continue; - - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - continue; - - if (populatingValueTypeGroupProperty) { - return recordError(binding->location, tr("Property assignment expected")); - } - - GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - groupProperties.insert(pos, binding); - } - - QmlIR::PropertyResolver propertyResolver(propertyCache); - - QString defaultPropertyName; - QQmlPropertyData *defaultProperty = nullptr; - if (obj->indexOfDefaultPropertyOrAlias != -1) { - QQmlPropertyCache *cache = propertyCache->parent(); - defaultPropertyName = cache->defaultPropertyName(); - defaultProperty = cache->defaultProperty(); - } else { - defaultPropertyName = propertyCache->defaultPropertyName(); - defaultProperty = propertyCache->defaultProperty(); - } - - QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); - - binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - QString name = stringAt(binding->propertyNameIndex); - - if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - customBindings << binding; - continue; - } - } else if (QmlIR::IRBuilder::isSignalPropertyName(name) - && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - customBindings << binding; - continue; - } - } - - bool bindingToDefaultProperty = false; - bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; - - bool notInRevision = false; - QQmlPropertyData *pd = nullptr; - if (!name.isEmpty()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { - pd = propertyResolver.signal(name, ¬InRevision); - } else { - pd = propertyResolver.property(name, ¬InRevision, - QmlIR::PropertyResolver::CheckRevision); - } - - if (notInRevision) { - QString typeName = stringAt(obj->inheritedTypeNameIndex); - auto *objectType = resolvedType(obj->inheritedTypeNameIndex); - if (objectType && objectType->type.isValid()) { - return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type.module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); - } else { - return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); - } - } - } else { - if (isGroupProperty) - return recordError(binding->location, tr("Cannot assign a value directly to a grouped property")); - - pd = defaultProperty; - name = defaultPropertyName; - bindingToDefaultProperty = true; - } - - if (pd) - collectedBindingPropertyData[i] = pd; - - if (name.constData()->isUpper() && !binding->isAttachedProperty()) { - QQmlType type; - QQmlImportNamespace *typeNamespace = nullptr; - imports.resolveType(stringAt(binding->propertyNameIndex), &type, nullptr, nullptr, &typeNamespace); - if (typeNamespace) - return recordError(binding->location, tr("Invalid use of namespace")); - return recordError(binding->location, tr("Invalid attached object assignment")); - } - - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - const bool populatingValueTypeGroupProperty - = pd - && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()) - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment); - const QVector<QQmlCompileError> subObjectValidatorErrors - = validateObject(binding->value.objectIndex, binding, - populatingValueTypeGroupProperty); - if (!subObjectValidatorErrors.isEmpty()) - return subObjectValidatorErrors; - } - - // Signal handlers were resolved and checked earlier in the signal handler conversion pass. - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { - return recordError(binding->location, tr("Attached properties cannot be used here")); - } - continue; - } - - if (pd) { - GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); - - if (!pd->isWritable() - && !pd->isQList() - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) - ) { - - if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) - return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); - return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - } - - if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { - QString error; - if (pd->propType() == qMetaTypeId<QQmlScriptString>()) - error = tr( "Cannot assign multiple values to a script property"); - else - error = tr( "Cannot assign multiple values to a singular property"); - return recordError(binding->valueLocation, error); - } - - if (!bindingToDefaultProperty - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - && assigningToGroupProperty) { - QV4::CompiledData::Location loc = binding->valueLocation; - if (loc < (*assignedGroupProperty)->valueLocation) - loc = (*assignedGroupProperty)->valueLocation; - - if (pd && QQmlValueTypeFactory::isValueType(pd->propType())) - return recordError(loc, tr("Property has already been assigned a value")); - return recordError(loc, tr("Cannot assign a value directly to a grouped property")); - } - - if (binding->type < QV4::CompiledData::Binding::Type_Script) { - QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding); - if (bindingError.isSet()) - return recordError(bindingError); - } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { - QQmlCompileError bindingError = validateObjectBinding(pd, name, binding); - if (bindingError.isSet()) - return recordError(bindingError); - } else if (binding->isGroupProperty()) { - if (QQmlValueTypeFactory::isValueType(pd->propType())) { - if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) { - if (!pd->isWritable()) { - return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - } - } else { - return recordError(binding->location, tr("Invalid grouped property access")); - } - } else { - if (!enginePrivate->propertyCacheForType(pd->propType())) { - return recordError(binding->location, - tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type") - .arg(name) - .arg(QString::fromLatin1(QMetaType::typeName(pd->propType()))) - ); - } - } - } - } else { - if (customParser) { - customBindings << binding; - continue; - } - if (bindingToDefaultProperty) { - return recordError(binding->location, tr("Cannot assign to non-existent default property")); - } else { - return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name)); - } - } - } - - if (obj->idNameIndex) { - if (populatingValueTypeGroupProperty) - return recordError(obj->locationOfIdProperty, tr("Invalid use of id property with a value type")); - - bool notInRevision = false; - collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); - } - - if (customParser && !customBindings.isEmpty()) { - customParser->clearErrors(); - customParser->validator = this; - customParser->engine = enginePrivate; - customParser->imports = &imports; - customParser->verifyBindings(compilationUnit, customBindings); - customParser->validator = nullptr; - customParser->engine = nullptr; - customParser->imports = (QQmlImports*)nullptr; - QVector<QQmlCompileError> parserErrors = customParser->errors(); - if (!parserErrors.isEmpty()) - return parserErrors; - } - - (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; - - QVector<QQmlCompileError> noError; - return noError; -} - -QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const -{ - if (property->isQList()) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); - } - - QQmlCompileError noError; - - if (property->isEnum()) { - if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) - return noError; - - QString value = binding->valueAsString(compilationUnit.data()); - QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); - bool ok; - if (p.isFlagType()) { - p.enumerator().keysToValue(value.toUtf8().constData(), &ok); - } else - p.enumerator().keyToValue(value.toUtf8().constData(), &ok); - - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); - } - return noError; - } - - auto warnOrError = [&](const QString &error) { - if (binding->type == QV4::CompiledData::Binding::Type_Null) { - QQmlError warning; - warning.setUrl(compilationUnit->url()); - warning.setLine(binding->valueLocation.line); - warning.setColumn(binding->valueLocation.column); - warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML " - "is deprecated. This will become a compile error in " - "future versions of Qt.")); - enginePrivate->warning(warning); - return noError; - } - return QQmlCompileError(binding->valueLocation, error); - }; - - switch (property->propType()) { - case QMetaType::QVariant: - break; - case QVariant::String: { - if (!binding->evaluatesToString()) { - return warnOrError(tr("Invalid property assignment: string expected")); - } - } - break; - case QVariant::StringList: { - if (!binding->evaluatesToString()) { - return warnOrError(tr("Invalid property assignment: string or string list expected")); - } - } - break; - case QVariant::ByteArray: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return warnOrError(tr("Invalid property assignment: byte array expected")); - } - } - break; - case QVariant::Url: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return warnOrError(tr("Invalid property assignment: url expected")); - } - } - break; - case QVariant::UInt: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(compilationUnit->constants); - if (double(uint(d)) == d) - return noError; - } - return warnOrError(tr("Invalid property assignment: unsigned int expected")); - } - break; - case QVariant::Int: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(compilationUnit->constants); - if (double(int(d)) == d) - return noError; - } - return warnOrError(tr("Invalid property assignment: int expected")); - } - break; - case QMetaType::Float: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return warnOrError(tr("Invalid property assignment: number expected")); - } - } - break; - case QVariant::Double: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return warnOrError(tr("Invalid property assignment: number expected")); - } - } - break; - case QVariant::Color: { - bool ok = false; - QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: color expected")); - } - } - break; -#if QT_CONFIG(datestring) - case QVariant::Date: { - bool ok = false; - QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: date expected")); - } - } - break; - case QVariant::Time: { - bool ok = false; - QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: time expected")); - } - } - break; - case QVariant::DateTime: { - bool ok = false; - QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: datetime expected")); - } - } - break; -#endif // datestring - case QVariant::Point: { - bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::PointF: { - bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::Size: { - bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: size expected")); - } - } - break; - case QVariant::SizeF: { - bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: size expected")); - } - } - break; - case QVariant::Rect: { - bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: rect expected")); - } - } - break; - case QVariant::RectF: { - bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::Bool: { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return warnOrError(tr("Invalid property assignment: boolean expected")); - } - } - break; - case QVariant::Vector2D: { - struct { - float xp; - float yp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 2D vector expected")); - } - } - break; - case QVariant::Vector3D: { - struct { - float xp; - float yp; - float zy; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 3D vector expected")); - } - } - break; - case QVariant::Vector4D: { - struct { - float xp; - float yp; - float zy; - float wp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 4D vector expected")); - } - } - break; - case QVariant::Quaternion: { - struct { - float wp; - float xp; - float yp; - float zp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: quaternion expected")); - } - } - break; - case QVariant::RegExp: - return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); - default: { - // generate single literal value assignment to a list property if required - if (property->propType() == qMetaTypeId<QList<qreal> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return warnOrError(tr("Invalid property assignment: number or array of numbers expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QList<int> >()) { - bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); - if (ok) { - double n = binding->valueAsNumber(compilationUnit->constants); - if (double(int(n)) != n) - ok = false; - } - if (!ok) - return warnOrError(tr("Invalid property assignment: int or array of ints expected")); - break; - } else if (property->propType() == qMetaTypeId<QList<bool> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return warnOrError(tr("Invalid property assignment: bool or array of bools expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return warnOrError(tr("Invalid property assignment: url or array of urls expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QList<QString> >()) { - if (!binding->evaluatesToString()) { - return warnOrError(tr("Invalid property assignment: string or array of strings expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QJSValue>()) { - break; - } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { - break; - } else if (property->isQObject() - && binding->type == QV4::CompiledData::Binding::Type_Null) { - break; - } - - // otherwise, try a custom type assignment - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); - if (!converter) { - return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); - } - } - break; - } - return noError; -} - -/*! - Returns true if from can be assigned to a (QObject) property of type - to. -*/ -bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const -{ - QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); - - while (fromMo) { - if (fromMo == toMo) - return true; - fromMo = fromMo->parent(); - } - return false; -} - -QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const -{ - QVector<QQmlCompileError> errors; - errors.append(QQmlCompileError(location, description)); - return errors; -} - -QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const -{ - QVector<QQmlCompileError> errors; - errors.append(error); - return errors; -} - -QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const -{ - QQmlCompileError noError; - - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); - - bool isValueSource = false; - bool isPropertyInterceptor = false; - - const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex); - if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) { - QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - const QMetaObject *mo = cache->firstCppMetaObject(); - QQmlType qmlType; - while (mo && !qmlType.isValid()) { - qmlType = QQmlMetaType::qmlType(mo); - mo = mo->superClass(); - } - Q_ASSERT(qmlType.isValid()); - - isValueSource = qmlType.propertyValueSourceCast() != -1; - isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1; - } - - if (!isValueSource && !isPropertyInterceptor) { - return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); - } - - return noError; - } - - if (QQmlMetaType::isInterface(property->propType())) { - // Can only check at instantiation time if the created sub-object successfully casts to the - // target interface. - return noError; - } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId<QJSValue>()) { - // We can convert everything to QVariant :) - return noError; - } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType()); - if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); - if (!canCoerce(listType, source)) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); - } - } - return noError; - } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { - return noError; - } else if (QQmlValueTypeFactory::isValueType(property->propType())) { - auto typeName = QMetaType::typeName(property->propType()); - return QQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object") - .arg(typeName ? QString::fromLatin1(typeName) : QString::fromLatin1("<unknown type>")) - .arg(propertyName)); - } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); - } else { - // We want to use the raw metaObject here as the raw metaobject is the - // actual property type before we applied any extensions that might - // effect the properties on the type, but don't effect assignability - // Using -1 for the minor version ensures that we get the raw metaObject. - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1); - - // Will be true if the assigned type inherits propertyMetaObject - bool isAssignable = false; - // Determine isAssignable value - if (propertyMetaObject) { - QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); - while (c && !isAssignable) { - isAssignable |= c == propertyMetaObject; - c = c->parent(); - } - } - - if (!isAssignable) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") - .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType())))); - } - } - return noError; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h deleted file mode 100644 index e9ae844ccb..0000000000 --- a/src/qml/compiler/qqmlpropertyvalidator_p.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QQMLPROPERTYVALIDATOR_P_H -#define QQMLPROPERTYVALIDATOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmltypecompiler_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlPropertyValidator -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) -public: - QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); - - QVector<QQmlCompileError> validate(); - -private: - QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; - QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; - QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; - - bool canCoerce(int to, QQmlPropertyCache *fromMo) const; - - Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const; - Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const; - QString stringAt(int index) const { return compilationUnit->stringAt(index); } - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const - { - return compilationUnit->resolvedType(id); - } - - QQmlEnginePrivate *enginePrivate; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - const QQmlImports &imports; - const QV4::CompiledData::Unit *qmlUnit; - const QQmlPropertyCacheVector &propertyCaches; - - QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject; -}; - -QT_END_NAMESPACE - -#endif // QQMLPROPERTYVALIDATOR_P_H diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp deleted file mode 100644 index 66d3afc7a0..0000000000 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ /dev/null @@ -1,1420 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmltypecompiler_p.h" - -#include <private/qqmlirbuilder_p.h> -#include <private/qqmlobjectcreator_p.h> -#include <private/qqmlcustomparser_p.h> -#include <private/qqmlvmemetaobject_p.h> -#include <private/qqmlcomponent_p.h> -#include <private/qqmldelegatecomponent_p.h> - -#define COMPILE_EXCEPTION(token, desc) \ - { \ - recordError((token)->location, desc); \ - return false; \ - } - -QT_BEGIN_NAMESPACE - -QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, - QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) - : resolvedTypes(resolvedTypeCache) - , engine(engine) - , typeData(typeData) - , dependencyHasher(dependencyHasher) - , typeNameCache(typeNameCache) - , document(parsedQML) -{ -} - -QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() -{ - // Build property caches and VME meta object data - - for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd(); - it != end; ++it) { - QQmlCustomParser *customParser = (*it)->type.customParser(); - if (customParser) - customParsers.insert(it.key(), customParser); - } - - QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; - - - { - QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings, - engine, this, imports()); - QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); - if (error.isSet()) { - recordError(error); - return nullptr; - } - } - - { - QQmlDefaultPropertyMerger merger(this); - merger.mergeDefaultProperties(); - } - - { - SignalHandlerConverter converter(this); - if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) - return nullptr; - } - - { - QQmlEnumTypeResolver enumResolver(this); - if (!enumResolver.resolveEnumBindings()) - return nullptr; - } - - { - QQmlCustomParserScriptIndexer cpi(this); - cpi.annotateBindingsWithScriptStrings(); - } - - { - QQmlAliasAnnotator annotator(this); - annotator.annotateBindingsToAliases(); - } - - // Resolve component boundaries and aliases - - { - // Scan for components, determine their scopes and resolve aliases within the scope. - QQmlComponentAndAliasResolver resolver(this); - if (!resolver.resolve()) - return nullptr; - - pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_propertyCaches); - } - - { - QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this); - if (!deferredAndCustomParserBindingScanner.scanObject()) - return nullptr; - } - - if (!document->javaScriptCompilationUnit) { - // Compile JS binding expressions and signal handlers if necessary - { - // We can compile script strings ahead of time, but they must be compiled - // without type optimizations as their scope is always entirely dynamic. - QQmlScriptStringScanner sss(this); - sss.scan(); - } - - document->jsModule.fileName = typeData->urlString(); - document->jsModule.finalUrl = typeData->finalUrlString(); - QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, - document->program, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); - QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); - if (!jsCodeGen.generateCodeForComponents()) - return nullptr; - - document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false); - } - - // Generate QML compiled type data structures - - QmlIR::QmlUnitGenerator qmlGenerator; - qmlGenerator.generate(*document, dependencyHasher); - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit; - compilationUnit->typeNameCache = typeNameCache; - compilationUnit->resolvedTypes = *resolvedTypes; - compilationUnit->propertyCaches = std::move(m_propertyCaches); - Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount())); - - if (errors.isEmpty()) - return compilationUnit; - else - return nullptr; -} - -void QQmlTypeCompiler::recordError(QQmlError error) -{ - error.setUrl(url()); - errors << error; -} - -void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) -{ - QQmlError error; - error.setLine(location.line); - error.setColumn(location.column); - error.setDescription(description); - error.setUrl(url()); - errors << error; -} - -void QQmlTypeCompiler::recordError(const QQmlCompileError &error) -{ - recordError(error.location, error.description); -} - -QString QQmlTypeCompiler::stringAt(int idx) const -{ - return document->stringAt(idx); -} - -int QQmlTypeCompiler::registerString(const QString &str) -{ - return document->jsGenerator.registerString(str); -} - -int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v) -{ - return document->jsGenerator.registerConstant(v); -} - -const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const -{ - return document->javaScriptCompilationUnit->unitData(); -} - -const QQmlImports *QQmlTypeCompiler::imports() const -{ - return &typeData->imports(); -} - -QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const -{ - return &document->objects; -} - -void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches) -{ - m_propertyCaches = std::move(caches); - Q_ASSERT(m_propertyCaches.count() > 0); -} - -const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const -{ - return &m_propertyCaches; -} - -QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches() -{ - return std::move(m_propertyCaches); -} - -QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool() -{ - return document->jsParserEngine.pool(); -} - -QStringRef QQmlTypeCompiler::newStringRef(const QString &string) -{ - return document->jsParserEngine.newStringRef(string); -} - -const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const -{ - return &document->jsGenerator.stringTable; -} - -void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData) -{ - document->javaScriptCompilationUnit->bindingPropertyDataPerObject = propertyData; -} - -QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const -{ - return object->bindingAsString(document, scriptIndex); -} - -void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion) -{ - const quint32 moduleIdx = registerString(module); - const quint32 qualifierIdx = registerString(qualifier); - - for (int i = 0, count = document->imports.count(); i < count; ++i) { - const QV4::CompiledData::Import *existingImport = document->imports.at(i); - if (existingImport->type == QV4::CompiledData::Import::ImportLibrary - && existingImport->uriIndex == moduleIdx - && existingImport->qualifierIndex == qualifierIdx) - return; - } - auto pool = memoryPool(); - QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>(); - import->type = QV4::CompiledData::Import::ImportLibrary; - import->majorVersion = majorVersion; - import->minorVersion = minorVersion; - import->uriIndex = moduleIdx; - import->qualifierIndex = qualifierIdx; - document->imports.append(import); -} - -QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) - : compiler(typeCompiler) -{ -} - - - -SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , qmlObjects(*typeCompiler->qmlObjects()) - , imports(typeCompiler->imports()) - , customParsers(typeCompiler->customParserCache()) - , illegalNames(typeCompiler->enginePrivate()->v8engine()->illegalNames()) - , propertyCaches(typeCompiler->propertyCaches()) -{ -} - -bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations() -{ - for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) { - const QmlIR::Object * const obj = qmlObjects.at(objectIndex); - QQmlPropertyCache *cache = propertyCaches->at(objectIndex); - if (!cache) - continue; - if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) { - if (!(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) - continue; - } - const QString elementName = stringAt(obj->inheritedTypeNameIndex); - if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache)) - return false; - } - return true; -} - -bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache) -{ - // map from signal name defined in qml itself to list of parameters - QHash<QString, QStringList> customSignals; - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - QString propertyName = stringAt(binding->propertyNameIndex); - // Attached property? - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); - auto *typeRef = resolvedType(binding->propertyNameIndex); - QQmlType type = typeRef ? typeRef->type : QQmlType(); - if (!type.isValid()) { - if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) { - if (type.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } - } - - const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); - if (!attachedType) - COMPILE_EXCEPTION(binding, tr("Non-existent attached object")); - QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType); - if (!convertSignalHandlerExpressionsToFunctionDeclarations(attachedObj, propertyName, cache)) - return false; - continue; - } - - if (!QmlIR::IRBuilder::isSignalPropertyName(propertyName)) - continue; - - QmlIR::PropertyResolver resolver(propertyCache); - - Q_ASSERT(propertyName.startsWith(QLatin1String("on"))); - propertyName.remove(0, 2); - - // Note that the property name could start with any alpha or '_' or '$' character, - // so we need to do the lower-casing of the first alpha character. - for (int firstAlphaIndex = 0; firstAlphaIndex < propertyName.size(); ++firstAlphaIndex) { - if (propertyName.at(firstAlphaIndex).isUpper()) { - propertyName[firstAlphaIndex] = propertyName.at(firstAlphaIndex).toLower(); - break; - } - } - - QList<QString> parameters; - - bool notInRevision = false; - QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); - if (signal) { - int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex()); - sigIndex = propertyCache->originalClone(sigIndex); - - bool unnamedParameter = false; - - QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex); - for (int i = 0; i < parameterNames.count(); ++i) { - const QString param = QString::fromUtf8(parameterNames.at(i)); - if (param.isEmpty()) - unnamedParameter = true; - else if (unnamedParameter) { - COMPILE_EXCEPTION(binding, tr("Signal uses unnamed parameter followed by named parameter.")); - } else if (illegalNames.contains(param)) { - COMPILE_EXCEPTION(binding, tr("Signal parameter \"%1\" hides global variable.").arg(param)); - } - parameters += param; - } - } else { - if (notInRevision) { - // Try assinging it as a property later - if (resolver.property(propertyName, /*notInRevision ptr*/nullptr)) - continue; - - const QString &originalPropertyName = stringAt(binding->propertyNameIndex); - - auto *typeRef = resolvedType(obj->inheritedTypeNameIndex); - const QQmlType type = typeRef ? typeRef->type : QQmlType(); - if (type.isValid()) { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type.module()).arg(type.majorVersion()).arg(type.minorVersion())); - } else { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName)); - } - } - - // Try to look up the signal parameter names in the object itself - - // build cache if necessary - if (customSignals.isEmpty()) { - for (const QmlIR::Signal *signal = obj->firstSignal(); signal; signal = signal->next) { - const QString &signalName = stringAt(signal->nameIndex); - customSignals.insert(signalName, signal->parameterStringList(compiler->stringPool())); - } - - for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) { - const QString propName = stringAt(property->nameIndex); - customSignals.insert(propName, QStringList()); - } - } - - QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(propertyName); - if (entry == customSignals.constEnd() && propertyName.endsWith(QLatin1String("Changed"))) { - QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed"))); - entry = customSignals.constFind(alternateName); - } - - if (entry == customSignals.constEnd()) { - // Can't find even a custom signal, then just don't do anything and try - // keeping the binding as a regular property assignment. - continue; - } - - parameters = entry.value(); - } - - // Binding object to signal means connect the signal to the object's default method. - if (binding->type == QV4::CompiledData::Binding::Type_Object) { - binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject; - continue; - } - - if (binding->type != QV4::CompiledData::Binding::Type_Script) { - if (binding->type < QV4::CompiledData::Binding::Type_Script) { - COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)")); - } else { - COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment")); - } - } - - QQmlJS::MemoryPool *pool = compiler->memoryPool(); - - QQmlJS::AST::FormalParameterList *paramList = nullptr; - for (const QString ¶m : qAsConst(parameters)) { - QStringRef paramNameRef = compiler->newStringRef(param); - - QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr); - paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b); - } - - if (paramList) - paramList = paramList->finish(pool); - - QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); - QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr; - if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(foe->node)) { - if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(es->expression)) { - functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body); - functionDeclaration->functionToken = fe->functionToken; - functionDeclaration->identifierToken = fe->identifierToken; - functionDeclaration->lparenToken = fe->lparenToken; - functionDeclaration->rparenToken = fe->rparenToken; - functionDeclaration->lbraceToken = fe->lbraceToken; - functionDeclaration->rbraceToken = fe->rbraceToken; - } - } - if (!functionDeclaration) { - QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node); - QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement); - body = body->finish(); - - functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); - functionDeclaration->lbraceToken = functionDeclaration->functionToken - = foe->node->firstSourceLocation(); - functionDeclaration->rbraceToken = foe->node->lastSourceLocation(); - } - foe->node = functionDeclaration; - binding->propertyNameIndex = compiler->registerString(propertyName); - binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; - } - return true; -} - -QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) - , imports(typeCompiler->imports()) -{ -} - -bool QQmlEnumTypeResolver::resolveEnumBindings() -{ - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); - if (!propertyCache) - continue; - const QmlIR::Object *obj = qmlObjects.at(i); - - QmlIR::PropertyResolver resolver(propertyCache); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - - const QString propertyName = stringAt(binding->propertyNameIndex); - bool notInRevision = false; - QQmlPropertyData *pd = resolver.property(propertyName, ¬InRevision); - if (!pd) - continue; - - if (!pd->isEnum() && pd->propType() != QMetaType::Int) - continue; - - if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding)) - return false; - } - } - - return true; -} - -struct StaticQtMetaObject : public QObject -{ - static const QMetaObject *get() - { return &staticQtMetaObject; } -}; - -bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject) -{ - if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) { - COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString())); - } - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue)); -// binding->setNumberValueInternal((double)enumValue); - binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; - return true; -} - -bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding) -{ - bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum(); - if (!prop->isEnum() && !isIntProp) - return true; - - if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)) - COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex))); - - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); - const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - if (!string.constData()->isUpper()) - return true; - - // we support one or two '.' in the enum phrase: - // * <TypeName>.<EnumValue> - // * <TypeName>.<ScopedEnumName>.<EnumValue> - - int dot = string.indexOf(QLatin1Char('.')); - if (dot == -1 || dot == string.length()-1) - return true; - - int dot2 = string.indexOf(QLatin1Char('.'), dot+1); - if (dot2 != -1 && dot2 != string.length()-1) { - if (!string.at(dot+1).isUpper()) - return true; - if (string.indexOf(QLatin1Char('.'), dot2+1) != -1) - return true; - } - - QHashedStringRef typeName(string.constData(), dot); - const bool isQtObject = (typeName == QLatin1String("Qt")); - const QStringRef scopedEnumName = (dot2 != -1 ? string.midRef(dot + 1, dot2 - dot - 1) : QStringRef()); - // ### consider supporting scoped enums in Qt namespace - const QStringRef enumValue = string.midRef(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1); - - if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here? - // Allow enum assignment to ints. - bool ok; - int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok); - if (ok) { - if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject)) - return false; - } - return true; - } - QQmlType type; - imports->resolveType(typeName, &type, nullptr, nullptr, nullptr); - - if (!type.isValid() && !isQtObject) - return true; - - int value = 0; - bool ok = false; - - auto *tr = resolvedType(obj->inheritedTypeNameIndex); - if (type.isValid() && tr && tr->type == type) { - // When these two match, we can short cut the search - QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); - QMetaEnum menum = mprop.enumerator(); - QByteArray enumName = enumValue.toUtf8(); - if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8()) - return true; - - if (mprop.isFlagType()) { - value = menum.keysToValue(enumName.constData(), &ok); - } else { - value = menum.keyToValue(enumName.constData(), &ok); - } - } else { - // Otherwise we have to search the whole type - if (type.isValid()) { - if (!scopedEnumName.isEmpty()) - value = type.scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok); - else - value = type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); - } else { - QByteArray enumName = enumValue.toUtf8(); - const QMetaObject *metaObject = StaticQtMetaObject::get(); - for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) { - QMetaEnum e = metaObject->enumerator(ii); - value = e.keyToValue(enumName.constData(), &ok); - } - } - } - - if (!ok) - return true; - - return assignEnumToBinding(binding, enumValue, value, isQtObject); -} - -int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const -{ - Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer"); - *ok = false; - - if (scope != QLatin1String("Qt")) { - QQmlType type; - imports->resolveType(scope, &type, nullptr, nullptr, nullptr); - if (!type.isValid()) - return -1; - if (!enumName.isEmpty()) - return type.scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok); - return type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok); - } - - const QMetaObject *mo = StaticQtMetaObject::get(); - int i = mo->enumeratorCount(); - const QByteArray ba = enumValue.toUtf8(); - while (i--) { - int v = mo->enumerator(i).keyToValue(ba.constData(), ok); - if (*ok) - return v; - } - return -1; -} - -QQmlCustomParserScriptIndexer::QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , customParsers(typeCompiler->customParserCache()) -{ -} - -void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings() -{ - scanObjectRecursively(/*root object*/0); -} - -void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings) -{ - const QmlIR::Object * const obj = qmlObjects.at(objectIndex); - if (!annotateScriptBindings) - annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex); - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings); - continue; - } else if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - if (!annotateScriptBindings) - continue; - const QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - binding->stringIndex = compiler->registerString(script); - } -} - -QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) -{ -} - -void QQmlAliasAnnotator::annotateBindingsToAliases() -{ - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); - if (!propertyCache) - continue; - - const QmlIR::Object *obj = qmlObjects.at(i); - - QmlIR::PropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (!binding->isValueBinding()) - continue; - bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; - if (pd && pd->isAlias()) - binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; - } - } -} - -QQmlScriptStringScanner::QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) -{ - -} - -void QQmlScriptStringScanner::scan() -{ - const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>(); - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); - if (!propertyCache) - continue; - - const QmlIR::Object *obj = qmlObjects.at(i); - - QmlIR::PropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; - if (!pd || pd->propType() != scriptStringMetaType) - continue; - - QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - binding->stringIndex = compiler->registerString(script); - } - } -} - -QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , pool(typeCompiler->memoryPool()) - , qmlObjects(typeCompiler->qmlObjects()) - , propertyCaches(std::move(typeCompiler->takePropertyCaches())) -{ -} - -void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache) -{ - QmlIR::PropertyResolver propertyResolver(propertyCache); - - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object) - continue; - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex); - auto *tr = resolvedType(targetObject->inheritedTypeNameIndex); - Q_ASSERT(tr); - - const QMetaObject *firstMetaObject = nullptr; - if (tr->type.isValid()) - firstMetaObject = tr->type.metaObject(); - else if (tr->compilationUnit) - firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject(); - // 1: test for QQmlComponent - if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject) - continue; - // 2: test for QQmlAbstractDelegateComponent - while (firstMetaObject && firstMetaObject != &QQmlAbstractDelegateComponent::staticMetaObject) - firstMetaObject = firstMetaObject->superClass(); - if (firstMetaObject) - continue; - // if here, not a QQmlComponent or a QQmlAbstractDelegateComponent, so needs wrapping - - QQmlPropertyData *pd = nullptr; - if (binding->propertyNameIndex != quint32(0)) { - bool notInRevision = false; - pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision); - } else { - pd = defaultProperty; - } - if (!pd || !pd->isQObject()) - continue; - - QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType(), pd->typeMinorVersion()); - const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr; - while (mo) { - if (mo == &QQmlComponent::staticMetaObject) - break; - mo = mo->superClass(); - } - - if (!mo) - continue; - - // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}" - QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); - Q_ASSERT(componentType.isValid()); - const QString qualifier = QStringLiteral("QmlInternals"); - - compiler->addImport(componentType.module(), qualifier, componentType.majorVersion(), componentType.minorVersion()); - - QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>(); - syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()), compiler->registerString(QString())); - syntheticComponent->location = binding->valueLocation; - syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; - - if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) { - auto typeRef = new QV4::CompiledData::ResolvedTypeReference; - typeRef->type = componentType; - typeRef->majorVersion = componentType.majorVersion(); - typeRef->minorVersion = componentType.minorVersion(); - insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef); - } - - qmlObjects->append(syntheticComponent); - const int componentIndex = qmlObjects->count() - 1; - // Keep property caches symmetric - QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject); - propertyCaches.append(componentCache); - - QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>(); - *syntheticBinding = *binding; - syntheticBinding->type = QV4::CompiledData::Binding::Type_Object; - QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false); - Q_ASSERT(error.isEmpty()); - Q_UNUSED(error); - - binding->value.objectIndex = componentIndex; - - componentRoots.append(componentIndex); - } -} - -bool QQmlComponentAndAliasResolver::resolve() -{ - // Detect real Component {} objects as well as implicitly defined components, such as - // someItemDelegate: Item {} - // In the implicit case Item is surrounded by a synthetic Component {} because the property - // on the left hand side is of QQmlComponent type. - const int objCountWithoutSynthesizedComponents = qmlObjects->count(); - for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { - QmlIR::Object *obj = qmlObjects->at(i); - QQmlPropertyCache *cache = propertyCaches.at(i); - if (obj->inheritedTypeNameIndex == 0 && !cache) - continue; - - bool isExplicitComponent = false; - - if (obj->inheritedTypeNameIndex) { - auto *tref = resolvedType(obj->inheritedTypeNameIndex); - Q_ASSERT(tref); - if (tref->type.metaObject() == &QQmlComponent::staticMetaObject) - isExplicitComponent = true; - } - if (!isExplicitComponent) { - if (cache) - findAndRegisterImplicitComponents(obj, cache); - continue; - } - - obj->flags |= QV4::CompiledData::Object::IsComponent; - - if (obj->functionCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); - if (obj->propertyCount() > 0 || obj->aliasCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); - if (obj->signalCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); - - if (obj->bindingCount() == 0) - COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); - - const QmlIR::Binding *rootBinding = obj->firstBinding(); - - for (const QmlIR::Binding *b = rootBinding; b; b = b->next) { - if (b->propertyNameIndex != 0) - COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); - } - - if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) - COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); - - // For the root object, we are going to collect ids/aliases and resolve them for as a separate - // last pass. - if (i != 0) - componentRoots.append(i); - - } - - for (int i = 0; i < componentRoots.count(); ++i) { - QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); - const QmlIR::Binding *rootBinding = component->firstBinding(); - - _idToObjectIndex.clear(); - - _objectsWithAliases.clear(); - - if (!collectIdsAndAliases(rootBinding->value.objectIndex)) - return false; - - component->namedObjectsInComponent.allocate(pool, _idToObjectIndex); - - if (!resolveAliases(componentRoots.at(i))) - return false; - } - - // Collect ids and aliases for root - _idToObjectIndex.clear(); - _objectsWithAliases.clear(); - - collectIdsAndAliases(/*root object*/0); - - QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/0); - rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); - - if (!resolveAliases(/*root object*/0)) - return false; - - // Implicit component insertion may have added objects and thus we also need - // to extend the symmetric propertyCaches. - compiler->setPropertyCaches(std::move(propertyCaches)); - compiler->setComponentRoots(componentRoots); - - return true; -} - -bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) -{ - QmlIR::Object *obj = qmlObjects->at(objectIndex); - - if (obj->idNameIndex != 0) { - if (_idToObjectIndex.contains(obj->idNameIndex)) { - recordError(obj->locationOfIdProperty, tr("id is not unique")); - return false; - } - obj->id = _idToObjectIndex.count(); - _idToObjectIndex.insert(obj->idNameIndex, objectIndex); - } - - if (obj->aliasCount() > 0) - _objectsWithAliases.append(objectIndex); - - // Stop at Component boundary - if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) - return true; - - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object - && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty - && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) - continue; - - if (!collectIdsAndAliases(binding->value.objectIndex)) - return false; - } - - return true; -} - -bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) -{ - if (_objectsWithAliases.isEmpty()) - return true; - - QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler); - - bool atLeastOneAliasResolved; - do { - atLeastOneAliasResolved = false; - QVector<int> pendingObjects; - - for (int objectIndex: qAsConst(_objectsWithAliases)) { - - QQmlCompileError error; - const auto result = resolveAliasesInObject(objectIndex, &error); - - if (error.isSet()) { - recordError(error); - return false; - } - - if (result == AllAliasesResolved) { - QQmlCompileError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); - if (error.isSet()) { - recordError(error); - return false; - } - atLeastOneAliasResolved = true; - } else if (result == SomeAliasesResolved) { - atLeastOneAliasResolved = true; - pendingObjects.append(objectIndex); - } else { - pendingObjects.append(objectIndex); - } - } - qSwap(_objectsWithAliases, pendingObjects); - } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved); - - if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) { - const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first()); - for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) { - if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) { - recordError(alias->location, tr("Circular alias reference detected")); - return false; - } - } - } - - return true; -} - -QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error) -{ - const QmlIR::Object * const obj = qmlObjects->at(objectIndex); - if (!obj->aliasCount()) - return AllAliasesResolved; - - int numResolvedAliases = 0; - bool seenUnresolvedAlias = false; - - for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) { - if (alias->flags & QV4::CompiledData::Alias::Resolved) - continue; - - seenUnresolvedAlias = true; - - const int idIndex = alias->idIndex; - const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); - if (targetObjectIndex == -1) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); - break; - } - - const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); - Q_ASSERT(targetObject->id >= 0); - alias->targetObjectId = targetObject->id; - alias->aliasToLocalAlias = false; - - const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); - - QStringRef property; - QStringRef subProperty; - - const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); - if (propertySeparator != -1) { - property = aliasPropertyValue.leftRef(propertySeparator); - subProperty = aliasPropertyValue.midRef(propertySeparator + 1); - } else - property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); - - QQmlPropertyIndex propIdx; - - if (property.isEmpty()) { - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; - } else { - QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); - if (!targetCache) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); - break; - } - - QmlIR::PropertyResolver resolver(targetCache); - - QQmlPropertyData *targetProperty = resolver.property(property.toString()); - - // If it's an alias that we haven't resolved yet, try again later. - if (!targetProperty) { - bool aliasPointsToOtherAlias = false; - int localAliasIndex = 0; - for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) { - if (stringAt(targetAlias->nameIndex) == property) { - aliasPointsToOtherAlias = true; - break; - } - } - if (aliasPointsToOtherAlias) { - if (targetObjectIndex == objectIndex) { - alias->localAliasIndex = localAliasIndex; - alias->aliasToLocalAlias = true; - alias->flags |= QV4::CompiledData::Alias::Resolved; - ++numResolvedAliases; - continue; - } - - // restore - alias->idIndex = idIndex; - // Try again later and resolve the target alias first. - break; - } - } - - if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); - break; - } - - propIdx = QQmlPropertyIndex(targetProperty->coreIndex()); - - if (!subProperty.isEmpty()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType()); - if (!valueTypeMetaObject) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } - - int valueTypeIndex = - valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); - if (valueTypeIndex == -1) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } - Q_ASSERT(valueTypeIndex <= 0x0000FFFF); - - propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); - } else { - if (targetProperty->isQObject()) - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; - } - } - - alias->encodedMetaPropertyIndex = propIdx.toEncoded(); - alias->flags |= QV4::CompiledData::Alias::Resolved; - numResolvedAliases++; - } - - if (numResolvedAliases == 0) - return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved; - - return SomeAliasesResolved; -} - -QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) - , customParsers(typeCompiler->customParserCache()) - , _seenObjectWithId(false) -{ -} - -bool QQmlDeferredAndCustomParserBindingScanner::scanObject() -{ - return scanObject(/*root object*/0); -} - -bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) -{ - QmlIR::Object *obj = qmlObjects->at(objectIndex); - if (obj->idNameIndex != 0) - _seenObjectWithId = true; - - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - return scanObject(componentBinding->value.objectIndex); - } - - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); - if (!propertyCache) - return true; - - QString defaultPropertyName; - QQmlPropertyData *defaultProperty = nullptr; - if (obj->indexOfDefaultPropertyOrAlias != -1) { - QQmlPropertyCache *cache = propertyCache->parent(); - defaultPropertyName = cache->defaultPropertyName(); - defaultProperty = cache->defaultProperty(); - } else { - defaultPropertyName = propertyCache->defaultPropertyName(); - defaultProperty = propertyCache->defaultProperty(); - } - - QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); - - QmlIR::PropertyResolver propertyResolver(propertyCache); - - QStringList deferredPropertyNames; - { - const QMetaObject *mo = propertyCache->firstCppMetaObject(); - const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); - if (namesIndex != -1) { - QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); - } - } - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - QQmlPropertyData *pd = nullptr; - QString name = stringAt(binding->propertyNameIndex); - - if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - continue; - } - } else if (QmlIR::IRBuilder::isSignalPropertyName(name) - && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; - continue; - } - } - - if (name.isEmpty()) { - pd = defaultProperty; - name = defaultPropertyName; - } else { - if (name.constData()->isUpper()) - continue; - - bool notInRevision = false; - pd = propertyResolver.property(name, ¬InRevision, QmlIR::PropertyResolver::CheckRevision); - } - - bool seenSubObjectWithId = false; - - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - qSwap(_seenObjectWithId, seenSubObjectWithId); - const bool subObjectValid = scanObject(binding->value.objectIndex); - qSwap(_seenObjectWithId, seenSubObjectWithId); - if (!subObjectValid) - return false; - _seenObjectWithId |= seenSubObjectWithId; - } - - if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty - && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { - - binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; - obj->flags |= QV4::CompiledData::Object::HasDeferredBindings; - } - - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - if (!pd) { - if (customParser) { - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; - } - } - } - - return true; -} - -QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) - : QQmlCompilePass(typeCompiler) - , customParsers(typeCompiler->customParserCache()) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) - , v4CodeGen(v4CodeGen) -{ -} - -bool QQmlJSCodeGenerator::generateCodeForComponents() -{ - const QVector<quint32> &componentRoots = compiler->componentRoots(); - for (int i = 0; i < componentRoots.count(); ++i) { - if (!compileComponent(componentRoots.at(i))) - return false; - } - - return compileComponent(/*root object*/0); -} - -bool QQmlJSCodeGenerator::compileComponent(int contextObject) -{ - const QmlIR::Object *obj = qmlObjects.at(contextObject); - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - contextObject = componentBinding->value.objectIndex; - } - - if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject)) - return false; - - return true; -} - -bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) -{ - QmlIR::Object *object = qmlObjects.at(objectIndex); - if (object->flags & QV4::CompiledData::Object::IsComponent) - return true; - - if (object->functionsAndExpressions->count > 0) { - QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; - for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) - functionsToCompile << *foe; - const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile); - const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors(); - if (!jsErrors.isEmpty()) { - for (const QQmlError &e : jsErrors) - compiler->recordError(e); - return false; - } - - QQmlJS::MemoryPool *pool = compiler->memoryPool(); - object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices); - } - - for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { - if (binding->type < QV4::CompiledData::Binding::Type_Object) - continue; - - int target = binding->value.objectIndex; - int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; - - if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope)) - return false; - } - - return true; -} - -QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) -{ - -} - -void QQmlDefaultPropertyMerger::mergeDefaultProperties() -{ - for (int i = 0; i < qmlObjects.count(); ++i) - mergeDefaultProperties(i); -} - -void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) -{ - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); - if (!propertyCache) - return; - - QmlIR::Object *object = qmlObjects.at(objectIndex); - - QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName(); - QmlIR::Binding *bindingsToReinsert = nullptr; - QmlIR::Binding *tail = nullptr; - - QmlIR::Binding *previousBinding = nullptr; - QmlIR::Binding *binding = object->firstBinding(); - while (binding) { - if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) { - previousBinding = binding; - binding = binding->next; - continue; - } - - QmlIR::Binding *toReinsert = binding; - binding = object->unlinkBinding(previousBinding, binding); - - if (!tail) { - bindingsToReinsert = toReinsert; - tail = toReinsert; - } else { - tail->next = toReinsert; - tail = tail->next; - } - tail->next = nullptr; - } - - binding = bindingsToReinsert; - while (binding) { - QmlIR::Binding *toReinsert = binding; - binding = binding->next; - object->insertSorted(toReinsert); - } -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h deleted file mode 100644 index a49b97453f..0000000000 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ /dev/null @@ -1,348 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QQMLTYPECOMPILER_P_H -#define QQMLTYPECOMPILER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qglobal.h> -#include <qqmlerror.h> -#include <qhash.h> -#include <private/qqmltypeloader_p.h> -#include <private/qqmlirbuilder_p.h> -#include <private/qqmlpropertycachecreator_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlEnginePrivate; -class QQmlError; -class QQmlTypeData; -class QQmlImports; - -namespace QmlIR { -struct Document; -} - -namespace QV4 { -namespace CompiledData { -struct QmlUnit; -struct Location; -} -} - -struct QQmlTypeCompiler -{ - Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) -public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, - const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher); - - // --- interface used by QQmlPropertyCacheCreator - typedef QmlIR::Object CompiledObject; - const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); } - int objectCount() const { return document->objects.count(); } - QString stringAt(int idx) const; - QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } - QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes = nullptr; - // --- - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(); - - QList<QQmlError> compilationErrors() const { return errors; } - void recordError(QQmlError error); - void recordError(const QV4::CompiledData::Location &location, const QString &description); - void recordError(const QQmlCompileError &error); - - int registerString(const QString &str); - int registerConstant(QV4::ReturnedValue v); - - const QV4::CompiledData::Unit *qmlUnit() const; - - QUrl url() const { return typeData->finalUrl(); } - QQmlEnginePrivate *enginePrivate() const { return engine; } - const QQmlImports *imports() const; - QVector<QmlIR::Object *> *qmlObjects() const; - void setPropertyCaches(QQmlPropertyCacheVector &&caches); - const QQmlPropertyCacheVector *propertyCaches() const; - QQmlPropertyCacheVector &&takePropertyCaches(); - void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; } - const QVector<quint32> &componentRoots() const { return m_componentRoots; } - QQmlJS::MemoryPool *memoryPool(); - QStringRef newStringRef(const QString &string); - const QV4::Compiler::StringTableGenerator *stringPool() const; - void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData); - - const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; } - - QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const; - - void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion); - - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const - { - return resolvedTypes->value(id); - } - -private: - QList<QQmlError> errors; - QQmlEnginePrivate *engine; - QQmlTypeData *typeData; - const QV4::CompiledData::DependentTypesHasher &dependencyHasher; - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - QmlIR::Document *document; - // index is string index of type name (use obj->inheritedTypeNameIndex) - QHash<int, QQmlCustomParser*> customParsers; - - // index in first hash is component index, vector inside contains object indices of objects with id property - QVector<quint32> m_componentRoots; - QQmlPropertyCacheVector m_propertyCaches; -}; - -struct QQmlCompilePass -{ - QQmlCompilePass(QQmlTypeCompiler *typeCompiler); - - QString stringAt(int idx) const { return compiler->stringAt(idx); } -protected: - void recordError(const QV4::CompiledData::Location &location, const QString &description) const - { compiler->recordError(location, description); } - void recordError(const QQmlCompileError &error) - { compiler->recordError(error); } - - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const - { return compiler->resolvedType(id); } - bool containsResolvedType(int id) const - { return compiler->resolvedTypes->contains(id); } - QV4::CompiledData::ResolvedTypeReferenceMap::iterator insertResolvedType( - int id, QV4::CompiledData::ResolvedTypeReference *value) - { return compiler->resolvedTypes->insert(id, value); } - - QQmlTypeCompiler *compiler; -}; - -// "Converts" signal expressions to full-fleged function declarations with -// parameters taken from the signal declarations -// It also updates the QV4::CompiledData::Binding objects to set the property name -// to the final signal name (onTextChanged -> textChanged) and sets the IsSignalExpression flag. -struct SignalHandlerConverter : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(SignalHandlerConverter) -public: - SignalHandlerConverter(QQmlTypeCompiler *typeCompiler); - - bool convertSignalHandlerExpressionsToFunctionDeclarations(); - -private: - bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache); - - QQmlEnginePrivate *enginePrivate; - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlImports *imports; - const QHash<int, QQmlCustomParser*> &customParsers; - const QSet<QString> &illegalNames; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -// ### This will go away when the codegen resolves all enums to constant expressions -// and we replace the constant expression with a literal binding instead of using -// a script. -class QQmlEnumTypeResolver : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlEnumTypeResolver) -public: - QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler); - - bool resolveEnumBindings(); - -private: - bool assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject); - bool assignEnumToBinding(QmlIR::Binding *binding, const QString &enumName, int enumValue, bool isQtObject) - { - return assignEnumToBinding(binding, QStringRef(&enumName), enumValue, isQtObject); - } - bool tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, - const QQmlPropertyData *prop, - QmlIR::Binding *binding); - int evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const; - - - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - const QQmlImports *imports; -}; - -class QQmlCustomParserScriptIndexer: public QQmlCompilePass -{ -public: - QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler); - - void annotateBindingsWithScriptStrings(); - -private: - void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false); - - const QVector<QmlIR::Object*> &qmlObjects; - const QHash<int, QQmlCustomParser*> &customParsers; -}; - -// Annotate properties bound to aliases with a flag -class QQmlAliasAnnotator : public QQmlCompilePass -{ -public: - QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler); - - void annotateBindingsToAliases(); -private: - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -class QQmlScriptStringScanner : public QQmlCompilePass -{ -public: - QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler); - - void scan(); - -private: - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -class QQmlComponentAndAliasResolver : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) -public: - QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler); - - bool resolve(); - -protected: - void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache); - bool collectIdsAndAliases(int objectIndex); - bool resolveAliases(int componentIndex); - void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags); - - enum AliasResolutionResult { - NoAliasResolved, - SomeAliasesResolved, - AllAliasesResolved - }; - - AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error); - - QQmlEnginePrivate *enginePrivate; - QQmlJS::MemoryPool *pool; - - QVector<QmlIR::Object*> *qmlObjects; - - // indices of the objects that are actually Component {} - QVector<quint32> componentRoots; - - // Deliberate choice of map over hash here to ensure stable generated output. - QMap<int, int> _idToObjectIndex; - QVector<int> _objectsWithAliases; - - QQmlPropertyCacheVector propertyCaches; -}; - -class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass -{ -public: - QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler); - - bool scanObject(); - -private: - bool scanObject(int objectIndex); - - QVector<QmlIR::Object*> *qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - const QHash<int, QQmlCustomParser*> &customParsers; - - bool _seenObjectWithId; -}; - -// ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone. -class QQmlJSCodeGenerator : public QQmlCompilePass -{ -public: - QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen); - - bool generateCodeForComponents(); - -private: - bool compileComponent(int componentRoot); - bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); - - const QHash<int, QQmlCustomParser*> &customParsers; - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - QmlIR::JSCodeGen * const v4CodeGen; -}; - -class QQmlDefaultPropertyMerger : public QQmlCompilePass -{ -public: - QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler); - - void mergeDefaultProperties(); - -private: - void mergeDefaultProperties(int objectIndex); - - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -QT_END_NAMESPACE - -#endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp index ea252a6013..7df1614ffe 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -206,7 +206,6 @@ int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, in lastInstrType = int(type); lastInstr = i; -#if QT_CONFIG(qml_debug) if (debugMode && type != Instr::Type::Debug) { QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug() @@ -219,9 +218,6 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instru } QT_WARNING_POP } -#else - Q_UNUSED(debugMode); -#endif const int pos = instructions.size(); diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 1d0a57c536..8c509dd9f1 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -62,12 +62,15 @@ class SourceLocation; } namespace QV4 { + +namespace Compiler { +struct Context; +} + namespace Moth { class BytecodeGenerator { public: - typedef CompiledData::Function::TraceInfoCount TraceInfoCount; - BytecodeGenerator(int line, bool debug) : startLine(line), debugMode(debug) {} @@ -164,15 +167,6 @@ public: addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr); } - // Same as addInstruction, but also add a trace slot. Move only, because the instruction cannot - // be reused afterwards. - template<int InstrT> - void addTracingInstruction(InstrData<InstrT> data) - { - data.traceSlot = nextTraceInfo(); - addInstruction(data); - } - Q_REQUIRED_RESULT Jump jump() { QT_WARNING_PUSH @@ -184,12 +178,12 @@ QT_WARNING_POP Q_REQUIRED_RESULT Jump jumpTrue() { - return addTracingJumpInstruction(Instruction::JumpTrue()); + return addJumpInstruction(Instruction::JumpTrue()); } Q_REQUIRED_RESULT Jump jumpFalse() { - return addTracingJumpInstruction(Instruction::JumpFalse()); + return addJumpInstruction(Instruction::JumpFalse()); } Q_REQUIRED_RESULT Jump jumpNotUndefined() @@ -209,7 +203,7 @@ QT_WARNING_POP Instruction::CmpStrictEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); } void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) @@ -217,7 +211,13 @@ QT_WARNING_POP Instruction::CmpStrictNotEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); + } + + void checkException() + { + Instruction::CheckException chk; + addInstruction(chk); } void setUnwindHandler(ExceptionHandler *handler) @@ -258,13 +258,6 @@ QT_WARNING_POP void finalize(Compiler::Context *context); template<int InstrT> - Jump addTracingJumpInstruction(InstrData<InstrT> &&data) - { - data.traceSlot = nextTraceInfo(); - return addJumpInstruction(data); - } - - template<int InstrT> Jump addJumpInstruction(const InstrData<InstrT> &data) { Instr genericInstr; @@ -275,9 +268,9 @@ QT_WARNING_POP void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) { if (jumpOnFalse) - addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); else - addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); } void clearLastInstruction() @@ -285,27 +278,6 @@ QT_WARNING_POP lastInstrType = -1; } - TraceInfoCount nextTraceInfo() - { - // If tracing is disabled, use slot 0 to unconditionally store all trace info - if (nTraceInfos == CompiledData::Function::NoTracing()) - return TraceInfoCount(0); - return nTraceInfos++; - } - - void setTracing(bool onoff, int argumentCount) - { - if (onoff) - nTraceInfos = argumentCount; - else - nTraceInfos = CompiledData::Function::NoTracing(); - } - - TraceInfoCount traceInfoCount() const - { - return nTraceInfos; - } - void addLoopStart(const Label &start) { _labelInfos.push_back({ start.index }); @@ -346,8 +318,6 @@ private: int lastInstrType = -1; Moth::Instr lastInstr; - TraceInfoCount nTraceInfos = TraceInfoCount(0); - struct LabelInfo { int labelIndex; }; diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index 92b112c2fa..f9f755b8c0 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -58,9 +58,10 @@ ByteCodeHandler::~ByteCodeHandler() Q_UNUSED(base_ptr); \ _currentOffset = _nextOffset; \ _nextOffset = code - start; \ - startInstruction(Instr::Type::instr); \ - INSTR_##instr(DISPATCH) \ - endInstruction(Instr::Type::instr); \ + if (startInstruction(Instr::Type::instr) == ProcessInstruction) { \ + INSTR_##instr(DISPATCH) \ + endInstruction(Instr::Type::instr); \ + } \ continue; \ } diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h index 797d25b8d0..f1e7c99447 100644 --- a/src/qml/compiler/qv4bytecodehandler_p.h +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -105,7 +105,8 @@ public: protected: FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER) - virtual void startInstruction(Moth::Instr::Type instr) = 0; + enum Verdict { ProcessInstruction, SkipInstruction }; + virtual Verdict startInstruction(Moth::Instr::Type instr) = 0; virtual void endInstruction(Moth::Instr::Type instr) = 0; private: diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 5acc64bd81..c43ea64e2e 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -43,18 +43,18 @@ #include <QtCore/QCoreApplication> #include <QtCore/QStringList> #include <QtCore/QStack> +#include <QtCore/qurl.h> #include <QScopeGuard> #include <private/qqmljsast_p.h> -#include <private/qv4string_p.h> -#include <private/qv4value_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4compilercontrolflow_p.h> #include <private/qv4bytecodegenerator_p.h> #include <private/qv4compilerscanfunctions_p.h> - -#ifndef V4_BOOTSTRAP -# include <qqmlerror.h> -#endif +#include <private/qv4stringtoarrayindex_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <cmath> #include <iostream> @@ -68,6 +68,7 @@ static const bool disable_lookups = false; QT_USE_NAMESPACE using namespace QV4; using namespace QV4::Compiler; +using namespace QQmlJS; using namespace QQmlJS::AST; static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator, @@ -97,7 +98,6 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) , jsUnitGenerator(jsUnitGenerator) , _strictMode(strict) , _fileNameIsUrl(false) - , hasError(false) { jsUnitGenerator->codeGeneratorName = QStringLiteral("moth"); pushExpr(); @@ -190,7 +190,7 @@ void Codegen::generateFromProgram(const QString &fileName, ScanFunctions scan(this, sourceCode, contextType); scan(node); - if (hasError) + if (hasError()) return; defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements); @@ -214,7 +214,7 @@ void Codegen::generateFromModule(const QString &fileName, ScanFunctions scan(this, sourceCode, ContextType::ESModule); scan(node); - if (hasError) + if (hasError()) return; { @@ -264,17 +264,30 @@ Context *Codegen::enterBlock(Node *node) Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) { - if (hasError) + if (hasError()) return exprResult(); if (expr.isConstant()) { - auto v = Value::fromReturnedValue(expr.constant); + auto v = StaticValue::fromReturnedValue(expr.constant); if (v.isNumber()) { switch (op) { case Not: return Reference::fromConst(this, Encode(!v.toBoolean())); case UMinus: - return Reference::fromConst(this, Runtime::method_uMinus(v)); + // This duplicates some of the logic from Runtime::UMinus::call() + ReturnedValue r; + if (v.isInteger()) { + int intVal = v.integerValue(); + if (intVal && intVal != std::numeric_limits<int>::min()) + r = QV4::Encode(-intVal); + else + r = QV4::Encode(-double(intVal)); + } else if (v.isDouble()) { + r = QV4::Encode(-v.doubleValue()); + } else { + r = QV4::Encode(-v.int_32()); + } + return Reference::fromConst(this, r); case UPlus: return expr; case Compl: @@ -289,12 +302,12 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case UMinus: { expr.loadInAccumulator(); Instruction::UMinus uminus = {}; - bytecodeGenerator->addTracingInstruction(uminus); + bytecodeGenerator->addInstruction(uminus); return Reference::fromAccumulator(this); } case UPlus: { expr.loadInAccumulator(); - Instruction::UPlus uplus; + Instruction::UPlus uplus = {}; bytecodeGenerator->addInstruction(uplus); return Reference::fromAccumulator(this); } @@ -314,11 +327,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) if (!exprAccept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::UPlus uplus; + Instruction::UPlus uplus = {}; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); e.storeConsumeAccumulator(); return originalValue; } else { @@ -330,7 +343,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -340,11 +353,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) if (!exprAccept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::UPlus uplus; + Instruction::UPlus uplus = {}; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); e.storeConsumeAccumulator(); return originalValue; } else { @@ -356,7 +369,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -402,7 +415,7 @@ void Codegen::statement(ExpressionNode *ast) qSwap(_volatileMemoryLocations, vLocs); Reference result = popResult(); - if (hasError) + if (hasError()) return; if (result.loadTriggersSideEffect()) result.loadInAccumulator(); // triggers side effects @@ -412,7 +425,7 @@ void Codegen::statement(ExpressionNode *ast) void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition) { - if (hasError) + if (hasError()) return; if (!ast) @@ -422,7 +435,7 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift accept(ast); Result r = popExpr(); - if (hasError) + if (hasError()) return; if (r.format() == ex) { @@ -576,7 +589,7 @@ Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p) if (!p->bindingTarget || p->destructuringPattern()) return Codegen::Reference::fromStackSlot(this); Reference lhs = expression(p->bindingTarget); - if (hasError) + if (hasError()) return lhs; if (!lhs.isLValue()) { throwReferenceError(p->bindingTarget->firstSourceLocation(), QStringLiteral("Binding target is not a reference.")); @@ -594,14 +607,16 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con Reference varToStore = targetForPatternElement(e); if (isDefinition) varToStore.isReferenceToConst = false; - if (hasError) + if (hasError()) return; + accept(e->typeAnnotation); + if (e->initializer) { if (!baseRef.isValid()) { // assignment Reference expr = expression(e->initializer); - if (hasError) + if (hasError()) return; expr.loadInAccumulator(); varToStore.storeConsumeAccumulator(); @@ -609,7 +624,7 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con baseRef.loadInAccumulator(); BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined(); Reference expr = expression(e->initializer); - if (hasError) { + if (hasError()) { jump.link(); return; } @@ -620,7 +635,7 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con baseRef.loadInAccumulator(); BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined(); Reference expr = expression(e->initializer); - if (hasError) { + if (hasError()) { jump.link(); return; } @@ -657,7 +672,7 @@ Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &o Reference property; if (cname) { Reference computedName = expression(cname->expression); - if (hasError) + if (hasError()) return Reference(); computedName = computedName.storeOnStack(); property = Reference::fromSubscript(object, computedName).asLValue(); @@ -680,10 +695,10 @@ void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternP PatternProperty *p = it->property; RegisterScope scope(this); Reference property = referenceForPropertyName(object, p->name); - if (hasError) + if (hasError()) return; initializeAndDestructureBindingElement(p, property, isDefinition); - if (hasError) + if (hasError()) return; } } @@ -739,7 +754,7 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); initializeAndDestructureBindingElement(e, iteratorValue, isDefinition); - if (hasError) + if (hasError()) return; } } @@ -872,6 +887,12 @@ bool Codegen::visit(ExportDeclaration *ast) return false; } +bool Codegen::visit(TypeAnnotation *ast) +{ + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet).")); + return false; +} + bool Codegen::visit(StatementList *) { Q_UNREACHABLE(); @@ -1003,11 +1024,11 @@ bool Codegen::visit(ClassExpression *ast) if (ast->heritage) { bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation()); Reference r = expression(ast->heritage); - if (hasError) + if (hasError()) return false; r.storeOnStack(heritage.stackSlot()); } else { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator(); + Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator(); heritage.storeConsumeAccumulator(); } @@ -1022,7 +1043,7 @@ bool Codegen::visit(ClassExpression *ast) RegisterScope scope(this); bytecodeGenerator->setLocation(cname->firstSourceLocation()); Reference computedName = expression(cname->expression); - if (hasError) + if (hasError()) return false; computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++); } @@ -1055,7 +1076,7 @@ bool Codegen::visit(ClassDeclaration *ast) bool Codegen::visit(Expression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -1068,7 +1089,7 @@ bool Codegen::visit(Expression *ast) bool Codegen::visit(ArrayPattern *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -1085,12 +1106,12 @@ bool Codegen::visit(ArrayPattern *ast) if (args == -1) args = temp; if (!arg) { - auto c = Reference::fromConst(this, Value::emptyValue().asReturnedValue()); + auto c = Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()); (void) c.storeOnStack(temp); } else { RegisterScope scope(this); Reference r = expression(arg); - if (hasError) + if (hasError()) return; (void) r.storeOnStack(temp); } @@ -1108,7 +1129,7 @@ bool Codegen::visit(ArrayPattern *ast) continue; push(e->initializer); - if (hasError) + if (hasError()) return false; } @@ -1140,13 +1161,14 @@ bool Codegen::visit(ArrayPattern *ast) index.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); index.storeConsumeAccumulator(); }; while (it) { for (Elision *elision = it->elision; elision; elision = elision->next) { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator(); + Reference::fromConst( + this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator(); pushAccumulator(); } @@ -1168,7 +1190,7 @@ bool Codegen::visit(ArrayPattern *ast) { RegisterScope innerScope(this); Reference expr = expression(it->element->initializer); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -1202,13 +1224,14 @@ bool Codegen::visit(ArrayPattern *ast) lhsValue.loadInAccumulator(); pushAccumulator(); + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(in); end.link(); } } else { RegisterScope innerScope(this); Reference expr = expression(it->element->initializer); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -1226,12 +1249,12 @@ bool Codegen::visit(ArrayPattern *ast) bool Codegen::visit(ArrayMemberExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { Reference index = expression(ast->expression).storeOnStack(); @@ -1239,11 +1262,11 @@ bool Codegen::visit(ArrayMemberExpression *ast) return false; } base = base.storeOnStack(); - if (hasError) + if (hasError()) return false; if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) { QString s = str->value.toString(); - uint arrayIndex = QV4::String::toArrayIndex(s); + uint arrayIndex = stringToArrayIndex(s); if (arrayIndex == UINT_MAX) { setExprResult(Reference::fromMember(base, str->value.toString())); return false; @@ -1253,7 +1276,7 @@ bool Codegen::visit(ArrayMemberExpression *ast) return false; } Reference index = expression(ast->expression); - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromSubscript(base, index)); return false; @@ -1280,7 +1303,7 @@ static QSOperator::Op baseOp(int op) bool Codegen::visit(BinaryExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -1298,7 +1321,7 @@ bool Codegen::visit(BinaryExpression *ast) auto endif = bytecodeGenerator->newLabel(); Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; left.loadInAccumulator(); @@ -1308,7 +1331,7 @@ bool Codegen::visit(BinaryExpression *ast) blockTailCalls.unblock(); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; right.loadInAccumulator(); @@ -1329,7 +1352,7 @@ bool Codegen::visit(BinaryExpression *ast) auto endif = bytecodeGenerator->newLabel(); Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; left.loadInAccumulator(); @@ -1339,7 +1362,7 @@ bool Codegen::visit(BinaryExpression *ast) blockTailCalls.unblock(); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; right.loadInAccumulator(); @@ -1352,7 +1375,7 @@ bool Codegen::visit(BinaryExpression *ast) if (AST::Pattern *p = ast->left->patternCast()) { RegisterScope scope(this); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; right = right.storeOnStack(); destructurePattern(p, right); @@ -1363,7 +1386,7 @@ bool Codegen::visit(BinaryExpression *ast) return false; } Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; if (!left.isLValue()) { @@ -1375,7 +1398,7 @@ bool Codegen::visit(BinaryExpression *ast) return false; blockTailCalls.unblock(); Reference r = expression(ast->right); - if (hasError) + if (hasError()) return false; r.loadInAccumulator(); if (exprAccept(nx)) @@ -1386,7 +1409,7 @@ bool Codegen::visit(BinaryExpression *ast) } Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; switch (ast->op) { @@ -1420,7 +1443,7 @@ bool Codegen::visit(BinaryExpression *ast) Reference tempLeft = left.storeOnStack(); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator(); @@ -1434,7 +1457,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::BitXor: if (left.isConstant()) { Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left)); break; @@ -1467,7 +1490,7 @@ bool Codegen::visit(BinaryExpression *ast) left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it right = expression(ast->right); } - if (hasError) + if (hasError()) return false; setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right)); @@ -1488,20 +1511,20 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Add add; add.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(add); + bytecodeGenerator->addInstruction(add); break; } case QSOperator::Sub: { if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); } else { left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Sub sub; sub.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(sub); + bytecodeGenerator->addInstruction(sub); } break; } @@ -1518,7 +1541,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mul mul; mul.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mul); + bytecodeGenerator->addInstruction(mul); break; } case QSOperator::Div: { @@ -1534,14 +1557,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mod mod; mod.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mod); + bytecodeGenerator->addInstruction(mod); break; } case QSOperator::BitAnd: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() & rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() & rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1557,9 +1580,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; case QSOperator::BitOr: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() | rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() | rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1575,9 +1598,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; case QSOperator::BitXor: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1595,7 +1618,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::UShrConst ushr; - ushr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + ushr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(ushr); } else { right.loadInAccumulator(); @@ -1608,7 +1631,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShrConst shr; - shr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + shr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(shr); } else { right.loadInAccumulator(); @@ -1621,7 +1644,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShlConst shl; - shl.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + shl.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(shl); } else { right.loadInAccumulator(); @@ -1749,7 +1772,7 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe qSwap(left, right); // null==a -> a==null if (right.isConstant()) { - Value c = Value::fromReturnedValue(right.constant); + StaticValue c = StaticValue::fromReturnedValue(right.constant); if (c.isNull() || c.isUndefined()) { left.loadInAccumulator(); if (oper == QSOperator::Equal) { @@ -1851,7 +1874,7 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe bool Codegen::visit(CallExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -1859,7 +1882,7 @@ bool Codegen::visit(CallExpression *ast) Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; switch (base.type) { case Reference::Member: @@ -1882,7 +1905,7 @@ bool Codegen::visit(CallExpression *ast) int functionObject = bytecodeGenerator->newRegister(); auto calldata = pushArgs(ast->arguments); - if (hasError) + if (hasError()) return false; blockTailCalls.unblock(); @@ -1903,7 +1926,7 @@ bool Codegen::visit(CallExpression *ast) call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::TailCall call; call.func = base.stackSlot(); @@ -1932,14 +1955,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.lookupIndex = registerGetterLookup(base.propertyNameIndex); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallProperty call; call.base = base.propertyBase.stackSlot(); call.name = base.propertyNameIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::Subscript) { Instruction::CallElement call; @@ -1947,33 +1970,33 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.index = base.elementSubscript.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (base.type == Reference::Name) { if (base.name == QStringLiteral("eval")) { Instruction::CallPossiblyDirectEval call; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { if (base.qmlGlobal) { Instruction::CallQmlContextPropertyLookup call; call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else { Instruction::CallName call; call.name = base.nameAsIndex(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::SuperProperty) { Reference receiver = base.baseObject(); @@ -1990,14 +2013,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.thisObject = receiver.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Q_ASSERT(base.isStackSlot()); Instruction::CallValue call; call.name = base.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } setExprResult(Reference::fromAccumulator(this)); @@ -2023,12 +2046,14 @@ Codegen::Arguments Codegen::pushArgs(ArgumentList *args) argc = 0; for (ArgumentList *it = args; it; it = it->next) { if (it->isSpreadElement) { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).storeOnStack(calldata + argc); + Reference::fromConst( + this, + StaticValue::emptyValue().asReturnedValue()).storeOnStack(calldata + argc); ++argc; } RegisterScope scope(this); Reference e = expression(it->expression); - if (hasError) + if (hasError()) break; if (!argc && !it->next && !hasSpread) { // avoid copy for functions taking a single argument @@ -2057,7 +2082,7 @@ Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args) for (TemplateLiteral *it = args; it && it->expression; it = it->next) { RegisterScope scope(this); Reference e = expression(it->expression); - if (hasError) + if (hasError()) break; (void) e.storeOnStack(calldata + argc); ++argc; @@ -2068,7 +2093,7 @@ Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args) bool Codegen::visit(ConditionalExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -2082,14 +2107,14 @@ bool Codegen::visit(ConditionalExpression *ast) iftrue.link(); Reference ok = expression(ast->ok); - if (hasError) + if (hasError()) return false; ok.loadInAccumulator(); BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); iffalse.link(); Reference ko = expression(ast->ko); - if (hasError) { + if (hasError()) { jump_endif.link(); // dummy link, to prevent assert in Jump destructor from triggering return false; } @@ -2103,13 +2128,13 @@ bool Codegen::visit(ConditionalExpression *ast) bool Codegen::visit(DeleteExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; switch (expr.type) { @@ -2174,7 +2199,7 @@ bool Codegen::visit(DeleteExpression *ast) bool Codegen::visit(FalseLiteral *) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromConst(this, QV4::Encode(false))); @@ -2183,7 +2208,7 @@ bool Codegen::visit(FalseLiteral *) bool Codegen::visit(SuperLiteral *) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromSuper(this)); @@ -2192,7 +2217,7 @@ bool Codegen::visit(SuperLiteral *) bool Codegen::visit(FieldMemberExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2215,7 +2240,7 @@ bool Codegen::visit(FieldMemberExpression *ast) } Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { Instruction::LoadRuntimeString load; @@ -2231,7 +2256,7 @@ bool Codegen::visit(FieldMemberExpression *ast) bool Codegen::visit(TaggedTemplate *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -2240,7 +2265,7 @@ bool Codegen::visit(TaggedTemplate *ast) bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast) { - if (hasError) + if (hasError()) return false; int functionObject = -1, thisObject = -1; @@ -2264,7 +2289,7 @@ bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast) int templateObjectTemp = Reference::fromAccumulator(this).storeOnStack().stackSlot(); Q_UNUSED(templateObjectTemp); auto calldata = pushTemplateArgs(ast->templateLiteral); - if (hasError) + if (hasError()) return false; ++calldata.argc; Q_ASSERT(calldata.argv == templateObjectTemp + 1); @@ -2293,7 +2318,7 @@ void Codegen::createTemplateObject(TemplateLiteral *t) bool Codegen::visit(FunctionExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2301,7 +2326,7 @@ bool Codegen::visit(FunctionExpression *ast) RegisterScope scope(this); int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); - if (hasError) + if (hasError()) return false; loadClosure(function); setExprResult(Reference::fromAccumulator(this)); @@ -2357,7 +2382,7 @@ void Codegen::loadClosure(int closureId) bool Codegen::visit(IdentifierExpression *ast) { - if (hasError) + if (hasError()) return false; setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation())); @@ -2366,7 +2391,7 @@ bool Codegen::visit(IdentifierExpression *ast) bool Codegen::visit(NestedExpression *ast) { - if (hasError) + if (hasError()) return false; accept(ast->expression); @@ -2385,7 +2410,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments) } auto calldata = pushArgs(arguments); - if (hasError) + if (hasError()) return; if (base.isSuper()) @@ -2415,14 +2440,14 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments) bool Codegen::visit(NewExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference base = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super.")); @@ -2435,14 +2460,14 @@ bool Codegen::visit(NewExpression *ast) bool Codegen::visit(NewMemberExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super.")); @@ -2455,7 +2480,7 @@ bool Codegen::visit(NewMemberExpression *ast) bool Codegen::visit(NotExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2465,7 +2490,7 @@ bool Codegen::visit(NotExpression *ast) bool Codegen::visit(NullExpression *) { - if (hasError) + if (hasError()) return false; if (exprAccept(cx)) @@ -2478,7 +2503,7 @@ bool Codegen::visit(NullExpression *) bool Codegen::visit(NumericLiteral *ast) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value))); @@ -2487,7 +2512,7 @@ bool Codegen::visit(NumericLiteral *ast) bool Codegen::visit(ObjectPattern *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2513,7 +2538,7 @@ bool Codegen::visit(ObjectPattern *ast) if (cname || p->type != PatternProperty::Literal) break; QString name = p->name->asString(); - uint arrayIndex = QV4::String::toArrayIndex(name); + uint arrayIndex = stringToArrayIndex(name); if (arrayIndex != UINT_MAX) break; if (members.contains(name)) @@ -2523,7 +2548,7 @@ bool Codegen::visit(ObjectPattern *ast) { RegisterScope innerScope(this); Reference value = expression(p->initializer, name); - if (hasError) + if (hasError()) return false; value.loadInAccumulator(); } @@ -2550,7 +2575,7 @@ bool Codegen::visit(ObjectPattern *ast) if (cname) { RegisterScope innerScope(this); Reference name = expression(cname->expression); - if (hasError) + if (hasError()) return false; name.loadInAccumulator(); } else { @@ -2575,12 +2600,12 @@ bool Codegen::visit(ObjectPattern *ast) FunctionExpression *f = p->initializer->asFunctionDefinition(); Q_ASSERT(f); int function = defineFunction(f->name.toString(), f, f->formals, f->body); - if (hasError) + if (hasError()) return false; Reference::fromConst(this, Encode(function)).loadInAccumulator(); } else { Reference value = expression(p->initializer); - if (hasError) + if (hasError()) return false; value.loadInAccumulator(); } @@ -2599,11 +2624,11 @@ bool Codegen::visit(ObjectPattern *ast) bool Codegen::visit(PostDecrementExpression *ast) { - if (hasError) + if (hasError()) return false; Reference expr = expression(ast->base); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); @@ -2619,11 +2644,11 @@ bool Codegen::visit(PostDecrementExpression *ast) bool Codegen::visit(PostIncrementExpression *ast) { - if (hasError) + if (hasError()) return false; Reference expr = expression(ast->base); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); @@ -2637,11 +2662,11 @@ bool Codegen::visit(PostIncrementExpression *ast) } bool Codegen::visit(PreDecrementExpression *ast) -{ if (hasError) +{ if (hasError()) return false; Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); @@ -2656,11 +2681,11 @@ bool Codegen::visit(PreDecrementExpression *ast) bool Codegen::visit(PreIncrementExpression *ast) { - if (hasError) + if (hasError()) return false; Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); @@ -2675,7 +2700,7 @@ bool Codegen::visit(PreIncrementExpression *ast) bool Codegen::visit(RegExpLiteral *ast) { - if (hasError) + if (hasError()) return false; auto r = Reference::fromStackSlot(this); @@ -2691,7 +2716,7 @@ bool Codegen::visit(RegExpLiteral *ast) bool Codegen::visit(StringLiteral *ast) { - if (hasError) + if (hasError()) return false; auto r = Reference::fromAccumulator(this); @@ -2706,7 +2731,7 @@ bool Codegen::visit(StringLiteral *ast) bool Codegen::visit(TemplateLiteral *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2723,7 +2748,7 @@ bool Codegen::visit(TemplateLiteral *ast) bytecodeGenerator->addInstruction(store); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (ast->next) { @@ -2733,14 +2758,14 @@ bool Codegen::visit(TemplateLiteral *ast) Instruction::Add instr; instr.lhs = temp2; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } else { expr.loadInAccumulator(); } Instruction::Add instr; instr.lhs = temp; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } auto r = Reference::fromAccumulator(this); @@ -2753,7 +2778,7 @@ bool Codegen::visit(TemplateLiteral *ast) bool Codegen::visit(ThisExpression *) { - if (hasError) + if (hasError()) return false; if (_context->isArrowFunction) { @@ -2768,7 +2793,7 @@ bool Codegen::visit(ThisExpression *) bool Codegen::visit(TildeExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2778,7 +2803,7 @@ bool Codegen::visit(TildeExpression *ast) bool Codegen::visit(TrueLiteral *) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromConst(this, QV4::Encode(true))); @@ -2787,14 +2812,14 @@ bool Codegen::visit(TrueLiteral *) bool Codegen::visit(TypeOfExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (expr.type == Reference::Name) { @@ -2814,7 +2839,7 @@ bool Codegen::visit(TypeOfExpression *ast) bool Codegen::visit(UnaryMinusExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2824,7 +2849,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) bool Codegen::visit(UnaryPlusExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2834,7 +2859,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) bool Codegen::visit(VoidExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -2847,7 +2872,7 @@ bool Codegen::visit(VoidExpression *ast) bool Codegen::visit(FunctionDeclaration * ast) { - if (hasError) + if (hasError()) return false; // no need to block tail calls: the function body isn't visited here. @@ -2869,7 +2894,7 @@ bool Codegen::visit(YieldExpression *ast) RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined()); - if (hasError) + if (hasError()) return false; Reference acc = Reference::fromAccumulator(this); @@ -2998,10 +3023,10 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bool savedFunctionEndsWithReturn = functionEndsWithReturn; functionEndsWithReturn = endsWithReturn(_module, body); - bytecodeGenerator->setTracing(_functionContext->canUseTracingJit(), _context->arguments.size()); // reserve the js stack frame (Context & js Function & accumulator) - bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); + bytecodeGenerator->newRegisterArray( + sizeof(CallData) / sizeof(StaticValue) - 1 + _context->arguments.size()); bool _inFormalParameterList = false; qSwap(_inFormalParameterList, inFormalParameterList); @@ -3047,7 +3072,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, } else { if (e->bindingTarget || e->initializer) { initializeAndDestructureBindingElement(e, arg); - if (hasError) + if (hasError()) break; } } @@ -3063,7 +3088,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, statementList(body); - if (!hasError) { + if (!hasError()) { bytecodeGenerator->setLocation(ast->lastSourceLocation()); _context->emitBlockFooter(this); @@ -3085,7 +3110,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, Q_ASSERT(_context == _functionContext); bytecodeGenerator->finalize(_context); _context->registerCountInFunction = bytecodeGenerator->registerCount(); - _context->nTraceInfos = bytecodeGenerator->traceInfoCount(); static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict @@ -3111,7 +3135,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bool Codegen::visit(Block *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3123,7 +3147,7 @@ bool Codegen::visit(Block *ast) bool Codegen::visit(BreakStatement *ast) { - if (hasError) + if (hasError()) return false; // no need to block tail calls here: children aren't visited @@ -3148,7 +3172,7 @@ bool Codegen::visit(BreakStatement *ast) bool Codegen::visit(ContinueStatement *ast) { - if (hasError) + if (hasError()) return false; // no need to block tail calls here: children aren't visited @@ -3181,7 +3205,7 @@ bool Codegen::visit(DebuggerStatement *) bool Codegen::visit(DoWhileStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3191,22 +3215,28 @@ bool Codegen::visit(DoWhileStatement *ast) BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); ControlFlowLoop flow(this, &end, &cond); - bytecodeGenerator->jump().link(body); - - cond.link(); - bytecodeGenerator->addLoopStart(cond); - if (!AST::cast<TrueLiteral *>(ast->expression)) { - TailCallBlocker blockTailCalls(this); - condition(ast->expression, &body, &end, true); - } + // special case that is not a loop: + // do {...} while (false) + if (!AST::cast<FalseLiteral *>(ast->expression)) + bytecodeGenerator->addLoopStart(body); body.link(); statement(ast->statement); setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken); - if (!AST::cast<FalseLiteral *>(ast->expression)) - bytecodeGenerator->jump().link(cond); + cond.link(); + if (AST::cast<TrueLiteral *>(ast->expression)) { + // do {} while (true) -> just jump back to the loop body, no need to generate a condition + bytecodeGenerator->checkException(); + bytecodeGenerator->jump().link(body); + } else if (AST::cast<FalseLiteral *>(ast->expression)) { + // do {} while (false) -> fall through, no need to generate a condition + } else { + TailCallBlocker blockTailCalls(this); + bytecodeGenerator->checkException(); + condition(ast->expression, &body, &end, false); + } end.link(); @@ -3220,7 +3250,7 @@ bool Codegen::visit(EmptyStatement *) bool Codegen::visit(ExpressionStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3228,7 +3258,7 @@ bool Codegen::visit(ExpressionStatement *ast) if (requiresReturnValue) { Reference e = expression(ast->expression); - if (hasError) + if (hasError()) return false; (void) e.storeOnStack(_returnAddress); } else { @@ -3239,7 +3269,7 @@ bool Codegen::visit(ExpressionStatement *ast) bool Codegen::visit(ForEachStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3255,7 +3285,7 @@ bool Codegen::visit(ForEachStatement *ast) RegisterScope innerScope(this); ControlFlowBlock controlFlow(this, ast); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -3285,7 +3315,7 @@ bool Codegen::visit(ForEachStatement *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); // each iteration gets it's own context, as per spec { @@ -3298,7 +3328,7 @@ bool Codegen::visit(ForEachStatement *ast) destructurePattern(p, lhsValue); } else { Reference lhs = expression(e); - if (hasError) + if (hasError()) goto error; if (!lhs.isLValue()) { throwReferenceError(e->firstSourceLocation(), QStringLiteral("Invalid left-hand side expression for 'in' expression")); @@ -3310,7 +3340,7 @@ bool Codegen::visit(ForEachStatement *ast) } } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) { initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true); - if (hasError) + if (hasError()) goto error; } else { Q_UNREACHABLE(); @@ -3321,6 +3351,7 @@ bool Codegen::visit(ForEachStatement *ast) setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); } + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(in); error: @@ -3335,7 +3366,7 @@ bool Codegen::visit(ForEachStatement *ast) bool Codegen::visit(ForStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3369,6 +3400,7 @@ bool Codegen::visit(ForStatement *ast) bytecodeGenerator->addInstruction(clone); } statement(ast->expression); + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(cond); end.link(); @@ -3378,7 +3410,7 @@ bool Codegen::visit(ForStatement *ast) bool Codegen::visit(IfStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3410,7 +3442,7 @@ bool Codegen::visit(IfStatement *ast) bool Codegen::visit(LabelledStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3458,7 +3490,7 @@ void Codegen::emitReturn(const Reference &expr) bool Codegen::visit(ReturnStatement *ast) { - if (hasError) + if (hasError()) return false; if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) { @@ -3468,7 +3500,7 @@ bool Codegen::visit(ReturnStatement *ast) Reference expr; if (ast->expression) { expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; } else { expr = Reference::fromConst(this, Encode::undefined()); @@ -3481,7 +3513,7 @@ bool Codegen::visit(ReturnStatement *ast) bool Codegen::visit(SwitchStatement *ast) { - if (hasError) + if (hasError()) return false; if (requiresReturnValue) @@ -3494,7 +3526,7 @@ bool Codegen::visit(SwitchStatement *ast) BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel(); Reference lhs = expression(ast->expression); - if (hasError) + if (hasError()) return false; lhs = lhs.storeOnStack(); @@ -3513,7 +3545,7 @@ bool Codegen::visit(SwitchStatement *ast) for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; Reference rhs = expression(clause->expression); - if (hasError) + if (hasError()) return false; rhs.loadInAccumulator(); bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); @@ -3522,7 +3554,7 @@ bool Codegen::visit(SwitchStatement *ast) for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; Reference rhs = expression(clause->expression); - if (hasError) + if (hasError()) return false; rhs.loadInAccumulator(); bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); @@ -3568,14 +3600,14 @@ bool Codegen::visit(SwitchStatement *ast) bool Codegen::visit(ThrowStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -3612,7 +3644,7 @@ void Codegen::handleTryFinally(TryStatement *ast) bool Codegen::visit(TryStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3628,7 +3660,7 @@ bool Codegen::visit(TryStatement *ast) bool Codegen::visit(VariableStatement *ast) { - if (hasError) + if (hasError()) return false; variableDeclarationList(ast->declarations); @@ -3637,7 +3669,7 @@ bool Codegen::visit(VariableStatement *ast) bool Codegen::visit(WhileStatement *ast) { - if (hasError) + if (hasError()) return false; if (AST::cast<FalseLiteral *>(ast->expression)) @@ -3651,6 +3683,8 @@ bool Codegen::visit(WhileStatement *ast) ControlFlowLoop flow(this, &end, &cond); bytecodeGenerator->addLoopStart(cond); + bytecodeGenerator->checkException(); + if (!AST::cast<TrueLiteral *>(ast->expression)) { TailCallBlocker blockTailCalls(this); condition(ast->expression, &start, &end, true); @@ -3667,14 +3701,14 @@ bool Codegen::visit(WhileStatement *ast) bool Codegen::visit(WithStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference src = expression(ast->expression); - if (hasError) + if (hasError()) return false; src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place src.loadInAccumulator(); @@ -3744,52 +3778,79 @@ bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, return isArgOrEval; } -void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +void Codegen::throwError(ErrorType errorType, const SourceLocation &loc, const QString &detail) { - if (hasError) + if (hasError()) return; - hasError = true; - QQmlJS::DiagnosticMessage error; - error.message = detail; - error.loc = loc; - _errors << error; + _errorType = errorType; + _error.message = detail; + _error.line = loc.startLine; + _error.column = loc.startColumn; } -void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) { - if (hasError) - return; - - hasError = true; - QQmlJS::DiagnosticMessage error; - error.message = detail; - error.loc = loc; - _errors << error; + throwError(SyntaxError, loc, detail); } -QList<QQmlJS::DiagnosticMessage> Codegen::errors() const +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) { - return _errors; + throwError(ReferenceError, loc, detail); } -QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData) +QQmlJS::DiagnosticMessage Codegen::error() const { - CompiledData::Unit *unitData = nullptr; - if (generateUnitData) - unitData = jsUnitGenerator->generateUnit(); - CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit(unitData); + return _error; +} - QQmlRefPointer<CompiledData::CompilationUnit> unit; - unit.adopt(compilationUnit); - return unit; +QV4::CompiledData::CompilationUnit Codegen::generateCompilationUnit( + bool generateUnitData) +{ + return QV4::CompiledData::CompilationUnit( + generateUnitData ? jsUnitGenerator->generateUnit() : nullptr); } -QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading() +CompiledData::CompilationUnit Codegen::compileModule( + bool debugMode, const QString &url, const QString &sourceCode, + const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics) { - QQmlRefPointer<CompiledData::CompilationUnit> result; - result.adopt(new CompiledData::CompilationUnit); - return result; + QQmlJS::Engine ee; + QQmlJS::Lexer lexer(&ee); + lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false); + QQmlJS::Parser parser(&ee); + + const bool parsed = parser.parseModule(); + + if (diagnostics) + *diagnostics = parser.diagnosticMessages(); + + if (!parsed) + return CompiledData::CompilationUnit(); + + QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode()); + if (!moduleNode) { + // if parsing was successful, and we have no module, then + // the file was empty. + if (diagnostics) + diagnostics->clear(); + return nullptr; + } + + using namespace QV4::Compiler; + Compiler::Module compilerModule(debugMode); + compilerModule.unitFlags |= CompiledData::Unit::IsESModule; + compilerModule.sourceTimeStamp = sourceTimeStamp; + JSUnitGenerator jsGenerator(&compilerModule); + Codegen cg(&jsGenerator, /*strictMode*/true); + cg.generateFromModule(url, url, sourceCode, moduleNode, &compilerModule); + if (cg.hasError()) { + if (diagnostics) + *diagnostics << cg.error(); + return CompiledData::CompilationUnit(); + } + + return cg.generateCompilationUnit(); } class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor @@ -3905,34 +3966,11 @@ Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node return scanner.scan(ast); } - -#ifndef V4_BOOTSTRAP - -QList<QQmlError> Codegen::qmlErrors() const +QUrl Codegen::url() const { - QList<QQmlError> qmlErrors; - - // Short circuit to avoid costly (de)heap allocation of QUrl if there are no errors. - if (_errors.size() == 0) - return qmlErrors; - - qmlErrors.reserve(_errors.size()); - - QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName)); - for (const QQmlJS::DiagnosticMessage &msg: qAsConst(_errors)) { - QQmlError e; - e.setUrl(url); - e.setLine(msg.loc.startLine); - e.setColumn(msg.loc.startColumn); - e.setDescription(msg.message); - qmlErrors << e; - } - - return qmlErrors; + return QUrl(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName)); } -#endif // V4_BOOTSTRAP - bool Codegen::RValue::operator==(const RValue &other) const { switch (type) { @@ -4222,7 +4260,7 @@ void Codegen::Reference::storeAccumulator() const Instruction::StoreElement store; store.base = elementBase; store.index = elementSubscript.stackSlot(); - codegen->bytecodeGenerator->addTracingInstruction(store); + codegen->bytecodeGenerator->addInstruction(store); } return; case Invalid: case Accumulator: @@ -4280,7 +4318,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str Instruction::LoadUndefined load; codegen->bytecodeGenerator->addInstruction(load); } else { - Value p = Value::fromReturnedValue(constant); + StaticValue p = StaticValue::fromReturnedValue(constant); if (p.isNumber()) { double d = p.asDouble(); int i = static_cast<int>(d); @@ -4291,7 +4329,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str return; } Instruction::LoadInt load; - load.value = Value::fromReturnedValue(constant).toInt32(); + load.value = StaticValue::fromReturnedValue(constant).toInt32(); codegen->bytecodeGenerator->addInstruction(load); return; } @@ -4312,12 +4350,12 @@ QT_WARNING_POP if (!scope) { Instruction::LoadLocal load; load.index = index; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadScopedLocal load; load.index = index; load.scope = scope; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } tdzCheck(requiresTDZCheck); return; @@ -4341,16 +4379,16 @@ QT_WARNING_POP if (qmlGlobal) { Instruction::LoadQmlContextPropertyLookup load; load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadGlobalLookup load; load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } } else { Instruction::LoadName load; load.name = nameAsIndex(); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Member: @@ -4359,11 +4397,11 @@ QT_WARNING_POP if (!disable_lookups && codegen->useFastLookups) { Instruction::GetLookup load; load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadProperty load; load.name = propertyNameIndex; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Import: { @@ -4378,7 +4416,7 @@ QT_WARNING_POP tdzCheck(subscriptRequiresTDZCheck); Instruction::LoadElement load; load.base = elementBase; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Invalid: break; diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 958dd16816..82a4fc3289 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -50,19 +50,18 @@ // We mean it. // -#include "private/qv4global_p.h" #include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsengine_p.h> #include <private/qqmljsast_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <private/qv4compiler_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4util_p.h> #include <private/qv4bytecodegenerator_p.h> -#include <private/qv4stackframe_p.h> +#include <private/qv4calldata_p.h> QT_BEGIN_NAMESPACE -using namespace QQmlJS; - namespace QV4 { namespace Moth { @@ -79,7 +78,7 @@ struct ControlFlow; struct ControlFlowCatch; struct ControlFlowFinally; -class Q_QML_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor +class Q_QMLCOMPILER_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor { protected: using BytecodeGenerator = QV4::Moth::BytecodeGenerator; @@ -91,14 +90,14 @@ public: void generateFromProgram(const QString &fileName, const QString &finalUrl, const QString &sourceCode, - AST::Program *ast, + QQmlJS::AST::Program *ast, Module *module, ContextType contextType = ContextType::Global); void generateFromModule(const QString &fileName, const QString &finalUrl, const QString &sourceCode, - AST::ESModule *ast, + QQmlJS::AST::ESModule *ast, Module *module); public: @@ -256,7 +255,8 @@ public: } static Reference fromArgument(Codegen *cg, int index, bool isVolatile) { Reference r(cg, StackSlot); - r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1); + r.theStackSlot = Moth::StackSlot::createRegister( + index + sizeof(CallData) / sizeof(StaticValue) - 1); r.stackSlotIsLocalOrArgument = true; r.isVolatile = isVolatile; return r; @@ -484,10 +484,10 @@ protected: } }; - void enterContext(AST::Node *node); + void enterContext(QQmlJS::AST::Node *node); int leaveContext(); public: - Context *enterBlock(AST::Node *node); + Context *enterBlock(QQmlJS::AST::Node *node); int leaveBlock() { return leaveContext(); } protected: void leaveLoop(); @@ -518,20 +518,20 @@ public: int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); } // Returns index in _module->functions - virtual int defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::StatementList *body); + virtual int defineFunction(const QString &name, QQmlJS::AST::Node *ast, + QQmlJS::AST::FormalParameterList *formals, + QQmlJS::AST::StatementList *body); protected: - void statement(AST::Statement *ast); - void statement(AST::ExpressionNode *ast); - void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, + void statement(QQmlJS::AST::Statement *ast); + void statement(QQmlJS::AST::ExpressionNode *ast); + void condition(QQmlJS::AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition); - inline Reference expression(AST::ExpressionNode *ast, const QString &name = QString()) + inline Reference expression(QQmlJS::AST::ExpressionNode *ast, const QString &name = QString()) { - if (!ast || hasError) + if (!ast || hasError()) return Reference(); pushExpr(name); @@ -539,160 +539,173 @@ protected: return popResult(); } - inline void accept(AST::Node *node) + inline void accept(QQmlJS::AST::Node *node) { - if (!hasError && node) + if (!hasError() && node) node->accept(this); } - void program(AST::Program *ast); - void statementList(AST::StatementList *ast); - void variableDeclaration(AST::PatternElement *ast); - void variableDeclarationList(AST::VariableDeclarationList *ast); + void program(QQmlJS::AST::Program *ast); + void statementList(QQmlJS::AST::StatementList *ast); + void variableDeclaration(QQmlJS::AST::PatternElement *ast); + void variableDeclarationList(QQmlJS::AST::VariableDeclarationList *ast); - Reference targetForPatternElement(AST::PatternElement *p); - void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false); - void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false); - void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false); - void destructurePattern(AST::Pattern *p, const Reference &rhs); + Reference targetForPatternElement(QQmlJS::AST::PatternElement *p); + void initializeAndDestructureBindingElement(QQmlJS::AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false); + void destructurePropertyList(const Reference &object, QQmlJS::AST::PatternPropertyList *bindingList, bool isDefinition = false); + void destructureElementList(const Reference &array, QQmlJS::AST::PatternElementList *bindingList, bool isDefinition = false); + void destructurePattern(QQmlJS::AST::Pattern *p, const Reference &rhs); - Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); + Reference referenceForPropertyName(const Codegen::Reference &object, QQmlJS::AST::PropertyName *name); void emitReturn(const Reference &expr); // nodes - bool visit(AST::ArgumentList *ast) override; - bool visit(AST::CaseBlock *ast) override; - bool visit(AST::CaseClause *ast) override; - bool visit(AST::CaseClauses *ast) override; - bool visit(AST::Catch *ast) override; - bool visit(AST::DefaultClause *ast) override; - bool visit(AST::Elision *ast) override; - bool visit(AST::Finally *ast) override; - bool visit(AST::FormalParameterList *ast) override; - bool visit(AST::Program *ast) override; - bool visit(AST::StatementList *ast) override; - bool visit(AST::UiArrayMemberList *ast) override; - bool visit(AST::UiImport *ast) override; - bool visit(AST::UiHeaderItemList *ast) override; - bool visit(AST::UiPragma *ast) override; - bool visit(AST::UiObjectInitializer *ast) override; - bool visit(AST::UiObjectMemberList *ast) override; - bool visit(AST::UiParameterList *ast) override; - bool visit(AST::UiProgram *ast) override; - bool visit(AST::UiQualifiedId *ast) override; - bool visit(AST::VariableDeclarationList *ast) override; - - bool visit(AST::PatternElement *ast) override; - bool visit(AST::PatternElementList *ast) override; - bool visit(AST::PatternProperty *ast) override; - bool visit(AST::PatternPropertyList *ast) override; - - bool visit(AST::ExportDeclaration *ast) override; + bool visit(QQmlJS::AST::ArgumentList *ast) override; + bool visit(QQmlJS::AST::CaseBlock *ast) override; + bool visit(QQmlJS::AST::CaseClause *ast) override; + bool visit(QQmlJS::AST::CaseClauses *ast) override; + bool visit(QQmlJS::AST::Catch *ast) override; + bool visit(QQmlJS::AST::DefaultClause *ast) override; + bool visit(QQmlJS::AST::Elision *ast) override; + bool visit(QQmlJS::AST::Finally *ast) override; + bool visit(QQmlJS::AST::FormalParameterList *ast) override; + bool visit(QQmlJS::AST::Program *ast) override; + bool visit(QQmlJS::AST::StatementList *ast) override; + bool visit(QQmlJS::AST::UiArrayMemberList *ast) override; + bool visit(QQmlJS::AST::UiImport *ast) override; + bool visit(QQmlJS::AST::UiHeaderItemList *ast) override; + bool visit(QQmlJS::AST::UiPragma *ast) override; + bool visit(QQmlJS::AST::UiObjectInitializer *ast) override; + bool visit(QQmlJS::AST::UiObjectMemberList *ast) override; + bool visit(QQmlJS::AST::UiParameterList *ast) override; + bool visit(QQmlJS::AST::UiProgram *ast) override; + bool visit(QQmlJS::AST::UiQualifiedId *ast) override; + bool visit(QQmlJS::AST::VariableDeclarationList *ast) override; + + bool visit(QQmlJS::AST::PatternElement *ast) override; + bool visit(QQmlJS::AST::PatternElementList *ast) override; + bool visit(QQmlJS::AST::PatternProperty *ast) override; + bool visit(QQmlJS::AST::PatternPropertyList *ast) override; + + bool visit(QQmlJS::AST::ExportDeclaration *ast) override; + + bool visit(QQmlJS::AST::TypeAnnotation *ast) override; // expressions - bool visit(AST::Expression *ast) override; - bool visit(AST::ArrayPattern *ast) override; - bool visit(AST::ArrayMemberExpression *ast) override; - bool visit(AST::BinaryExpression *ast) override; - bool visit(AST::CallExpression *ast) override; - bool visit(AST::ConditionalExpression *ast) override; - bool visit(AST::DeleteExpression *ast) override; - bool visit(AST::FalseLiteral *ast) override; - bool visit(AST::SuperLiteral *ast) override; - bool visit(AST::FieldMemberExpression *ast) override; - bool visit(AST::TaggedTemplate *ast) override; - bool visit(AST::FunctionExpression *ast) override; - bool visit(AST::IdentifierExpression *ast) override; - bool visit(AST::NestedExpression *ast) override; - bool visit(AST::NewExpression *ast) override; - bool visit(AST::NewMemberExpression *ast) override; - bool visit(AST::NotExpression *ast) override; - bool visit(AST::NullExpression *ast) override; - bool visit(AST::NumericLiteral *ast) override; - bool visit(AST::ObjectPattern *ast) override; - bool visit(AST::PostDecrementExpression *ast) override; - bool visit(AST::PostIncrementExpression *ast) override; - bool visit(AST::PreDecrementExpression *ast) override; - bool visit(AST::PreIncrementExpression *ast) override; - bool visit(AST::RegExpLiteral *ast) override; - bool visit(AST::StringLiteral *ast) override; - bool visit(AST::TemplateLiteral *ast) override; - bool visit(AST::ThisExpression *ast) override; - bool visit(AST::TildeExpression *ast) override; - bool visit(AST::TrueLiteral *ast) override; - bool visit(AST::TypeOfExpression *ast) override; - bool visit(AST::UnaryMinusExpression *ast) override; - bool visit(AST::UnaryPlusExpression *ast) override; - bool visit(AST::VoidExpression *ast) override; - bool visit(AST::FunctionDeclaration *ast) override; - bool visit(AST::YieldExpression *ast) override; - bool visit(AST::ClassExpression *ast) override; - bool visit(AST::ClassDeclaration *ast) override; + bool visit(QQmlJS::AST::Expression *ast) override; + bool visit(QQmlJS::AST::ArrayPattern *ast) override; + bool visit(QQmlJS::AST::ArrayMemberExpression *ast) override; + bool visit(QQmlJS::AST::BinaryExpression *ast) override; + bool visit(QQmlJS::AST::CallExpression *ast) override; + bool visit(QQmlJS::AST::ConditionalExpression *ast) override; + bool visit(QQmlJS::AST::DeleteExpression *ast) override; + bool visit(QQmlJS::AST::FalseLiteral *ast) override; + bool visit(QQmlJS::AST::SuperLiteral *ast) override; + bool visit(QQmlJS::AST::FieldMemberExpression *ast) override; + bool visit(QQmlJS::AST::TaggedTemplate *ast) override; + bool visit(QQmlJS::AST::FunctionExpression *ast) override; + bool visit(QQmlJS::AST::IdentifierExpression *ast) override; + bool visit(QQmlJS::AST::NestedExpression *ast) override; + bool visit(QQmlJS::AST::NewExpression *ast) override; + bool visit(QQmlJS::AST::NewMemberExpression *ast) override; + bool visit(QQmlJS::AST::NotExpression *ast) override; + bool visit(QQmlJS::AST::NullExpression *ast) override; + bool visit(QQmlJS::AST::NumericLiteral *ast) override; + bool visit(QQmlJS::AST::ObjectPattern *ast) override; + bool visit(QQmlJS::AST::PostDecrementExpression *ast) override; + bool visit(QQmlJS::AST::PostIncrementExpression *ast) override; + bool visit(QQmlJS::AST::PreDecrementExpression *ast) override; + bool visit(QQmlJS::AST::PreIncrementExpression *ast) override; + bool visit(QQmlJS::AST::RegExpLiteral *ast) override; + bool visit(QQmlJS::AST::StringLiteral *ast) override; + bool visit(QQmlJS::AST::TemplateLiteral *ast) override; + bool visit(QQmlJS::AST::ThisExpression *ast) override; + bool visit(QQmlJS::AST::TildeExpression *ast) override; + bool visit(QQmlJS::AST::TrueLiteral *ast) override; + bool visit(QQmlJS::AST::TypeOfExpression *ast) override; + bool visit(QQmlJS::AST::UnaryMinusExpression *ast) override; + bool visit(QQmlJS::AST::UnaryPlusExpression *ast) override; + bool visit(QQmlJS::AST::VoidExpression *ast) override; + bool visit(QQmlJS::AST::FunctionDeclaration *ast) override; + bool visit(QQmlJS::AST::YieldExpression *ast) override; + bool visit(QQmlJS::AST::ClassExpression *ast) override; + bool visit(QQmlJS::AST::ClassDeclaration *ast) override; // statements - bool visit(AST::Block *ast) override; - bool visit(AST::BreakStatement *ast) override; - bool visit(AST::ContinueStatement *ast) override; - bool visit(AST::DebuggerStatement *ast) override; - bool visit(AST::DoWhileStatement *ast) override; - bool visit(AST::EmptyStatement *ast) override; - bool visit(AST::ExpressionStatement *ast) override; - bool visit(AST::ForEachStatement *ast) override; - bool visit(AST::ForStatement *ast) override; - bool visit(AST::IfStatement *ast) override; - bool visit(AST::LabelledStatement *ast) override; - bool visit(AST::ReturnStatement *ast) override; - bool visit(AST::SwitchStatement *ast) override; - bool visit(AST::ThrowStatement *ast) override; - bool visit(AST::TryStatement *ast) override; - bool visit(AST::VariableStatement *ast) override; - bool visit(AST::WhileStatement *ast) override; - bool visit(AST::WithStatement *ast) override; + bool visit(QQmlJS::AST::Block *ast) override; + bool visit(QQmlJS::AST::BreakStatement *ast) override; + bool visit(QQmlJS::AST::ContinueStatement *ast) override; + bool visit(QQmlJS::AST::DebuggerStatement *ast) override; + bool visit(QQmlJS::AST::DoWhileStatement *ast) override; + bool visit(QQmlJS::AST::EmptyStatement *ast) override; + bool visit(QQmlJS::AST::ExpressionStatement *ast) override; + bool visit(QQmlJS::AST::ForEachStatement *ast) override; + bool visit(QQmlJS::AST::ForStatement *ast) override; + bool visit(QQmlJS::AST::IfStatement *ast) override; + bool visit(QQmlJS::AST::LabelledStatement *ast) override; + bool visit(QQmlJS::AST::ReturnStatement *ast) override; + bool visit(QQmlJS::AST::SwitchStatement *ast) override; + bool visit(QQmlJS::AST::ThrowStatement *ast) override; + bool visit(QQmlJS::AST::TryStatement *ast) override; + bool visit(QQmlJS::AST::VariableStatement *ast) override; + bool visit(QQmlJS::AST::WhileStatement *ast) override; + bool visit(QQmlJS::AST::WithStatement *ast) override; // ui object members - bool visit(AST::UiArrayBinding *ast) override; - bool visit(AST::UiObjectBinding *ast) override; - bool visit(AST::UiObjectDefinition *ast) override; - bool visit(AST::UiPublicMember *ast) override; - bool visit(AST::UiScriptBinding *ast) override; - bool visit(AST::UiSourceElement *ast) override; - - bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc); - virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); - virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + bool visit(QQmlJS::AST::UiArrayBinding *ast) override; + bool visit(QQmlJS::AST::UiObjectBinding *ast) override; + bool visit(QQmlJS::AST::UiObjectDefinition *ast) override; + bool visit(QQmlJS::AST::UiPublicMember *ast) override; + bool visit(QQmlJS::AST::UiScriptBinding *ast) override; + bool visit(QQmlJS::AST::UiSourceElement *ast) override; + + bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, + const QQmlJS::AST::SourceLocation &loc); + virtual void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail); + virtual void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail); void throwRecursionDepthError() override { - throwSyntaxError(AST::SourceLocation(), + throwSyntaxError(QQmlJS::AST::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } public: - QList<DiagnosticMessage> errors() const; -#ifndef V4_BOOTSTRAP - QList<QQmlError> qmlErrors() const; -#endif + enum ErrorType { + NoError, + SyntaxError, + ReferenceError + }; + + ErrorType errorType() const { return _errorType; } + bool hasError() const { return _errorType != NoError; } + QQmlJS::DiagnosticMessage error() const; + QUrl url() const; Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right); Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right); struct Arguments { int argc; int argv; bool hasSpread; }; - Arguments pushArgs(AST::ArgumentList *args); + Arguments pushArgs(QQmlJS::AST::ArgumentList *args); void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject); - Arguments pushTemplateArgs(AST::TemplateLiteral *args); - bool handleTaggedTemplate(Reference base, AST::TaggedTemplate *ast); - void createTemplateObject(AST::TemplateLiteral *t); + Arguments pushTemplateArgs(QQmlJS::AST::TemplateLiteral *args); + bool handleTaggedTemplate(Reference base, QQmlJS::AST::TaggedTemplate *ast); + void createTemplateObject(QQmlJS::AST::TemplateLiteral *t); void setUseFastLookups(bool b) { useFastLookups = b; } - void handleTryCatch(AST::TryStatement *ast); - void handleTryFinally(AST::TryStatement *ast); + void handleTryCatch(QQmlJS::AST::TryStatement *ast); + void handleTryFinally(QQmlJS::AST::TryStatement *ast); - Reference referenceForName(const QString &name, bool lhs, const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation()); + Reference referenceForName( + const QString &name, bool lhs, + const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation()); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true); - static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading(); + QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData = true); + static QV4::CompiledData::CompilationUnit compileModule( + bool debugMode, const QString &url, const QString &sourceCode, + const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics); Context *currentContext() const { return _context; } BytecodeGenerator *generator() const { return bytecodeGenerator; } @@ -751,7 +764,7 @@ protected: int _returnAddress; Context *_context; Context *_functionContext = nullptr; - AST::LabelledStatement *_labelledStatement; + QQmlJS::AST::LabelledStatement *_labelledStatement; QV4::Compiler::JSUnitGenerator *jsUnitGenerator; BytecodeGenerator *bytecodeGenerator = nullptr; Moth::BytecodeGenerator::Label *_returnLabel = nullptr; @@ -767,8 +780,8 @@ protected: ControlFlow *controlFlow = nullptr; bool _fileNameIsUrl; - bool hasError; - QList<QQmlJS::DiagnosticMessage> _errors; + ErrorType _errorType = NoError; + QQmlJS::DiagnosticMessage _error; class TailCallBlocker { @@ -795,8 +808,10 @@ protected: }; private: - VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast); - void handleConstruct(const Reference &base, AST::ArgumentList *args); + VolatileMemoryLocations scanVolatileMemoryLocations(QQmlJS::AST::Node *ast); + void handleConstruct(const Reference &base, QQmlJS::AST::ArgumentList *args); + void throwError(ErrorType errorType, const QQmlJS::AST::SourceLocation &loc, + const QString &detail); }; } diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp deleted file mode 100644 index 350f6f9485..0000000000 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compilationunitmapper_p.h" - -#include "qv4compileddata_p.h" -#include <QFileInfo> -#include <QDateTime> -#include <QCoreApplication> - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -CompilationUnitMapper::CompilationUnitMapper() - : dataPtr(nullptr) -{ - -} - -CompilationUnitMapper::~CompilationUnitMapper() -{ - close(); -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp deleted file mode 100644 index 6768bc9596..0000000000 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compilationunitmapper_p.h" - -#include <sys/mman.h> -#include <functional> -#include <private/qcore_unix_p.h> -#include <QScopeGuard> -#include <QDateTime> - -#include "qv4compileddata_p.h" - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString) -{ - close(); - - int fd = qt_safe_open(QFile::encodeName(cacheFileName).constData(), O_RDONLY); - if (fd == -1) { - *errorString = qt_error_string(errno); - return nullptr; - } - - auto cleanup = qScopeGuard([fd]{ - qt_safe_close(fd) ; - }); - - CompiledData::Unit header; - qint64 bytesRead = qt_safe_read(fd, reinterpret_cast<char *>(&header), sizeof(header)); - - if (bytesRead != sizeof(header)) { - *errorString = QStringLiteral("File too small for the header fields"); - return nullptr; - } - - if (!header.verifyHeader(sourceTimeStamp, errorString)) - return nullptr; - - // Data structure and qt version matched, so now we can access the rest of the file safely. - - length = static_cast<size_t>(lseek(fd, 0, SEEK_END)); - - void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0); - if (ptr == MAP_FAILED) { - *errorString = qt_error_string(errno); - return nullptr; - } - dataPtr = ptr; - - return reinterpret_cast<CompiledData::Unit*>(dataPtr); -} - -void CompilationUnitMapper::close() -{ - // Do not unmap the data here. - if (dataPtr != nullptr) { - // Do not unmap cache files that are built with the StaticData flag. That's the majority of - // them and it's necessary to benefit from the QString literal optimization. There might - // still be QString instances around that point into that memory area. The memory is backed - // on the disk, so the kernel is free to release the pages and all that remains is the - // address space allocation. - if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData)) - munmap(dataPtr, length); - } - dataPtr = nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp deleted file mode 100644 index 779c1288fe..0000000000 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compilationunitmapper_p.h" - -#include "qv4compileddata_p.h" -#include <QScopeGuard> -#include <QFileInfo> -#include <QDateTime> -#include <qt_windows.h> - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString) -{ - close(); - - // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry - // is exported from QtCore. - HANDLE handle = -#if defined(Q_OS_WINRT) - CreateFile2(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), - GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, - OPEN_EXISTING, nullptr); -#else - CreateFile(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), - GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, - nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - nullptr); -#endif - if (handle == INVALID_HANDLE_VALUE) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - auto fileHandleCleanup = qScopeGuard([handle]{ - CloseHandle(handle); - }); - - CompiledData::Unit header; - DWORD bytesRead; - if (!ReadFile(handle, reinterpret_cast<char *>(&header), sizeof(header), &bytesRead, nullptr)) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - if (bytesRead != sizeof(header)) { - *errorString = QStringLiteral("File too small for the header fields"); - return nullptr; - } - - if (!header.verifyHeader(sourceTimeStamp, errorString)) - return nullptr; - - // Data structure and qt version matched, so now we can access the rest of the file safely. - - HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0); - if (!fileMappingHandle) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - auto mappingCleanup = qScopeGuard([fileMappingHandle]{ - CloseHandle(fileMappingHandle); - }); - - dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0); - if (!dataPtr) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - return reinterpret_cast<CompiledData::Unit*>(dataPtr); -} - -void CompilationUnitMapper::close() -{ - if (dataPtr != nullptr) { - // Do not unmap cache files that are built with the StaticData flag. That's the majority of - // them and it's necessary to benefit from the QString literal optimization. There might - // still be QString instances around that point into that memory area. The memory is backed - // on the disk, so the kernel is free to release the pages and all that remains is the - // address space allocation. - if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData)) - UnmapViewOfFile(dataPtr); - } - dataPtr = nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp deleted file mode 100644 index c0ce125741..0000000000 --- a/src/qml/compiler/qv4compileddata.cpp +++ /dev/null @@ -1,1030 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compileddata_p.h" -#include <private/qv4value_p.h> -#ifndef V4_BOOTSTRAP -#include <private/qv4engine_p.h> -#include <private/qv4function_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qv4lookup_p.h> -#include <private/qv4regexpobject_p.h> -#include <private/qv4regexp_p.h> -#include <private/qqmlpropertycache_p.h> -#include <private/qqmltypeloader_p.h> -#include <private/qqmlengine_p.h> -#include <private/qv4vme_moth_p.h> -#include <private/qv4module_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <private/qqmlvaluetypewrapper_p.h> -#include "qv4compilationunitmapper_p.h" -#include <QQmlPropertyMap> -#include <QDateTime> -#include <QFile> -#include <QFileInfo> -#include <QScopedValueRollback> -#include <QStandardPaths> -#include <QDir> -#include <private/qv4identifiertable_p.h> -#endif -#include <private/qqmlirbuilder_p.h> -#include <QCoreApplication> -#include <QCryptographicHash> -#include <QSaveFile> -#include <QScopeGuard> - -// generated by qmake: -#include "qml_compile_hash_p.h" - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -namespace CompiledData { - -#if defined(QML_COMPILE_HASH) -# ifdef Q_OS_LINUX -// Place on a separate section on Linux so it's easier to check from outside -// what the hash version is. -__attribute__((section(".qml_compile_hash"))) -# endif -const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH; -static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); -#else -# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" -#endif - - -CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName, const QString &finalUrlString) -{ - setUnitData(unitData, nullptr, fileName, finalUrlString); -} - -CompilationUnit::~CompilationUnit() -{ -#ifndef V4_BOOTSTRAP - unlink(); -#endif - - if (data) { - if (data->qmlUnit() != qmlData) - free(const_cast<QmlUnit *>(qmlData)); - qmlData = nullptr; - -#ifndef V4_BOOTSTRAP - if (!(data->flags & QV4::CompiledData::Unit::StaticData)) - free(const_cast<Unit *>(data)); -#else - // Unconditionally free the memory. In the dev tools we create units that have - // the flag set and will be saved to disk, so intended to persist later. - free(const_cast<Unit *>(data)); -#endif - } - data = nullptr; -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - delete [] constants; - constants = nullptr; -#endif - - delete [] imports; - imports = nullptr; -} -#ifndef V4_BOOTSTRAP - -QString CompilationUnit::localCacheFilePath(const QUrl &url) -{ - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix(); - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix; -} - -QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) -{ - this->engine = engine; - engine->compilationUnits.insert(this); - - Q_ASSERT(!runtimeStrings); - Q_ASSERT(data); - const quint32 stringCount = totalStringCount(); - runtimeStrings = (QV4::Heap::String **)malloc(stringCount * sizeof(QV4::Heap::String*)); - // memset the strings to 0 in case a GC run happens while we're within the loop below - memset(runtimeStrings, 0, stringCount * sizeof(QV4::Heap::String*)); - for (uint i = 0; i < stringCount; ++i) - runtimeStrings[i] = engine->newString(stringAt(i)); - - runtimeRegularExpressions = new QV4::Value[data->regexpTableSize]; - // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value)); - for (uint i = 0; i < data->regexpTableSize; ++i) { - const CompiledData::RegExp *re = data->regexpAt(i); - uint f = re->flags; - const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f); - runtimeRegularExpressions[i] = QV4::RegExp::create(engine, stringAt(re->stringIndex), flags); - } - - if (data->lookupTableSize) { - runtimeLookups = new QV4::Lookup[data->lookupTableSize]; - memset(runtimeLookups, 0, data->lookupTableSize * sizeof(QV4::Lookup)); - const CompiledData::Lookup *compiledLookups = data->lookupTable(); - for (uint i = 0; i < data->lookupTableSize; ++i) { - QV4::Lookup *l = runtimeLookups + i; - - Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags)); - if (type == CompiledData::Lookup::Type_Getter) - l->getter = QV4::Lookup::getterGeneric; - else if (type == CompiledData::Lookup::Type_Setter) - l->setter = QV4::Lookup::setterGeneric; - else if (type == CompiledData::Lookup::Type_GlobalGetter) - l->globalGetter = QV4::Lookup::globalGetterGeneric; - else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) - l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; - l->nameIndex = compiledLookups[i].nameIndex; - } - } - - if (data->jsClassTableSize) { - runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); - // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); - for (uint i = 0; i < data->jsClassTableSize; ++i) { - int memberCount = 0; - const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount); - runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object); - for (int j = 0; j < memberCount; ++j, ++member) - runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); - } - } - - runtimeFunctions.resize(data->functionTableSize); - for (int i = 0 ;i < runtimeFunctions.size(); ++i) { - const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction); - } - - Scope scope(engine); - Scoped<InternalClass> ic(scope); - - runtimeBlocks.resize(data->blockTableSize); - for (int i = 0 ;i < runtimeBlocks.size(); ++i) { - const QV4::CompiledData::Block *compiledBlock = data->blockAt(i); - ic = engine->internalClasses(EngineBase::Class_CallContext); - - // first locals - const quint32_le *localsIndices = compiledBlock->localsTable(); - for (quint32 j = 0; j < compiledBlock->nLocals; ++j) - ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]), Attr_NotConfigurable); - runtimeBlocks[i] = ic->d(); - } - - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); - if (showCode) { - qDebug() << "=== Constant table"; - Moth::dumpConstantTable(constants, data->constantTableSize); - qDebug() << "=== String table"; - for (uint i = 0, end = totalStringCount(); i < end; ++i) - qDebug() << " " << i << ":" << runtimeStrings[i]->toQString(); - qDebug() << "=== Closure table"; - for (uint i = 0; i < data->functionTableSize; ++i) - qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString(); - qDebug() << "root function at index " << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0); - } - - if (data->indexOfRootFunction != -1) - return runtimeFunctions[data->indexOfRootFunction]; - else - return nullptr; -} - -Heap::Object *CompilationUnit::templateObjectAt(int index) const -{ - Q_ASSERT(index < int(data->templateObjectTableSize)); - if (!templateObjects.size()) - templateObjects.resize(data->templateObjectTableSize); - Heap::Object *o = templateObjects.at(index); - if (o) - return o; - - // create the template object - Scope scope(engine); - const CompiledData::TemplateObject *t = data->templateObjectAt(index); - Scoped<ArrayObject> a(scope, engine->newArrayObject(t->size)); - Scoped<ArrayObject> raw(scope, engine->newArrayObject(t->size)); - ScopedValue s(scope); - for (uint i = 0; i < t->size; ++i) { - s = runtimeStrings[t->stringIndexAt(i)]; - a->arraySet(i, s); - s = runtimeStrings[t->rawStringIndexAt(i)]; - raw->arraySet(i, s); - } - - ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, raw, 1); - a->defineReadonlyProperty(QStringLiteral("raw"), raw); - ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, a, 1); - - templateObjects[index] = a->objectValue()->d(); - return templateObjects.at(index); -} - -void CompilationUnit::unlink() -{ - if (engine) - nextCompilationUnit.remove(); - - if (isRegisteredWithEngine) { - Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0)); - if (qmlEngine) - qmlEngine->unregisterInternalCompositeType(this); - QQmlMetaType::unregisterInternalCompositeType(this); - isRegisteredWithEngine = false; - } - - propertyCaches.clear(); - - if (runtimeLookups) { - for (uint i = 0; i < data->lookupTableSize; ++i) { - QV4::Lookup &l = runtimeLookups[i]; - if (l.getter == QV4::QObjectWrapper::lookupGetter) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) { - if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) - pc->release(); - } - - if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } - } - } - - dependentScripts.clear(); - - typeNameCache = nullptr; - - qDeleteAll(resolvedTypes); - resolvedTypes.clear(); - - engine = nullptr; - qmlEngine = nullptr; - free(runtimeStrings); - runtimeStrings = nullptr; - delete [] runtimeLookups; - runtimeLookups = nullptr; - delete [] runtimeRegularExpressions; - runtimeRegularExpressions = nullptr; - free(runtimeClasses); - runtimeClasses = nullptr; - for (QV4::Function *f : qAsConst(runtimeFunctions)) - f->destroy(); - runtimeFunctions.clear(); -} - -void CompilationUnit::markObjects(QV4::MarkStack *markStack) -{ - if (runtimeStrings) { - for (uint i = 0, end = totalStringCount(); i < end; ++i) - if (runtimeStrings[i]) - runtimeStrings[i]->mark(markStack); - } - if (runtimeRegularExpressions) { - for (uint i = 0; i < data->regexpTableSize; ++i) - runtimeRegularExpressions[i].mark(markStack); - } - if (runtimeClasses) { - for (uint i = 0; i < data->jsClassTableSize; ++i) - if (runtimeClasses[i]) - runtimeClasses[i]->mark(markStack); - } - for (QV4::Function *f : qAsConst(runtimeFunctions)) - if (f && f->internalClass) - f->internalClass->mark(markStack); - for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks)) - if (c) - c->mark(markStack); - - for (QV4::Heap::Object *o : qAsConst(templateObjects)) - if (o) - o->mark(markStack); - - if (runtimeLookups) { - for (uint i = 0; i < data->lookupTableSize; ++i) - runtimeLookups[i].markObjects(markStack); - } - - if (m_module) - m_module->mark(markStack); -} - -IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex) -{ - IdentifierHash namedObjectCache(engine); - const CompiledData::Object *component = objectAt(componentObjectIndex); - const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable(); - for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { - const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr); - namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); - } - return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); -} - -void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) -{ - this->qmlEngine = qmlEngine; - - // Add to type registry of composites - if (propertyCaches.needsVMEMetaObject(/*root object*/0)) { - QQmlMetaType::registerInternalCompositeType(this); - qmlEngine->registerInternalCompositeType(this); - } else { - const QV4::CompiledData::Object *obj = objectAt(/*root object*/0); - auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - if (typeRef->compilationUnit) { - metaTypeId = typeRef->compilationUnit->metaTypeId; - listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; - } else { - metaTypeId = typeRef->type.typeId(); - listMetaTypeId = typeRef->type.qListTypeId(); - } - } - - // Collect some data for instantiation later. - int bindingCount = 0; - int parserStatusCount = 0; - int objectCount = 0; - for (quint32 i = 0, count = this->objectCount(); i < count; ++i) { - const QV4::CompiledData::Object *obj = objectAt(i); - bindingCount += obj->nBindings; - if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) { - if (typeRef->type.parserStatusCast() != -1) - ++parserStatusCount; - } - ++objectCount; - if (typeRef->compilationUnit) { - bindingCount += typeRef->compilationUnit->totalBindingsCount; - parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; - objectCount += typeRef->compilationUnit->totalObjectCount; - } - } - } - - totalBindingsCount = bindingCount; - totalParserStatusCount = parserStatusCount; - totalObjectCount = objectCount; -} - -bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const -{ - if (!dependencyHasher) { - for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { - if (data->dependencyMD5Checksum[i] != 0) - return false; - } - return true; - } - QCryptographicHash hash(QCryptographicHash::Md5); - if (!dependencyHasher(&hash)) - return false; - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum)); - return memcmp(data->dependencyMD5Checksum, checksum.constData(), - sizeof(data->dependencyMD5Checksum)) == 0; -} - -QStringList CompilationUnit::moduleRequests() const -{ - QStringList requests; - requests.reserve(data->moduleRequestTableSize); - for (uint i = 0; i < data->moduleRequestTableSize; ++i) - requests << stringAt(data->moduleRequestTable()[i]); - return requests; -} - -Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine) -{ - if (isESModule() && m_module) - return m_module; - - if (data->indexOfRootFunction < 0) - return nullptr; - - if (!this->engine) - linkToEngine(engine); - - Scope scope(engine); - Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this)); - - if (isESModule()) - m_module = module->d(); - - for (const QString &request: moduleRequests()) { - auto dependentModuleUnit = engine->loadModule(QUrl(request), this); - if (engine->hasException) - return nullptr; - dependentModuleUnit->instantiate(engine); - } - - ScopedString importName(scope); - - const uint importCount = data->importEntryTableSize; - if (importCount > 0) { - imports = new const Value *[importCount]; - memset(imports, 0, importCount * sizeof(Value *)); - } - for (uint i = 0; i < importCount; ++i) { - const CompiledData::ImportEntry &entry = data->importEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); - importName = runtimeStrings[entry.importName]; - const Value *valuePtr = dependentModuleUnit->resolveExport(importName); - if (!valuePtr) { - QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference "); - referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); - return nullptr; - } - imports[i] = valuePtr; - } - - for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); - if (!dependentModuleUnit) - return nullptr; - - ScopedString importName(scope, runtimeStrings[entry.importName]); - if (!dependentModuleUnit->resolveExport(importName)) { - QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference "); - referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); - return nullptr; - } - } - - return module->d(); -} - -const Value *CompilationUnit::resolveExport(QV4::String *exportName) -{ - QVector<ResolveSetEntry> resolveSet; - return resolveExportRecursively(exportName, &resolveSet); -} - -QStringList CompilationUnit::exportedNames() const -{ - QStringList names; - QVector<const CompiledData::CompilationUnit*> exportNameSet; - getExportedNamesRecursively(&names, &exportNameSet); - names.sort(); - auto last = std::unique(names.begin(), names.end()); - names.erase(last, names.end()); - return names; -} - -const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet) -{ - if (!m_module) - return nullptr; - - for (const auto &entry: *resolveSet) - if (entry.module == this && entry.exportName->isEqualTo(exportName)) - return nullptr; - - (*resolveSet) << ResolveSetEntry(this, exportName); - - if (exportName->toQString() == QLatin1String("*")) - return &m_module->self; - - Scope scope(engine); - - if (auto localExport = lookupNameInExportTable(data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) { - ScopedString localName(scope, runtimeStrings[localExport->localName]); - uint index = m_module->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey()); - if (index == UINT_MAX) - return nullptr; - if (index >= m_module->scope->locals.size) - return imports[index - m_module->scope->locals.size]; - return &m_module->scope->locals[index]; - } - - if (auto indirectExport = lookupNameInExportTable(data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) { - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(indirectExport->moduleRequest)), this); - if (!dependentModuleUnit) - return nullptr; - ScopedString importName(scope, runtimeStrings[indirectExport->importName]); - return dependentModuleUnit->resolveExportRecursively(importName, resolveSet); - } - - - if (exportName->toQString() == QLatin1String("default")) - return nullptr; - - const Value *starResolution = nullptr; - - for (uint i = 0; i < data->starExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); - if (!dependentModuleUnit) - return nullptr; - - const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet); - // ### handle ambiguous - if (resolution) { - if (!starResolution) { - starResolution = resolution; - continue; - } - if (resolution != starResolution) - return nullptr; - } - } - - return starResolution; -} - -const ExportEntry *CompilationUnit::lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const -{ - const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize; - auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) { - return stringAt(lhs.exportName) < name->toQString(); - }); - if (matchingExport == lastExportEntry || stringAt(matchingExport->exportName) != name->toQString()) - return nullptr; - return matchingExport; -} - -void CompilationUnit::getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit*> *exportNameSet, bool includeDefaultExport) const -{ - if (exportNameSet->contains(this)) - return; - exportNameSet->append(this); - - const auto append = [names, includeDefaultExport](const QString &name) { - if (!includeDefaultExport && name == QLatin1String("default")) - return; - names->append(name); - }; - - for (uint i = 0; i < data->localExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i]; - append(stringAt(entry.exportName)); - } - - for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; - append(stringAt(entry.exportName)); - } - - for (uint i = 0; i < data->starExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); - if (!dependentModuleUnit) - return; - dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false); - } -} - -void CompilationUnit::evaluate() -{ - QV4::Scope scope(engine); - QV4::Scoped<Module> module(scope, m_module); - module->evaluate(); -} - -void CompilationUnit::evaluateModuleRequests() -{ - for (const QString &request: moduleRequests()) { - auto dependentModuleUnit = engine->loadModule(QUrl(request), this); - if (engine->hasException) - return; - dependentModuleUnit->evaluate(); - if (engine->hasException) - return; - } -} - -bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) -{ - if (!QQmlFile::isLocalFile(url)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } - - const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); - QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); - - const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) }; - for (const QString &cachePath : cachePaths) { - CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString); - if (!mappedUnit) - continue; - - const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - const Unit *oldData = data; - auto dataPtrRevert = qScopeGuard([this, oldData](){ - setUnitData(oldData); - }); - setUnitData(mappedUnit); - - if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - continue; - } - - dataPtrRevert.dismiss(); - free(const_cast<Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; - } - - return false; -} - -#endif // V4_BOOTSTRAP - -#if defined(V4_BOOTSTRAP) -bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString) -#else -bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) -#endif -{ - errorString->clear(); - -#if !defined(V4_BOOTSTRAP) - if (data->sourceTimeStamp == 0) { - *errorString = QStringLiteral("Missing time stamp for source file"); - return false; - } - - if (!QQmlFile::isLocalFile(unitUrl)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } - const QString outputFileName = localCacheFilePath(unitUrl); -#endif - -#if QT_CONFIG(temporaryfile) - // Foo.qml -> Foo.qmlc - QSaveFile cacheFile(outputFileName); - if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - *errorString = cacheFile.errorString(); - return false; - } - - QByteArray modifiedUnit; - modifiedUnit.resize(data->unitSize); - memcpy(modifiedUnit.data(), data, data->unitSize); - const char *dataPtr = modifiedUnit.data(); - Unit *unitPtr; - memcpy(&unitPtr, &dataPtr, sizeof(unitPtr)); - unitPtr->flags |= Unit::StaticData; - - qint64 headerWritten = cacheFile.write(modifiedUnit); - if (headerWritten != modifiedUnit.size()) { - *errorString = cacheFile.errorString(); - return false; - } - - if (!cacheFile.commit()) { - *errorString = cacheFile.errorString(); - return false; - } - - return true; -#else - Q_UNUSED(outputFileName) - *errorString = QStringLiteral("features.temporaryfile is disabled."); - return false; -#endif // QT_CONFIG(temporaryfile) -} - -void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit, - const QString &fileName, const QString &finalUrlString) -{ - data = unitData; - qmlData = nullptr; -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - delete [] constants; -#endif - constants = nullptr; - m_fileName.clear(); - m_finalUrlString.clear(); - if (!data) - return; - - qmlData = qmlUnit ? qmlUnit : data->qmlUnit(); - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - Value *bigEndianConstants = new Value[data->constantTableSize]; - const quint64_le *littleEndianConstants = data->constants(); - for (uint i = 0; i < data->constantTableSize; ++i) - bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]); - constants = bigEndianConstants; -#else - constants = reinterpret_cast<const Value*>(data->constants()); -#endif - - m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex); - m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex); -} - -#ifndef V4_BOOTSTRAP -QString Binding::valueAsString(const CompilationUnit *unit) const -{ - switch (type) { - case Type_Script: - case Type_String: - return unit->stringAt(stringIndex); - case Type_Null: - return QStringLiteral("null"); - case Type_Boolean: - return value.b ? QStringLiteral("true") : QStringLiteral("false"); - case Type_Number: - return QString::number(valueAsNumber(unit->constants)); - case Type_Invalid: - return QString(); -#if !QT_CONFIG(translation) - case Type_TranslationById: - case Type_Translation: - return unit->stringAt(unit->unitData()->translations()[value.translationDataIndex].stringIndex); -#else - case Type_TranslationById: { - const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex]; - QByteArray id = unit->stringAt(translation.stringIndex).toUtf8(); - return qtTrId(id.constData(), translation.number); - } - case Type_Translation: { - const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex]; - // This code must match that in the qsTr() implementation - const QString &path = unit->fileName(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5) - : QStringRef(); - QByteArray contextUtf8 = context.toUtf8(); - QByteArray comment = unit->stringAt(translation.commentIndex).toUtf8(); - QByteArray text = unit->stringAt(translation.stringIndex).toUtf8(); - return QCoreApplication::translate(contextUtf8.constData(), text.constData(), - comment.constData(), translation.number); - } -#endif - default: - break; - } - return QString(); -} - -//reverse of Lexer::singleEscape() -QString Binding::escapedString(const QString &string) -{ - QString tmp = QLatin1String("\""); - for (int i = 0; i < string.length(); ++i) { - const QChar &c = string.at(i); - switch (c.unicode()) { - case 0x08: - tmp += QLatin1String("\\b"); - break; - case 0x09: - tmp += QLatin1String("\\t"); - break; - case 0x0A: - tmp += QLatin1String("\\n"); - break; - case 0x0B: - tmp += QLatin1String("\\v"); - break; - case 0x0C: - tmp += QLatin1String("\\f"); - break; - case 0x0D: - tmp += QLatin1String("\\r"); - break; - case 0x22: - tmp += QLatin1String("\\\""); - break; - case 0x27: - tmp += QLatin1String("\\\'"); - break; - case 0x5C: - tmp += QLatin1String("\\\\"); - break; - default: - tmp += c; - break; - } - } - tmp += QLatin1Char('\"'); - return tmp; -} - -QString Binding::valueAsScriptString(const CompilationUnit *unit) const -{ - if (type == Type_String) - return escapedString(unit->stringAt(stringIndex)); - else - return valueAsString(unit); -} - -/*! -Returns the property cache, if one alread exists. The cache is not referenced. -*/ -QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const -{ - if (type.isValid()) - return typePropertyCache; - else - return compilationUnit->rootPropertyCache(); -} - -/*! -Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. -*/ -QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) -{ - if (typePropertyCache) { - return typePropertyCache; - } else if (type.isValid()) { - typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject(), minorVersion); - return typePropertyCache; - } else { - return compilationUnit->rootPropertyCache(); - } -} - -bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine) -{ - if (type.isValid()) { - bool ok = false; - hash->addData(createPropertyCache(engine)->checksum(&ok)); - return ok; - } - if (!compilationUnit) - return false; - hash->addData(compilationUnit->unitData()->md5Checksum, sizeof(compilationUnit->unitData()->md5Checksum)); - return true; -} - -template <typename T> -bool qtTypeInherits(const QMetaObject *mo) { - while (mo) { - if (mo == &T::staticMetaObject) - return true; - mo = mo->superClass(); - } - return false; -} - -void ResolvedTypeReference::doDynamicTypeCheck() -{ - const QMetaObject *mo = nullptr; - if (typePropertyCache) - mo = typePropertyCache->firstCppMetaObject(); - else if (type.isValid()) - mo = type.metaObject(); - else if (compilationUnit) - mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); - isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); -} - -bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const -{ - for (auto it = constBegin(), end = constEnd(); it != end; ++it) { - if (!it.value()->addToHash(hash, engine)) - return false; - } - - return true; -} - -#endif - -void CompilationUnit::destroy() -{ -#if !defined(V4_BOOTSTRAP) - if (qmlEngine) - QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); - else -#endif - delete this; -} - - -void Unit::generateChecksum() -{ -#ifndef V4_BOOTSTRAP - QCryptographicHash hash(QCryptographicHash::Md5); - - const int checksummableDataOffset = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum); - - const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset; - hash.addData(dataPtr, unitSize - checksummableDataOffset); - - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(md5Checksum)); - memcpy(md5Checksum, checksum.constData(), sizeof(md5Checksum)); -#else - memset(md5Checksum, 0, sizeof(md5Checksum)); -#endif -} - -bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const -{ -#ifndef V4_BOOTSTRAP - if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) { - *errorString = QStringLiteral("Magic bytes in the header do not match"); - return false; - } - - if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); - return false; - } - - if (qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16); - return false; - } - - if (sourceTimeStamp) { - // Files from the resource system do not have any time stamps, so fall back to the application - // executable. - if (!expectedSourceTimeStamp.isValid()) - expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); - - if (expectedSourceTimeStamp.isValid() && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); - return false; - } - } - -#if defined(QML_COMPILE_HASH) - if (qstrcmp(CompiledData::qml_compile_hash, libraryVersionHash) != 0) { - *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); - return false; - } -#else -#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" -#endif - - return true; -#else - Q_UNUSED(expectedSourceTimeStamp) - Q_UNUSED(errorString) - return false; -#endif -} - -Location &Location::operator=(const QQmlJS::AST::SourceLocation &astLocation) -{ - line = astLocation.startLine; - column = astLocation.startColumn; - return *this; -} - -} - -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h deleted file mode 100644 index 7b26939da0..0000000000 --- a/src/qml/compiler/qv4compileddata_p.h +++ /dev/null @@ -1,1316 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4COMPILEDDATA_P_H -#define QV4COMPILEDDATA_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qstring.h> -#include <QVector> -#include <QStringList> -#include <QHash> -#include <QUrl> - -#include <private/qv4value_p.h> -#include <private/qv4executableallocator_p.h> -#include <private/qqmlrefcount_p.h> -#include <private/qqmlnullablevalue_p.h> -#include <private/qv4identifier_p.h> -#include <private/qflagpointer_p.h> -#include <private/qendian_p.h> -#include <private/qqmljsastfwd_p.h> -#ifndef V4_BOOTSTRAP -#include <private/qqmltypenamecache_p.h> -#include <private/qqmlpropertycache_p.h> -#include "private/qintrusivelist_p.h" -#endif - -QT_BEGIN_NAMESPACE - -// Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x21 - -class QIODevice; -class QQmlPropertyCache; -class QQmlPropertyData; -class QQmlTypeNameCache; -class QQmlScriptData; -class QQmlType; -class QQmlEngine; - -namespace QmlIR { -struct Document; -} - -namespace QV4 { - -namespace Heap { -struct Module; -}; - -struct Function; -class EvalISelFactory; -class CompilationUnitMapper; - -namespace CompiledData { - -struct String; -struct Function; -struct Lookup; -struct RegExp; -struct Unit; - -template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const> -struct TableIterator -{ - TableIterator(const Container *container, int index) : container(container), index(index) {} - const Container *container; - int index; - - const ItemType *operator->() { return (container->*IndexedGetter)(index); } - void operator++() { ++index; } - bool operator==(const TableIterator &rhs) const { return index == rhs.index; } - bool operator!=(const TableIterator &rhs) const { return index != rhs.index; } -}; - -struct Location -{ - union { - quint32 _dummy; - quint32_le_bitfield<0, 20> line; - quint32_le_bitfield<20, 12> column; - }; - - Location() : _dummy(0) { } - - Location &operator=(const QQmlJS::AST::SourceLocation &astLocation); - - inline bool operator<(const Location &other) const { - return line < other.line || - (line == other.line && column < other.column); - } -}; -static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct RegExp -{ - enum Flags : unsigned int { - RegExp_NoFlags = 0x0, - RegExp_Global = 0x01, - RegExp_IgnoreCase = 0x02, - RegExp_Multiline = 0x04, - RegExp_Unicode = 0x08, - RegExp_Sticky = 0x10 - }; - union { - quint32 _dummy; - quint32_le_bitfield<0, 5> flags; - quint32_le_bitfield<5, 27> stringIndex; - }; - - RegExp() : _dummy(0) { } -}; -static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Lookup -{ - enum Type : unsigned int { - Type_Getter = 0, - Type_Setter = 1, - Type_GlobalGetter = 2, - Type_QmlContextPropertyGetter = 3 - }; - - union { - quint32 _dummy; - quint32_le_bitfield<0, 4> type_and_flags; - quint32_le_bitfield<4, 28> nameIndex; - }; - - Lookup() : _dummy(0) { } -}; -static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct JSClassMember -{ - union { - quint32 _dummy; - quint32_le_bitfield<0, 31> nameOffset; - quint32_le_bitfield<31, 1> isAccessor; - }; - - JSClassMember() : _dummy(0) { } -}; -static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct JSClass -{ - quint32_le nMembers; - // JSClassMember[nMembers] - - static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; } -}; -static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -// This data structure is intended to be binary compatible with QStringData/QStaticStringData on -// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped -// from a file must be castable to a QStringData regardless of the pointer size. With the first -// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a -// ptrdiff_t and thus variable in size. -// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while -// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain -// the same value. -struct String -{ - qint32_le refcount; // -1 - qint32_le size; - quint32_le allocAndCapacityReservedFlag; // 0 - quint32_le offsetOn32Bit; - quint64_le offsetOn64Bit; - // uint16 strdata[] - - static int calculateSize(const QString &str) { - return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7; - } -}; -static_assert(sizeof(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -// Ensure compatibility with QString -static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location"); -static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location"); -static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location"); -static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location"); -#if QT_POINTER_SIZE == 8 -static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location"); -#else -static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location"); -#endif - -struct CodeOffsetToLine { - quint32_le codeOffset; - quint32_le line; -}; -static_assert(sizeof(CodeOffsetToLine) == 8, "CodeOffsetToLine structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Block -{ - quint32_le nLocals; - quint32_le localsOffset; - quint16_le sizeOfLocalTemporalDeadZone; - quint16_le padding; - - const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } - - static int calculateSize(int nLocals) { - int trailingData = nLocals*sizeof (quint32); - size_t size = align(align(sizeof(Block)) + size_t(trailingData)); - Q_ASSERT(size < INT_MAX); - return int(size); - } - - static size_t align(size_t a) { - return (a + 7) & ~size_t(7); - } -}; -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"); - -// 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 -{ - enum Flags : unsigned int { - IsStrict = 0x1, - IsArrowFunction = 0x2, - IsGenerator = 0x4 - }; - - // Absolute offset into file where the code for this function is located. - quint32_le codeOffset; - quint32_le codeSize; - - quint32_le nameIndex; - quint16_le length; - quint16_le nFormals; - quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData. - quint32_le localsOffset; - quint16_le nLocals; - quint16_le nLineNumbers; - size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); } - quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers - quint16_le sizeOfLocalTemporalDeadZone; - quint16_le firstTemporalDeadZoneRegister; - quint16_le sizeOfRegisterTemporalDeadZone; - quint16_le nRegisters; - Location location; - - quint32_le nLabelInfos; - size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } - - typedef quint16_le TraceInfoCount; - TraceInfoCount nTraceInfos; - static Q_DECL_CONSTEXPR TraceInfoCount NoTracing() { return TraceInfoCount::max(); } - - // Keep all unaligned data at the end - quint8 flags; - quint8 padding1; - - // quint32 formalsIndex[nFormals] - // quint32 localsIndex[nLocals] - - const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); } - const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } - const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); } - - // --- QQmlPropertyCacheCreator interface - const quint32_le *formalsBegin() const { return formalsTable(); } - const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } - // --- - - const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); } - - const char *code() const { return reinterpret_cast<const char *>(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) - + nLines*sizeof(CodeOffsetToLine); - size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); - Q_ASSERT(size < INT_MAX); - return int(size); - } - - static size_t align(size_t a) { - 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"); - -struct Method { - enum Type { - Regular, - Getter, - Setter - }; - - quint32_le name; - quint32_le type; - quint32_le function; -}; -static_assert(sizeof(Method) == 12, "Method structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Class -{ - quint32_le nameIndex; - quint32_le scopeIndex; - quint32_le constructorFunction; - quint32_le nStaticMethods; - quint32_le nMethods; - quint32_le methodTableOffset; - - const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); } - - static int calculateSize(int nStaticMethods, int nMethods) { - int trailingData = (nStaticMethods + nMethods) * sizeof(Method); - size_t size = align(sizeof(Class) + trailingData); - Q_ASSERT(size < INT_MAX); - return int(size); - } - - static size_t align(size_t a) { - return (a + 7) & ~size_t(7); - } -}; -static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct TemplateObject -{ - quint32_le size; - - static int calculateSize(int size) { - int trailingData = 2 * size * sizeof(quint32_le); - size_t s = align(sizeof(TemplateObject) + trailingData); - Q_ASSERT(s < INT_MAX); - return int(s); - } - - static size_t align(size_t a) { - return (a + 7) & ~size_t(7); - } - - const quint32_le *stringTable() const { - return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1)); - } - - uint stringIndexAt(uint i) const { - return stringTable()[i]; - } - uint rawStringIndexAt(uint i) const { - return stringTable()[size + i]; - } -}; -static_assert(sizeof(TemplateObject) == 4, "Template object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct ExportEntry -{ - quint32_le exportName; - quint32_le moduleRequest; - quint32_le importName; - quint32_le localName; - Location location; -}; -static_assert(sizeof(ExportEntry) == 20, "ExportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct ImportEntry -{ - quint32_le moduleRequest; - quint32_le importName; - quint32_le localName; - Location location; -}; -static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -// Qml data structures - -struct Q_QML_EXPORT TranslationData -{ - quint32_le stringIndex; - quint32_le commentIndex; - qint32_le number; - quint32_le padding; -}; -static_assert(sizeof(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Q_QML_PRIVATE_EXPORT Binding -{ - quint32_le propertyNameIndex; - - enum ValueType : unsigned int { - Type_Invalid, - Type_Boolean, - Type_Number, - Type_String, - Type_Null, - Type_Translation, - Type_TranslationById, - Type_Script, - Type_Object, - Type_AttachedProperty, - Type_GroupProperty - }; - - enum Flags : unsigned int { - IsSignalHandlerExpression = 0x1, - IsSignalHandlerObject = 0x2, - IsOnAssignment = 0x4, - InitializerForReadOnlyDeclaration = 0x8, - IsResolvedEnum = 0x10, - IsListItem = 0x20, - IsBindingToAlias = 0x40, - IsDeferredBinding = 0x80, - IsCustomParserBinding = 0x100, - IsFunctionExpression = 0x200 - }; - - union { - quint32_le_bitfield<0, 16> flags; - quint32_le_bitfield<16, 16> type; - }; - union { - bool b; - quint32_le constantValueIndex; - quint32_le compiledScriptIndex; // used when Type_Script - quint32_le objectIndex; - quint32_le translationDataIndex; // used when Type_Translation - quint32 nullMarker; - } value; - quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings) - - Location location; - Location valueLocation; - - bool isValueBinding() const - { - if (type == Type_AttachedProperty - || type == Type_GroupProperty) - return false; - if (flags & IsSignalHandlerExpression - || flags & IsSignalHandlerObject) - return false; - return true; - } - - bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); } - bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); } - - bool isSignalHandler() const - { - if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) { - Q_ASSERT(!isValueBinding()); - Q_ASSERT(!isAttachedProperty()); - Q_ASSERT(!isGroupProperty()); - return true; - } - return false; - } - - bool isAttachedProperty() const - { - if (type == Type_AttachedProperty) { - Q_ASSERT(!isValueBinding()); - Q_ASSERT(!isSignalHandler()); - Q_ASSERT(!isGroupProperty()); - return true; - } - return false; - } - - bool isGroupProperty() const - { - if (type == Type_GroupProperty) { - Q_ASSERT(!isValueBinding()); - Q_ASSERT(!isSignalHandler()); - Q_ASSERT(!isAttachedProperty()); - return true; - } - return false; - } - - bool isFunctionExpression() const { return (flags & IsFunctionExpression); } - - static QString escapedString(const QString &string); - - bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; } - bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); } - -#ifndef V4_BOOTSTRAP - QString valueAsString(const CompilationUnit *unit) const; - QString valueAsScriptString(const CompilationUnit *unit) const; -#endif - double valueAsNumber(const Value *constantTable) const - { - if (type != Type_Number) - return 0.0; - return constantTable[value.constantValueIndex].doubleValue(); - } - - bool valueAsBoolean() const - { - if (type == Type_Boolean) - return value.b; - return false; - } - -}; - -static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct EnumValue -{ - quint32_le nameIndex; - qint32_le value; - Location location; -}; -static_assert(sizeof(EnumValue) == 12, "EnumValue structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Enum -{ - quint32_le nameIndex; - quint32_le nEnumValues; - Location location; - - const EnumValue *enumValueAt(int idx) const { - return reinterpret_cast<const EnumValue*>(this + 1) + idx; - } - - static int calculateSize(int nEnumValues) { - return (sizeof(Enum) - + nEnumValues * sizeof(EnumValue) - + 7) & ~0x7; - } - - // --- QQmlPropertyCacheCreatorInterface - const EnumValue *enumValuesBegin() const { return enumValueAt(0); } - const EnumValue *enumValuesEnd() const { return enumValueAt(nEnumValues); } - int enumValueCount() const { return nEnumValues; } - // --- -}; -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"); - -struct Parameter -{ - quint32_le nameIndex; - quint32_le type; - quint32_le customTypeNameIndex; - Location location; -}; -static_assert(sizeof(Parameter) == 16, "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; - quint32_le nParameters; - Location location; - // Parameter parameters[1]; - - const Parameter *parameterAt(int idx) const { - return reinterpret_cast<const Parameter*>(this + 1) + idx; - } - - static int calculateSize(int nParameters) { - return (sizeof(Signal) - + nParameters * sizeof(Parameter) - + 7) & ~0x7; - } - - // --- QQmlPropertyCacheCceatorInterface - const Parameter *parametersBegin() const { return parameterAt(0); } - const Parameter *parametersEnd() const { return parameterAt(nParameters); } - int parameterCount() const { return nParameters; } - // --- -}; -static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Property -{ - enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color, - Font, Time, Date, DateTime, Rect, Point, Size, - Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, - Custom, CustomList }; - - enum Flags : unsigned int { - IsReadOnly = 0x1 - }; - - quint32_le nameIndex; - union { - quint32_le_bitfield<0, 31> type; - quint32_le_bitfield<31, 1> flags; // readonly - }; - quint32_le customTypeNameIndex; // If type >= Custom - Location location; -}; -static_assert(sizeof(Property) == 16, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Alias { - enum Flags : unsigned int { - IsReadOnly = 0x1, - Resolved = 0x2, - AliasPointsToPointerObject = 0x4 - }; - union { - quint32_le_bitfield<0, 29> nameIndex; - quint32_le_bitfield<29, 3> flags; - }; - union { - quint32_le idIndex; // string index - quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) - quint32_le_bitfield<31, 1> aliasToLocalAlias; - }; - union { - quint32_le propertyNameIndex; // string index - qint32_le encodedMetaPropertyIndex; - quint32_le localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId) - }; - Location location; - Location referenceLocation; - - bool isObjectAlias() const { - Q_ASSERT(flags & Resolved); - return encodedMetaPropertyIndex == -1; - } -}; -static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Object -{ - enum Flags : unsigned int { - NoFlag = 0x0, - IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary - HasDeferredBindings = 0x2, // any of the bindings are deferred - HasCustomParserBindings = 0x4 - }; - - // Depending on the use, this may be the type name to instantiate before instantiating this - // object. For grouped properties the type name will be empty and for attached properties - // it will be the name of the attached type. - quint32_le inheritedTypeNameIndex; - quint32_le idNameIndex; - union { - quint32_le_bitfield<0, 15> flags; - quint32_le_bitfield<15, 1> defaultPropertyIsAlias; - qint32_le_bitfield<16, 16> id; - }; - qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object - quint16_le nFunctions; - quint16_le nProperties; - quint32_le offsetToFunctions; - quint32_le offsetToProperties; - quint32_le offsetToAliases; - quint16_le nAliases; - quint16_le nEnums; - quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects - quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects - quint16_le nSignals; - quint16_le nBindings; - quint32_le offsetToBindings; - quint32_le nNamedObjectsInComponent; - quint32_le offsetToNamedObjectsInComponent; - Location location; - Location locationOfIdProperty; -// Function[] -// Property[] -// Signal[] -// Binding[] - - static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent) - { - return ( sizeof(Object) - + nFunctions * sizeof(quint32) - + nProperties * sizeof(Property) - + nAliases * sizeof(Alias) - + nEnums * sizeof(quint32) - + nSignals * sizeof(quint32) - + nBindings * sizeof(Binding) - + nNamedObjectsInComponent * sizeof(int) - + 0x7 - ) & ~0x7; - } - - const quint32_le *functionOffsetTable() const - { - return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToFunctions); - } - - const Property *propertyTable() const - { - return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties); - } - - const Alias *aliasTable() const - { - return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases); - } - - const Binding *bindingTable() const - { - return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings); - } - - const Enum *enumAt(int idx) const - { - const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToEnums); - const quint32_le offset = offsetTable[idx]; - return reinterpret_cast<const Enum*>(reinterpret_cast<const char*>(this) + offset); - } - - const Signal *signalAt(int idx) const - { - const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToSignals); - const quint32_le offset = offsetTable[idx]; - return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset); - } - - const quint32_le *namedObjectsInComponentTable() const - { - return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent); - } - - // --- QQmlPropertyCacheCreator interface - int propertyCount() const { return nProperties; } - int aliasCount() const { return nAliases; } - int enumCount() const { return nEnums; } - int signalCount() const { return nSignals; } - int functionCount() const { return nFunctions; } - - const Binding *bindingsBegin() const { return bindingTable(); } - const Binding *bindingsEnd() const { return bindingTable() + nBindings; } - - const Property *propertiesBegin() const { return propertyTable(); } - const Property *propertiesEnd() const { return propertyTable() + nProperties; } - - const Alias *aliasesBegin() const { return aliasTable(); } - const Alias *aliasesEnd() const { return aliasTable() + nAliases; } - - typedef TableIterator<Enum, Object, &Object::enumAt> EnumIterator; - EnumIterator enumsBegin() const { return EnumIterator(this, 0); } - EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); } - - typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator; - SignalIterator signalsBegin() const { return SignalIterator(this, 0); } - SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } - - int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } - // --- -}; -static_assert(sizeof(Object) == 68, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct Import -{ - enum ImportType : unsigned int { - ImportLibrary = 0x1, - ImportFile = 0x2, - ImportScript = 0x3 - }; - quint32_le type; - - quint32_le uriIndex; - quint32_le qualifierIndex; - - qint32_le majorVersion; - qint32_le minorVersion; - - Location location; - - Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; } -}; -static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct QmlUnit -{ - quint32_le nImports; - quint32_le offsetToImports; - quint32_le nObjects; - quint32_le offsetToObjects; - - const Import *importAt(int idx) const { - return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import)); - } - - const Object *objectAt(int idx) const { - const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects); - const quint32_le offset = offsetTable[idx]; - return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset); - } -}; -static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -enum { QmlCompileHashSpace = 48 }; -static const char magic_str[] = "qv4cdata"; -extern const char qml_compile_hash[QmlCompileHashSpace + 1]; - -struct Unit -{ - // DO NOT CHANGE THESE FIELDS EVER - char magic[8]; - quint32_le version; - quint32_le qtVersion; - qint64_le sourceTimeStamp; - quint32_le unitSize; // Size of the Unit and any depending data. - // END DO NOT CHANGE THESE FIELDS EVER - - char libraryVersionHash[QmlCompileHashSpace]; - - char md5Checksum[16]; // checksum of all bytes following this field. - void generateChecksum(); - - char dependencyMD5Checksum[16]; - - enum : unsigned int { - IsJavascript = 0x1, - StaticData = 0x2, // Unit data persistent in memory? - IsSingleton = 0x4, - IsSharedLibrary = 0x8, // .pragma shared? - IsESModule = 0x10, - PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation - }; - quint32_le flags; - quint32_le stringTableSize; - quint32_le offsetToStringTable; - quint32_le functionTableSize; - quint32_le offsetToFunctionTable; - quint32_le classTableSize; - quint32_le offsetToClassTable; - quint32_le templateObjectTableSize; - quint32_le offsetToTemplateObjectTable; - quint32_le blockTableSize; - quint32_le offsetToBlockTable; - quint32_le lookupTableSize; - quint32_le offsetToLookupTable; - quint32_le regexpTableSize; - quint32_le offsetToRegexpTable; - quint32_le constantTableSize; - quint32_le offsetToConstantTable; - quint32_le jsClassTableSize; - quint32_le offsetToJSClassTable; - quint32_le translationTableSize; - quint32_le offsetToTranslationTable; - quint32_le localExportEntryTableSize; - quint32_le offsetToLocalExportEntryTable; - quint32_le indirectExportEntryTableSize; - quint32_le offsetToIndirectExportEntryTable; - quint32_le starExportEntryTableSize; - quint32_le offsetToStarExportEntryTable; - quint32_le importEntryTableSize; - quint32_le offsetToImportEntryTable; - quint32_le moduleRequestTableSize; - quint32_le offsetToModuleRequestTable; - qint32_le indexOfRootFunction; - quint32_le sourceFileIndex; - quint32_le finalUrlIndex; - - quint32_le offsetToQmlUnit; - - bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const; - - /* QML specific fields */ - - const QmlUnit *qmlUnit() const { - return reinterpret_cast<const QmlUnit *>(reinterpret_cast<const char *>(this) + offsetToQmlUnit); - } - - QmlUnit *qmlUnit() { - return reinterpret_cast<QmlUnit *>(reinterpret_cast<char *>(this) + offsetToQmlUnit); - } - - bool isSingleton() const { - return flags & Unit::IsSingleton; - } - /* end QML specific fields*/ - - QString stringAtInternal(int idx) const { - Q_ASSERT(idx < int(stringTableSize)); - const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable); - const quint32_le offset = offsetTable[idx]; - const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset); - if (str->size == 0) - return QString(); -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - if (flags & StaticData) { - const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) }; - return QString(holder); - } - const QChar *characters = reinterpret_cast<const QChar *>(str + 1); - return QString(characters, str->size); -#else - const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1); - QString qstr(str->size, Qt::Uninitialized); - QChar *ch = qstr.data(); - for (int i = 0; i < str->size; ++i) - ch[i] = QChar(characters[i]); - return qstr; -#endif - } - - const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } - const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); } - const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); } - const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); } - - const Function *functionAt(int idx) const { - const quint32_le *offsetTable = functionOffsetTable(); - const quint32_le offset = offsetTable[idx]; - return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset); - } - - const Class *classAt(int idx) const { - const quint32_le *offsetTable = classOffsetTable(); - const quint32_le offset = offsetTable[idx]; - return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset); - } - - const TemplateObject *templateObjectAt(int idx) const { - const quint32_le *offsetTable = templateObjectOffsetTable(); - const quint32_le offset = offsetTable[idx]; - return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset); - } - - const Block *blockAt(int idx) const { - const quint32_le *offsetTable = blockOffsetTable(); - const quint32_le offset = offsetTable[idx]; - return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset); - } - - const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); } - const RegExp *regexpAt(int index) const { - return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp)); - } - const quint64_le *constants() const { - return reinterpret_cast<const quint64_le*>(reinterpret_cast<const char *>(this) + offsetToConstantTable); - } - - const JSClassMember *jsClassAt(int idx, int *nMembers) const { - const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable); - const quint32_le offset = offsetTable[idx]; - const char *ptr = reinterpret_cast<const char *>(this) + offset; - const JSClass *klass = reinterpret_cast<const JSClass *>(ptr); - *nMembers = klass->nMembers; - return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass)); - } - - const TranslationData *translations() const { - return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable); - } - - const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); } - const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); } - const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); } - const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); } - - const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); } -}; - -static_assert(sizeof(Unit) == 248, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - -struct TypeReference -{ - TypeReference(const Location &loc) - : location(loc) - , needsCreation(false) - , errorWhenNotFound(false) - {} - Location location; // first use - bool needsCreation : 1; // whether the type needs to be creatable or not - bool errorWhenNotFound: 1; -}; - -// Map from name index to location of first use. -struct TypeReferenceMap : QHash<int, TypeReference> -{ - TypeReference &add(int nameIndex, const Location &loc) { - Iterator it = find(nameIndex); - if (it != end()) - return *it; - return *insert(nameIndex, loc); - } - - template <typename CompiledObject> - void collectFromObject(const CompiledObject *obj) - { - if (obj->inheritedTypeNameIndex != 0) { - TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location); - r.needsCreation = true; - r.errorWhenNotFound = true; - } - - auto prop = obj->propertiesBegin(); - auto propEnd = obj->propertiesEnd(); - for ( ; prop != propEnd; ++prop) { - if (prop->type >= QV4::CompiledData::Property::Custom) { - TypeReference &r = this->add(prop->customTypeNameIndex, prop->location); - r.errorWhenNotFound = true; - } - } - - auto binding = obj->bindingsBegin(); - auto bindingEnd = obj->bindingsEnd(); - for ( ; binding != bindingEnd; ++binding) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) - this->add(binding->propertyNameIndex, binding->location); - } - } - - template <typename Iterator> - void collectFromObjects(Iterator it, Iterator end) - { - for (; it != end; ++it) - collectFromObject(*it); - } -}; - -#ifndef V4_BOOTSTRAP -struct ResolvedTypeReference; -// map from name index -// While this could be a hash, a map is chosen here to provide a stable -// order, which is used to calculating a check-sum on dependent meta-objects. -struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*> -{ - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; -}; - -using DependentTypesHasher = std::function<bool(QCryptographicHash *)>; -#else -struct DependentTypesHasher {}; -#endif - -// index is per-object binding index -typedef QVector<QQmlPropertyData*> BindingPropertyData; - -// This is how this hooks into the existing structures: - -struct Q_QML_PRIVATE_EXPORT CompilationUnitBase -{ - // pointers either to data->constants() or little-endian memory copy. - QV4::Heap::String **runtimeStrings = nullptr; // Array - const Value* constants = nullptr; - QV4::Value *runtimeRegularExpressions = nullptr; - QV4::Heap::InternalClass **runtimeClasses = nullptr; - const Value** imports = nullptr; -}; - -Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *)); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *)); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *)); - -struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase -{ - const Unit *data = nullptr; - const QmlUnit *qmlData = nullptr; -public: - CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString()); - ~CompilationUnit(); - - void addref() - { - Q_ASSERT(refCount.load() > 0); - refCount.ref(); - } - - void release() - { - Q_ASSERT(refCount.load() > 0); - if (!refCount.deref()) - destroy(); - } - int count() const - { - return refCount.load(); - } - - const Unit *unitData() const { return data; } - void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr, - const QString &fileName = QString(), const QString &finalUrlString = QString()); - -#ifndef V4_BOOTSTRAP - QIntrusiveListNode nextCompilationUnit; - ExecutionEngine *engine = nullptr; - QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case. - - // url() and fileName() shall be used to load the actual QML/JS code or to show errors or - // warnings about that code. They include any potential URL interceptions and thus represent the - // "physical" location of the code. - // - // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code - // They are _not_ intercepted and thus represent the "logical" name for the code. - - QString fileName() const { return m_fileName; } - QString finalUrlString() const { return m_finalUrlString; } - QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QUrl finalUrl() const - { - if (m_finalUrl.isNull) - m_finalUrl = QUrl(finalUrlString()); - return m_finalUrl; - } - - QV4::Lookup *runtimeLookups = nullptr; - QVector<QV4::Function *> runtimeFunctions; - QVector<QV4::Heap::InternalClass *> runtimeBlocks; - mutable QVector<QV4::Heap::Object *> templateObjects; - mutable QQmlNullableValue<QUrl> m_url; - mutable QQmlNullableValue<QUrl> m_finalUrl; - - // QML specific fields - QQmlPropertyCacheVector propertyCaches; - QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } - - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - - // index is object index. This allows fast access to the - // property data when initializing bindings, avoiding expensive - // lookups by string (property name). - QVector<BindingPropertyData> bindingPropertyDataPerObject; - - // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects - // this is initialized on-demand by QQmlContextData - QHash<int, IdentifierHash> namedObjectsPerComponentCache; - inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); - - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); - - int totalBindingsCount = 0; // Number of bindings used in this type - int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount = 0; // Number of objects explicitly instantiated - - QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; - ResolvedTypeReferenceMap resolvedTypes; - ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); } - - bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const; - - int metaTypeId = -1; - int listMetaTypeId = -1; - bool isRegisteredWithEngine = false; - - QScopedPointer<CompilationUnitMapper> backingFile; - QStringList dynamicStrings; - - // --- interface for QQmlPropertyCacheCreator - typedef Object CompiledObject; - int objectCount() const { return qmlData->nObjects; } - const Object *objectAt(int index) const { return qmlData->objectAt(index); } - int importCount() const { return qmlData->nImports; } - const Import *importAt(int index) const { return qmlData->importAt(index); } - QString stringAt(int index) const - { - if (uint(index) >= data->stringTableSize) - return dynamicStrings.at(index - data->stringTableSize); - return data->stringAtInternal(index); - } - - Heap::Object *templateObjectAt(int index) const; - - struct FunctionIterator - { - FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {} - const Unit *unit; - const Object *object; - int index; - - const Function *operator->() const { return unit->functionAt(object->functionOffsetTable()[index]); } - void operator++() { ++index; } - bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; } - bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; } - }; - FunctionIterator objectFunctionsBegin(const Object *object) const { return FunctionIterator(data, object, 0); } - FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); } - // --- - - bool isESModule() const { return data->flags & Unit::IsESModule; } - bool isSharedLibrary() const { return data->flags & Unit::IsSharedLibrary; } - QStringList moduleRequests() const; - Heap::Module *instantiate(ExecutionEngine *engine); - const Value *resolveExport(QV4::String *exportName); - QStringList exportedNames() const; - void evaluate(); - void evaluateModuleRequests(); - - QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); - void unlink(); - - void markObjects(MarkStack *markStack); - - bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); - - static QString localCacheFilePath(const QUrl &url); - -protected: - quint32 totalStringCount() const - { return data->stringTableSize; } - -#else // V4_BOOTSTRAP - QString stringAt(int index) const { return data->stringAtInternal(index); } -#endif // V4_BOOTSTRAP - -private: - void destroy(); - - struct ResolveSetEntry - { - ResolveSetEntry() {} - ResolveSetEntry(CompilationUnit *module, QV4::String *exportName) - : module(module), exportName(exportName) {} - CompilationUnit *module = nullptr; - QV4::String *exportName = nullptr; - }; - - const Value *resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet); - const ExportEntry *lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const; - void getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit *> *exportNameSet, bool includeDefaultExport = true) const; - - QString m_fileName; // initialized from data->sourceFileIndex - QString m_finalUrlString; // initialized from data->finalUrlIndex - - QAtomicInt refCount = 1; - - Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex); - - Heap::Module *m_module = nullptr; - -public: -#if defined(V4_BOOTSTRAP) - bool saveToDisk(const QString &outputFileName, QString *errorString); -#else - bool saveToDisk(const QUrl &unitUrl, QString *errorString); -#endif -}; - -#ifndef V4_BOOTSTRAP -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType type; - QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlRefPointer<QQmlPropertyCache> propertyCache() const; - QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); - - void doDynamicTypeCheck(); -}; - -IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) -{ - auto it = namedObjectsPerComponentCache.find(componentObjectIndex); - if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end())) - return createNamedObjectsPerComponent(componentObjectIndex); - return *it; -} -#endif // V4_BOOTSTRAP - -} // CompiledData namespace -} // QV4 namespace - -Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE); - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 01c033cb2a..acc4b02e96 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -38,16 +38,24 @@ ****************************************************************************/ #include <qv4compiler_p.h> -#include <qv4compileddata_p.h> #include <qv4codegen_p.h> -#include <private/qv4string_p.h> -#include <private/qv4value_p.h> +#include <private/qv4compileddata_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4alloca_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsast_p.h> -#include <wtf/MathExtras.h> +#include <private/qml_compile_hash_p.h> +#include <private/qqmlirbuilder_p.h> #include <QCryptographicHash> +// Efficient implementation that takes advantage of powers of two. +static inline size_t roundUpToMultipleOf(size_t divisor, size_t x) +{ + Q_ASSERT(divisor && !(divisor & (divisor - 1))); + const size_t remainderMask = divisor - 1; + return (x + remainderMask) & ~remainderMask; +} + QV4::Compiler::StringTableGenerator::StringTableGenerator() { clear(); @@ -92,7 +100,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) { char *dataStart = reinterpret_cast<char *>(unit); quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable); - char *stringData = reinterpret_cast<char *>(stringTable) + WTF::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); + char *stringData = reinterpret_cast<char *>(stringTable) + roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); for (int i = backingUnitTableSize ; i < strings.size(); ++i) { const int index = i - backingUnitTableSize; stringTable[index] = stringData - dataStart; @@ -119,6 +127,25 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) } } +void QV4::Compiler::JSUnitGenerator::generateUnitChecksum(QV4::CompiledData::Unit *unit) +{ +#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 + QCryptographicHash hash(QCryptographicHash::Md5); + + const int checksummableDataOffset + = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(unit->md5Checksum); + + const char *dataPtr = reinterpret_cast<const char *>(unit) + checksummableDataOffset; + hash.addData(dataPtr, unit->unitSize - checksummableDataOffset); + + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(unit->md5Checksum)); + memcpy(unit->md5Checksum, checksum.constData(), sizeof(unit->md5Checksum)); +#else + memset(unit->md5Checksum, 0, sizeof(unit->md5Checksum)); +#endif +} + QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module) : module(module) { @@ -242,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->arguments.at(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)); } @@ -385,7 +415,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO if (option == GenerateWithStringTable) stringTable.serialize(unit); - unit->generateChecksum(); + generateUnitChecksum(unit); return unit; } @@ -394,7 +424,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; - quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*function))); + quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*function))); function->nameIndex = getStringId(irFunction->name); function->flags = 0; @@ -410,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; @@ -424,7 +456,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte Q_ASSERT(function->lineNumberOffset() == currentOffset); currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); - function->nTraceInfos = irFunction->nTraceInfos; function->nRegisters = irFunction->registerCountInFunction; if (!irFunction->labelInfo.empty()) { @@ -440,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)); + 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); @@ -545,7 +578,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context { QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b); - quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*block))); + quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*block))); block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone; block->nLocals = irBlock->locals.size(); @@ -575,7 +608,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.flags |= module->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; - qstrcpy(unit.libraryVersionHash, CompiledData::qml_compile_hash); + qstrcpy(unit.libraryVersionHash, QML_COMPILE_HASH); memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); @@ -608,7 +641,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.constantTableSize = constants.size(); // Ensure we load constants from well-aligned addresses into for example SSE registers. - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(16, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(16, nextOffset)); unit.offsetToConstantTable = nextOffset; nextOffset += unit.constantTableSize * sizeof(ReturnedValue); @@ -619,19 +652,19 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp *jsClassDataOffset = nextOffset; nextOffset += jsClassData.size(); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.translationTableSize = translations.count(); unit.offsetToTranslationTable = nextOffset; nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) { *tableSizePtr = count; *offsetPtr = nextOffset; nextOffset += count * sizeof(CompiledData::ExportEntry); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); }; reserveExportTable(module->localExportEntries.count(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable); @@ -641,12 +674,12 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.importEntryTableSize = module->importEntries.count(); unit.offsetToImportEntryTable = nextOffset; nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.moduleRequestTableSize = module->moduleRequests.count(); unit.offsetToModuleRequestTable = nextOffset; nextOffset += unit.moduleRequestTableSize * sizeof(uint); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); quint32 functionSize = 0; for (int i = 0; i < module->functions.size(); ++i) { @@ -686,7 +719,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp if (option == GenerateWithStringTable) { unit.stringTableSize = stringTable.stringCount(); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.offsetToStringTable = nextOffset; nextOffset += stringTable.sizeOfTableAndData(); } else { diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 49e334bb81..4f3c718175 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -53,9 +53,10 @@ #include <QtCore/qstring.h> #include <QtCore/qhash.h> #include <QtCore/qstringlist.h> -#include <private/qv4global_p.h> +#include <private/qv4compilerglobal_p.h> #include <private/qqmljsastfwd_p.h> #include <private/qv4compileddata_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE @@ -72,10 +73,12 @@ struct JSClassMember; namespace Compiler { +struct Context; +struct Module; struct Class; struct TemplateObject; -struct Q_QML_PRIVATE_EXPORT StringTableGenerator { +struct Q_QMLCOMPILER_PRIVATE_EXPORT StringTableGenerator { StringTableGenerator(); int registerString(const QString &str); @@ -102,7 +105,9 @@ private: bool frozen = false; }; -struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { +struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator { + static void generateUnitChecksum(CompiledData::Unit *unit); + struct MemberInfo { QString name; bool isAccessor; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index ca2d5128f4..88837b0feb 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -156,7 +156,7 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS result.isConst = false; return result; } else { - result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1; + result.index = argIdx + sizeof(CallData) / sizeof(StaticValue) - 1; result.scope = 0; result.type = ResolvedName::Stack; result.isConst = false; @@ -410,28 +410,4 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) nRegisters = bytecodeGenerator->currentRegister() - registerOffset; } -bool Context::canUseTracingJit() const -{ -#if QT_CONFIG(qml_tracing) - static bool forceTracing = !qEnvironmentVariableIsEmpty("QV4_FORCE_TRACING"); - if (forceTracing) //### we can probably remove this when tracing is turned on by default - return true; // to be used by unittests - - static bool disableTracing = !qEnvironmentVariableIsEmpty("QV4_DISABLE_TRACING"); - if (disableTracing) - return false; - - static QStringList onlyTrace = - qEnvironmentVariable("QV4_ONLY_TRACE").split(QLatin1Char(','), QString::SkipEmptyParts); - if (!onlyTrace.isEmpty()) - return onlyTrace.contains(name); - - //### the next condition should be refined and have the IR distinguish between escaping and - // non-escaping locals - return !requiresExecutionContext && !hasNestedFunctions; -#else - return false; -#endif -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 57ef4be36e..8c124ac409 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -50,7 +50,6 @@ // We mean it. // -#include "private/qv4global_p.h" #include <private/qqmljsast_p.h> #include <private/qv4compileddata_p.h> #include <QtCore/QStringList> @@ -62,8 +61,13 @@ QT_BEGIN_NAMESPACE namespace QV4 { +namespace Moth { +class BytecodeGenerator; +} + namespace Compiler { +class Codegen; struct ControlFlow; enum class ContextType { @@ -162,7 +166,6 @@ struct Context { int line = 0; int column = 0; int registerCountInFunction = 0; - uint nTraceInfos = 0; int functionIndex = -1; int blockIndex = -1; @@ -190,7 +193,8 @@ struct Context { MemberMap members; QSet<QString> usedVariables; QQmlJS::AST::FormalParameterList *formals = nullptr; - QStringList arguments; + QQmlJS::AST::BoundNames arguments; + QString returnType; QStringList locals; QStringList moduleRequests; QVector<ImportEntry> importEntries; @@ -289,7 +293,7 @@ struct Context { { // search backwards to handle duplicate argument names correctly for (int i = arguments.size() - 1; i >= 0; --i) { - if (arguments.at(i) == name) + if (arguments.at(i).id == name) return i; } return -1; @@ -363,8 +367,6 @@ struct Context { return parent->canHaveTailCalls(); return false; } - - bool canUseTracingJit() const; }; diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h index 5b622e81d8..5623473726 100644 --- a/src/qml/compiler/qv4compilercontrolflow_p.h +++ b/src/qml/compiler/qv4compilercontrolflow_p.h @@ -50,7 +50,6 @@ // We mean it. // -#include <private/qv4global_p.h> #include <private/qv4codegen_p.h> #include <private/qqmljsast_p.h> #include <private/qv4bytecodegenerator_p.h> @@ -279,7 +278,7 @@ struct ControlFlowWith : public ControlFlowUnwind struct ControlFlowBlock : public ControlFlowUnwind { - ControlFlowBlock(Codegen *cg, AST::Node *ast) + ControlFlowBlock(Codegen *cg, QQmlJS::AST::Node *ast) : ControlFlowUnwind(cg, Block) { block = cg->enterBlock(ast); @@ -314,11 +313,11 @@ struct ControlFlowBlock : public ControlFlowUnwind struct ControlFlowCatch : public ControlFlowUnwind { - AST::Catch *catchExpression; + QQmlJS::AST::Catch *catchExpression; bool insideCatch = false; BytecodeGenerator::ExceptionHandler exceptionLabel; - ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression) + ControlFlowCatch(Codegen *cg, QQmlJS::AST::Catch *catchExpression) : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression), exceptionLabel(generator()->newExceptionHandler()) { @@ -372,10 +371,10 @@ struct ControlFlowCatch : public ControlFlowUnwind struct ControlFlowFinally : public ControlFlowUnwind { - AST::Finally *finally; + QQmlJS::AST::Finally *finally; bool insideFinally = false; - ControlFlowFinally(Codegen *cg, AST::Finally *finally) + ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally) : ControlFlowUnwind(cg, Finally), finally(finally) { Q_ASSERT(finally != nullptr); diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilerglobal_p.h index 80f914c141..3478074827 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/compiler/qv4compilerglobal_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QV4COMPILATIONUNITMAPPER_H -#define QV4COMPILATIONUNITMAPPER_H +#ifndef QV4COMPILERGLOBAL_H +#define QV4COMPILERGLOBAL_H // // W A R N I N G @@ -51,35 +51,24 @@ // We mean it. // -#include <private/qv4global_p.h> -#include <QFile> +#include <QtCore/qglobal.h> +#include <QString> + +#include <private/qtqmlcompilerglobal_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -namespace CompiledData { -struct Unit; -} - -class CompilationUnitMapper -{ -public: - CompilationUnitMapper(); - ~CompilationUnitMapper(); - - CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); - void close(); - -private: -#if defined(Q_OS_UNIX) - size_t length; -#endif - void *dataPtr; +enum class ObjectLiteralArgument { + Value, + Method, + Getter, + Setter }; -} +} // namespace QV4 QT_END_NAMESPACE -#endif // QV4COMPILATIONUNITMAPPER_H +#endif // QV4COMPILERGLOBAL_H diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 8984b6931e..ab0ebf3d4b 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -44,18 +44,26 @@ #include <QtCore/QSet> #include <QtCore/QBuffer> #include <QtCore/QBitArray> -#include <QtCore/QLinkedList> #include <QtCore/QStack> #include <private/qqmljsast_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4codegen_p.h> -#include <private/qv4string_p.h> QT_USE_NAMESPACE using namespace QV4; using namespace QV4::Compiler; +using namespace QQmlJS; using namespace QQmlJS::AST; +static CompiledData::Location location(const QQmlJS::AST::SourceLocation &astLocation) +{ + CompiledData::Location target; + target.line = astLocation.startLine; + target.column = astLocation.startColumn; + return target; +} + + ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType) : QQmlJS::AST::Visitor(cg->recursionDepth()) , _cg(cg) @@ -177,7 +185,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) Compiler::ExportEntry entry; entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString(); entry.importName = QStringLiteral("*"); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->exportEntries << entry; } else if (declaration->exportClause) { for (ExportsList *it = declaration->exportClause->exportsList; it; it = it->next) { @@ -190,22 +198,22 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) entry.moduleRequest = module; entry.exportName = spec->exportedIdentifier.toString(); - entry.location = it->firstSourceLocation(); + entry.location = location(it->firstSourceLocation()); _context->exportEntries << entry; } } else if (auto *vstmt = AST::cast<AST::VariableStatement*>(declaration->variableStatementOrDeclaration)) { - QStringList boundNames; + BoundNames boundNames; for (VariableDeclarationList *it = vstmt->declarations; it; it = it->next) { if (!it->declaration) continue; it->declaration->boundNames(&boundNames); } - for (const QString &name: boundNames) { + for (const auto &name: boundNames) { Compiler::ExportEntry entry; - entry.localName = name; - entry.exportName = name; - entry.location = vstmt->firstSourceLocation(); + entry.localName = name.id; + entry.exportName = name.id; + entry.location = location(vstmt->firstSourceLocation()); _context->exportEntries << entry; } } else if (auto *classDecl = AST::cast<AST::ClassDeclaration*>(declaration->variableStatementOrDeclaration)) { @@ -214,7 +222,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) Compiler::ExportEntry entry; entry.localName = name; entry.exportName = name; - entry.location = classDecl->firstSourceLocation(); + entry.location = location(classDecl->firstSourceLocation()); _context->exportEntries << entry; if (declaration->exportDefault) localNameForDefaultExport = entry.localName; @@ -233,7 +241,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) Compiler::ExportEntry entry; entry.localName = functionName; entry.exportName = functionName; - entry.location = fdef->firstSourceLocation(); + entry.location = location(fdef->firstSourceLocation()); _context->exportEntries << entry; if (declaration->exportDefault) localNameForDefaultExport = entry.localName; @@ -245,7 +253,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) entry.localName = localNameForDefaultExport; _context->localNameForDefaultExport = localNameForDefaultExport; entry.exportName = QStringLiteral("default"); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->exportEntries << entry; } @@ -270,7 +278,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration) entry.moduleRequest = module; entry.importName = QStringLiteral("default"); entry.localName = import->importedDefaultBinding.toString(); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->importEntries << entry; } @@ -279,7 +287,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration) entry.moduleRequest = module; entry.importName = QStringLiteral("*"); entry.localName = import->nameSpaceImport->importedBinding.toString(); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->importEntries << entry; } @@ -292,7 +300,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration) entry.importName = it->importSpecifier->identifier.toString(); else entry.importName = entry.localName; - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->importEntries << entry; } } @@ -319,26 +327,26 @@ bool ScanFunctions::visit(PatternElement *ast) if (!ast->isVariableDeclaration()) return true; - QStringList names; + BoundNames names; ast->boundNames(&names); QQmlJS::AST::SourceLocation lastInitializerLocation = ast->lastSourceLocation(); if (_context->lastBlockInitializerLocation.isValid()) lastInitializerLocation = _context->lastBlockInitializerLocation; - for (const QString &name : qAsConst(names)) { - if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments"))) + for (const auto &name : qAsConst(names)) { + if (_context->isStrict && (name.id == QLatin1String("eval") || name.id == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - checkName(QStringRef(&name), ast->identifierToken); - if (name == QLatin1String("arguments")) + checkName(QStringRef(&name.id), ast->identifierToken); + if (name.id == QLatin1String("arguments")) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) { _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); return false; } - if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope, + if (!_context->addLocalVar(name.id, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope, /*function*/nullptr, lastInitializerLocation)) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name.id)); return false; } } @@ -672,6 +680,9 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete _context->isArrowFunction = true; else if (expr->isGenerator) _context->isGenerator = true; + + if (expr->typeAnnotation) + _context->returnType = expr->typeAnnotation->type->toString(); } @@ -684,11 +695,11 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete bool isSimpleParameterList = formals && formals->isSimpleParameterList(); - _context->arguments = formals ? formals->formals() : QStringList(); + _context->arguments = formals ? formals->formals() : BoundNames(); - const QStringList boundNames = formals ? formals->boundNames() : QStringList(); + const BoundNames boundNames = formals ? formals->boundNames() : BoundNames(); for (int i = 0; i < boundNames.size(); ++i) { - const QString &arg = boundNames.at(i); + const QString &arg = boundNames.at(i).id; if (_context->isStrict || !isSimpleParameterList) { bool duplicate = (boundNames.indexOf(arg, i + 1) != -1); if (duplicate) { @@ -705,6 +716,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete if (!_context->arguments.contains(arg)) _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var); } + return true; } diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 0f7bf1818a..2de80eac44 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -50,7 +50,7 @@ // We mean it. // -#include "private/qv4global_p.h" +#include <private/qtqmlcompilerglobal_p.h> #include <private/qqmljsastvisitor_p.h> #include <private/qqmljsast_p.h> #include <private/qqmljsengine_p.h> @@ -62,8 +62,6 @@ QT_BEGIN_NAMESPACE -using namespace QQmlJS; - namespace QV4 { namespace Moth { @@ -83,84 +81,87 @@ class ScanFunctions: protected QQmlJS::AST::Visitor typedef QScopedValueRollback<bool> TemporaryBoolAssignment; public: ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType); - void operator()(AST::Node *node); + void operator()(QQmlJS::AST::Node *node); void enterGlobalEnvironment(ContextType compilationMode); - void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name); + void enterEnvironment(QQmlJS::AST::Node *node, ContextType compilationMode, + const QString &name); void leaveEnvironment(); - void enterQmlFunction(AST::FunctionDeclaration *ast) + void enterQmlFunction(QQmlJS::AST::FunctionExpression *ast) { enterFunction(ast, false); } protected: using Visitor::visit; using Visitor::endVisit; - void checkDirectivePrologue(AST::StatementList *ast); + void checkDirectivePrologue(QQmlJS::AST::StatementList *ast); - void checkName(const QStringRef &name, const AST::SourceLocation &loc); + void checkName(const QStringRef &name, const QQmlJS::AST::SourceLocation &loc); - bool visit(AST::Program *ast) override; - void endVisit(AST::Program *) override; + bool visit(QQmlJS::AST::Program *ast) override; + void endVisit(QQmlJS::AST::Program *) override; - bool visit(AST::ESModule *ast) override; - void endVisit(AST::ESModule *) override; + bool visit(QQmlJS::AST::ESModule *ast) override; + void endVisit(QQmlJS::AST::ESModule *) override; - bool visit(AST::ExportDeclaration *declaration) override; - bool visit(AST::ImportDeclaration *declaration) override; + bool visit(QQmlJS::AST::ExportDeclaration *declaration) override; + bool visit(QQmlJS::AST::ImportDeclaration *declaration) override; - bool visit(AST::CallExpression *ast) override; - bool visit(AST::PatternElement *ast) override; - bool visit(AST::IdentifierExpression *ast) override; - bool visit(AST::ExpressionStatement *ast) override; - bool visit(AST::FunctionExpression *ast) override; - bool visit(AST::TemplateLiteral *ast) override; - bool visit(AST::SuperLiteral *) override; - bool visit(AST::FieldMemberExpression *) override; - bool visit(AST::ArrayPattern *) override; + bool visit(QQmlJS::AST::CallExpression *ast) override; + bool visit(QQmlJS::AST::PatternElement *ast) override; + bool visit(QQmlJS::AST::IdentifierExpression *ast) override; + bool visit(QQmlJS::AST::ExpressionStatement *ast) override; + bool visit(QQmlJS::AST::FunctionExpression *ast) override; + bool visit(QQmlJS::AST::TemplateLiteral *ast) override; + bool visit(QQmlJS::AST::SuperLiteral *) override; + bool visit(QQmlJS::AST::FieldMemberExpression *) override; + bool visit(QQmlJS::AST::ArrayPattern *) override; - bool enterFunction(AST::FunctionExpression *ast, bool enterName); + bool enterFunction(QQmlJS::AST::FunctionExpression *ast, bool enterName); - void endVisit(AST::FunctionExpression *) override; + void endVisit(QQmlJS::AST::FunctionExpression *) override; - bool visit(AST::ObjectPattern *ast) override; + bool visit(QQmlJS::AST::ObjectPattern *ast) override; - bool visit(AST::PatternProperty *ast) override; - void endVisit(AST::PatternProperty *) override; + bool visit(QQmlJS::AST::PatternProperty *ast) override; + void endVisit(QQmlJS::AST::PatternProperty *) override; - bool visit(AST::FunctionDeclaration *ast) override; - void endVisit(AST::FunctionDeclaration *) override; + bool visit(QQmlJS::AST::FunctionDeclaration *ast) override; + void endVisit(QQmlJS::AST::FunctionDeclaration *) override; - bool visit(AST::ClassExpression *ast) override; - void endVisit(AST::ClassExpression *) override; + bool visit(QQmlJS::AST::ClassExpression *ast) override; + void endVisit(QQmlJS::AST::ClassExpression *) override; - bool visit(AST::ClassDeclaration *ast) override; - void endVisit(AST::ClassDeclaration *) override; + bool visit(QQmlJS::AST::ClassDeclaration *ast) override; + void endVisit(QQmlJS::AST::ClassDeclaration *) override; - bool visit(AST::DoWhileStatement *ast) override; - bool visit(AST::ForStatement *ast) override; - void endVisit(AST::ForStatement *) override; - bool visit(AST::ForEachStatement *ast) override; - void endVisit(AST::ForEachStatement *) override; + bool visit(QQmlJS::AST::DoWhileStatement *ast) override; + bool visit(QQmlJS::AST::ForStatement *ast) override; + void endVisit(QQmlJS::AST::ForStatement *) override; + bool visit(QQmlJS::AST::ForEachStatement *ast) override; + void endVisit(QQmlJS::AST::ForEachStatement *) override; - bool visit(AST::ThisExpression *ast) override; + bool visit(QQmlJS::AST::ThisExpression *ast) override; - bool visit(AST::Block *ast) override; - void endVisit(AST::Block *ast) override; + bool visit(QQmlJS::AST::Block *ast) override; + void endVisit(QQmlJS::AST::Block *ast) override; - bool visit(AST::CaseBlock *ast) override; - void endVisit(AST::CaseBlock *ast) override; + bool visit(QQmlJS::AST::CaseBlock *ast) override; + void endVisit(QQmlJS::AST::CaseBlock *ast) override; - bool visit(AST::Catch *ast) override; - void endVisit(AST::Catch *ast) override; + bool visit(QQmlJS::AST::Catch *ast) override; + void endVisit(QQmlJS::AST::Catch *ast) override; - bool visit(AST::WithStatement *ast) override; - void endVisit(AST::WithStatement *ast) override; + bool visit(QQmlJS::AST::WithStatement *ast) override; + void endVisit(QQmlJS::AST::WithStatement *ast) override; void throwRecursionDepthError() override; protected: - bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName); + bool enterFunction(QQmlJS::AST::Node *ast, const QString &name, + QQmlJS::AST::FormalParameterList *formals, + QQmlJS::AST::StatementList *body, bool enterName); void calcEscapingVariables(); // fields: @@ -173,7 +174,7 @@ protected: ContextType defaultProgramType; private: - static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr; + static constexpr QQmlJS::AST::Node *astNodeForGlobalEnvironment = nullptr; }; } diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 345f03ae8a..640a908dd3 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -39,7 +39,9 @@ #include "qv4instr_moth_p.h" #include <private/qv4compileddata_p.h> -#include <private/qv4stackframe_p.h> +#include <private/qv4calldata_p.h> + +#include <QtCore/qdebug.h> using namespace QV4; using namespace QV4::Moth; @@ -56,9 +58,7 @@ int InstrInfo::size(Instr::Type type) static QByteArray alignedNumber(int n) { QByteArray number = QByteArray::number(n); - while (number.size() < 8) - number.prepend(' '); - return number; + return number.prepend(8 - number.size(), ' '); } static QByteArray alignedLineNumber(int line) { @@ -83,25 +83,6 @@ static QByteArray rawBytes(const char *data, int n) return ba; } -static QString toString(QV4::ReturnedValue v) -{ -#ifdef V4_BOOTSTRAP - return QStringLiteral("string-const(%1)").arg(v); -#else // !V4_BOOTSTRAP - Value val = Value::fromReturnedValue(v); - QString result; - if (val.isInt32()) - result = QLatin1String("int "); - else if (val.isDouble()) - result = QLatin1String("double "); - if (val.isEmpty()) - result += QLatin1String("empty"); - else - result += val.toQStringNoThrow(); - return result; -#endif // V4_BOOTSTRAP -} - #define ABSOLUTE_OFFSET() \ (code - start + offset) @@ -128,22 +109,12 @@ const int InstrInfo::argumentCount[] = { FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS) }; - -void dumpConstantTable(const Value *constants, uint count) -{ - QDebug d = qDebug(); - d.nospace(); - for (uint i = 0; i < count; ++i) - d << alignedNumber(int(i)).constData() << ": " - << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n"; -} - QString dumpRegister(int reg, int nFormals) { Q_STATIC_ASSERT(offsetof(CallData, function) == 0); - Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value)); - Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value)); - Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value)); + Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(StaticValue)); + Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(StaticValue)); + Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(StaticValue)); if (reg == CallData::Function) return QStringLiteral("(function)"); else if (reg == CallData::Context) @@ -171,8 +142,6 @@ QString dumpArguments(int argc, int argv, int nFormals) return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); } -#define TRACE_SLOT QStringLiteral(" {%1}").arg(traceSlot) - void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) { MOTH_JUMP_TABLE; @@ -241,9 +210,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadLocal) if (index < nLocals) - d << "l" << index << TRACE_SLOT; + d << "l" << index; else - d << "a" << (index - nLocals) << TRACE_SLOT; + d << "a" << (index - nLocals); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -255,9 +224,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadScopedLocal) if (index < nLocals) - d << "l" << index << "@" << scope << TRACE_SLOT; + d << "l" << index << "@" << scope; else - d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT; + d << "a" << (index - nLocals) << "@" << scope; MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -280,15 +249,15 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - d << name << TRACE_SLOT; + d << name; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameSloppy) @@ -300,20 +269,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(LoadElement) - d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[acc]"; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << "acc[" << name << "]" << TRACE_SLOT; + d << "acc[" << name << "]"; MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - d << "acc(" << index << ")" << TRACE_SLOT; + d << "acc(" << index << ")"; MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -343,49 +311,48 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Resume) MOTH_BEGIN_INSTR(CallValue) - d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + ; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) d << dumpRegister(base, nFormals) << "." << lookupIndex - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) - d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << name << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(Construct) @@ -528,11 +495,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNotUndefined) @@ -543,6 +510,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpNoException) + MOTH_BEGIN_INSTR(CheckException) + MOTH_END_INSTR(CheckException) + MOTH_BEGIN_INSTR(CmpEqNull) MOTH_END_INSTR(CmpEqNull) @@ -596,22 +566,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - d << TRACE_SLOT; MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) MOTH_END_INSTR(UCompl) MOTH_BEGIN_INSTR(Increment) - d << TRACE_SLOT; MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - d << TRACE_SLOT; MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) @@ -667,7 +634,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Exp) MOTH_BEGIN_INSTR(Mul) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) @@ -675,11 +642,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(Sub) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(CmpIn) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 26901c297c..c0dd696b8a 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -50,9 +50,8 @@ // // We mean it. // -#include <private/qv4global_p.h> -#include <private/qv4value_p.h> -#include <private/qv4runtime_p.h> + +#include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper #include <qendian.h> @@ -77,20 +76,20 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) #define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) #define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index) -#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) #define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) -#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) #define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index) #define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId) #define INSTR_MoveRegExp(op) INSTRUCTION(op, MoveRegExp, 2, regExpId, destReg) #define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) -#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot) -#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot) -#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 1, index) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) -#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot) -#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) @@ -100,18 +99,18 @@ QT_BEGIN_NAMESPACE #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) -#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot) -#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot) -#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot) -#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 5, name, thisObject, argc, argv, traceSlot) -#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 5, name, base, argc, argv, traceSlot) -#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 5, lookupIndex, base, argc, argv, traceSlot) -#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 5, base, index, argc, argv, traceSlot) -#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot) -#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot) -#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base) +#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) +#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv) +#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv) +#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) +#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) +#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) +#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) +#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) +#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv) +#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) #define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) #define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) #define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset) @@ -148,10 +147,11 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0) #define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0) #define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) -#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset) -#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) #define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset) #define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset) +#define INSTR_CheckException(op) INSTRUCTION(op, CheckException, 0) #define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) #define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0) #define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs) @@ -168,11 +168,11 @@ QT_BEGIN_NAMESPACE #define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) #define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) #define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) -#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) #define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) -#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot) -#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 1, traceSlot) -#define INSTR_Add(op) INSTRUCTION(op, Add, 2, lhs, traceSlot) +#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0) +#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0) +#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs) #define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs) #define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs) #define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs) @@ -186,10 +186,10 @@ QT_BEGIN_NAMESPACE #define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) #define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) #define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs) -#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) -#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot) -#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) @@ -241,6 +241,7 @@ QT_BEGIN_NAMESPACE F(JumpFalse) \ F(JumpNoException) \ F(JumpNotUndefined) \ + F(CheckException) \ F(CmpEqNull) \ F(CmpNeNull) \ F(CmpEqInt) \ @@ -532,7 +533,6 @@ inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackS // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h -void dumpConstantTable(const Value *constants, uint count); void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()); inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1, diff --git a/src/qml/compiler/qv4util_p.h b/src/qml/compiler/qv4util_p.h new file mode 100644 index 0000000000..bd9758c1fb --- /dev/null +++ b/src/qml/compiler/qv4util_p.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4UTIL_H +#define QV4UTIL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QBitArray> +#include <algorithm> +#include <vector> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND) +// Sanity: +class BitVector +{ + std::vector<bool> bits; + +public: + BitVector(int size = 0, bool value = false) + : bits(size, value) + {} + + void clear() + { bits = std::vector<bool>(bits.size(), false); } + + void reserve(int size) + { bits.reserve(size); } + + int size() const + { + Q_ASSERT(bits.size() < INT_MAX); + return static_cast<int>(bits.size()); + } + + void resize(int newSize) + { bits.resize(newSize); } + + void resize(int newSize, bool newValue) + { bits.resize(newSize, newValue); } + + void assign(int newSize, bool value) + { bits.assign(newSize, value); } + + int findNext(int start, bool value, bool wrapAround) const + { + // The ++operator of std::vector<bool>::iterator in libc++ has a bug when using it on an + // iterator pointing to the last element. It will not be set to ::end(), but beyond + // that. (It will be set to the first multiple of the native word size that is bigger + // than size().) + // + // See http://llvm.org/bugs/show_bug.cgi?id=19663 + // + // The work-around is to calculate the distance, and compare it to the size() to see if it's + // beyond the end, or take the minimum of the distance and the size. + + size_t pos = std::distance(bits.begin(), + std::find(bits.begin() + start, bits.end(), value)); + if (wrapAround && pos >= static_cast<size_t>(size())) + pos = std::distance(bits.begin(), + std::find(bits.begin(), bits.begin() + start, value)); + + pos = qMin(pos, static_cast<size_t>(size())); + + Q_ASSERT(pos <= static_cast<size_t>(size())); + Q_ASSERT(pos < INT_MAX); + + return static_cast<int>(pos); + } + + bool at(int idx) const + { return bits.at(idx); } + + void setBit(int idx) + { bits[idx] = true; } + + void clearBit(int idx) + { bits[idx] = false; } +}; +#else // Insanity: +class BitVector +{ + QBitArray bits; + +public: + BitVector(int size = 0, bool value = false) + : bits(size, value) + {} + + void clear() + { bits = QBitArray(bits.size(), false); } + + void reserve(int size) + { Q_UNUSED(size); } + + int size() const + { return bits.size(); } + + void resize(int newSize) + { bits.resize(newSize); } + + void resize(int newSize, bool newValue) + { + int oldSize = bits.size(); + bits.resize(newSize); + bits.fill(newValue, oldSize, bits.size()); + } + + void assign(int newSize, bool value) + { + bits.resize(newSize); + bits.fill(value); + } + + int findNext(int start, bool value, bool wrapAround) const + { + for (int i = start, ei = size(); i < ei; ++i) { + if (at(i) == value) + return i; + } + + if (wrapAround) { + for (int i = 0, ei = start; i < ei; ++i) { + if (at(i) == value) + return i; + } + } + + return size(); + } + + bool at(int idx) const + { return bits.at(idx); } + + void setBit(int idx) + { bits[idx] = true; } + + void clearBit(int idx) + { bits[idx] = false; } +}; +#endif + +} + +QT_END_NAMESPACE + +#endif // QV4UTIL_H |