From b21f63763d43cecf96c9359d95705411dbdc4ecd Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 14 Dec 2011 11:27:18 +0100 Subject: [PATCH] Add support for QUrl types to V4 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 --- src/declarative/qml/v4/qv4bindings.cpp | 74 ++++++++++++++++++++++ src/declarative/qml/v4/qv4compiler.cpp | 67 +++++++++++++++++--- src/declarative/qml/v4/qv4instruction.cpp | 12 ++++ src/declarative/qml/v4/qv4instruction_p.h | 4 ++ src/declarative/qml/v4/qv4ir.cpp | 24 +++++-- .../qdeclarativeecmascript/data/urlProperty.1.qml | 10 +++ .../declarative/qdeclarativeecmascript/testtypes.h | 12 ++++ .../tst_qdeclarativeecmascript.cpp | 16 +++++ 8 files changed, 207 insertions(+), 12 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/urlProperty.1.qml diff --git a/src/declarative/qml/v4/qv4bindings.cpp b/src/declarative/qml/v4/qv4bindings.cpp index 04c410c..0ccf25c 100644 --- a/src/declarative/qml/v4/qv4bindings.cpp +++ b/src/declarative/qml/v4/qv4bindings.cpp @@ -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]; diff --git a/src/declarative/qml/v4/qv4compiler.cpp b/src/declarative/qml/v4/qv4compiler.cpp index e091ce2..5d732d5 100644 --- a/src/declarative/qml/v4/qv4compiler.cpp +++ b/src/declarative/qml/v4/qv4compiler.cpp @@ -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()) { @@ -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(); } diff --git a/src/declarative/qml/v4/qv4instruction.cpp b/src/declarative/qml/v4/qv4instruction.cpp index ccf91d5..18b2a24 100644 --- a/src/declarative/qml/v4/qv4instruction.cpp +++ b/src/declarative/qml/v4/qv4instruction.cpp @@ -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; diff --git a/src/declarative/qml/v4/qv4instruction_p.h b/src/declarative/qml/v4/qv4instruction_p.h index 6f90117..f1966bb 100644 --- a/src/declarative/qml/v4/qv4instruction_p.h +++ b/src/declarative/qml/v4/qv4instruction_p.h @@ -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) \ diff --git a/src/declarative/qml/v4/qv4ir.cpp b/src/declarative/qml/v4/qv4ir.cpp index 48d6c9b..0a0228b 100644 --- a/src/declarative/qml/v4/qv4ir.cpp +++ b/src/declarative/qml/v4/qv4ir.cpp @@ -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 index 0000000..451cb03 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/urlProperty.1.qml @@ -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 +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h index cd5bac5..2f1d3f9 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h +++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h @@ -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 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 m_objectQList; int m_value; int m_resetProperty; diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index a01c80b..f3d810e 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -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(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")); -- 2.7.4