diff options
Diffstat (limited to 'src/qml/jsruntime/qv4executablecompilationunit.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit.cpp | 730 |
1 files changed, 269 insertions, 461 deletions
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 8ef8ae2221..34d737cdae 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -1,46 +1,9 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qml/qqmlprivate.h" #include "qv4engine_p.h" #include "qv4executablecompilationunit_p.h" -#include "qv4stackframe_p.h" #include <private/qv4engine_p.h> #include <private/qv4regexp_p.h> @@ -54,33 +17,14 @@ #include <private/qqmlscriptdata_p.h> #include <private/qv4module_p.h> #include <private/qv4compilationunitmapper_p.h> -#include <private/qml_compile_hash_p.h> #include <private/qqmltypewrapper_p.h> -#include <private/inlinecomponentutils_p.h> #include <private/qv4resolvedtypereference_p.h> +#include <private/qv4objectiterator_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> #include <QtCore/qcryptographichash.h> -#include <QtCore/QScopedValueRollback> - -#if defined(QML_COMPILE_HASH) -# ifdef Q_OS_LINUX -// Place on a separate section on Linux so it's easier to check from outside -// what the hash version is. -__attribute__((section(".qml_compile_hash"))) -# endif -const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH; -static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, - "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); -#else -# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" -#endif QT_BEGIN_NAMESPACE @@ -89,28 +33,16 @@ namespace QV4 { ExecutableCompilationUnit::ExecutableCompilationUnit() = default; ExecutableCompilationUnit::ExecutableCompilationUnit( - CompiledData::CompilationUnit &&compilationUnit) - : CompiledData::CompilationUnit(std::move(compilationUnit)) -{} - -ExecutableCompilationUnit::~ExecutableCompilationUnit() + QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit) + : m_compilationUnit(std::move(compilationUnit)) { - unlink(); + constants = m_compilationUnit->constants; } -QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url) +ExecutableCompilationUnit::~ExecutableCompilationUnit() { - static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH"); - - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix(); - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = envCachePath.isEmpty() - ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/") - : QString::fromLocal8Bit(envCachePath) + QLatin1String("/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix; + if (engine) + clear(); } static QString toString(QV4::ReturnedValue v) @@ -138,31 +70,36 @@ static void dumpConstantTable(const StaticValue *constants, uint count) } } -QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) +void ExecutableCompilationUnit::populate() { - this->engine = engine; - engine->compilationUnits.insert(this); + /* In general, we should use QV4::Scope whenever we allocate heap objects, and employ write barriers + for member variables pointing to heap objects. However, ExecutableCompilationUnit is special, as it + is always part of the root set. So instead of using scopde allocations and write barriers, we use a + slightly different approach: We temporarily block the gc from running. Afterwards, at the end of the + function we check whether the gc was already running, and mark the ExecutableCompilationUnit. This + ensures that all the newly allocated objects of the compilation unit will be marked in turn. + If the gc was not running, we don't have to do anything, because everything will be marked when the + gc starts marking the root set at the start of a run. + */ + const CompiledData::Unit *data = m_compilationUnit->data; + GCCriticalSection<ExecutableCompilationUnit> criticalSection(engine, this); Q_ASSERT(!runtimeStrings); + Q_ASSERT(engine); 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*)); + runtimeStrings = (QV4::Heap::String **)calloc(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; + 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); + engine, stringAt(re->stringIndex()), flags); } if (data->lookupTableSize) { @@ -173,7 +110,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) QV4::Lookup *l = runtimeLookups + i; CompiledData::Lookup::Type type - = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags)); + = CompiledData::Lookup::Type(uint(compiledLookups[i].type())); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -182,17 +119,16 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) l->globalGetter = QV4::Lookup::globalGetterGeneric; else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; - l->nameIndex = compiledLookups[i].nameIndex; + l->forCall = compiledLookups[i].mode() == CompiledData::Lookup::Mode_ForCall; + 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 *)); + = (QV4::Heap::InternalClass **)calloc(data->jsClassTableSize, + sizeof(QV4::Heap::InternalClass *)); + for (uint i = 0; i < data->jsClassTableSize; ++i) { int memberCount = 0; const CompiledData::JSClassMember *member @@ -203,22 +139,25 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) runtimeClasses[i] = runtimeClasses[i]->addMember( engine->identifierTable->asPropertyKey( - runtimeStrings[member->nameOffset]), - member->isAccessor + runtimeStrings[member->nameOffset()]), + member->isAccessor() ? QV4::Attr_Accessor : QV4::Attr_Data); } } runtimeFunctions.resize(data->functionTableSize); - static bool forceInterpreter = qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"); + static bool ignoreAotCompiledFunctions + = qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER") + || !(engine->diskCacheOptions() & ExecutionEngine::DiskCache::AotNative); + const QQmlPrivate::AOTCompiledFunction *aotFunction - = forceInterpreter ? nullptr : aotCompiledFunctions; + = ignoreAotCompiledFunctions ? nullptr : m_compilationUnit->aotCompiledFunctions; auto advanceAotFunction = [&](int i) -> const QQmlPrivate::AOTCompiledFunction * { if (aotFunction) { if (aotFunction->functionPtr) { - if (aotFunction->index == i) + if (aotFunction->functionIndex == i) return aotFunction++; } else { aotFunction = nullptr; @@ -264,15 +203,14 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0); } - - if (data->indexOfRootFunction != -1) - return runtimeFunctions[data->indexOfRootFunction]; - else - return nullptr; } Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const { + const CompiledData::Unit *data = m_compilationUnit->data; + Q_ASSERT(data); + Q_ASSERT(engine); + Q_ASSERT(index < int(data->templateObjectTableSize)); if (!templateObjects.size()) templateObjects.resize(data->templateObjectTableSize); @@ -301,50 +239,21 @@ Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const return templateObjects.at(index); } -void ExecutableCompilationUnit::unlink() +void ExecutableCompilationUnit::clear() { - if (engine) - nextCompilationUnit.remove(); - - if (isRegisteredWithEngine) { - Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0)); - if (qmlEngine) - qmlEngine->unregisterInternalCompositeType(this); - isRegisteredWithEngine = false; - } - - propertyCaches.clear(); + delete [] imports; + imports = nullptr; if (runtimeLookups) { - for (uint i = 0; i < data->lookupTableSize; ++i) { - QV4::Lookup &l = runtimeLookups[i]; - if (l.getter == QV4::QObjectWrapper::lookupGetter - || l.getter == QQmlTypeWrapper::lookupSingletonProperty) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } - if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty - || l.qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } - } + const uint lookupTableSize = unitData()->lookupTableSize; + for (uint i = 0; i < lookupTableSize; ++i) + runtimeLookups[i].releasePropertyCache(); } - dependentScripts.clear(); - - typeNameCache = nullptr; - - qDeleteAll(resolvedTypes); - resolvedTypes.clear(); - - engine = nullptr; - qmlEngine = nullptr; - delete [] runtimeLookups; runtimeLookups = nullptr; - for (QV4::Function *f : qAsConst(runtimeFunctions)) + for (QV4::Function *f : std::as_const(runtimeFunctions)) f->destroy(); runtimeFunctions.clear(); @@ -356,8 +265,10 @@ void ExecutableCompilationUnit::unlink() runtimeClasses = nullptr; } -void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) +void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) const { + const CompiledData::Unit *data = m_compilationUnit->data; + if (runtimeStrings) { for (uint i = 0, end = totalStringCount(); i < end; ++i) if (runtimeStrings[i]) @@ -372,14 +283,14 @@ void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) if (runtimeClasses[i]) runtimeClasses[i]->mark(markStack); } - for (QV4::Function *f : qAsConst(runtimeFunctions)) + for (QV4::Function *f : std::as_const(runtimeFunctions)) if (f && f->internalClass) f->internalClass->mark(markStack); - for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks)) + for (QV4::Heap::InternalClass *c : std::as_const(runtimeBlocks)) if (c) c->mark(markStack); - for (QV4::Heap::Object *o : qAsConst(templateObjects)) + for (QV4::Heap::Object *o : std::as_const(templateObjects)) if (o) o->mark(markStack); @@ -399,178 +310,35 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com 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); + namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->objectId()); } + Q_ASSERT(!namedObjectCache.isEmpty()); return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); } -void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds types) -{ - this->qmlEngine = qmlEngine; - - // Add to type registry of composites - if (propertyCaches.needsVMEMetaObject(/*root object*/0)) { - // typeIds is only valid for types that have references to themselves. - if (!types.isValid()) - types = CompositeMetaTypeIds::fromCompositeName(rootPropertyCache()->className()); - typeIds = types; - qmlEngine->registerInternalCompositeType(this); - - } else { - const QV4::CompiledData::Object *obj = objectAt(/*root object*/0); - auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - if (const auto compilationUnit = typeRef->compilationUnit()) { - typeIds = compilationUnit->typeIds; - } else { - const auto type = typeRef->type(); - typeIds = CompositeMetaTypeIds{ type.typeId(), type.qListTypeId() }; - } - } - - // Collect some data for instantiation later. - using namespace icutils; - std::vector<QV4::CompiledData::InlineComponent> allICs {}; - for (int i=0; i != objectCount(); ++i) { - const CompiledObject *obj = objectAt(i); - for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) { - allICs.push_back(*it); - } - } - std::vector<Node> nodes; - nodes.resize(allICs.size()); - std::iota(nodes.begin(), nodes.end(), 0); - AdjacencyList adjacencyList; - adjacencyList.resize(nodes.size()); - fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs); - bool hasCycle = false; - auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle); - Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator - - // We need to first iterate over all inline components, as the containing component might create instances of them - // and in that case we need to add its object count - for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) { - const auto &ic = allICs.at(nodeIt->index); - int lastICRoot = ic.objectIndex; - for (int i = ic.objectIndex; i<objectCount(); ++i) { - const QV4::CompiledData::Object *obj = objectAt(i); - bool leftCurrentInlineComponent = - (i != lastICRoot && obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot) - || !(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent); - if (leftCurrentInlineComponent) - break; - inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings; - - if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - const auto type = typeRef->type(); - if (type.isValid() && type.parserStatusCast() != -1) - ++inlineComponentData[lastICRoot].totalParserStatusCount; - - ++inlineComponentData[lastICRoot].totalObjectCount; - if (const auto compilationUnit = typeRef->compilationUnit()) { - // if the type is an inline component type, we have to extract the information from it - // This requires that inline components are visited in the correct order - auto icRoot = compilationUnit->icRoot; - if (type.isInlineComponentType()) - icRoot = type.inlineComponentId(); - QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot}; - inlineComponentData[lastICRoot].totalBindingCount += compilationUnit->totalBindingsCount(); - inlineComponentData[lastICRoot].totalParserStatusCount += compilationUnit->totalParserStatusCount(); - inlineComponentData[lastICRoot].totalObjectCount += compilationUnit->totalObjectCount(); - } - } - } - } - 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); - if (obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) { - continue; - } - bindingCount += obj->nBindings; - if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - const auto type = typeRef->type(); - if (type.isValid() && type.parserStatusCast() != -1) - ++parserStatusCount; - ++objectCount; - if (const auto compilationUnit = typeRef->compilationUnit()) { - auto icRoot = compilationUnit->icRoot; - if (type.isInlineComponentType()) - icRoot = type.inlineComponentId(); - QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot}; - bindingCount += compilationUnit->totalBindingsCount(); - parserStatusCount += compilationUnit->totalParserStatusCount(); - objectCount += compilationUnit->totalObjectCount(); - } - } - } - - m_totalBindingsCount = bindingCount; - m_totalParserStatusCount = parserStatusCount; - m_totalObjectCount = objectCount; -} - -int ExecutableCompilationUnit::totalBindingsCount() const { - if (icRoot == -1) - return m_totalBindingsCount; - return inlineComponentData[icRoot].totalBindingCount; -} - -int ExecutableCompilationUnit::totalObjectCount() const { - if (icRoot == -1) - return m_totalObjectCount; - return inlineComponentData[icRoot].totalObjectCount; -} - -int ExecutableCompilationUnit::totalParserStatusCount() const { - if (icRoot == -1) - return m_totalParserStatusCount; - return inlineComponentData[icRoot].totalParserStatusCount; -} - -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; -} - -CompositeMetaTypeIds ExecutableCompilationUnit::typeIdsForComponent(int objectid) const +QQmlRefPointer<ExecutableCompilationUnit> ExecutableCompilationUnit::create( + QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit, ExecutionEngine *engine) { - if (objectid == 0) - return typeIds; - return inlineComponentData[objectid].typeIds; + auto result = QQmlRefPointer<ExecutableCompilationUnit>( + new ExecutableCompilationUnit(std::move(compilationUnit)), + QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + result->engine = engine; + return result; } -QStringList ExecutableCompilationUnit::moduleRequests() const +Heap::Module *ExecutableCompilationUnit::instantiate() { - QStringList requests; - requests.reserve(data->moduleRequestTableSize); - for (uint i = 0; i < data->moduleRequestTableSize; ++i) - requests << stringAt(data->moduleRequestTable()[i]); - return requests; -} + const CompiledData::Unit *data = m_compilationUnit->data; -Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) -{ if (isESModule() && module()) return module(); if (data->indexOfRootFunction < 0) return nullptr; - if (!this->engine) - linkToEngine(engine); + Q_ASSERT(engine); + if (!runtimeStrings) + populate(); Scope scope(engine); Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this)); @@ -578,11 +346,14 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) if (isESModule()) setModule(module->d()); - for (const QString &request: moduleRequests()) { - auto dependentModuleUnit = engine->loadModule(QUrl(request), this); + const QStringList moduleRequests = m_compilationUnit->moduleRequests(); + for (const QString &request: moduleRequests) { + const QUrl url(request); + const auto dependentModuleUnit = engine->loadModule(url, this); if (engine->hasException) return nullptr; - dependentModuleUnit->instantiate(engine); + if (dependentModuleUnit.compiled) + dependentModuleUnit.compiled->instantiate(); } ScopedString importName(scope); @@ -594,30 +365,87 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) } for (uint i = 0; i < importCount; ++i) { const CompiledData::ImportEntry &entry = data->importEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); + QUrl url = urlAt(entry.moduleRequest); 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; + + const auto module = engine->loadModule(url, this); + if (module.compiled) { + const Value *valuePtr = module.compiled->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; + } else if (Value *value = module.native) { + const QString name = importName->toQString(); + if (value->isNullOrUndefined()) { + QString errorMessage = name; + errorMessage += QStringLiteral(" from "); + errorMessage += url.toString(); + errorMessage += QStringLiteral(" is null"); + engine->throwError(errorMessage); + return nullptr; + } + + if (name == QStringLiteral("default")) { + imports[i] = value; + } else { + url.setFragment(name); + const auto fragment = engine->moduleForUrl(url, this); + if (fragment.native) { + imports[i] = fragment.native; + } else { + Scope scope(this->engine); + ScopedObject o(scope, value); + if (!o) { + QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference "); + referenceErrorMessage += name; + referenceErrorMessage += QStringLiteral(" because "); + referenceErrorMessage += url.toString(QUrl::RemoveFragment); + referenceErrorMessage += QStringLiteral(" is not an object"); + engine->throwReferenceError( + referenceErrorMessage, fileName(), + entry.location.line(), entry.location.column()); + return nullptr; + } + + const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name)); + const ScopedValue result(scope, o->get(key)); + imports[i] = engine->registerNativeModule(url, result); + } + } } - imports[i] = valuePtr; } + const auto throwReferenceError = [&](const CompiledData::ExportEntry &entry, const QString &importName) { + QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference "); + referenceErrorMessage += importName; + engine->throwReferenceError( + referenceErrorMessage, fileName(), + entry.location.line(), entry.location.column()); + }; + 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; - + auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this); 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; + if (const auto dependentModuleUnit = dependentModule.compiled) { + if (!dependentModuleUnit->resolveExport(importName)) { + throwReferenceError(entry, importName->toQString()); + return nullptr; + } + } else if (const auto native = dependentModule.native) { + ScopedObject o(scope, native); + const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(importName)); + const ScopedValue result(scope, o->get(key)); + if (result->isUndefined()) { + throwReferenceError(entry, importName->toQString()); + return nullptr; + } } } @@ -639,6 +467,11 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively( if (exportName->toQString() == QLatin1String("*")) return &module()->self; + const CompiledData::Unit *data = m_compilationUnit->data; + + Q_ASSERT(data); + Q_ASSERT(engine); + Scope scope(engine); if (auto localExport = lookupNameInExportTable( @@ -654,13 +487,31 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively( if (auto indirectExport = lookupNameInExportTable( data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) { - auto dependentModuleUnit = engine->loadModule(urlAt(indirectExport->moduleRequest), this); - if (!dependentModuleUnit) - return nullptr; + QUrl request = urlAt(indirectExport->moduleRequest); + auto dependentModule = engine->loadModule(request, this); ScopedString importName(scope, runtimeStrings[indirectExport->importName]); - return dependentModuleUnit->resolveExportRecursively(importName, resolveSet); - } + if (dependentModule.compiled) { + return dependentModule.compiled->resolveExportRecursively(importName, resolveSet); + } else if (dependentModule.native) { + if (exportName->toQString() == QLatin1String("*")) + return dependentModule.native; + if (exportName->toQString() == QLatin1String("default")) + return nullptr; + + request.setFragment(importName->toQString()); + const auto fragment = engine->moduleForUrl(request); + if (fragment.native) + return fragment.native; + ScopedObject o(scope, dependentModule.native); + if (o) + return engine->registerNativeModule(request, o->get(importName)); + + return nullptr; + } else { + return nullptr; + } + } if (exportName->toQString() == QLatin1String("default")) return nullptr; @@ -669,11 +520,28 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively( 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; + QUrl request = urlAt(entry.moduleRequest); + auto dependentModule = engine->loadModule(request, this); + const Value *resolution = nullptr; + if (dependentModule.compiled) { + resolution = dependentModule.compiled->resolveExportRecursively( + exportName, resolveSet); + } else if (dependentModule.native) { + if (exportName->toQString() == QLatin1String("*")) { + resolution = dependentModule.native; + } else if (exportName->toQString() != QLatin1String("default")) { + request.setFragment(exportName->toQString()); + const auto fragment = engine->moduleForUrl(request); + if (fragment.native) { + resolution = fragment.native; + } else { + ScopedObject o(scope, dependentModule.native); + if (o) + resolution = engine->registerNativeModule(request, o->get(exportName)); + } + } + } - const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet); // ### handle ambiguous if (resolution) { if (!starResolution) { @@ -714,6 +582,11 @@ void ExecutableCompilationUnit::getExportedNamesRecursively( names->append(name); }; + const CompiledData::Unit *data = m_compilationUnit->data; + + Q_ASSERT(data); + Q_ASSERT(engine); + for (uint i = 0; i < data->localExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i]; append(stringAt(entry.exportName)); @@ -726,15 +599,28 @@ void ExecutableCompilationUnit::getExportedNamesRecursively( 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); + auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this); + if (dependentModule.compiled) { + dependentModule.compiled->getExportedNamesRecursively( + names, exportNameSet, /*includeDefaultExport*/false); + } else if (dependentModule.native) { + Scope scope(engine); + ScopedObject o(scope, dependentModule.native); + ObjectIterator iterator(scope, o, ObjectIterator::EnumerableOnly); + while (true) { + ScopedValue val(scope, iterator.nextPropertyNameAsString()); + if (val->isNull()) + break; + append(val->toQString()); + } + } } } void ExecutableCompilationUnit::evaluate() { + Q_ASSERT(engine); + QV4::Scope scope(engine); QV4::Scoped<Module> mod(scope, module()); mod->evaluate(); @@ -742,159 +628,81 @@ void ExecutableCompilationUnit::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->get(cachePath, sourceTimeStamp, errorString); - if (!mappedUnit) - continue; + Q_ASSERT(engine); - 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."); + const QStringList moduleRequests = m_compilationUnit->moduleRequests(); + for (const QString &request: moduleRequests) { + auto dependentModule = engine->loadModule(QUrl(request), this); + if (dependentModule.native) 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 CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>( - [&unitUrl, errorString](const char *data, quint32 size) { - return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data, - size, errorString); - }); -} + if (engine->hasException) + return; -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; + Q_ASSERT(dependentModule.compiled); + dependentModule.compiled->evaluate(); + if (engine->hasException) + return; } - - return true; } QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const { - using namespace CompiledData; #if QT_CONFIG(translation) - switch (binding->type) { - case Binding::Type_TranslationById: { - const TranslationData &translation - = data->translations()[binding->value.translationDataIndex]; - QByteArray id = stringAt(translation.stringIndex).toUtf8(); - return qtTrId(id.constData(), translation.number); - } + using namespace CompiledData; + bool byId = false; + switch (binding->type()) { + case Binding::Type_TranslationById: + byId = true; + Q_FALLTHROUGH(); 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('/')); - QStringView context = (lastSlash > -1) ? QStringView{path}.mid(lastSlash + 1, path.length() - lastSlash - 5) - : QStringView(); - 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); + return translateFrom({ binding->value.translationDataIndex, byId }); } default: break; } #endif - return CompilationUnit::bindingValueAsString(binding); + return m_compilationUnit->bindingValueAsString(binding); } -bool ExecutableCompilationUnit::verifyHeader( - const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString) +QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) const { - if (strncmp(unit->magic, CompiledData::magic_str, sizeof(unit->magic))) { - *errorString = QStringLiteral("Magic bytes in the header do not match"); - return false; - } +#if !QT_CONFIG(translation) + return QString(); +#else + const CompiledData::TranslationData &translation = unitData()->translations()[index.index]; - if (unit->version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2") - .arg(unit->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); - return false; + if (index.byId) { + QByteArray id = stringAt(translation.stringIndex).toUtf8(); + return qtTrId(id.constData(), translation.number); } - if (unit->qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2") - .arg(unit->qtVersion, 0, 16).arg(QT_VERSION, 0, 16); - return false; - } + const auto fileContext = [this]() { + // This code must match that in the qsTr() implementation + const QString &path = fileName(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (unit->sourceTimeStamp) { - // Files from the resource system do not have any time stamps, so fall back to the application - // executable. - if (!expectedSourceTimeStamp.isValid()) - expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); + QStringView context = (lastSlash > -1) + ? QStringView{ path }.mid(lastSlash + 1, path.size() - lastSlash - 5) + : QStringView(); + return context.toUtf8(); + }; - if (expectedSourceTimeStamp.isValid() - && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); - return false; - } + const bool hasContext + = translation.contextIndex != QV4::CompiledData::TranslationData::NoContextIndex; + QByteArray context; + if (hasContext) { + context = stringAt(translation.contextIndex).toUtf8(); + } else { + auto pragmaTranslationContext = unitData()->translationContextIndex(); + context = stringAt(*pragmaTranslationContext).toUtf8(); + context = context.isEmpty() ? fileContext() : context; } -#if defined(QML_COMPILE_HASH) - if (qstrcmp(qml_compile_hash, unit->libraryVersionHash) != 0) { - *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); - return false; - } -#else -#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" + QByteArray comment = stringAt(translation.commentIndex).toUtf8(); + QByteArray text = stringAt(translation.stringIndex).toUtf8(); + return QCoreApplication::translate(context, text, comment, translation.number); #endif - return true; } } // namespace QV4 |