aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4engine.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-07-10 14:52:34 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-09 13:18:39 +0000
commit82da798499aa8b656e771191332864a703069739 (patch)
tree35cb1d0ef8dd3d949f8b6f6324d19ec577b3f4df /src/qml/jsruntime/qv4engine.cpp
parent6510046ee32ef69d7f250fd1d829063983f93fdd (diff)
Add initial basic support for ES6 modules
The entry point from the parsing perspective into modules is not QV4::Script but QV4::ExecutionEngine::compileModule. For convenience, the ESModule AST node gets a body, which is the statement list connected between the ModuleItemList items that are not import/export declarations. The QV4::Module allocates a call context where the exported variables are stored as named locals. This will also become the module namespace object. The imports in turn is an array of value pointers that point into the locals array of the context of the imported modules. The default module loading in ExecutionEngine assumes the accessibility of module urls via QFile (so local file system or resource). This is what qmljs also uses and QJSEngine as well via public API in the future. The test runner compiles the modules manually and injects them, because they need to be compiled together with the test harness code. The QML type loader will the mechanism for injection in the future for module imports from .qml files. Change-Id: I93be9cfe54c651fdbd08c5e1d22d58f47284e54f Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4engine.cpp')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp99
1 files changed, 99 insertions, 0 deletions
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.