From: Sebastian Sauer Date: Tue, 12 Aug 2014 11:52:50 +0000 (+0700) Subject: v4: Enable primitive conversation to QQmlScriptString in javascript X-Git-Tag: v5.3.99+beta1~139 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=595340f1622783e97c53b035b78691572537f00a;p=platform%2Fupstream%2Fqtdeclarative.git v4: Enable primitive conversation to QQmlScriptString in javascript This makes following QML-code proper working: ParentChange { x: 0 Component.onCompleted: x = 10 } where x is a QQmlScriptString. Before this patch an error-message would be thrown that the bool/int/string/etc cannot be converted to a QQmlScriptString. With the patch primitive types including null and undefined are proper converted to a QQmlScriptString. The patch ignores (as in not implements) function/binding assignment. Unfortunately since commit aa25ad8d5f4 its not possible any longer to instanciate QQmlScriptString what means there is otherwise no (easy) way to inject a QQmlScriptString from within Javascript. Change-Id: I18aac6a6e9a57f3b7d0a2d66cdab2be6c3c153c5 Reviewed-by: Simon Hausmann --- diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index cd9d8fe..01561eb 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -233,7 +233,7 @@ QString Binding::valueAsString(const Unit *unit) const } //reverse of Lexer::singleEscape() -static QString escapedString(const QString &string) +QString Binding::escapedString(const QString &string) { QString tmp = QLatin1String("\""); for (int i = 0; i < string.length(); ++i) { diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 869caad..78b7b46 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -284,6 +284,8 @@ struct Q_QML_PRIVATE_EXPORT Binding return false; } + static QString escapedString(const QString &string); + bool evaluatesToString() const { return type == Type_String || type == Type_Translation || type == Type_TranslationById; } QString valueAsString(const Unit *unit) const; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index e12b8f1..c5c04c0 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -65,6 +65,8 @@ #include #include #include +#include +#include #include #include @@ -511,7 +513,7 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); } else if (!newBinding && property->propType == qMetaTypeId()) { PROPERTY_STORE(QJSValue, new QJSValuePrivate(ctx->d()->engine, value)); - } else if (value->isUndefined()) { + } else if (value->isUndefined() && property->propType != qMetaTypeId()) { QString error = QLatin1String("Cannot assign [undefined] to "); if (!QMetaType::typeName(property->propType)) error += QLatin1String("[unknown property type]"); @@ -535,6 +537,16 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); vmemo->setVMEProperty(property->coreIndex, value); + } else if (property->propType == qMetaTypeId() && (value->isUndefined() || value->isPrimitive())) { + QQmlScriptString ss(value->toQStringNoThrow(), 0 /* context */, object); + if (value->isNumber()) { + ss.d->numberValue = value->toNumber(); + ss.d->isNumberLiteral = true; + } else if (value->isString()) { + ss.d->script = QV4::CompiledData::Binding::escapedString(ss.d->script); + ss.d->isStringLiteral = true; + } + PROPERTY_STORE(QQmlScriptString, ss); } else { QVariant v; if (property->isQList()) diff --git a/src/qml/qml/qqmlscriptstring.h b/src/qml/qml/qqmlscriptstring.h index 2c10df6..d85df0e 100644 --- a/src/qml/qml/qqmlscriptstring.h +++ b/src/qml/qml/qqmlscriptstring.h @@ -54,6 +54,9 @@ class QObject; class QQmlContext; class QQmlScriptStringPrivate; class QQmlObjectCreator; +namespace QV4 { + struct QObjectWrapper; +} class Q_QML_EXPORT QQmlScriptString { public: @@ -79,6 +82,7 @@ private: friend class QQmlScriptStringPrivate; friend class QQmlExpression; friend class QQmlBinding; + friend struct QV4::QObjectWrapper; }; QT_END_NAMESPACE diff --git a/tests/auto/qml/qqmllanguage/data/scriptStringJs.qml b/tests/auto/qml/qqmllanguage/data/scriptStringJs.qml new file mode 100644 index 0000000..bf6a8b9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scriptStringJs.qml @@ -0,0 +1,5 @@ +import Test 1.0 + +MyTypeObject { + scriptProperty: " hello \" world " +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index bba558d..adf3f09 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -146,6 +146,7 @@ private slots: void onCompleted(); void onDestruction(); void scriptString(); + void scriptStringJs(); void scriptStringWithoutSourceCode(); void defaultPropertyListOrder(); void declaredPropertyValues(); @@ -1946,6 +1947,84 @@ void tst_qqmllanguage::scriptString() } } +// Check that assignments to QQmlScriptString properties works also from within Javascript +void tst_qqmllanguage::scriptStringJs() +{ + QQmlComponent component(&engine, testFileUrl("scriptStringJs.qml")); + VERIFY_ERRORS(0); + + MyTypeObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + QQmlContext *context = QQmlEngine::contextForObject(object); + QVERIFY(context != 0); + bool ok; + + QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\" hello \\\" world \"")); + QVERIFY(!object->scriptProperty().isEmpty()); + QVERIFY(!object->scriptProperty().isUndefinedLiteral()); + QVERIFY(!object->scriptProperty().isNullLiteral()); + QCOMPARE(object->scriptProperty().stringLiteral(), QString(" hello \\\" world ")); + QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); + QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); + + QJSValue inst = engine.newQObject(object); + QJSValue func = engine.evaluate("function(value) { this.scriptProperty = value }"); + + func.callWithInstance(inst, QJSValueList() << "test a \"string "); + QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\"test a \\\"string \"")); + QVERIFY(!object->scriptProperty().isEmpty()); + QVERIFY(!object->scriptProperty().isUndefinedLiteral()); + QVERIFY(!object->scriptProperty().isNullLiteral()); + QCOMPARE(object->scriptProperty().stringLiteral(), QString("test a \\\"string ")); + QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); + QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); + + func.callWithInstance(inst, QJSValueList() << QJSValue::UndefinedValue); + QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("undefined")); + QVERIFY(!object->scriptProperty().isEmpty()); + QVERIFY(object->scriptProperty().isUndefinedLiteral()); + QVERIFY(!object->scriptProperty().isNullLiteral()); + QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); + QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); + QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); + + func.callWithInstance(inst, QJSValueList() << true); + QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("true")); + QVERIFY(!object->scriptProperty().isEmpty()); + QVERIFY(!object->scriptProperty().isUndefinedLiteral()); + QVERIFY(!object->scriptProperty().isNullLiteral()); + QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); + QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); + QVERIFY(object->scriptProperty().booleanLiteral(&ok) && ok); + + func.callWithInstance(inst, QJSValueList() << false); + QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("false")); + QVERIFY(!object->scriptProperty().isEmpty()); + QVERIFY(!object->scriptProperty().isUndefinedLiteral()); + QVERIFY(!object->scriptProperty().isNullLiteral()); + QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); + QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); + QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && ok); + + func.callWithInstance(inst, QJSValueList() << QJSValue::NullValue); + QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("null")); + QVERIFY(!object->scriptProperty().isEmpty()); + QVERIFY(!object->scriptProperty().isUndefinedLiteral()); + QVERIFY(object->scriptProperty().isNullLiteral()); + QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); + QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); + QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); + + func.callWithInstance(inst, QJSValueList() << 12.34); + QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("12.34")); + QVERIFY(!object->scriptProperty().isEmpty()); + QVERIFY(!object->scriptProperty().isUndefinedLiteral()); + QVERIFY(!object->scriptProperty().isNullLiteral()); + QVERIFY(object->scriptProperty().stringLiteral().isEmpty()); + QVERIFY(object->scriptProperty().numberLiteral(&ok) == 12.34 && ok); + QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); +} + void tst_qqmllanguage::scriptStringWithoutSourceCode() { QUrl url = testFileUrl("scriptString7.qml");