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()
{
}
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)
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");
QTest::newRow("basic functionality")
<< testFileUrl("jsimport/testImport.qml")
+ << true /* compilation should succeed */
<< QString()
<< QStringList()
<< (QStringList() << QLatin1String("importedScriptStringValue")
QTest::newRow("import scoping")
<< testFileUrl("jsimport/testImportScoping.qml")
+ << true /* compilation should succeed */
<< QString()
<< QStringList()
<< (QStringList() << QLatin1String("componentError"))
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"))
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"))
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"))
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"))
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"))
QTest::newRow("import pragma library script")
<< testFileUrl("jsimport/testImportPragmaLibrary.qml")
+ << true /* compilation should succeed */
<< QString()
<< QStringList()
<< (QStringList() << QLatin1String("testValue"))
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"))
QTest::newRow("import pragma library script which has an import")
<< testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml")
+ << true /* compilation should succeed */
<< QString()
<< QStringList()
<< (QStringList() << QLatin1String("testValue"))
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"))
QTest::newRow("import singleton type into js import")
<< testFileUrl("jsimport/testImportSingletonType.qml")
+ << true /* compilation should succeed */
<< QString()
<< QStringList()
<< (QStringList() << QLatin1String("testValue"))
QTest::newRow("import module which exports a script")
<< testFileUrl("jsimport/testJsImport.qml")
+ << true /* compilation should succeed */
<< QString()
<< QStringList()
<< (QStringList() << QLatin1String("importedScriptStringValue")
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")
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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);
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()) {
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);
}