diff options
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/qv4bytecodegenerator_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 70 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 12 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 117 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 48 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 62 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext.cpp | 14 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext_p.h | 30 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions.cpp | 129 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions_p.h | 6 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 4 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 2 |
13 files changed, 491 insertions, 9 deletions
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<int> 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 <private/qqmltypeloader_p.h> #include <private/qqmlengine_p.h> #include <private/qv4vme_moth_p.h> +#include <private/qv4module_p.h> #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> @@ -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<Module>(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<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable); } + + const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); } + const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); } + const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); } + const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); } }; -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<CompilationUnitBase>::value); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *)); struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase { @@ -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<Compiler::ExportEntry> &table, quint32_le offset) { + CompiledData::ExportEntry *entryToWrite = reinterpret_cast<CompiledData::ExportEntry *>(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<CompiledData::ImportEntry *>(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<Method> 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<ExportEntry> localExportEntries; + QVector<ExportEntry> indirectExportEntries; + QVector<ExportEntry> starExportEntries; + QVector<ImportEntry> importEntries; }; @@ -153,6 +176,8 @@ struct Context { QQmlJS::AST::FormalParameterList *formals = nullptr; QStringList arguments; QStringList locals; + QVector<ImportEntry> importEntries; + QVector<ExportEntry> exportEntries; QVector<Context *> 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<AST::VariableStatement*>(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<AST::ClassDeclaration*>(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) \ |