aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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