aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-09-29 21:20:09 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-30 18:21:26 +0200
commitc48d727e25f0a07d709a81765af6196bc0ddb4c5 (patch)
tree1ad34d61bf20e8756d52f44fd17956844a793d84 /src/qml
parent2b6dfcf23b8e9d672dff0a083ed4e7adc28115eb (diff)
Compile imported scripts in the loader thread
This has the benefit of blocking the GUI thread less and speeding up type creation in the GUI thread (for types that import js libraries). This patch also brings one behavioral change: Due to the parsing at type instantiation type, things like syntax errors for script imports would only generate a run-time warning and the code in the QML file would just see "undefined". Errors in the script now generate real errors at component compilation time, meaning the errors come out earlier and as real errors. This patch implements the separation for the VME only (to keep the size of this patch small). Change-Id: I82f7f3a2d3d4524ea12a7ab62abd8640aba6a47f Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/compiler/qv4codegen.cpp5
-rw-r--r--src/qml/compiler/qv4codegen_p.h3
-rw-r--r--src/qml/jsruntime/qv4script.cpp95
-rw-r--r--src/qml/jsruntime/qv4script_p.h6
-rw-r--r--src/qml/qml/qqmltypeloader.cpp24
-rw-r--r--src/qml/qml/qqmltypeloader_p.h7
-rw-r--r--src/qml/qml/qqmlvme.cpp32
7 files changed, 133 insertions, 39 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 0f60bdc9f3..43756a6f35 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -2577,6 +2577,11 @@ void Codegen::throwReferenceError(const SourceLocation &loc, const QString &deta
_errors << error;
}
+QList<QQmlError> Codegen::errors() const
+{
+ return _errors;
+}
+
void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail)
{
context->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn);
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index d03f91b654..ea32b5b349 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -414,7 +414,8 @@ protected:
virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
- QList<QQmlError> errors();
+public:
+ QList<QQmlError> errors() const;
protected:
Result _expr;
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index c2d35d5926..2350224c32 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -142,6 +142,22 @@ struct CompilationUnitHolder : public QV4::Object
DEFINE_MANAGED_VTABLE(CompilationUnitHolder);
+Script::Script(ExecutionEngine *v4, ObjectRef qml, CompiledData::CompilationUnit *compilationUnit)
+ : line(0), column(0), scope(v4->rootContext), strictMode(false), inheritContext(true), parsed(false)
+ , qml(qml.asReturnedValue()), vmFunction(0), parseAsBinding(true)
+{
+ parsed = true;
+
+ if (compilationUnit) {
+ vmFunction = compilationUnit->linkToEngine(v4);
+ Q_ASSERT(vmFunction);
+ Scope valueScope(v4);
+ ScopedValue holder(valueScope, Value::fromObject(new (v4->memoryManager) CompilationUnitHolder(v4, compilationUnit)));
+ compilationUnitHolder = holder;
+ } else
+ vmFunction = 0;
+}
+
Script::~Script()
{
}
@@ -264,6 +280,85 @@ Function *Script::function()
return vmFunction;
}
+struct PrecompilingCodeGen : public QQmlJS::Codegen
+{
+ struct CompileError {};
+
+ PrecompilingCodeGen(bool strict)
+ : QQmlJS::Codegen(strict)
+ {}
+
+ virtual void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail)
+ {
+ QQmlJS::Codegen::throwSyntaxError(loc, detail);
+ throw CompileError();
+ }
+
+ virtual void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail)
+ {
+ QQmlJS::Codegen::throwReferenceError(loc, detail);
+ throw CompileError();
+ }
+};
+
+CompiledData::CompilationUnit *Script::precompile(ExecutionEngine *engine, const QUrl &url, const QString &source, bool parseAsBinding, QList<QQmlError> *reportedErrors)
+{
+ using namespace QQmlJS;
+ using namespace QQmlJS::AST;
+
+ QQmlJS::V4IR::Module module;
+
+ QQmlJS::Engine ee;
+ QQmlJS::Lexer lexer(&ee);
+ lexer.setCode(source, /*line*/1, /*qml mode*/true);
+ QQmlJS::Parser parser(&ee);
+
+ parser.parseProgram();
+
+ QList<QQmlError> errors;
+
+ foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) {
+ if (m.isWarning()) {
+ qWarning("%s:%d : %s", qPrintable(url.toString()), m.loc.startLine, qPrintable(m.message));
+ continue;
+ }
+
+ QQmlError error;
+ error.setUrl(url);
+ error.setDescription(m.message);
+ error.setLine(m.loc.startLine);
+ error.setColumn(m.loc.startColumn);
+ errors << error;
+ }
+
+ if (!errors.isEmpty()) {
+ if (reportedErrors)
+ *reportedErrors << errors;
+ return 0;
+ }
+
+ Program *program = AST::cast<Program *>(parser.rootNode());
+ if (!program) {
+ // if parsing was successful, and we have no program, then
+ // we're done...:
+ return 0;
+ }
+
+ PrecompilingCodeGen cg(/*strict mode*/false);
+ try {
+ cg.generateFromProgram(url.toString(), source, program, &module, parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::GlobalCode);
+ } catch (const PrecompilingCodeGen::CompileError &) {
+ if (reportedErrors)
+ *reportedErrors << cg.errors();
+ return 0;
+ }
+
+ Compiler::JSUnitGenerator jsGenerator(&module);
+ QScopedPointer<QQmlJS::EvalInstructionSelection> isel(engine->iselFactory->create(engine->executableAllocator, &module, &jsGenerator));
+ isel->setUseFastLookups(false);
+ return isel->compile();
+}
+
ReturnedValue Script::qmlBinding()
{
if (!parsed)
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index 5442e265fd..52ad4dd78c 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -45,6 +45,8 @@
#include "qv4engine_p.h"
#include "qv4functionobject_p.h"
+#include <QQmlError>
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -77,6 +79,7 @@ struct Q_QML_EXPORT Script {
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
, scope(engine->rootContext), strictMode(false), inheritContext(true), parsed(false)
, qml(qml.asReturnedValue()), vmFunction(0), parseAsBinding(true) {}
+ Script(ExecutionEngine *engine, ObjectRef qml, CompiledData::CompilationUnit *compilationUnit);
~Script();
QString sourceFile;
int line;
@@ -97,6 +100,9 @@ struct Q_QML_EXPORT Script {
Function *function();
+ static CompiledData::CompilationUnit *precompile(ExecutionEngine *engine, const QUrl &url, const QString &source,
+ bool parseAsBinding,
+ QList<QQmlError> *reportedErrors = 0);
static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, ObjectRef scopeObject);
};
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 1bd3ba25d5..07639aa6ba 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -2604,13 +2604,21 @@ void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Locati
}
QQmlScriptData::QQmlScriptData()
-: importCache(0), pragmas(QQmlScript::Object::ScriptBlock::None), m_loaded(false), m_program(0)
+ : importCache(0)
+ , pragmas(QQmlScript::Object::ScriptBlock::None)
+ , m_loaded(false)
+ , m_program(0)
+ , m_precompiledScript(0)
{
}
QQmlScriptData::~QQmlScriptData()
{
delete m_program;
+ if (m_precompiledScript) {
+ m_precompiledScript->deref();
+ m_precompiledScript = 0;
+ }
}
void QQmlScriptData::clear()
@@ -2663,7 +2671,8 @@ void QQmlScriptBlob::dataReceived(const Data &data)
m_metadata = QQmlScript::Parser::extractMetaData(m_source, &metaDataError);
if (metaDataError.isValid()) {
metaDataError.setUrl(finalUrl());
- m_scriptData->setError(metaDataError);
+ setError(metaDataError);
+ return;
}
m_imports.setBaseUrl(finalUrl(), finalUrlString());
@@ -2726,8 +2735,17 @@ void QQmlScriptBlob::done()
m_imports.populateCache(m_scriptData->importCache);
m_scriptData->pragmas = m_metadata.pragmas;
- m_scriptData->m_programSource = m_source.toUtf8();
+
+ QList<QQmlError> errors;
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine());
+ m_scriptData->m_precompiledScript = QV4::Script::precompile(v4, m_scriptData->url, m_source, /*parseAsBinding*/true, &errors);
+ if (m_scriptData->m_precompiledScript)
+ m_scriptData->m_precompiledScript->ref();
m_source.clear();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
}
void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace)
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index be1ffba09e..f6eea6c248 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -511,10 +511,6 @@ public:
bool isInitialized() const { return hasEngine(); }
void initialize(QQmlEngine *);
- bool hasError() const { return m_error.isValid(); }
- void setError(const QQmlError &error) { m_error = error; }
- QQmlError error() const { return m_error; }
-
protected:
virtual void clear(); // From QQmlCleanup
@@ -523,10 +519,9 @@ private:
friend class QQmlScriptBlob;
bool m_loaded;
- QByteArray m_programSource;
+ QV4::CompiledData::CompilationUnit *m_precompiledScript;
QV4::Script *m_program;
QV4::PersistentValue m_value;
- QQmlError m_error;
};
class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlTypeLoader::Blob
diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp
index f256f3a1ce..5bf679d7a3 100644
--- a/src/qml/qml/qqmlvme.cpp
+++ b/src/qml/qml/qqmlvme.cpp
@@ -1088,18 +1088,7 @@ void QQmlScriptData::initialize(QQmlEngine *engine)
QV8Engine *v8engine = ep->v8engine();
QV4::ExecutionEngine *v4 = QV8Engine::getV4(v8engine);
- // If compilation throws an error, a surrounding catch will record it.
- // pass 0 as the QML object, we set it later before calling run()
- QV4::Script *program = new QV4::Script(v4, QV4::ObjectRef::null(), m_programSource, urlString, 1);
- try {
- program->parse();
- } catch (QV4::Exception &) {
- delete program;
- throw;
- }
-
- m_program = program;
- m_programSource.clear(); // We don't need this anymore
+ m_program = new QV4::Script(v4, QV4::ObjectRef::null(), m_precompiledScript);
addToEngine(engine);
@@ -1119,11 +1108,6 @@ QV4::PersistentValue QQmlVME::run(QQmlContextData *parentCtxt, QQmlScriptData *s
QV4::ExecutionEngine *v4 = QV8Engine::getV4(parentCtxt->engine);
QV4::Scope scope(v4);
- if (script->hasError()) {
- ep->warning(script->error());
- return rv;
- }
-
bool shared = script->pragmas & QQmlScript::Object::ScriptBlock::Shared;
QQmlContextData *effectiveCtxt = parentCtxt;
@@ -1164,18 +1148,8 @@ QV4::PersistentValue QQmlVME::run(QQmlContextData *parentCtxt, QQmlScriptData *s
ctxt->importedScripts << run(ctxt, script->scripts.at(ii)->scriptData());
}
- if (!script->isInitialized()) {
- QV4::ExecutionContext *ctx = QV8Engine::getV4(parentCtxt->engine)->current;
- try {
- script->initialize(parentCtxt->engine);
- } catch (QV4::Exception &e) {
- e.accept(ctx);
- QQmlError error;
- QQmlExpressionPrivate::exceptionToError(e, error);
- if (error.isValid())
- ep->warning(error);
- }
- }
+ if (!script->isInitialized())
+ script->initialize(parentCtxt->engine);
if (!script->m_program) {
if (shared)