aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-07-10 14:52:34 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-09 13:18:39 +0000
commit82da798499aa8b656e771191332864a703069739 (patch)
tree35cb1d0ef8dd3d949f8b6f6324d19ec577b3f4df /src/qml/compiler
parent6510046ee32ef69d7f250fd1d829063983f93fdd (diff)
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 <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h3
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp3
-rw-r--r--src/qml/compiler/qv4codegen.cpp70
-rw-r--r--src/qml/compiler/qv4codegen_p.h12
-rw-r--r--src/qml/compiler/qv4compileddata.cpp117
-rw-r--r--src/qml/compiler/qv4compileddata_p.h48
-rw-r--r--src/qml/compiler/qv4compiler.cpp62
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp14
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h30
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp129
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h6
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp4
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
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) \