Skip to content

Commit

Permalink
Add support for QUrl types to V4
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Roberto Raggi authored and Qt by Nokia committed Dec 15, 2011
1 parent b6291c9 commit b21f637
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 12 deletions.
74 changes: 74 additions & 0 deletions src/declarative/qml/v4/qv4bindings.cpp
Expand Up @@ -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];
Expand Down
67 changes: 59 additions & 8 deletions src/declarative/qml/v4/qv4compiler.cpp
Expand Up @@ -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>()) {
Expand Down Expand Up @@ -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)
Expand All @@ -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();
}
Expand Down
12 changes: 12 additions & 0 deletions src/declarative/qml/v4/qv4instruction.cpp
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/declarative/qml/v4/qv4instruction_p.h
Expand Up @@ -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) \
Expand Down
24 changes: 20 additions & 4 deletions src/declarative/qml/v4/qv4ir.cpp
Expand Up @@ -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;
Expand Down
@@ -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
}
12 changes: 12 additions & 0 deletions tests/auto/declarative/qdeclarativeecmascript/testtypes.h
Expand Up @@ -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)
Expand All @@ -118,6 +119,15 @@ class MyQmlObject : public QObject
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)
Expand Down Expand Up @@ -171,6 +181,7 @@ class MyQmlObject : public QObject
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();
Expand All @@ -196,6 +207,7 @@ public slots:

QObject *m_object;
QString m_string;
QUrl m_url;
QList<QObject *> m_objectQList;
int m_value;
int m_resetProperty;
Expand Down
Expand Up @@ -220,6 +220,7 @@ private slots:
void aliasWritesOverrideBindings();
void aliasToCompositeElement();
void realToInt();
void urlProperty();
void dynamicString();
void include();
void signalHandlers();
Expand Down Expand Up @@ -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"));
Expand Down

0 comments on commit b21f637

Please sign in to comment.