aboutsummaryrefslogtreecommitdiffstats
path: root/src
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
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')
-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
-rw-r--r--src/qml/jit/qv4assembler.cpp10
-rw-r--r--src/qml/jit/qv4assembler_p.h1
-rw-r--r--src/qml/jit/qv4baselinejit.cpp5
-rw-r--r--src/qml/jit/qv4baselinejit_p.h1
-rw-r--r--src/qml/jsruntime/jsruntime.pri6
-rw-r--r--src/qml/jsruntime/qv4engine.cpp99
-rw-r--r--src/qml/jsruntime/qv4engine_p.h15
-rw-r--r--src/qml/jsruntime/qv4module.cpp69
-rw-r--r--src/qml/jsruntime/qv4module_p.h82
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp4
-rw-r--r--src/qml/parser/qqmljsast.cpp23
-rw-r--r--src/qml/parser/qqmljsast_p.h9
25 files changed, 812 insertions, 12 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) \
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 <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+#include <private/qqmljsast_p.h>
#if QT_CONFIG(qml_sequence_object)
#include "qv4sequenceobject_p.h"
@@ -92,12 +96,16 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmllist_p.h>
+#include <private/qqmltypeloader_p.h>
#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
#endif
+#include <qqmlfile.h>
#include <QtCore/QTextStream>
#include <QDateTime>
+#include <QDir>
+#include <QFileInfo>
#if USE(PTHREADS)
# include <pthread.h>
@@ -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<CompiledData::CompilationUnit> 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<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url, const QString &sourceCode)
+{
+ QList<QQmlJS::DiagnosticMessage> 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<CompiledData::CompilationUnit> ExecutionEngine::compileModule(bool debugMode, const QUrl &url, const QString &sourceCode, QList<QQmlJS::DiagnosticMessage> *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<QQmlJS::AST::ESModule*>(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<CompiledData::CompilationUnit> &moduleUnit)
+{
+ modules.insert(moduleUnit->finalUrl(), moduleUnit);
+}
+
+QQmlRefPointer<CompiledData::CompilationUnit> 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<CompiledData::CompilationUnit> compileModule(const QUrl &url);
+ QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url, const QString &sourceCode);
+ static QQmlRefPointer<CompiledData::CompilationUnit> compileModule(bool debugMode, const QUrl &url, const QString &sourceCode, QList<QQmlJS::DiagnosticMessage> *diagnostics);
+
+ QHash<QUrl, QQmlRefPointer<CompiledData::CompilationUnit>> modules;
+ void injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit);
+ QQmlRefPointer<CompiledData::CompilationUnit> loadModule(const QUrl &_url, CompiledData::CompilationUnit *referrer = nullptr);
+#endif
+
private:
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> 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 <private/qv4mm_p.h>
+#include <private/qv4vme_moth_p.h>
+#include <private/qv4context_p.h>
+
+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<QV4::CallContext>(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<Heap::CallContext *>(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<AST::StatementList*>(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