diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-05-07 12:47:33 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-05-16 12:09:23 +0000 |
commit | 7f7d87c68da4cb29b2b2b9c324c6863228da0c26 (patch) | |
tree | 727a7b7483fe5322984e3068b9cd8696a98efe4b /src/qml/compiler | |
parent | cf2bf011ae0f33bed963d088f8267746a3369c4c (diff) |
Split CompiledData::CompilationUnit in two
We need a CompilationUnit that only holds the data needed for
compilation and another one that is executable by the runtime.
Change-Id: I704d859ba028576a18460f5e3a59f210f64535d3
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/compiler.pri | 8 | ||||
-rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 14 | ||||
-rw-r--r-- | src/qml/compiler/qqmlirbuilder_p.h | 2 | ||||
-rw-r--r-- | src/qml/compiler/qqmlirloader_p.h | 1 | ||||
-rw-r--r-- | src/qml/compiler/qqmlpropertyvalidator.cpp | 32 | ||||
-rw-r--r-- | src/qml/compiler/qqmlpropertyvalidator_p.h | 6 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 19 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 15 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 26 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 5 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 742 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 248 | ||||
-rw-r--r-- | src/qml/compiler/qv4executablecompilationunit.cpp | 784 | ||||
-rw-r--r-- | src/qml/compiler/qv4executablecompilationunit_p.h | 318 |
14 files changed, 1217 insertions, 1003 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index c15c45a1cb..3291d6e1f5 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -10,7 +10,6 @@ HEADERS += \ $$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 @@ -33,8 +32,8 @@ HEADERS += \ $$PWD/qqmltypecompiler_p.h \ $$PWD/qqmlpropertycachecreator_p.h \ $$PWD/qqmlpropertyvalidator_p.h \ - $$PWD/qv4compilationunitmapper_p.h - + $$PWD/qv4compilationunitmapper_p.h \ + $$PWD/qv4executablecompilationunit_p.h SOURCES += \ $$PWD/qqmlirloader.cpp \ @@ -42,7 +41,8 @@ SOURCES += \ $$PWD/qqmltypecompiler.cpp \ $$PWD/qqmlpropertycachecreator.cpp \ $$PWD/qqmlpropertyvalidator.cpp \ - $$PWD/qv4compilationunitmapper.cpp + $$PWD/qv4compilationunitmapper.cpp \ + $$PWD/qv4executablecompilationunit.cpp unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 41bdc3ae92..e63aa3b66d 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1548,15 +1548,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); - output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings(); + 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(); @@ -1748,7 +1745,7 @@ 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; @@ -1781,7 +1778,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 diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index c937158a33..b49eaee420 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -379,7 +379,7 @@ 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); } diff --git a/src/qml/compiler/qqmlirloader_p.h b/src/qml/compiler/qqmlirloader_p.h index cf46ca3cb0..aa303c923f 100644 --- a/src/qml/compiler/qqmlirloader_p.h +++ b/src/qml/compiler/qqmlirloader_p.h @@ -52,6 +52,7 @@ // #include <private/qv4compileddata_p.h> +#include <private/qqmljsmemorypool_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 1812ad6546..5b0bd8858a 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) : enginePrivate(enginePrivate) , compilationUnit(compilationUnit) , imports(imports) @@ -339,7 +339,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) return noError; - QString value = binding->valueAsString(compilationUnit.data()); + QString value = compilationUnit->bindingValueAsString(binding); QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); bool ok; if (p.isFlagType()) { @@ -427,7 +427,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Color: { bool ok = false; - QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: color expected")); } @@ -436,7 +436,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache #if QT_CONFIG(datestring) case QVariant::Date: { bool ok = false; - QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: date expected")); } @@ -444,7 +444,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Time: { bool ok = false; - QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: time expected")); } @@ -452,7 +452,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::DateTime: { bool ok = false; - QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: datetime expected")); } @@ -461,7 +461,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache #endif // datestring case QVariant::Point: { bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } @@ -469,7 +469,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::PointF: { bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } @@ -477,7 +477,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Size: { bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: size expected")); } @@ -485,7 +485,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::SizeF: { bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: size expected")); } @@ -493,7 +493,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Rect: { bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: rect expected")); } @@ -501,7 +501,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::RectF: { bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } @@ -518,7 +518,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float xp; float yp; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: 2D vector expected")); } } @@ -529,7 +529,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float yp; float zy; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: 3D vector expected")); } } @@ -541,7 +541,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float zy; float wp; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: 4D vector expected")); } } @@ -553,7 +553,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float yp; float zp; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: quaternion expected")); } } diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h index e9ae844ccb..8244b2df21 100644 --- a/src/qml/compiler/qqmlpropertyvalidator_p.h +++ b/src/qml/compiler/qqmlpropertyvalidator_p.h @@ -58,7 +58,7 @@ class QQmlPropertyValidator { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: - QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit); QVector<QQmlCompileError> validate(); @@ -72,13 +72,13 @@ private: 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 + QV4::ResolvedTypeReference *resolvedType(int id) const { return compilationUnit->resolvedType(id); } QQmlEnginePrivate *enginePrivate; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; const QQmlImports &imports; const QV4::CompiledData::Unit *qmlUnit; const QQmlPropertyCacheVector &propertyCaches; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 996b2f16ae..66320b8db9 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -56,7 +56,7 @@ 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) + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) : resolvedTypes(resolvedTypeCache) , engine(engine) , typeData(typeData) @@ -66,7 +66,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type { } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() +QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() { // Build property caches and VME meta object data @@ -134,7 +134,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() return nullptr; } - if (!document->javaScriptCompilationUnit) { + if (!document->javaScriptCompilationUnit.unitData()) { // Compile JS binding expressions and signal handlers if necessary { // We can compile script strings ahead of time, but they must be compiled @@ -159,7 +159,9 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() QmlIR::QmlUnitGenerator qmlGenerator; qmlGenerator.generate(*document, dependencyHasher); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit + = QV4::ExecutableCompilationUnit::create(std::move( + document->javaScriptCompilationUnit)); compilationUnit->typeNameCache = typeNameCache; compilationUnit->resolvedTypes = *resolvedTypes; compilationUnit->propertyCaches = std::move(m_propertyCaches); @@ -209,7 +211,7 @@ int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v) const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const { - return document->javaScriptCompilationUnit->unitData(); + return document->javaScriptCompilationUnit.unitData(); } const QQmlImports *QQmlTypeCompiler::imports() const @@ -253,11 +255,6 @@ 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); @@ -857,7 +854,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) { - auto typeRef = new QV4::CompiledData::ResolvedTypeReference; + auto typeRef = new QV4::ResolvedTypeReference; typeRef->type = componentType; typeRef->majorVersion = componentType.majorVersion(); typeRef->minorVersion = componentType.minorVersion(); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index a49b97453f..f588909c42 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -81,7 +81,7 @@ struct QQmlTypeCompiler public: QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher); // --- interface used by QQmlPropertyCacheCreator @@ -91,10 +91,10 @@ public: 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; + QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr; // --- - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(); + QQmlRefPointer<QV4::ExecutableCompilationUnit> compile(); QList<QQmlError> compilationErrors() const { return errors; } void recordError(QQmlError error); @@ -118,7 +118,6 @@ public: 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; } @@ -126,7 +125,7 @@ public: void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion); - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const + QV4::ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes->value(id); } @@ -157,12 +156,12 @@ protected: void recordError(const QQmlCompileError &error) { compiler->recordError(error); } - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const + QV4::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) + QV4::ResolvedTypeReferenceMap::iterator insertResolvedType( + int id, QV4::ResolvedTypeReference *value) { return compiler->resolvedTypes->insert(id, value); } QQmlTypeCompiler *compiler; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index edd479a0bd..d17d58588c 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -3783,26 +3783,14 @@ QList<QQmlJS::DiagnosticMessage> Codegen::errors() const return _errors; } -QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData) +QV4::CompiledData::CompilationUnit Codegen::generateCompilationUnit( + bool generateUnitData) { - CompiledData::Unit *unitData = nullptr; - if (generateUnitData) - unitData = jsUnitGenerator->generateUnit(); - CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit(unitData); - - QQmlRefPointer<CompiledData::CompilationUnit> unit; - unit.adopt(compilationUnit); - return unit; -} - -QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading() -{ - QQmlRefPointer<CompiledData::CompilationUnit> result; - result.adopt(new CompiledData::CompilationUnit); - return result; + return QV4::CompiledData::CompilationUnit( + generateUnitData ? jsUnitGenerator->generateUnit() : nullptr); } -QQmlRefPointer<CompiledData::CompilationUnit> Codegen::compileModule( +CompiledData::CompilationUnit Codegen::compileModule( bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics) { @@ -3817,7 +3805,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::compileModule( *diagnostics = parser.diagnosticMessages(); if (!parsed) - return nullptr; + return CompiledData::CompilationUnit(); QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode()); if (!moduleNode) { @@ -3840,7 +3828,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::compileModule( *diagnostics << errors; if (!errors.isEmpty()) - return nullptr; + return CompiledData::CompilationUnit(); return cg.generateCompilationUnit(); } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 962ccc8562..8cf56b6cb7 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -684,9 +684,8 @@ public: 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(); - static QQmlRefPointer<CompiledData::CompilationUnit> compileModule( + QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData = true); + static QV4::CompiledData::CompilationUnit compileModule( bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<DiagnosticMessage> *diagnostics); diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6e09eac977..091c29c8c7 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -39,34 +39,12 @@ #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/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> +#include <QFileInfo> // generated by qmake: #include "qml_compile_hash_p.h" @@ -99,10 +77,6 @@ CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName, CompilationUnit::~CompilationUnit() { -#ifndef V4_BOOTSTRAP - unlink(); -#endif - if (data) { if (data->qmlUnit() != qmlData) free(const_cast<QmlUnit *>(qmlData)); @@ -126,582 +100,11 @@ CompilationUnit::~CompilationUnit() 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; - } - const QByteArray checksum = dependencyHasher(); - return checksum.size() == sizeof(data->dependencyMD5Checksum) - && 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 +bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString) const { 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); @@ -767,51 +170,6 @@ void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit, 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) { @@ -855,98 +213,16 @@ QString Binding::escapedString(const QString &string) 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() +void CompilationUnit::unlink() { -#if !defined(V4_BOOTSTRAP) - if (qmlEngine) - QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); - else -#endif - delete this; + free(runtimeStrings); + runtimeStrings = nullptr; + delete [] runtimeRegularExpressions; + runtimeRegularExpressions = nullptr; + free(runtimeClasses); + runtimeClasses = nullptr; } - void Unit::generateChecksum() { #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 4a90c841bb..9301939bff 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -65,11 +65,6 @@ #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/qqmlpropertycachevector_p.h> -#include "private/qintrusivelist_p.h" -#endif QT_BEGIN_NAMESPACE @@ -101,7 +96,6 @@ struct Module; struct Function; class EvalISelFactory; -class CompilationUnitMapper; namespace CompiledData { @@ -528,10 +522,6 @@ struct Q_QML_PRIVATE_EXPORT Binding 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) @@ -1053,17 +1043,6 @@ struct TypeReferenceMap : QHash<int, TypeReference> } }; -#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; -}; -#endif - using DependentTypesHasher = std::function<QByteArray()>; // index is per-object binding index @@ -1073,6 +1052,30 @@ typedef QVector<QQmlPropertyData*> BindingPropertyData; struct Q_QML_PRIVATE_EXPORT CompilationUnitBase { + Q_DISABLE_COPY(CompilationUnitBase) + + CompilationUnitBase() = default; + ~CompilationUnitBase() = default; + + CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); } + + CompilationUnitBase &operator=(CompilationUnitBase &&other) noexcept + { + if (this != &other) { + runtimeStrings = other.runtimeStrings; + other.runtimeStrings = nullptr; + constants = other.constants; + other.constants = nullptr; + runtimeRegularExpressions = other.runtimeRegularExpressions; + other.runtimeRegularExpressions = nullptr; + runtimeClasses = other.runtimeClasses; + other.runtimeClasses = nullptr; + imports = other.imports; + other.imports = nullptr; + } + return *this; + } + // pointers either to data->constants() or little-endian memory copy. QV4::Heap::String **runtimeStrings = nullptr; // Array const Value* constants = nullptr; @@ -1088,30 +1091,42 @@ Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offs 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 +struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase { + Q_DISABLE_COPY(CompilationUnit) + const Unit *data = nullptr; const QmlUnit *qmlData = nullptr; QStringList dynamicStrings; public: + using CompiledObject = CompiledData::Object; + CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString()); ~CompilationUnit(); - void addref() + CompilationUnit(CompilationUnit &&other) noexcept { - Q_ASSERT(refCount.load() > 0); - refCount.ref(); + *this = std::move(other); } - void release() - { - Q_ASSERT(refCount.load() > 0); - if (!refCount.deref()) - destroy(); - } - int count() const + CompilationUnit &operator=(CompilationUnit &&other) noexcept { - return refCount.load(); + if (this != &other) { + data = other.data; + other.data = nullptr; + qmlData = other.qmlData; + other.qmlData = nullptr; + dynamicStrings = std::move(other.dynamicStrings); + other.dynamicStrings.clear(); + m_fileName = std::move(other.m_fileName); + other.m_fileName.clear(); + m_finalUrlString = std::move(other.m_finalUrlString); + other.m_finalUrlString.clear(); + m_module = other.m_module; + other.m_module = nullptr; + CompilationUnitBase::operator=(std::move(other)); + } + return *this; } const Unit *unitData() const { return data; } @@ -1125,185 +1140,24 @@ public: return data->stringAtInternal(index); } -#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; + Heap::Module *module() const { return m_module; } + void setModule(Heap::Module *module) { m_module = module; } - // --- 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); } - - 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; } -#endif - 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(); + bool saveToDisk(const QString &outputFileName, QString *errorString) const; }; -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 diff --git a/src/qml/compiler/qv4executablecompilationunit.cpp b/src/qml/compiler/qv4executablecompilationunit.cpp new file mode 100644 index 0000000000..432e0c5e37 --- /dev/null +++ b/src/qml/compiler/qv4executablecompilationunit.cpp @@ -0,0 +1,784 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "qv4executablecompilationunit_p.h" + +#include <private/qv4engine_p.h> +#include <private/qv4regexp_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4instr_moth_p.h> +#include <private/qv4objectproto_p.h> +#include <private/qqmlengine_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv4module_p.h> +#include <private/qv4compilationunitmapper_p.h> + +#include <QtQml/qqmlfile.h> +#include <QtQml/qqmlpropertymap.h> + +#include <QtCore/qdir.h> +#include <QtCore/qstandardpaths.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qscopeguard.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +ExecutableCompilationUnit::ExecutableCompilationUnit() = default; + +ExecutableCompilationUnit::ExecutableCompilationUnit( + CompiledData::CompilationUnit &&compilationUnit) + : CompiledData::CompilationUnit(std::move(compilationUnit)) +{} + +ExecutableCompilationUnit::~ExecutableCompilationUnit() +{ + unlink(); +} + +QString ExecutableCompilationUnit::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 *ExecutableCompilationUnit::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; + + CompiledData::Lookup::Type type + = CompiledData::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 *ExecutableCompilationUnit::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 ExecutableCompilationUnit::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; + + delete [] runtimeLookups; + runtimeLookups = nullptr; + + for (QV4::Function *f : qAsConst(runtimeFunctions)) + f->destroy(); + runtimeFunctions.clear(); + + CompiledData::CompilationUnit::unlink(); +} + +void ExecutableCompilationUnit::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 (auto mod = module()) + mod->mark(markStack); +} + +IdentifierHash ExecutableCompilationUnit::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 ExecutableCompilationUnit::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 ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const +{ + if (!dependencyHasher) { + for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { + if (data->dependencyMD5Checksum[i] != 0) + return false; + } + return true; + } + const QByteArray checksum = dependencyHasher(); + return checksum.size() == sizeof(data->dependencyMD5Checksum) + && memcmp(data->dependencyMD5Checksum, checksum.constData(), + sizeof(data->dependencyMD5Checksum)) == 0; +} + +QStringList ExecutableCompilationUnit::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 *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) +{ + if (isESModule() && module()) + return 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()) + setModule(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(urlAt(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(urlAt(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 *ExecutableCompilationUnit::resolveExportRecursively( + QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet) +{ + if (!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 &module()->self; + + Scope scope(engine); + + if (auto localExport = lookupNameInExportTable( + data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) { + ScopedString localName(scope, runtimeStrings[localExport->localName]); + uint index = module()->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey()); + if (index == UINT_MAX) + return nullptr; + if (index >= module()->scope->locals.size) + return imports[index - module()->scope->locals.size]; + return &module()->scope->locals[index]; + } + + if (auto indirectExport = lookupNameInExportTable( + data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) { + auto dependentModuleUnit = engine->loadModule(urlAt(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(urlAt(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 CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable( + const CompiledData::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 ExecutableCompilationUnit::getExportedNamesRecursively( + QStringList *names, QVector<const ExecutableCompilationUnit*> *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(urlAt(entry.moduleRequest), this); + if (!dependentModuleUnit) + return; + dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false); + } +} + +void ExecutableCompilationUnit::evaluate() +{ + QV4::Scope scope(engine); + QV4::Scoped<Module> mod(scope, module()); + mod->evaluate(); +} + +void ExecutableCompilationUnit::evaluateModuleRequests() +{ + for (const QString &request: moduleRequests()) { + auto dependentModuleUnit = engine->loadModule(QUrl(request), this); + if (engine->hasException) + return; + dependentModuleUnit->evaluate(); + if (engine->hasException) + return; + } +} + +bool ExecutableCompilationUnit::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 CompiledData::Unit * const oldDataPtr + = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data + : nullptr; + const CompiledData::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<CompiledData::Unit*>(oldDataPtr)); + backingFile.reset(cacheFile.take()); + return true; + } + + return false; +} + +bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) +{ + 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; + } + + return CompilationUnit::saveToDisk(localCacheFilePath(unitUrl), errorString); +} + +/*! +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->data->md5Checksum, + sizeof(compilationUnit->data->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; +} + +QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const +{ + using namespace CompiledData; + switch (binding->type) { + case Binding::Type_Script: + case Binding::Type_String: + return stringAt(binding->stringIndex); + case Binding::Type_Null: + return QStringLiteral("null"); + case Binding::Type_Boolean: + return binding->value.b ? QStringLiteral("true") : QStringLiteral("false"); + case Binding::Type_Number: + return QString::number(binding->valueAsNumber(constants)); + case Binding::Type_Invalid: + return QString(); +#if !QT_CONFIG(translation) + case Binding::Type_TranslationById: + case Binding::Type_Translation: + return unit->stringAt( + unit->data->translations()[binding->value.translationDataIndex].stringIndex); +#else + case Binding::Type_TranslationById: { + const TranslationData &translation + = data->translations()[binding->value.translationDataIndex]; + QByteArray id = stringAt(translation.stringIndex).toUtf8(); + return qtTrId(id.constData(), translation.number); + } + case Binding::Type_Translation: { + const TranslationData &translation + = data->translations()[binding->value.translationDataIndex]; + // This code must match that in the qsTr() implementation + const QString &path = 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 = stringAt(translation.commentIndex).toUtf8(); + QByteArray text = stringAt(translation.stringIndex).toUtf8(); + return QCoreApplication::translate(contextUtf8.constData(), text.constData(), + comment.constData(), translation.number); + } +#endif + default: + break; + } + return QString(); +} + +QString ExecutableCompilationUnit::bindingValueAsScriptString( + const CompiledData::Binding *binding) const +{ + return (binding->type == CompiledData::Binding::Type_String) + ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex)) + : bindingValueAsString(binding); +} + +} // namespace QV4 + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4executablecompilationunit_p.h b/src/qml/compiler/qv4executablecompilationunit_p.h new file mode 100644 index 0000000000..dd3918cc84 --- /dev/null +++ b/src/qml/compiler/qv4executablecompilationunit_p.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 QV4EXECUTABLECOMPILATIONUNIT_P_H +#define QV4EXECUTABLECOMPILATIONUNIT_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/qv4compileddata_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qintrusivelist_p.h> +#include <private/qqmlpropertycachevector_p.h> +#include <private/qqmltype_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlEnginePrivate; +namespace QV4 { + +class CompilationUnitMapper; +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; +}; + +class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit, + public QQmlRefCount +{ + Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit) +public: + friend class QQmlRefPointer<ExecutableCompilationUnit>; + + static QQmlRefPointer<ExecutableCompilationUnit> create( + CompiledData::CompilationUnit &&compilationUnit) + { + return QQmlRefPointer<ExecutableCompilationUnit>( + new ExecutableCompilationUnit(std::move(compilationUnit)), + QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + } + + static QQmlRefPointer<ExecutableCompilationUnit> create() + { + return QQmlRefPointer<ExecutableCompilationUnit>( + new ExecutableCompilationUnit, + QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + } + + 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. + + 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<CompiledData::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 CompiledData::DependentTypesHasher &dependencyHasher) const; + + int metaTypeId = -1; + int listMetaTypeId = -1; + bool isRegisteredWithEngine = false; + + QScopedPointer<CompilationUnitMapper> backingFile; + + // --- interface for QQmlPropertyCacheCreator + using CompiledObject = CompiledData::Object; + using CompiledFunction = CompiledData::Function; + + int objectCount() const { return qmlData->nObjects; } + const CompiledObject *objectAt(int index) const + { + return qmlData->objectAt(index); + } + + int importCount() const { return qmlData->nImports; } + const CompiledData::Import *importAt(int index) const + { + return qmlData->importAt(index); + } + + Heap::Object *templateObjectAt(int index) const; + + struct FunctionIterator + { + FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index) + : unit(unit), object(object), index(index) {} + const CompiledData::Unit *unit; + const CompiledObject *object; + int index; + + const CompiledFunction *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 CompiledObject *object) const + { + return FunctionIterator(data, object, 0); + } + + FunctionIterator objectFunctionsEnd(const CompiledObject *object) const + { + return FunctionIterator(data, object, object->nFunctions); + } + + bool isESModule() const + { + return data->flags & CompiledData::Unit::IsESModule; + } + + bool isSharedLibrary() const + { + return data->flags & CompiledData::Unit::IsSharedLibrary; + } + + QStringList moduleRequests() const; + Heap::Module *instantiate(ExecutionEngine *engine); + const Value *resolveExport(QV4::String *exportName) + { + QVector<ResolveSetEntry> resolveSet; + return resolveExportRecursively(exportName, &resolveSet); + } + + QStringList exportedNames() const + { + QStringList names; + QVector<const ExecutableCompilationUnit*> exportNameSet; + getExportedNamesRecursively(&names, &exportNameSet); + names.sort(); + auto last = std::unique(names.begin(), names.end()); + names.erase(last, names.end()); + return names; + } + + 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); + bool saveToDisk(const QUrl &unitUrl, QString *errorString); + + QString bindingValueAsString(const CompiledData::Binding *binding) const; + QString bindingValueAsScriptString(const CompiledData::Binding *binding) const; + +protected: + quint32 totalStringCount() const + { return data->stringTableSize; } + +private: + struct ResolveSetEntry + { + ResolveSetEntry() {} + ResolveSetEntry(ExecutableCompilationUnit *module, QV4::String *exportName) + : module(module), exportName(exportName) {} + ExecutableCompilationUnit *module = nullptr; + QV4::String *exportName = nullptr; + }; + + ExecutableCompilationUnit(); + ExecutableCompilationUnit(CompiledData::CompilationUnit &&compilationUnit); + ~ExecutableCompilationUnit(); + + const Value *resolveExportRecursively(QV4::String *exportName, + QVector<ResolveSetEntry> *resolveSet); + + QUrl urlAt(int index) const { return QUrl(stringAt(index)); } + + Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex); + const CompiledData::ExportEntry *lookupNameInExportTable( + const CompiledData::ExportEntry *firstExportEntry, int tableSize, + QV4::String *name) const; + + void getExportedNamesRecursively( + QStringList *names, QVector<const ExecutableCompilationUnit *> *exportNameSet, + bool includeDefaultExport = true) const; +}; + +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType type; + QQmlRefPointer<QQmlPropertyCache> typePropertyCache; + QQmlRefPointer<QV4::ExecutableCompilationUnit> 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 ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex) +{ + auto it = namedObjectsPerComponentCache.find(componentObjectIndex); + if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end())) + return createNamedObjectsPerComponent(componentObjectIndex); + return *it; +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4EXECUTABLECOMPILATIONUNIT_P_H |