From 82da798499aa8b656e771191332864a703069739 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 10 Jul 2018 14:52:34 +0200 Subject: Add initial basic support for ES6 modules The entry point from the parsing perspective into modules is not QV4::Script but QV4::ExecutionEngine::compileModule. For convenience, the ESModule AST node gets a body, which is the statement list connected between the ModuleItemList items that are not import/export declarations. The QV4::Module allocates a call context where the exported variables are stored as named locals. This will also become the module namespace object. The imports in turn is an array of value pointers that point into the locals array of the context of the imported modules. The default module loading in ExecutionEngine assumes the accessibility of module urls via QFile (so local file system or resource). This is what qmljs also uses and QJSEngine as well via public API in the future. The test runner compiles the modules manually and injects them, because they need to be compiled together with the test harness code. The QML type loader will the mechanism for injection in the future for module imports from .qml files. Change-Id: I93be9cfe54c651fdbd08c5e1d22d58f47284e54f Reviewed-by: Qt CI Bot Reviewed-by: Lars Knoll --- src/qml/compiler/qv4bytecodegenerator_p.h | 3 + src/qml/compiler/qv4bytecodehandler.cpp | 3 + src/qml/compiler/qv4codegen.cpp | 70 +++++++++++++- src/qml/compiler/qv4codegen_p.h | 12 +++ src/qml/compiler/qv4compileddata.cpp | 117 +++++++++++++++++++++++ src/qml/compiler/qv4compileddata_p.h | 48 +++++++++- src/qml/compiler/qv4compiler.cpp | 62 ++++++++++++- src/qml/compiler/qv4compilercontext.cpp | 14 ++- src/qml/compiler/qv4compilercontext_p.h | 30 +++++- src/qml/compiler/qv4compilerscanfunctions.cpp | 129 ++++++++++++++++++++++++++ src/qml/compiler/qv4compilerscanfunctions_p.h | 6 ++ src/qml/compiler/qv4instr_moth.cpp | 4 + src/qml/compiler/qv4instr_moth_p.h | 2 + src/qml/jit/qv4assembler.cpp | 10 ++ src/qml/jit/qv4assembler_p.h | 1 + src/qml/jit/qv4baselinejit.cpp | 5 + src/qml/jit/qv4baselinejit_p.h | 1 + src/qml/jsruntime/jsruntime.pri | 6 +- src/qml/jsruntime/qv4engine.cpp | 99 ++++++++++++++++++++ src/qml/jsruntime/qv4engine_p.h | 15 +++ src/qml/jsruntime/qv4module.cpp | 69 ++++++++++++++ src/qml/jsruntime/qv4module_p.h | 82 ++++++++++++++++ src/qml/jsruntime/qv4vme_moth.cpp | 4 + src/qml/parser/qqmljsast.cpp | 23 +++++ src/qml/parser/qqmljsast_p.h | 9 +- 25 files changed, 812 insertions(+), 12 deletions(-) create mode 100644 src/qml/jsruntime/qv4module.cpp create mode 100644 src/qml/jsruntime/qv4module_p.h (limited to 'src') diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index dca5771356..4f3dc27acc 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -163,8 +163,11 @@ public: Q_REQUIRED_RESULT Jump jump() { +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug() Instruction::Jump data; return addJumpInstruction(data); +QT_WARNING_POP } Q_REQUIRED_RESULT Jump jumpTrue() diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index 23f7051718..d3c5582b28 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -161,6 +161,9 @@ std::vector ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(MoveConst) COLLECTOR_END_INSTR(MoveConst) + COLLECTOR_BEGIN_INSTR(LoadImport) + COLLECTOR_END_INSTR(LoadImport) + COLLECTOR_BEGIN_INSTR(LoadLocal) COLLECTOR_END_INSTR(LoadLocal) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 30a73d31bd..7b059b3fb4 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -126,6 +126,49 @@ void Codegen::generateFromProgram(const QString &fileName, defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements); } +void Codegen::generateFromModule(const QString &fileName, + const QString &finalUrl, + const QString &sourceCode, + ESModule *node, + Module *module) +{ + Q_ASSERT(node); + + _module = module; + _context = nullptr; + + // ### should be set on the module outside of this method + _module->fileName = fileName; + _module->finalUrl = finalUrl; + + ScanFunctions scan(this, sourceCode, ContextType::ESModule); + scan(node); + + if (hasError) + return; + + { + Compiler::Context *moduleContext = _module->contextMap.value(node); + for (const auto &entry: moduleContext->exportEntries) { + if (entry.moduleRequest.isEmpty()) { + // ### check against imported bound names + _module->localExportEntries << entry; + } else if (entry.importName == QLatin1Char('*')) { + _module->starExportEntries << entry; + } else { + _module->indirectExportEntries << entry; + } + } + _module->importEntries = moduleContext->importEntries; + } + + std::sort(_module->localExportEntries.begin(), _module->localExportEntries.end(), ExportEntry::lessThan); + std::sort(_module->starExportEntries.begin(), _module->starExportEntries.end(), ExportEntry::lessThan); + std::sort(_module->indirectExportEntries.begin(), _module->indirectExportEntries.end(), ExportEntry::lessThan); + + defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements); +} + void Codegen::enterContext(Node *node) { _context = _module->contextMap.value(node); @@ -2163,13 +2206,21 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) { Context::ResolvedName resolved = _context->resolveName(name); - if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack) { + if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack + || resolved.type == Context::ResolvedName::Import) { if (resolved.isArgOrEval && isLhs) // ### add correct source location throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); - Reference r = (resolved.type == Context::ResolvedName::Local) ? - Reference::fromScopedLocal(this, resolved.index, resolved.scope) : - Reference::fromStackSlot(this, resolved.index, true /*isLocal*/); + Reference r; + switch (resolved.type) { + case Context::ResolvedName::Local: + r = Reference::fromScopedLocal(this, resolved.index, resolved.scope); break; + case Context::ResolvedName::Stack: + r = Reference::fromStackSlot(this, resolved.index, true /*isLocal*/); break; + case Context::ResolvedName::Import: + r = Reference::fromImport(this, resolved.index); break; + default: Q_UNREACHABLE(); + } if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name)) r.isVolatile = true; r.isArgOrEval = resolved.isArgOrEval; @@ -3744,6 +3795,9 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other) elementBase = other.elementBase; elementSubscript = other.elementSubscript; break; + case Import: + index = other.index; + break; case Const: constant = other.constant; break; @@ -3789,6 +3843,8 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex; case Subscript: return elementBase == other.elementBase && elementSubscript == other.elementSubscript; + case Import: + return index == other.index; case Const: return constant == other.constant; case QmlScopeObject: @@ -4031,6 +4087,7 @@ void Codegen::Reference::storeAccumulator() const case Invalid: case Accumulator: case Const: + case Import: break; } @@ -4145,6 +4202,11 @@ QT_WARNING_POP codegen->bytecodeGenerator->addInstruction(load); } return; + case Import: { + Instruction::LoadImport load; + load.index = index; + codegen->bytecodeGenerator->addInstruction(load); + } return; case Subscript: { elementSubscript.loadInAccumulator(); Instruction::LoadElement load; diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 92f8b0b13d..e3617254a7 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -104,6 +104,12 @@ public: Module *module, ContextType contextType = ContextType::Global); + void generateFromModule(const QString &fileName, + const QString &finalUrl, + const QString &sourceCode, + AST::ESModule *ast, + Module *module); + public: class VolatileMemoryLocationScanner; class VolatileMemoryLocations { @@ -186,6 +192,7 @@ public: Name, Member, Subscript, + Import, QmlScopeObject, QmlContextObject, LastLValue = QmlContextObject, @@ -273,6 +280,11 @@ public: r.scope = scope; return r; } + static Reference fromImport(Codegen *cg, int index) { + Reference r(cg, Import); + r.index = index; + return r; + } static Reference fromName(Codegen *cg, const QString &name) { Reference r(cg, Name); r.name = name; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 00a36cbb46..8e46ebf230 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include "qv4compilationunitmapper_p.h" #include #include @@ -113,6 +114,9 @@ CompilationUnit::~CompilationUnit() delete [] constants; constants = nullptr; #endif + + delete [] imports; + imports = nullptr; } QString CompilationUnit::localCacheFilePath(const QUrl &url) @@ -291,6 +295,9 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack) for (uint i = 0; i < data->lookupTableSize; ++i) runtimeLookups[i].markObjects(markStack); } + + if (m_module) + m_module->mark(markStack); } IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex) @@ -370,6 +377,116 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe sizeof(data->dependencyMD5Checksum)) == 0; } +QStringList CompilationUnit::moduleRequests() const +{ + QStringList requests; + + for (uint i = 0; i < data->importEntryTableSize; ++i) { + const ImportEntry &entry = data->importEntryTable()[i]; + requests << stringAt(entry.moduleRequest); + } + + return requests; +} + +Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine) +{ + if (m_module) + return m_module; + + if (!this->engine) + linkToEngine(engine); + + m_module = engine->memoryManager->allocate(engine, this); + + for (const QString &request: moduleRequests()) { + auto dependentModuleUnit = engine->loadModule(QUrl(request), this); + if (engine->hasException) + return nullptr; + dependentModuleUnit->instantiate(engine); + } + + Scope scope(engine); + ScopedString importName(scope); + + const uint importCount = data->importEntryTableSize; + 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(), /*### line*/1, /*### column*/1); + return nullptr; + } + imports[i] = valuePtr; + } + + return m_module; +} + +const Value *CompilationUnit::resolveExport(QV4::String *exportName) +{ + if (!m_module) + return nullptr; + + Scope scope(engine); + ScopedString localName(scope, localNameForExportName(exportName)); + if (!localName) + return nullptr; + + uint index = m_module->scope->internalClass->find(localName->toPropertyKey()); + if (index < UINT_MAX) + return &m_module->scope->locals[index]; + + return nullptr; +} + +Heap::String *CompilationUnit::localNameForExportName(QV4::String *exportName) const +{ + const CompiledData::ExportEntry *firstExport = data->localExportEntryTable(); + const CompiledData::ExportEntry *lastExport = data->localExportEntryTable() + data->localExportEntryTableSize; + auto matchingExport = std::lower_bound(firstExport, lastExport, exportName, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) { + return stringAt(lhs.exportName) < name->toQString(); + }); + if (matchingExport == lastExport || stringAt(matchingExport->exportName) != exportName->toQString()) + return nullptr; + return runtimeStrings[matchingExport->localName]; +} + +void CompilationUnit::evaluate() +{ + if (m_moduleEvaluated) + return; + m_moduleEvaluated = true; + + for (const QString &request: moduleRequests()) { + auto dependentModuleUnit = engine->loadModule(QUrl(request), this); + if (engine->hasException) + return; + dependentModuleUnit->evaluate(); + if (engine->hasException) + return; + } + + QV4::Function *moduleFunction = runtimeFunctions[data->indexOfRootFunction]; + CppStackFrame frame; + frame.init(engine, moduleFunction, nullptr, 0); + frame.setupJSFrame(engine->jsStackTop, Primitive::undefinedValue(), m_module->scope, + Primitive::undefinedValue(), Primitive::undefinedValue()); + + frame.push(); + engine->jsStackTop += frame.requiredJSStackFrameSize(); + auto frameCleanup = qScopeGuard([&frame]() { + frame.pop(); + }); + Moth::VME::exec(&frame, engine); +} + bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) { if (!QQmlFile::isLocalFile(url)) { diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index e7d152c7f8..e65c04ad69 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -88,6 +88,10 @@ struct Document; namespace QV4 { +namespace Heap { +struct Module; +}; + struct Function; class EvalISelFactory; class CompilationUnitMapper; @@ -358,6 +362,23 @@ struct Class }; static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +struct ExportEntry +{ + quint32_le exportName; + quint32_le moduleRequest; + quint32_le importName; + quint32_le localName; +}; +static_assert(sizeof(ExportEntry) == 16, "ExportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct ImportEntry +{ + quint32_le moduleRequest; + quint32_le importName; + quint32_le localName; + quint32_le padding; +}; +static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); // Qml data structures @@ -819,6 +840,14 @@ struct Unit quint32_le offsetToJSClassTable; quint32_le translationTableSize; quint32_le offsetToTranslationTable; + quint32_le localExportEntryTableSize; + quint32_le offsetToLocalExportEntryTable; + quint32_le indirectExportEntryTableSize; + quint32_le offsetToIndirectExportEntryTable; + quint32_le starExportEntryTableSize; + quint32_le offsetToStarExportEntryTable; + quint32_le importEntryTableSize; + quint32_le offsetToImportEntryTable; qint32_le indexOfRootFunction; quint32_le sourceFileIndex; quint32_le finalUrlIndex; @@ -904,9 +933,14 @@ struct Unit const TranslationData *translations() const { return reinterpret_cast(reinterpret_cast(this) + offsetToTranslationTable); } + + const ImportEntry *importEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToImportEntryTable); } + const ExportEntry *localExportEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToLocalExportEntryTable); } + const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToIndirectExportEntryTable); } + const ExportEntry *starExportEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToStarExportEntryTable); } }; -static_assert(sizeof(Unit) == 200, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Unit) == 232, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct TypeReference { @@ -991,12 +1025,15 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnitBase const Value* constants = nullptr; QV4::Value *runtimeRegularExpressions = nullptr; QV4::Heap::InternalClass **runtimeClasses = nullptr; + const Value** imports = nullptr; }; Q_STATIC_ASSERT(std::is_standard_layout::value); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *)); struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase { @@ -1122,6 +1159,12 @@ public: FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); } // --- + QStringList moduleRequests() const; + Heap::Module *instantiate(ExecutionEngine *engine); + const Value *resolveExport(QV4::String *exportName); + Heap::String *localNameForExportName(QV4::String *exportName) const; + void evaluate(); + QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); @@ -1149,6 +1192,9 @@ private: Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex); + Heap::Module *m_module = nullptr; + bool m_moduleEvaluated = false; + public: #if defined(V4_BOOTSTRAP) bool saveToDisk(const QString &outputFileName, QString *errorString); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index f301f867c1..4e902eca65 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -241,6 +241,24 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO for (int i = 0; i < c->locals.size(); ++i) registerString(c->locals.at(i)); } + { + const auto registerExportEntry = [this](const Compiler::ExportEntry &entry) { + registerString(entry.exportName); + registerString(entry.moduleRequest); + registerString(entry.importName); + registerString(entry.localName); + }; + std::for_each(module->localExportEntries.constBegin(), module->localExportEntries.constEnd(), registerExportEntry); + std::for_each(module->indirectExportEntries.constBegin(), module->indirectExportEntries.constEnd(), registerExportEntry); + std::for_each(module->starExportEntries.constBegin(), module->starExportEntries.constEnd(), registerExportEntry); + } + { + for (const auto &entry: module->importEntries) { + registerString(entry.moduleRequest); + registerString(entry.importName); + registerString(entry.localName); + } + } Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le)); uint jsClassDataOffset = 0; @@ -304,8 +322,35 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i); } + memcpy(dataPtr + unit->offsetToTranslationTable, translations.constData(), translations.count() * sizeof(CompiledData::TranslationData)); + { + const auto populateExportEntryTable = [this, dataPtr](const QVector &table, quint32_le offset) { + CompiledData::ExportEntry *entryToWrite = reinterpret_cast(dataPtr + offset); + for (const Compiler::ExportEntry &entry: table) { + entryToWrite->exportName = getStringId(entry.exportName); + entryToWrite->moduleRequest = getStringId(entry.moduleRequest); + entryToWrite->importName = getStringId(entry.importName); + entryToWrite->localName = getStringId(entry.localName); + entryToWrite++; + } + }; + populateExportEntryTable(module->localExportEntries, unit->offsetToLocalExportEntryTable); + populateExportEntryTable(module->indirectExportEntries, unit->offsetToIndirectExportEntryTable); + populateExportEntryTable(module->starExportEntries, unit->offsetToStarExportEntryTable); + } + + { + CompiledData::ImportEntry *entryToWrite = reinterpret_cast(dataPtr + unit->offsetToImportEntryTable); + for (const Compiler::ImportEntry &entry: module->importEntries) { + entryToWrite->moduleRequest = getStringId(entry.moduleRequest); + entryToWrite->importName = getStringId(entry.importName); + entryToWrite->localName = getStringId(entry.localName); + entryToWrite++; + } + } + // write strings and string table if (option == GenerateWithStringTable) stringTable.serialize(unit); @@ -547,8 +592,23 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp nextOffset = (nextOffset + 7) & ~quint32(0x7); - quint32 functionSize = 0; + const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) { + *tableSizePtr = count; + *offsetPtr = nextOffset; + nextOffset += count * sizeof(CompiledData::ExportEntry); + nextOffset = (nextOffset + 7) & ~quint32(0x7); + }; + reserveExportTable(module->localExportEntries.count(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable); + reserveExportTable(module->indirectExportEntries.count(), &unit.indirectExportEntryTableSize, &unit.offsetToIndirectExportEntryTable); + reserveExportTable(module->starExportEntries.count(), &unit.starExportEntryTableSize, &unit.offsetToStarExportEntryTable); + + unit.importEntryTableSize = module->importEntries.count(); + unit.offsetToImportEntryTable = nextOffset; + nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry); + nextOffset = (nextOffset + 7) & ~quint32(0x7); + + quint32 functionSize = 0; for (int i = 0; i < module->functions.size(); ++i) { Context *f = module->functions.at(i); blockAndFunctionOffsets[i] = nextOffset; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 9dfe3be7e0..77ac703ee3 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -156,6 +156,17 @@ Context::ResolvedName Context::resolveName(const QString &name) c = c->parent; } + if (c && c->contextType == ContextType::ESModule) { + for (int i = 0; i < c->importEntries.count(); ++i) { + if (c->importEntries.at(i).localName == name) { + result.index = i; + result.type = ResolvedName::Import; + result.isConst = true; + return result; + } + } + } + // ### can we relax the restrictions here? if (contextType == ContextType::Eval || c->contextType == ContextType::Binding) return result; @@ -219,7 +230,7 @@ void Context::emitBlockHeader(Codegen *codegen) } } - if (contextType == ContextType::Function || contextType == ContextType::Binding) { + if (contextType == ContextType::Function || contextType == ContextType::Binding || contextType == ContextType::ESModule) { for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) { if (it->canEscape && it->type == Context::ThisFunctionName) { // move the function from the stack to the call context @@ -285,6 +296,7 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) registerOffset = bytecodeGenerator->currentRegister(); switch (contextType) { + case ContextType::ESModule: case ContextType::Block: case ContextType::Function: case ContextType::Binding: { diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 52c3fc5b05..6a54be2aca 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -74,7 +74,8 @@ enum class ContextType { // * function declarations are moved to the return address when encountered // * return statements are allowed everywhere (like in FunctionCode) // * variable declarations are treated as true locals (like in FunctionCode) - Block + Block, + ESModule }; struct Context; @@ -97,6 +98,24 @@ struct Class { QVector methods; }; +struct ExportEntry +{ + QString exportName; + QString moduleRequest; + QString importName; + QString localName; + + static bool lessThan(const ExportEntry &lhs, const ExportEntry &rhs) + { return lhs.exportName < rhs.exportName; } +}; + +struct ImportEntry +{ + QString moduleRequest; + QString importName; + QString localName; +}; + struct Module { Module(bool debugMode) : debugMode(debugMode) @@ -117,6 +136,10 @@ struct Module { QDateTime sourceTimeStamp; uint unitFlags = 0; // flags merged into CompiledData::Unit::flags bool debugMode = false; + QVector localExportEntries; + QVector indirectExportEntries; + QVector starExportEntries; + QVector importEntries; }; @@ -153,6 +176,8 @@ struct Context { QQmlJS::AST::FormalParameterList *formals = nullptr; QStringList arguments; QStringList locals; + QVector importEntries; + QVector exportEntries; QVector nestedContexts; ControlFlow *controlFlow = nullptr; @@ -289,7 +314,8 @@ struct Context { Unresolved, Global, Local, - Stack + Stack, + Import }; Type type = Unresolved; bool isArgOrEval = false; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 9be55c6ad0..63ed748048 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -149,6 +149,120 @@ void ScanFunctions::endVisit(Program *) leaveEnvironment(); } +bool ScanFunctions::visit(ESModule *ast) +{ + enterEnvironment(ast, defaultProgramType, QStringLiteral("%ModuleCode")); + _context->isStrict = true; + return true; +} + +void ScanFunctions::endVisit(ESModule *) +{ + leaveEnvironment(); +} + +bool ScanFunctions::visit(ExportDeclaration *declaration) +{ + QString module; + if (declaration->fromClause) + module = declaration->fromClause->moduleSpecifier.toString(); + + if (declaration->exportAll) { + Compiler::ExportEntry entry; + entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString(); + entry.importName = QStringLiteral("*"); + _context->exportEntries << entry; + } else if (declaration->exportClause) { + for (ExportsList *it = declaration->exportClause->exportsList; it; it = it->next) { + ExportSpecifier *spec = it->exportSpecifier; + Compiler::ExportEntry entry; + if (module.isEmpty()) + entry.localName = spec->identifier.toString(); + else + entry.importName = spec->identifier.toString(); + + entry.moduleRequest = module; + entry.exportName = spec->exportedIdentifier.toString(); + + _context->exportEntries << entry; + } + } else if (auto *vstmt = AST::cast(declaration->variableStatementOrDeclaration)) { + QStringList boundNames; + for (VariableDeclarationList *it = vstmt->declarations; it; it = it->next) { + if (!it->declaration) + continue; + it->declaration->boundNames(&boundNames); + } + for (const QString &name: boundNames) { + Compiler::ExportEntry entry; + entry.localName = name; + entry.exportName = name; + _context->exportEntries << entry; + } + } else if (auto *classDecl = AST::cast(declaration->variableStatementOrDeclaration)) { + QString name = classDecl->name.toString(); + Compiler::ExportEntry entry; + entry.localName = name; + entry.exportName = name; + _context->exportEntries << entry; + } else if (auto *fdef = declaration->variableStatementOrDeclaration->asFunctionDefinition()) { + QString name = fdef->name.toString(); + Compiler::ExportEntry entry; + entry.localName = name; + entry.exportName = name; + _context->exportEntries << entry; + } else if (declaration->exportDefault) { + Compiler::ExportEntry entry; + entry.localName = QStringLiteral("*default*"); + entry.exportName = QStringLiteral("default"); + _context->exportEntries << entry; + } + + return true; // scan through potential assignment expression code, etc. +} + +bool ScanFunctions::visit(ImportDeclaration *declaration) +{ + QString module; + if (declaration->fromClause) + module = declaration->fromClause->moduleSpecifier.toString(); + + if (ImportClause *import = declaration->importClause) { + if (!import->importedDefaultBinding.isEmpty()) { + Compiler::ImportEntry entry; + entry.moduleRequest = module; + entry.importName = QStringLiteral("default"); + entry.localName = import->importedDefaultBinding.toString(); + _context->importEntries << entry; + } + + if (import->nameSpaceImport) { + Compiler::ImportEntry entry; + entry.moduleRequest = module; + entry.importName = QStringLiteral("*"); + entry.localName = import->nameSpaceImport->importedBinding.toString(); + _context->importEntries << entry; + + _cg->throwSyntaxError(import->nameSpaceImport->importedBindingToken, QStringLiteral("* imports are currently not supported.")); + return false; + } + + if (import->namedImports) { + for (ImportsList *it = import->namedImports->importsList; it; it = it->next) { + Compiler::ImportEntry entry; + entry.moduleRequest = module; + entry.localName = it->importSpecifier->importedBinding.toString(); + if (!it->importSpecifier->identifier.isEmpty()) + entry.importName = it->importSpecifier->identifier.toString(); + else + entry.importName = entry.localName; + _context->importEntries << entry; + } + } + } + return false; +} + bool ScanFunctions::visit(CallExpression *ast) { if (!_context->hasDirectEval) { @@ -529,6 +643,17 @@ void ScanFunctions::calcEscapingVariables() } } + for (Context *c : qAsConst(m->contextMap)) { + if (c->contextType != ContextType::ESModule) + continue; + for (const auto &entry: c->exportEntries) { + auto m = c->members.find(entry.localName); + if (m != c->members.end()) + m->canEscape = true; + } + break; + } + for (Context *inner : qAsConst(m->contextMap)) { for (const QString &var : qAsConst(inner->usedVariables)) { Context *c = inner; @@ -545,6 +670,10 @@ void ScanFunctions::calcEscapingVariables() if (c->parent || it->isLexicallyScoped()) { it->canEscape = true; c->requiresExecutionContext = true; + } else if (c->contextType == ContextType::ESModule) { + // Module instantiation provides a context, but vars used from inner + // scopes need to be stored in its locals[]. + it->canEscape = true; } break; } diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 4c273600b3..53b2336cb1 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -103,6 +103,12 @@ protected: bool visit(AST::Program *ast) override; void endVisit(AST::Program *) override; + bool visit(AST::ESModule *ast) override; + void endVisit(AST::ESModule *) override; + + bool visit(AST::ExportDeclaration *declaration) override; + bool visit(AST::ImportDeclaration *declaration) override; + bool visit(AST::CallExpression *ast) override; bool visit(AST::PatternElement *ast) override; bool visit(AST::IdentifierExpression *ast) override; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 8e474b3783..d186c4e7e6 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -207,6 +207,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals); MOTH_END_INSTR(MoveReg) + MOTH_BEGIN_INSTR(LoadImport) + d << "i" << index; + MOTH_END_INSTR(LoadImport) + MOTH_BEGIN_INSTR(LoadConst) d << "C" << index; MOTH_END_INSTR(LoadConst) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index ce92a31590..25a07208c2 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -76,6 +76,7 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadReg(op) INSTRUCTION(op, LoadReg, 1, reg) #define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) #define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) +#define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index) #define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) #define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) #define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) @@ -210,6 +211,7 @@ QT_BEGIN_NAMESPACE F(LoadReg) \ F(StoreReg) \ F(MoveReg) \ + F(LoadImport) \ F(LoadLocal) \ F(StoreLocal) \ F(LoadScopedLocal) \ diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index efd226539e..3379d9a0e4 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -1509,6 +1509,16 @@ void JIT::Assembler::storeHeapObject(int reg) pasm()->storeHeapObject(PlatformAssembler::ReturnValueRegisterValue, regAddr(reg)); } +void JIT::Assembler::loadImport(int index) +{ + Address addr = pasm()->loadCompilationUnitPtr(PlatformAssembler::ScratchRegister); + addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, imports); + pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister); + addr.offset = index * int(sizeof(QV4::Value*)); + pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister); + pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister)); +} + void Assembler::toNumber() { pasm()->toNumber(); diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 2cf59f53ee..1379c72f9a 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -97,6 +97,7 @@ public: void loadString(int stringId); void loadValue(ReturnedValue value); void storeHeapObject(int reg); + void loadImport(int index); // numeric ops void unot(); diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 7200e44f0c..bf442de741 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -140,6 +140,11 @@ void BaselineJIT::generate_MoveReg(int srcReg, int destReg) as->moveReg(srcReg, destReg); } +void BaselineJIT::generate_LoadImport(int index) +{ + as->loadImport(index); +} + void BaselineJIT::generate_LoadLocal(int index) { as->loadLocal(index); diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index d96fd6ea6a..71b9dda9b9 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -87,6 +87,7 @@ public: void generate_LoadReg(int reg) override; void generate_StoreReg(int reg) override; void generate_MoveReg(int srcReg, int destReg) override; + void generate_LoadImport(int index) override; void generate_LoadLocal(int index) override; void generate_StoreLocal(int index) override; void generate_LoadScopedLocal(int scope, int index) override; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index ec5803b2df..c42c2d48c8 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -55,7 +55,8 @@ SOURCES += \ $$PWD/qv4vme_moth.cpp \ $$PWD/qv4mapobject.cpp \ $$PWD/qv4mapiterator.cpp \ - $$PWD/qv4estable.cpp + $$PWD/qv4estable.cpp \ + $$PWD/qv4module.cpp qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp @@ -123,7 +124,8 @@ HEADERS += \ $$PWD/qv4mapobject_p.h \ $$PWD/qv4mapiterator_p.h \ $$PWD/qv4estable_p.h \ - $$PWD/qv4vtable_p.h + $$PWD/qv4vtable_p.h \ + $$PWD/qv4module_p.h qtConfig(qml-sequence-object) { HEADERS += \ diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index fcc2feced4..69b23484a8 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -75,6 +75,10 @@ #include "qv4reflect_p.h" #include "qv4proxy_p.h" #include "qv4stackframe_p.h" +#include +#include +#include +#include #if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" @@ -92,12 +96,16 @@ #include #include #include +#include #if QT_CONFIG(qml_locale) #include #endif +#include #include #include +#include +#include #if USE(PTHREADS) # include @@ -593,6 +601,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ExecutionEngine::~ExecutionEngine() { + modules.clear(); delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = nullptr; delete identifierTable; @@ -1621,6 +1630,96 @@ ReturnedValue ExecutionEngine::global() return globalObject->asReturnedValue(); } +QQmlRefPointer ExecutionEngine::compileModule(const QUrl &url) +{ + QFile f(QQmlFile::urlToLocalFileOrQrc(url)); + if (!f.open(QIODevice::ReadOnly)) + return nullptr; + + const QString sourceCode = QString::fromUtf8(f.readAll()); + f.close(); + + return compileModule(url, sourceCode); +} + + +QQmlRefPointer ExecutionEngine::compileModule(const QUrl &url, const QString &sourceCode) +{ + QList diagnostics; + auto unit = compileModule(/*debugMode*/debugger() != nullptr, url, sourceCode, &diagnostics); + for (const QQmlJS::DiagnosticMessage &m : diagnostics) { + if (m.isError()) { + throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn); + return nullptr; + } else { + qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << m.message; + } + } + return unit; +} + +QQmlRefPointer ExecutionEngine::compileModule(bool debugMode, const QUrl &url, const QString &sourceCode, QList *diagnostics) +{ + QQmlJS::Engine ee; + QQmlJS::Lexer lexer(&ee); + lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false); + QQmlJS::Parser parser(&ee); + + const bool parsed = parser.parseModule(); + + if (diagnostics) + *diagnostics = parser.diagnosticMessages(); + + if (!parsed) + return nullptr; + + QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast(parser.rootNode()); + if (!moduleNode) { + // if parsing was successful, and we have no module, then + // the file was empty. + if (diagnostics) + diagnostics->clear(); + return nullptr; + } + + using namespace QV4::Compiler; + Compiler::Module compilerModule(debugMode); + JSUnitGenerator jsGenerator(&compilerModule); + Codegen cg(&jsGenerator, /*strictMode*/true); + cg.generateFromModule(url.fileName(), url.toString(), sourceCode, moduleNode, &compilerModule); + auto errors = cg.errors(); + if (diagnostics) + *diagnostics << errors; + + if (!errors.isEmpty()) + return nullptr; + + return cg.generateCompilationUnit(); +} + +void ExecutionEngine::injectModule(const QQmlRefPointer &moduleUnit) +{ + modules.insert(moduleUnit->finalUrl(), moduleUnit); +} + +QQmlRefPointer ExecutionEngine::loadModule(const QUrl &_url, CompiledData::CompilationUnit *referrer) +{ + QUrl url = QQmlTypeLoader::normalize(_url); + if (referrer) + url = referrer->finalUrl().resolved(url); + + auto existingModule = modules.find(url); + if (existingModule != modules.end()) + return *existingModule; + + auto newModule = compileModule(url); + if (newModule) + modules.insert(url, newModule); + + return newModule; +} + // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index e1ea89c699..e605be9901 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -86,6 +86,10 @@ namespace CompiledData { struct CompilationUnit; } +namespace Heap { +struct Module; +}; + struct Function; @@ -556,6 +560,17 @@ public: QV4::ReturnedValue global(); double localTZA = 0.0; // local timezone, initialized at startup + +#ifndef V4_BOOTSTRAP + QQmlRefPointer compileModule(const QUrl &url); + QQmlRefPointer compileModule(const QUrl &url, const QString &sourceCode); + static QQmlRefPointer compileModule(bool debugMode, const QUrl &url, const QString &sourceCode, QList *diagnostics); + + QHash> modules; + void injectModule(const QQmlRefPointer &moduleUnit); + QQmlRefPointer loadModule(const QUrl &_url, CompiledData::CompilationUnit *referrer = nullptr); +#endif + private: #if QT_CONFIG(qml_debug) QScopedPointer m_debugger; diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp new file mode 100644 index 0000000000..ed7df459b6 --- /dev/null +++ b/src/qml/jsruntime/qv4module.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4module_p.h" + +#include +#include +#include + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(Module); + +void Heap::Module::init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit) +{ + Object::init(); + + // This is a back pointer and there is no need to call addref() on the unit, because the unit + // owns this object instead. + unit = moduleUnit; + + Function *moduleFunction = unit->runtimeFunctions[unit->unitData()->indexOfRootFunction]; + + const uint locals = moduleFunction->compiledFunction->nLocals; + const size_t requiredMemory = sizeof(QV4::CallContext::Data) - sizeof(Value) + sizeof(Value) * locals; + scope.set(engine, engine->memoryManager->allocManaged(requiredMemory, moduleFunction->internalClass)); + scope->init(); + scope->outer.set(engine, engine->rootContext()->d()); + scope->locals.size = locals; + scope->locals.alloc = locals; + scope->nArgs = 0; +} diff --git a/src/qml/jsruntime/qv4module_p.h b/src/qml/jsruntime/qv4module_p.h new file mode 100644 index 0000000000..1958258ef0 --- /dev/null +++ b/src/qml/jsruntime/qv4module_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4MODULE +#define QV4MODULE + +// +// 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 "qv4object_p.h" +#include "qv4context_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define ModuleMembers(class, Member) \ + Member(class, NoMark, CompiledData::CompilationUnit *, unit) \ + Member(class, Pointer, CallContext *, scope) + +DECLARE_EXPORTED_HEAP_OBJECT(Module, Object) { + DECLARE_MARKOBJECTS(Module) + + void init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit); +}; + +} + +struct Q_QML_EXPORT Module : public Object { + V4_OBJECT2(Module, Object) +}; + +} + +QT_END_NAMESPACE + +#endif // QV4MODULE diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 53e5632eff..575cad70e4 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -503,6 +503,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STACK_VALUE(destReg) = STACK_VALUE(srcReg); MOTH_END_INSTR(MoveReg) + MOTH_BEGIN_INSTR(LoadImport) + acc = function->compilationUnit->imports[index]->asReturnedValue(); + MOTH_END_INSTR(LoadImport) + MOTH_BEGIN_INSTR(LoadLocal) auto cc = static_cast(stack[CallData::Context].m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index f43956814a..6b315c9140 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -1166,6 +1166,10 @@ void ModuleItemList::accept0(Visitor *visitor) { if (visitor->visit(this)) { for (ModuleItemList *it = this; it; it = it->next) { + // The statement list is concatenated together between module list + // items and stored in the ESModule, thus not visited from there. + if (it->item && it->item->kind == Kind_StatementList) + continue; accept(it->item, visitor); } } @@ -1173,10 +1177,29 @@ void ModuleItemList::accept0(Visitor *visitor) visitor->endVisit(this); } +StatementList *ModuleItemList::buildStatementList() const +{ + StatementList *statements = nullptr; + for (const ModuleItemList *item = this; item; item = item->next) { + AST::StatementList *listItem = AST::cast(item->item); + if (!listItem) + continue; + if (statements) + statements = statements->append(listItem); + else + statements = listItem; + } + if (statements) + statements = statements->finish(); + return statements; +} + + void ESModule::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(body, visitor); + accept(statements, visitor); } visitor->endVisit(this); diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 4d161e9e51..0729c99931 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -2753,6 +2753,8 @@ public: return head; } + StatementList *buildStatementList() const; + void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override @@ -2773,7 +2775,11 @@ public: ESModule(ModuleItemList *body) : body(body) - { kind = K; } + { + kind = K; + if (body) + statements = body->buildStatementList(); + } void accept0(Visitor *visitor) override; @@ -2785,6 +2791,7 @@ public: // attributes ModuleItemList *body; + StatementList *statements = nullptr; }; class QML_PARSER_EXPORT DebuggerStatement: public Statement -- cgit v1.2.3