Add support for QUrl types to V4
authorRoberto Raggi <roberto.raggi@nokia.com>
Wed, 14 Dec 2011 10:27:18 +0000 (11:27 +0100)
committerQt by Nokia <qt-info@nokia.com>
Thu, 15 Dec 2011 11:17:28 +0000 (12:17 +0100)
Extended the V4 instruction set with instructions to
`fast convert' url registers to string and bool registers
and `resolve' urls using QDeclarativeContext::resolvedUrl.

Also, made IR::UrlType a special `string' type. It's a little trick
to ensure that the compiler will generate correct conversions for
the binary expressions.

Change-Id: Ibc9e5b99302bd513f0cc52b598a1b198b11d4d30
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>

src/declarative/qml/v4/qv4bindings.cpp
src/declarative/qml/v4/qv4compiler.cpp
src/declarative/qml/v4/qv4instruction.cpp
src/declarative/qml/v4/qv4instruction_p.h
src/declarative/qml/v4/qv4ir.cpp
tests/auto/declarative/qdeclarativeecmascript/data/urlProperty.1.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/testtypes.h
tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp

index 04c410c..0ccf25c 100644 (file)
@@ -999,6 +999,80 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks,
     }
     QML_V4_END_INSTR(ConvertStringToReal, unaryop)
 
+    QML_V4_BEGIN_INSTR(ConvertStringToUrl, unaryop)
+    {
+        const Register &src = registers[instr->unaryop.src];
+        Register &output = registers[instr->unaryop.output];
+        // ### NaN
+        if (src.isUndefined()) {
+            output.setUndefined();
+        } else {
+            const QString tmp(*src.getstringptr());
+            if (instr->unaryop.src == instr->unaryop.output) {
+                output.cleanupString();
+                MARK_CLEAN_REGISTER(instr->unaryop.output);
+            }
+            new (output.geturlptr()) QUrl(tmp);
+            URL_REGISTER(instr->unaryop.output);
+        }
+    }
+    QML_V4_END_INSTR(ConvertStringToUrl, unaryop)
+
+    QML_V4_BEGIN_INSTR(ConvertUrlToBool, unaryop)
+    {
+        const Register &src = registers[instr->unaryop.src];
+        Register &output = registers[instr->unaryop.output];
+        // ### NaN
+        if (src.isUndefined()) {
+            output.setUndefined();
+        } else {
+            const QUrl tmp(*src.geturlptr());
+            if (instr->unaryop.src == instr->unaryop.output) {
+                output.cleanupUrl();
+                MARK_CLEAN_REGISTER(instr->unaryop.output);
+            }
+            output.setbool(!tmp.isEmpty());
+        }
+    }
+    QML_V4_END_INSTR(ConvertUrlToBool, unaryop)
+
+    QML_V4_BEGIN_INSTR(ConvertUrlToString, unaryop)
+    {
+        const Register &src = registers[instr->unaryop.src];
+        Register &output = registers[instr->unaryop.output];
+        // ### NaN
+        if (src.isUndefined()) {
+            output.setUndefined();
+        } else {
+            const QUrl tmp(*src.geturlptr());
+            if (instr->unaryop.src == instr->unaryop.output) {
+                output.cleanupUrl();
+                MARK_CLEAN_REGISTER(instr->unaryop.output);
+            }
+            new (output.getstringptr()) QString(tmp.toString());
+            STRING_REGISTER(instr->unaryop.output);
+        }
+    }
+    QML_V4_END_INSTR(ConvertUrlToString, unaryop)
+
+    QML_V4_BEGIN_INSTR(ResolveUrl, unaryop)
+    {
+        const Register &src = registers[instr->unaryop.src];
+        Register &output = registers[instr->unaryop.output];
+        if (src.isUndefined()) {
+            output.setUndefined();
+        } else {
+            const QUrl tmp(*src.geturlptr());
+            if (instr->unaryop.src == instr->unaryop.output) {
+                *output.geturlptr() = context->resolvedUrl(tmp);
+            } else {
+                new (output.geturlptr()) QUrl(context->resolvedUrl(tmp));
+                URL_REGISTER(instr->unaryop.output);
+            }
+        }
+    }
+    QML_V4_END_INSTR(ResolveUrl, unaryop)
+
     QML_V4_BEGIN_INSTR(MathSinReal, unaryop)
     {
         const Register &src = registers[instr->unaryop.src];
index e091ce2..5d732d5 100644 (file)
@@ -340,6 +340,9 @@ void QV4CompilerPrivate::visitName(IR::Name *e)
         case QMetaType::QString:
             regType = QStringType;
             break;
+        case QMetaType::QUrl:
+            regType = QUrlType;
+            break;
 
         default:
             if (propTy == qMetaTypeId<QDeclarative1AnchorLine>()) {
@@ -848,15 +851,40 @@ void QV4CompilerPrivate::visitMove(IR::Move *s)
             traceExpression(s->source, dest);
 
         V4Instr::Type opcode = V4Instr::Noop;
-        if (target->type == IR::BoolType) {
-            switch (s->source->type) {
+        IR::Type targetTy = s->target->type;
+        IR::Type sourceTy = s->source->type;
+
+        if (sourceTy == IR::UrlType) {
+            switch (targetTy) {
+            case IR::BoolType:
+            case IR::StringType:
+                // nothing to do. V4 will generate optimized
+                // url-to-xxx conversions.
+                break;
+            default: {
+                // generate a UrlToString conversion and fix
+                // the type of the source expression.
+                V4Instr conv;
+                conv.unaryop.output = V4Instr::ConvertUrlToString;
+                conv.unaryop.src = src;
+                gen(opcode, conv);
+
+                sourceTy = IR::StringType;
+                break;
+            }
+            } // switch
+        }
+
+        if (targetTy == IR::BoolType) {
+            switch (sourceTy) {
             case IR::IntType: opcode = V4Instr::ConvertIntToBool; break;
             case IR::RealType: opcode = V4Instr::ConvertRealToBool; break;
             case IR::StringType: opcode = V4Instr::ConvertStringToBool; break;
+            case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break;
             default: break;
             } // switch
-        } else if (target->type == IR::IntType) {
-            switch (s->source->type) {
+        } else if (targetTy == IR::IntType) {
+            switch (sourceTy) {
             case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
             case IR::RealType: {
                 if (s->isMoveForReturn)
@@ -868,26 +896,49 @@ void QV4CompilerPrivate::visitMove(IR::Move *s)
             case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
             default: break;
             } // switch
-        } else if (target->type == IR::RealType) {
-            switch (s->source->type) {
+        } else if (targetTy == IR::RealType) {
+            switch (sourceTy) {
             case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break;
             case IR::IntType: opcode = V4Instr::ConvertIntToReal; break;
             case IR::StringType: opcode = V4Instr::ConvertStringToReal; break;
             default: break;
             } // switch
-        } else if (target->type == IR::StringType) {
-            switch (s->source->type) {
+        } else if (targetTy == IR::StringType) {
+            switch (sourceTy) {
             case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break;
             case IR::IntType:  opcode = V4Instr::ConvertIntToString; break;
             case IR::RealType: opcode = V4Instr::ConvertRealToString; break;
+            case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break;
+            default: break;
+            } // switch
+        } else if (targetTy == IR::UrlType) {
+            V4Instr convToString;
+            convToString.unaryop.output = dest;
+            convToString.unaryop.src = src;
+
+            // try to convert the source expression to a string.
+            switch (sourceTy) {
+            case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break;
+            case IR::IntType:  gen(V4Instr::ConvertIntToString,  convToString); sourceTy = IR::StringType; break;
+            case IR::RealType: gen(V4Instr::ConvertRealToString, convToString); sourceTy = IR::StringType; break;
             default: break;
             } // switch
+
+            if (sourceTy == IR::StringType)
+                opcode = V4Instr::ConvertStringToUrl;
         }
         if (opcode != V4Instr::Noop) {
             V4Instr conv;
             conv.unaryop.output = dest;
             conv.unaryop.src = src;
             gen(opcode, conv);
+
+            if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
+                V4Instr resolveUrl;
+                resolveUrl.unaryop.output = dest;
+                resolveUrl.unaryop.src = dest;
+                gen(V4Instr::ResolveUrl, resolveUrl);
+            }
         } else {
             discard();
         }
index ccf91d5..18b2a24 100644 (file)
@@ -168,6 +168,18 @@ void Bytecode::dump(const V4Instr *i, int address) const
     case V4Instr::ConvertStringToReal:
         INSTR_DUMP << "\t" << "ConvertStringToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
         break;
+    case V4Instr::ConvertStringToUrl:
+        INSTR_DUMP << "\t" << "ConvertStringToUrl" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+        break;
+    case V4Instr::ConvertUrlToBool:
+        INSTR_DUMP << "\t" << "ConvertUrlToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+        break;
+    case V4Instr::ConvertUrlToString:
+        INSTR_DUMP << "\t" << "ConvertUrlToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+        break;
+    case V4Instr::ResolveUrl:
+        INSTR_DUMP << "\t" << "ResolveUrl" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
+        break;
     case V4Instr::MathSinReal:
         INSTR_DUMP << "\t" << "MathSinReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")";
         break;
index 6f90117..f1966bb 100644 (file)
@@ -91,6 +91,10 @@ QT_BEGIN_NAMESPACE
     F(ConvertStringToBool, unaryop) \
     F(ConvertStringToInt, unaryop) \
     F(ConvertStringToReal, unaryop) \
+    F(ConvertStringToUrl, unaryop) \
+    F(ConvertUrlToBool, unaryop) \
+    F(ConvertUrlToString, unaryop) \
+    F(ResolveUrl, unaryop) \
     F(MathSinReal, unaryop) \
     F(MathCosReal, unaryop) \
     F(MathRoundReal, unaryop) \
index 48d6c9b..0a0228b 100644 (file)
@@ -71,14 +71,30 @@ inline const char *typeName(Type t)
     }
 }
 
+inline bool isNumberType(IR::Type ty)
+{
+    return ty >= IR::FirstNumberType;
+}
+
+inline bool isStringType(IR::Type ty)
+{
+    return ty == IR::StringType || ty == IR::UrlType;
+}
+
 IR::Type maxType(IR::Type left, IR::Type right)
 {
-    if (left == right)
+    if (isStringType(left) && isStringType(right)) {
+        // String promotions (url to string) are more specific than
+        // identity conversions (AKA left == right). That's because
+        // we want to ensure we convert urls to strings in binary
+        // expressions.
+        return IR::StringType;
+    } else if (left == right)
         return left;
-    else if (left >= IR::FirstNumberType && right >= IR::FirstNumberType)
+    else if (isNumberType(left) && isNumberType(right))
         return qMax(left, right);
-    else if ((left >= IR::FirstNumberType && right == IR::StringType) ||
-             (right >= IR::FirstNumberType && left == IR::StringType))
+    else if ((isNumberType(left) && isStringType(right)) ||
+             (isNumberType(right) && isStringType(left)))
         return IR::StringType;
     else
         return IR::InvalidType;
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/urlProperty.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/urlProperty.1.qml
new file mode 100644 (file)
index 0000000..451cb03
--- /dev/null
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+MyQmlObject {
+    property bool result
+    urlProperty: stringProperty + "/index.html"
+    intProperty: if (urlProperty) 123; else 321
+    value: urlProperty == stringProperty + "/index.html"
+    result: urlProperty == urlProperty
+}
index cd5bac5..2f1d3f9 100644 (file)
@@ -93,6 +93,7 @@ class MyQmlObject : public QObject
     Q_PROPERTY(int value READ value WRITE setValue)
     Q_PROPERTY(int console READ console CONSTANT)
     Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringChanged)
+    Q_PROPERTY(QUrl urlProperty READ urlProperty WRITE setUrlProperty NOTIFY urlChanged)
     Q_PROPERTY(QObject *objectProperty READ objectProperty WRITE setObjectProperty NOTIFY objectChanged)
     Q_PROPERTY(QDeclarativeListProperty<QObject> objectListProperty READ objectListProperty CONSTANT)
     Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty)
@@ -118,6 +119,15 @@ public:
         emit stringChanged();
     }
 
+    QUrl urlProperty() const { return m_url; }
+    void setUrlProperty(const QUrl &url)
+    {
+        if (url == m_url)
+            return;
+        m_url = url;
+        emit urlChanged();
+    }
+
     QObject *objectProperty() const { return m_object; }
     void setObjectProperty(QObject *obj) { 
         if (obj == m_object)
@@ -171,6 +181,7 @@ signals:
     void basicSignal();
     void argumentSignal(int a, QString b, qreal c, MyEnum2 d, Qt::MouseButtons e);
     void stringChanged();
+    void urlChanged();
     void objectChanged();
     void anotherBasicSignal();
     void thirdBasicSignal();
@@ -196,6 +207,7 @@ private:
 
     QObject *m_object;
     QString m_string;
+    QUrl m_url;
     QList<QObject *> m_objectQList;
     int m_value;
     int m_resetProperty;
index a01c80b..f3d810e 100644 (file)
@@ -220,6 +220,7 @@ private slots:
     void aliasWritesOverrideBindings();
     void aliasToCompositeElement();
     void realToInt();
+    void urlProperty();
     void dynamicString();
     void include();
     void signalHandlers();
@@ -5462,6 +5463,21 @@ void tst_qdeclarativeecmascript::realToInt()
     QMetaObject::invokeMethod(object, "test2");
     QCOMPARE(object->value(), int(8));
 }
+
+void tst_qdeclarativeecmascript::urlProperty()
+{
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("urlProperty.1.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+        QVERIFY(object != 0);
+        object->setStringProperty("http://qt-project.org");
+        QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
+        QCOMPARE(object->intProperty(), 123);
+        QCOMPARE(object->value(), 1);
+        QCOMPARE(object->property("result").toBool(), true);
+    }
+}
+
 void tst_qdeclarativeecmascript::dynamicString()
 {
     QDeclarativeComponent component(&engine, TEST_FILE("dynamicString.qml"));