From d406a899943c6d56d921bc290a009415a3c4eba5 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Sun, 29 Nov 2009 20:46:00 +1000 Subject: Support array-literal Script::source values --- src/declarative/qml/qmlcompiler.cpp | 54 ++++++++++++++-------- src/declarative/qml/qmlcompiler_p.h | 1 + src/declarative/qml/qmlcontext.cpp | 15 +++--- src/declarative/qml/qmlcontext_p.h | 3 +- src/declarative/qml/qmlinstruction.cpp | 2 +- src/declarative/qml/qmlinstruction_p.h | 2 - src/declarative/qml/qmlparser.cpp | 54 ++++++++++++++++++++++ src/declarative/qml/qmlparser_p.h | 11 +++-- src/declarative/qml/qmlscriptparser.cpp | 8 ++-- src/declarative/qml/qmlvme.cpp | 7 +-- .../qmlecmascript/data/externalScript.1.qml | 11 +++++ .../qmlecmascript/data/externalScript.2.js | 8 ++++ .../qmlecmascript/data/externalScript.2.qml | 11 +++++ .../qmlecmascript/data/externalScript.3.qml | 13 ++++++ .../qmlecmascript/data/externalScript.4.qml | 15 ++++++ .../qmlecmascript/data/externalScript.js | 6 +++ .../qmlecmascript/tst_qmlecmascript.cpp | 48 +++++++++++++++++++ 17 files changed, 227 insertions(+), 42 deletions(-) create mode 100644 tests/auto/declarative/qmlecmascript/data/externalScript.1.qml create mode 100644 tests/auto/declarative/qmlecmascript/data/externalScript.2.js create mode 100644 tests/auto/declarative/qmlecmascript/data/externalScript.2.qml create mode 100644 tests/auto/declarative/qmlecmascript/data/externalScript.3.qml create mode 100644 tests/auto/declarative/qmlecmascript/data/externalScript.4.qml create mode 100644 tests/auto/declarative/qmlecmascript/data/externalScript.js diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 9000339..a4e14b2 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -846,13 +846,13 @@ void QmlCompiler::genObject(QmlParser::Object *obj) } // Set any script blocks - for (int ii = 0; ii < obj->scriptBlocks.count(); ++ii) { + for (int ii = 0; ii < obj->scripts.count(); ++ii) { QmlInstruction script; script.type = QmlInstruction::StoreScript; script.line = 0; // ### - script.storeScript.fileName = output->indexForString(obj->scriptBlocksFile.at(ii)); - script.storeScript.lineNumber = obj->scriptBlocksLineNumber.at(ii); - script.storeScript.value = output->indexForString(obj->scriptBlocks.at(ii)); + int idx = output->scripts.count(); + output->scripts << obj->scripts.at(ii); + script.storeScript.value = idx; output->bytecode << script; } @@ -1086,9 +1086,7 @@ bool QmlCompiler::buildComponent(QmlParser::Object *obj, bool QmlCompiler::buildScript(QmlParser::Object *obj, QmlParser::Object *script) { - QString scriptCode; - QString sourceUrl; - int lineNumber = 1; + Object::ScriptBlock scriptBlock; if (script->properties.count() == 1 && script->properties.begin().key() == QByteArray("source")) { @@ -1098,22 +1096,37 @@ bool QmlCompiler::buildScript(QmlParser::Object *obj, QmlParser::Object *script) COMPILE_EXCEPTION(source, qApp->translate("QmlCompiler","Invalid Script block. Specify either the source property or inline script")); if (source->value || source->values.count() != 1 || - source->values.at(0)->object || !source->values.at(0)->value.isString()) + source->values.at(0)->object || !source->values.at(0)->value.isStringList()) COMPILE_EXCEPTION(source, qApp->translate("QmlCompiler","Invalid Script source value")); - sourceUrl = output->url.resolved(QUrl(source->values.at(0)->value.asString())).toString(); + QStringList sources = source->values.at(0)->value.asStringList(); - for (int ii = 0; ii < unit->resources.count(); ++ii) { - if (unit->resources.at(ii)->url == sourceUrl) { - scriptCode = QString::fromUtf8(unit->resources.at(ii)->data); - break; + for (int jj = 0; jj < sources.count(); ++jj) { + QString sourceUrl = output->url.resolved(QUrl(sources.at(jj))).toString(); + QString scriptCode; + int lineNumber = 1; + + for (int ii = 0; ii < unit->resources.count(); ++ii) { + if (unit->resources.at(ii)->url == sourceUrl) { + scriptCode = QString::fromUtf8(unit->resources.at(ii)->data); + break; + } + } + + if (!scriptCode.isEmpty()) { + scriptBlock.codes.append(scriptCode); + scriptBlock.files.append(sourceUrl); + scriptBlock.lineNumbers.append(lineNumber); } } } else if (!script->properties.isEmpty()) { COMPILE_EXCEPTION(*script->properties.begin(), qApp->translate("QmlCompiler","Properties cannot be set on Script block")); } else if (script->defaultProperty) { - sourceUrl = output->url.toString(); + + QString scriptCode; + int lineNumber = 1; + QString sourceUrl = output->url.toString(); QmlParser::Location currentLocation; @@ -1141,14 +1154,17 @@ bool QmlCompiler::buildScript(QmlParser::Object *obj, QmlParser::Object *script) currentLocation = v->location.end; currentLocation.column++; } - } - if (!scriptCode.isEmpty()) { - obj->scriptBlocks.append(scriptCode); - obj->scriptBlocksFile.append(sourceUrl); - obj->scriptBlocksLineNumber.append(lineNumber); + if (!scriptCode.isEmpty()) { + scriptBlock.codes.append(scriptCode); + scriptBlock.files.append(sourceUrl); + scriptBlock.lineNumbers.append(lineNumber); + } } + if (!scriptBlock.codes.isEmpty()) + obj->scripts << scriptBlock; + return true; } diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index 9597753..b54a62a 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -116,6 +116,7 @@ public: QList programs; QList propertyCaches; QList contextCaches; + QList scripts; void dumpInstructions(); private: diff --git a/src/declarative/qml/qmlcontext.cpp b/src/declarative/qml/qmlcontext.cpp index 42f467b..38cbcfa 100644 --- a/src/declarative/qml/qmlcontext.cpp +++ b/src/declarative/qml/qmlcontext.cpp @@ -59,8 +59,7 @@ QmlContextPrivate::QmlContextPrivate() { } -void QmlContextPrivate::addScript(const QString &script, QObject *scopeObject, - const QString &fileName, int lineNumber) +void QmlContextPrivate::addScript(const QmlParser::Object::ScriptBlock &script, QObject *scopeObject) { Q_Q(QmlContext); @@ -76,12 +75,14 @@ void QmlContextPrivate::addScript(const QString &script, QObject *scopeObject, QScriptValue scope = scriptEngine->newObject(); scriptContext->setActivationObject(scope); - QScriptValue val = scriptEngine->evaluate(script, fileName, lineNumber); + for (int ii = 0; ii < script.codes.count(); ++ii) { + scriptEngine->evaluate(script.codes.at(ii), script.files.at(ii), script.lineNumbers.at(ii)); - if (scriptEngine->hasUncaughtException()) { - QmlError error; - QmlExpressionPrivate::exceptionToError(scriptEngine, error); - qWarning().nospace() << qPrintable(error.toString()); + if (scriptEngine->hasUncaughtException()) { + QmlError error; + QmlExpressionPrivate::exceptionToError(scriptEngine, error); + qWarning().nospace() << qPrintable(error.toString()); + } } scriptEngine->popContext(); diff --git a/src/declarative/qml/qmlcontext_p.h b/src/declarative/qml/qmlcontext_p.h index a9c25a3..c8d0b2d 100644 --- a/src/declarative/qml/qmlcontext_p.h +++ b/src/declarative/qml/qmlcontext_p.h @@ -92,8 +92,7 @@ public: int highPriorityCount; QList scripts; - void addScript(const QString &script, QObject *scope, - const QString &fileName = QString(), int lineNumber = 1); + void addScript(const QmlParser::Object::ScriptBlock &, QObject *); QUrl url; diff --git a/src/declarative/qml/qmlinstruction.cpp b/src/declarative/qml/qmlinstruction.cpp index 6bab1c4..65d070c 100644 --- a/src/declarative/qml/qmlinstruction.cpp +++ b/src/declarative/qml/qmlinstruction.cpp @@ -140,7 +140,7 @@ void QmlCompiledData::dump(QmlInstruction *instr, int idx) qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SIGNAL\t\t" << instr->storeSignal.signalIndex << "\t" << instr->storeSignal.value << "\t\t" << primitives.at(instr->storeSignal.value); break; case QmlInstruction::StoreScript: - qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SCRIPT\t\t" << instr->storeScript.value << "\t" << instr->storeScript.fileName << "\t" << instr->storeScript.lineNumber; + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SCRIPT\t\t" << instr->storeScript.value; break; case QmlInstruction::StoreScriptString: qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope; diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index 50d4b62..a5fc40c 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -252,8 +252,6 @@ public: } storeScriptString; struct { int value; - int fileName; - int lineNumber; } storeScript; struct { int propertyIndex; diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index ee69b14..402c93e 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -54,10 +54,13 @@ #include "private/qmetaobjectbuilder_p.h" #include #include +#include "parser/qmljsast_p.h" +#include "parser/qmljsengine_p.h" #include QT_BEGIN_NAMESPACE +using namespace QmlJS; using namespace QmlParser; QmlParser::Object::Object() @@ -327,4 +330,55 @@ QmlJS::AST::Node *QmlParser::Variant::asAST() const return 0; } +bool QmlParser::Variant::isStringList() const +{ + if (isString()) + return true; + + if (type() != Script || !n) + return false; + + AST::ArrayLiteral *array = AST::cast(n); + if (!array) + return false; + + AST::ElementList *elements = array->elements; + + while (elements) { + + if (!AST::cast(elements->expression)) + return false; + + elements = elements->next; + } + + return true; +} + +QStringList QmlParser::Variant::asStringList() const +{ + QStringList rv; + if (isString()) { + rv << asString(); + return rv; + } + + AST::ArrayLiteral *array = AST::cast(n); + if (!array) + return rv; + + AST::ElementList *elements = array->elements; + while (elements) { + + AST::StringLiteral *string = AST::cast(elements->expression); + if (!string) + return QStringList(); + rv.append(string->value->asString()); + + elements = elements->next; + } + + return rv; +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index 2ece51f..73bb498 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -169,9 +169,12 @@ namespace QmlParser QList > scriptStringProperties; // Script blocks that were nested under this object - QStringList scriptBlocks; - QStringList scriptBlocksFile; - QList scriptBlocksLineNumber; + struct ScriptBlock { + QStringList codes; + QStringList files; + QList lineNumbers; + }; + QList scripts; // The bytes to cast instances by to get to the QmlParserStatus // interface. -1 indicates the type doesn't support this interface. @@ -243,12 +246,14 @@ namespace QmlParser bool isNumber() const { return type() == Number; } bool isString() const { return type() == String; } bool isScript() const { return type() == Script; } + bool isStringList() const; bool asBoolean() const; QString asString() const; double asNumber() const; QString asScript() const; QmlJS::AST::Node *asAST() const; + QStringList asStringList() const; private: Type t; diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp index b622c24..23c050c 100644 --- a/src/declarative/qml/qmlscriptparser.cpp +++ b/src/declarative/qml/qmlscriptparser.cpp @@ -383,10 +383,12 @@ Object *ProcessAST::defineObjectBinding(AST::UiQualifiedId *qualifiedId, QString propertyName = asString(scriptBinding->qualifiedId); if (propertyName == QLatin1String("source")) { if (AST::ExpressionStatement *stmt = AST::cast(scriptBinding->statement)) { - AST::StringLiteral *string = AST::cast(stmt->expression); - if (string) { + QmlParser::Variant string = getVariant(stmt->expression); + if (string.isStringList()) { + QStringList urls = string.asStringList(); // We need to add this as a resource - _parser->_refUrls << QUrl(string->value->asString()); + for (int ii = 0; ii < urls.count(); ++ii) + _parser->_refUrls << QUrl(urls.at(ii)); } } } diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index da09288..f2fb217 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -142,7 +142,7 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, const QList &intData = comp->intData; const QList &floatData = comp->floatData; const QList &propertyCaches = comp->propertyCaches; - + const QList &scripts = comp->scripts; QmlEnginePrivate::SimpleList bindValues; QmlEnginePrivate::SimpleList parserStatus; @@ -224,7 +224,6 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, case QmlInstruction::SetId: { QObject *target = stack.top(); -// ctxt->setContextProperty(primitives.at(instr.setId.value), target); cp->setIdProperty(instr.setId.index, target); } break; @@ -551,9 +550,7 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, case QmlInstruction::StoreScript: { QObject *target = stack.top(); - cp->addScript(primitives.at(instr.storeScript.value), target, - primitives.at(instr.storeScript.fileName), - instr.storeScript.lineNumber); + cp->addScript(scripts.at(instr.storeScript.value), target); } break; diff --git a/tests/auto/declarative/qmlecmascript/data/externalScript.1.qml b/tests/auto/declarative/qmlecmascript/data/externalScript.1.qml new file mode 100644 index 0000000..2ac7b6e --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/externalScript.1.qml @@ -0,0 +1,11 @@ +import Qt 4.6 + +QtObject { + property int test: external_script_func(); + + Script { + // Single source as non-array literal + source: "externalScript.js" + } +} + diff --git a/tests/auto/declarative/qmlecmascript/data/externalScript.2.js b/tests/auto/declarative/qmlecmascript/data/externalScript.2.js new file mode 100644 index 0000000..78c3a86 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/externalScript.2.js @@ -0,0 +1,8 @@ +function external_script_func2() { + return a; +} + +function is_a_undefined() { + return a == undefined; +} + diff --git a/tests/auto/declarative/qmlecmascript/data/externalScript.2.qml b/tests/auto/declarative/qmlecmascript/data/externalScript.2.qml new file mode 100644 index 0000000..dec657c --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/externalScript.2.qml @@ -0,0 +1,11 @@ +import Qt 4.6 + +QtObject { + property int test: external_script_func(); + + Script { + // Single source as array + source: [ "externalScript.js" ] + } +} + diff --git a/tests/auto/declarative/qmlecmascript/data/externalScript.3.qml b/tests/auto/declarative/qmlecmascript/data/externalScript.3.qml new file mode 100644 index 0000000..d7acf38 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/externalScript.3.qml @@ -0,0 +1,13 @@ +import Qt 4.6 + +QtObject { + property int test: external_script_func(); + property int test2: external_script_func2(); + property bool test3: is_a_undefined(); + + Script { + // Multiple script + source: [ "externalScript.js", "externalScript.2.js" ] + } +} + diff --git a/tests/auto/declarative/qmlecmascript/data/externalScript.4.qml b/tests/auto/declarative/qmlecmascript/data/externalScript.4.qml new file mode 100644 index 0000000..16211aa --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/externalScript.4.qml @@ -0,0 +1,15 @@ +import Qt 4.6 + +QtObject { + property int test: external_script_func(); + property bool test2: is_a_undefined(); + + // Disconnected scripts + Script { + source: "externalScript.js" + } + + Script { + source: "externalScript.2.js" + } +} diff --git a/tests/auto/declarative/qmlecmascript/data/externalScript.js b/tests/auto/declarative/qmlecmascript/data/externalScript.js new file mode 100644 index 0000000..8928652 --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/externalScript.js @@ -0,0 +1,6 @@ +var a = 92; + +function external_script_func() { + return a; +} + diff --git a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp index fe3ae6b..fb8bfb3 100644 --- a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp +++ b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp @@ -108,6 +108,7 @@ private slots: void exceptionClearsOnReeval(); void transientErrors(); void shutdownErrors(); + void externalScript(); private: QmlEngine engine; @@ -946,6 +947,53 @@ void tst_qmlecmascript::shutdownErrors() QCOMPARE(transientErrorsMsgCount, 0); } +// Check that Script::source property works as expected +void tst_qmlecmascript::externalScript() +{ + { + QmlComponent component(&engine, TEST_FILE("externalScript.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 92); + + delete object; + } + + { + QmlComponent component(&engine, TEST_FILE("externalScript.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 92); + + delete object; + } + + { + QmlComponent component(&engine, TEST_FILE("externalScript.3.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 92); + QCOMPARE(object->property("test2").toInt(), 92); + QCOMPARE(object->property("test3").toBool(), false); + + delete object; + } + + { + QmlComponent component(&engine, TEST_FILE("externalScript.4.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 92); + QCOMPARE(object->property("test2").toBool(), true); + + delete object; + } +} + QTEST_MAIN(tst_qmlecmascript) #include "tst_qmlecmascript.moc" -- cgit v0.12