Compile imported scripts in the loader thread
authorSimon Hausmann <simon.hausmann@digia.com>
Sun, 29 Sep 2013 19:20:09 +0000 (21:20 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 30 Sep 2013 16:21:26 +0000 (18:21 +0200)
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>
src/qml/compiler/qv4codegen.cpp
src/qml/compiler/qv4codegen_p.h
src/qml/jsruntime/qv4script.cpp
src/qml/jsruntime/qv4script_p.h
src/qml/qml/qqmltypeloader.cpp
src/qml/qml/qqmltypeloader_p.h
src/qml/qml/qqmlvme.cpp
tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp

index 0f60bdc..43756a6 100644 (file)
@@ -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);
index d03f91b..ea32b5b 100644 (file)
@@ -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;
index c2d35d5..2350224 100644 (file)
@@ -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)
index 5442e26..52ad4dd 100644 (file)
@@ -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);
 };
index 1bd3ba2..07639aa 100644 (file)
@@ -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)
index be1ffba..f6eea6c 100644 (file)
@@ -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
index f256f3a..5bf679d 100644 (file)
@@ -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)
index 0ea6a25..5f923c7 100644 (file)
@@ -3932,6 +3932,7 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) {
 void tst_qqmlecmascript::importScripts_data()
 {
     QTest::addColumn<QUrl>("testfile");
+    QTest::addColumn<bool>("compilationShouldSucceed");
     QTest::addColumn<QString>("errorMessage");
     QTest::addColumn<QStringList>("warningMessages");
     QTest::addColumn<QStringList>("propertyNames");
@@ -3939,6 +3940,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("basic functionality")
             << testFileUrl("jsimport/testImport.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("importedScriptStringValue")
@@ -3952,6 +3954,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import scoping")
             << testFileUrl("jsimport/testImportScoping.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("componentError"))
@@ -3959,6 +3962,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("parent scope shouldn't be inherited by import with imports")
             << testFileUrl("jsimportfail/failOne.qml")
+            << true /* compilation should succeed */
             << QString()
             << (QStringList() << QString(testFileUrl("jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
             << (QStringList() << QLatin1String("importScriptFunctionValue"))
@@ -3966,6 +3970,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("javascript imports in an import should be private to the import scope")
             << testFileUrl("jsimportfail/failTwo.qml")
+            << true /* compilation should succeed */
             << QString()
             << (QStringList() << QString(testFileUrl("jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
             << (QStringList() << QLatin1String("importScriptFunctionValue"))
@@ -3973,6 +3978,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("module imports in an import should be private to the import scope")
             << testFileUrl("jsimportfail/failThree.qml")
+            << true /* compilation should succeed */
             << QString()
             << (QStringList() << QString(testFileUrl("jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
             << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
@@ -3980,6 +3986,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("typenames in an import should be private to the import scope")
             << testFileUrl("jsimportfail/failFour.qml")
+            << true /* compilation should succeed */
             << QString()
             << (QStringList() << QString(testFileUrl("jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
             << (QStringList() << QLatin1String("importedModuleEnumValue"))
@@ -3987,6 +3994,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import with imports has it's own activation scope")
             << testFileUrl("jsimportfail/failFive.qml")
+            << true /* compilation should succeed */
             << QString()
             << (QStringList() << QString(testFileUrl("jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
             << (QStringList() << QLatin1String("componentError"))
@@ -3994,6 +4002,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import pragma library script")
             << testFileUrl("jsimport/testImportPragmaLibrary.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("testValue"))
@@ -4001,6 +4010,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
             << testFileUrl("jsimportfail/testImportPragmaLibrary.qml")
+            << true /* compilation should succeed */
             << QString()
             << (QStringList() << QString(testFileUrl("jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
             << (QStringList() << QLatin1String("testValue"))
@@ -4008,6 +4018,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import pragma library script which has an import")
             << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("testValue"))
@@ -4015,6 +4026,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import pragma library script which has a pragma library import")
             << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("testValue"))
@@ -4022,6 +4034,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import singleton type into js import")
             << testFileUrl("jsimport/testImportSingletonType.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("testValue"))
@@ -4029,6 +4042,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import module which exports a script")
             << testFileUrl("jsimport/testJsImport.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("importedScriptStringValue")
@@ -4040,6 +4054,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("import module which exports a script which imports a remote module")
             << testFileUrl("jsimport/testJsRemoteImport.qml")
+            << true /* compilation should succeed */
             << QString()
             << QStringList()
             << (QStringList() << QLatin1String("importedScriptStringValue")
@@ -4051,6 +4066,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed import statement")
             << testFileUrl("jsimportfail/malformedImport.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedImport.js").toString() + QLatin1String(":1:1: Syntax error"))
             << QStringList()
@@ -4058,6 +4074,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed file name")
             << testFileUrl("jsimportfail/malformedFile.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedFile.js").toString() + QLatin1String(":1:9: Imported file must be a script"))
             << QStringList()
@@ -4065,6 +4082,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("missing file qualifier")
             << testFileUrl("jsimportfail/missingFileQualifier.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":1:1: File import requires a qualifier"))
             << QStringList()
@@ -4072,6 +4090,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed file qualifier")
             << testFileUrl("jsimportfail/malformedFileQualifier.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":1:20: File import requires a qualifier"))
             << QStringList()
@@ -4079,6 +4098,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed module qualifier 2")
             << testFileUrl("jsimportfail/malformedFileQualifier.2.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
             << QStringList()
@@ -4086,6 +4106,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed module uri")
             << testFileUrl("jsimportfail/malformedModule.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedModule.js").toString() + QLatin1String(":1:17: Invalid module URI"))
             << QStringList()
@@ -4093,6 +4114,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("missing module version")
             << testFileUrl("jsimportfail/missingModuleVersion.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
             << QStringList()
@@ -4100,6 +4122,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed module version")
             << testFileUrl("jsimportfail/malformedModuleVersion.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
             << QStringList()
@@ -4107,6 +4130,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("missing module qualifier")
             << testFileUrl("jsimportfail/missingModuleQualifier.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":1:1: Module import requires a qualifier"))
             << QStringList()
@@ -4114,6 +4138,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed module qualifier")
             << testFileUrl("jsimportfail/malformedModuleQualifier.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":1:21: Module import requires a qualifier"))
             << QStringList()
@@ -4121,6 +4146,7 @@ void tst_qqmlecmascript::importScripts_data()
 
     QTest::newRow("malformed module qualifier 2")
             << testFileUrl("jsimportfail/malformedModuleQualifier.2.qml")
+            << false /* compilation should succeed */
             << QString()
             << (QStringList() << testFileUrl("jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":1:1: Invalid import qualifier"))
             << QStringList()
@@ -4130,8 +4156,9 @@ void tst_qqmlecmascript::importScripts_data()
 void tst_qqmlecmascript::importScripts()
 {
     QFETCH(QUrl, testfile);
+    QFETCH(bool, compilationShouldSucceed);
     QFETCH(QString, errorMessage);
-    QFETCH(QStringList, warningMessages);
+    QFETCH(QStringList, warningMessages); // error messages if !compilationShouldSucceed
     QFETCH(QStringList, propertyNames);
     QFETCH(QVariantList, propertyValues);
 
@@ -4149,11 +4176,19 @@ void tst_qqmlecmascript::importScripts()
     if (!errorMessage.isEmpty())
         QTest::ignoreMessage(QtWarningMsg, errorMessage.toLatin1().constData());
 
-    if (warningMessages.size())
+    if (compilationShouldSucceed && warningMessages.size())
         foreach (const QString &warning, warningMessages)
             QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
 
-    QTRY_VERIFY(component.isReady());
+    if (compilationShouldSucceed)
+        QTRY_VERIFY(component.isReady());
+    else {
+        QVERIFY(component.isError());
+        QCOMPARE(warningMessages.size(), 1);
+        QCOMPARE(component.errors().count(), 2);
+        QCOMPARE(component.errors().at(1).toString(), warningMessages.first());
+        return;
+    }
 
     QObject *object = component.create();
     if (!errorMessage.isEmpty()) {
@@ -6653,26 +6688,12 @@ void tst_qqmlecmascript::qtbug_22843()
     fileName += QLatin1String(".qml");
 
     QQmlComponent component(&engine, testFileUrl(fileName));
+
     QString url = component.url().toString();
-    QString warning1 = url.left(url.length()-3) + QLatin1String("js:4:16: Expected token `;'");
-    QString warning2 = url + QLatin1String(":5: TypeError: Cannot call method 'func' of undefined");
+    QString expectedError = url.left(url.length()-3) + QLatin1String("js:4:16: Expected token `;'");
 
-    qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>");
-    QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
-    for (int x = 0; x < 3; ++x) {
-        warningsSpy.clear();
-        // For libraries, only the first import attempt should produce a
-        // SyntaxError warning; subsequent component creation should not
-        // attempt to reload the script.
-        bool expectSyntaxError = !library || (x == 0);
-        if (expectSyntaxError)
-            QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
-        QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
-        QObject *object = component.create();
-        QVERIFY(object != 0);
-        QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
-        delete object;
-    }
+    QVERIFY(component.isError());
+    QCOMPARE(component.errors().value(1).toString(), expectedError);
 }