aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/qml/qmltypes.qdoc4
-rw-r--r--src/declarative/qml/v8/qv8sequencewrapper_p_p.h119
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/sequenceConversion.indexes.qml89
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/testtypes.h19
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp25
5 files changed, 219 insertions, 37 deletions
diff --git a/doc/src/qml/qmltypes.qdoc b/doc/src/qml/qmltypes.qdoc
index 71985c2e1c..964ff98a15 100644
--- a/doc/src/qml/qmltypes.qdoc
+++ b/doc/src/qml/qmltypes.qdoc
@@ -406,7 +406,9 @@ Q_PROPERTY(int size READ size CONSTANT)
value. Similarly, setting the length property of the Array to a value larger
than its current value will result in the Array being padded out to the
specified length with default-constructed elements rather than Undefined
- elements.
+ elements. Finally, the Qt container classes support signed (rather than
+ unsigned) integer indexes; thus, attempting to access any index greater
+ than INT_MAX will fail.
The default-constructed values for each sequence type are as follows:
\table
diff --git a/src/declarative/qml/v8/qv8sequencewrapper_p_p.h b/src/declarative/qml/v8/qv8sequencewrapper_p_p.h
index b8faafd88a..41cdcaa8b7 100644
--- a/src/declarative/qml/v8/qv8sequencewrapper_p_p.h
+++ b/src/declarative/qml/v8/qv8sequencewrapper_p_p.h
@@ -101,6 +101,26 @@ protected:
}
};
+// helper function to generate valid warnings if errors occur during sequence operations.
+static void generateWarning(QV8Engine *engine, const QString& description)
+{
+ if (!engine)
+ return;
+ v8::Local<v8::StackTrace> currStack = v8::StackTrace::CurrentStackTrace(1);
+ if (currStack.IsEmpty())
+ return;
+ v8::Local<v8::StackFrame> currFrame = currStack->GetFrame(0);
+ if (currFrame.IsEmpty())
+ return;
+
+ QDeclarativeError retn;
+ retn.setDescription(description);
+ retn.setLine(currFrame->GetLineNumber());
+ retn.setUrl(QUrl(engine->toString(currFrame->GetScriptName())));
+ QDeclarativeEnginePrivate::warning(engine->engine(), retn);
+}
+
+
static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v)
{
return v->Int32Value();
@@ -187,7 +207,7 @@ static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v)
static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v)
{
- return e->toString(v.toEncoded());
+ return e->toString(QLatin1String(v.toEncoded().data()));
}
static QString convertUrlToString(QV8Engine *, const QUrl &v)
@@ -286,7 +306,7 @@ static QString convertUrlToString(QV8Engine *, const QUrl &v)
return 0; \
loadReference(); \
} \
- return c.count(); \
+ return static_cast<quint32>(c.count()); \
} \
void lengthSetter(v8::Handle<v8::Value> value) \
{ \
@@ -294,28 +314,39 @@ static QString convertUrlToString(QV8Engine *, const QUrl &v)
if (value.IsEmpty() || !value->IsUint32()) \
return; \
quint32 newLength = value->Uint32Value(); \
+ /* Qt containers have int (rather than uint) allowable indexes. */ \
+ if (newLength > INT_MAX) { \
+ generateWarning(engine, QLatin1String("Index out of range during length set")); \
+ return; \
+ } \
/* Read the sequence from the QObject property if we're a reference */ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return; \
- void *a[] = { &c, 0 }; \
- QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
+ loadReference(); \
} \
/* Determine whether we need to modify the sequence */ \
- quint32 count = c.count(); \
- if (newLength == count) { \
+ qint32 newCount = static_cast<qint32>(newLength); \
+ qint32 count = c.count(); \
+ if (newCount == count) { \
return; \
- } else if (newLength > count) { \
+ } else if (newCount > count) { \
/* according to ECMA262r3 we need to insert */ \
/* undefined values increasing length to newLength. */ \
/* We cannot, so we insert default-values instead. */ \
- while (newLength > count++) { \
- c.append(DefaultValue); \
+ while (newCount > count++) { \
+ QT_TRY { \
+ c.append(DefaultValue); \
+ } QT_CATCH (std::bad_alloc &exception) { \
+ generateWarning(engine, QString(QLatin1String(exception.what()) \
+ + QLatin1String(" during length set"))); \
+ return; /* failed; don't write back any result. */ \
+ } \
} \
} else { \
/* according to ECMA262r3 we need to remove */ \
/* elements until the sequence is the required length. */ \
- while (newLength < count) { \
+ while (newCount < count) { \
count--; \
c.removeAt(count); \
} \
@@ -323,15 +354,17 @@ static QString convertUrlToString(QV8Engine *, const QUrl &v)
/* write back if required. */ \
if (objectType == QV8SequenceResource::Reference) { \
/* write back. already checked that object is non-null, so skip that check here. */ \
- int status = -1; \
- QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::DontRemoveBinding; \
- void *a[] = { &c, 0, &status, &flags }; \
- QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
+ storeReference(); \
} \
return; \
} \
v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \
{ \
+ /* Qt containers have int (rather than uint) allowable indexes. */ \
+ if (index > INT_MAX) { \
+ generateWarning(engine, QLatin1String("Index out of range during indexed set")); \
+ return v8::Undefined(); \
+ } \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return v8::Undefined(); \
@@ -339,18 +372,25 @@ static QString convertUrlToString(QV8Engine *, const QUrl &v)
} \
/* modify the sequence */ \
SequenceElementType elementValue = ConversionFromV8fn(engine, value); \
- quint32 count = c.count(); \
- if (index == count) { \
+ qint32 count = c.count(); \
+ qint32 signedIdx = static_cast<qint32>(index); \
+ if (signedIdx == count) { \
c.append(elementValue); \
- } else if (index < count) { \
+ } else if (signedIdx < count) { \
c[index] = elementValue; \
} else { \
/* according to ECMA262r3 we need to insert */ \
/* the value at the given index, increasing length to index+1. */ \
- while (index > count++) { \
- c.append(DefaultValue); \
+ QT_TRY { \
+ while (signedIdx > count++) { \
+ c.append(DefaultValue); \
+ } \
+ c.append(elementValue); \
+ } QT_CATCH (std::bad_alloc &exception) { \
+ generateWarning(engine, QString(QLatin1String(exception.what()) \
+ + QLatin1String(" during indexed set"))); \
+ return v8::Undefined(); /* failed; don't write back any result. */ \
} \
- c.append(elementValue); \
} \
/* write back. already checked that object is non-null, so skip that check here. */ \
if (objectType == QV8SequenceResource::Reference) \
@@ -359,34 +399,41 @@ static QString convertUrlToString(QV8Engine *, const QUrl &v)
} \
v8::Handle<v8::Value> indexedGetter(quint32 index) \
{ \
+ /* Qt containers have int (rather than uint) allowable indexes. */ \
+ if (index > INT_MAX) { \
+ generateWarning(engine, QLatin1String("Index out of range during indexed get")); \
+ return v8::Undefined(); \
+ } \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return v8::Undefined(); \
loadReference(); \
} \
- quint32 count = c.count(); \
- if (index < count) \
- return ConversionToV8fn(engine, c.at(index)); \
+ qint32 count = c.count(); \
+ qint32 signedIdx = static_cast<qint32>(index); \
+ if (signedIdx < count) \
+ return ConversionToV8fn(engine, c.at(signedIdx)); \
return v8::Undefined(); \
} \
v8::Handle<v8::Boolean> indexedDeleter(quint32 index) \
{ \
+ /* Qt containers have int (rather than uint) allowable indexes. */ \
+ if (index > INT_MAX) \
+ return v8::Boolean::New(false); \
+ /* Read in the sequence from the QObject */ \
if (objectType == QV8SequenceResource::Reference) { \
if (!object) \
return v8::Boolean::New(false); \
- void *a[] = { &c, 0 }; \
- QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
+ loadReference(); \
} \
- if (index < c.count()) { \
+ qint32 signedIdx = static_cast<qint32>(index); \
+ if (signedIdx < c.count()) { \
/* according to ECMA262r3 it should be Undefined, */ \
/* but we cannot, so we insert a default-value instead. */ \
- c.replace(index, DefaultValue); \
+ c.replace(signedIdx, DefaultValue); \
if (objectType == QV8SequenceResource::Reference) { \
/* write back. already checked that object is non-null, so skip that check here. */ \
- int status = -1; \
- QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::DontRemoveBinding; \
- void *a[] = { &c, 0, &status, &flags }; \
- QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
+ storeReference(); \
} \
return v8::Boolean::New(true); \
} \
@@ -399,10 +446,10 @@ static QString convertUrlToString(QV8Engine *, const QUrl &v)
return v8::Handle<v8::Array>(); \
loadReference(); \
} \
- quint32 count = c.count(); \
+ qint32 count = c.count(); \
v8::Local<v8::Array> retn = v8::Array::New(count); \
- for (quint32 i = 0; i < count; ++i) { \
- retn->Set(i, v8::Integer::NewFromUnsigned(i)); \
+ for (qint32 i = 0; i < count; ++i) { \
+ retn->Set(static_cast<quint32>(i), v8::Integer::NewFromUnsigned(static_cast<quint32>(i))); \
} \
return retn; \
} \
@@ -414,8 +461,8 @@ static QString convertUrlToString(QV8Engine *, const QUrl &v)
loadReference(); \
} \
QString str; \
- quint32 count = c.count(); \
- for (quint32 i = 0; i < count; ++i) { \
+ qint32 count = c.count(); \
+ for (qint32 i = 0; i < count; ++i) { \
str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \
} \
str.chop(1); \
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/sequenceConversion.indexes.qml b/tests/auto/declarative/qdeclarativeecmascript/data/sequenceConversion.indexes.qml
new file mode 100644
index 0000000000..23f1e90417
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/sequenceConversion.indexes.qml
@@ -0,0 +1,89 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+ id: root
+ objectName: "root"
+
+ MySequenceConversionObject {
+ id: msco
+ objectName: "msco"
+ }
+
+ property bool success: false
+
+ function verifyExpected(array, idx) {
+ for (var i = 0; i < idx; ++i) {
+ if (array[i] != i) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function indexedAccess() {
+ success = true;
+
+ msco.intListProperty = [ 0, 1, 2, 3, 4 ];
+ var expectedLength = msco.intListProperty.length;
+ var maxIndex = msco.maxIndex;
+ var tooBigIndex = msco.tooBigIndex;
+ var negativeIndex = msco.negativeIndex;
+
+ // shouldn't be able to set the length > maxIndex.
+ msco.intListProperty.length = tooBigIndex;
+ if (msco.intListProperty.length != expectedLength)
+ success = false;
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+
+ // shouldn't be able to set any index > maxIndex.
+ msco.intListProperty[tooBigIndex] = 12;
+ if (msco.intListProperty.length != expectedLength)
+ success = false;
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+
+ // shouldn't be able to access any index > maxIndex.
+ var valueAtTBI = msco.intListProperty[tooBigIndex];
+ if (valueAtTBI != undefined)
+ success = false;
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+
+ // shouldn't be able to set the length to < 0
+ msco.intListProperty.length = negativeIndex;
+ if (msco.intListProperty.length != expectedLength)
+ success = false; // shouldn't have changed.
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+
+ // shouldn't be able to set any index < 0.
+ msco.intListProperty[negativeIndex] = 12;
+ if (msco.intListProperty.length != expectedLength)
+ success = false;
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+
+ // shouldn't be able to access any index < 0.
+ var valueAtNI = msco.intListProperty[negativeIndex];
+ if (valueAtNI != undefined)
+ success = false;
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+
+ // NOTE: while these two operations are technically
+ // fine, we expect std::bad_alloc exceptions here
+ // which we handle in the sequence wrapper.
+ msco.intListProperty.length = maxIndex;
+ if (msco.intListProperty.length != expectedLength)
+ success = false;
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+ msco.intListProperty[maxIndex] = 15;
+ if (msco.intListProperty.length != expectedLength)
+ success = false;
+ if (!verifyExpected(msco.intListProperty, 4))
+ success = false;
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
index d413209062..a463d3f64a 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
@@ -1194,6 +1194,10 @@ class MySequenceConversionObject : public QObject
Q_PROPERTY (QList<QPoint> pointListProperty READ pointListProperty WRITE setPointListProperty NOTIFY pointListPropertyChanged)
Q_PROPERTY (QList<QVariant> variantListProperty READ variantListProperty WRITE setVariantListProperty NOTIFY variantListPropertyChanged)
+ Q_PROPERTY (qint32 maxIndex READ maxIndex CONSTANT)
+ Q_PROPERTY (quint32 tooBigIndex READ tooBigIndex CONSTANT)
+ Q_PROPERTY (qint32 negativeIndex READ negativeIndex CONSTANT)
+
public:
MySequenceConversionObject()
{
@@ -1211,6 +1215,21 @@ public:
~MySequenceConversionObject() {}
+ qint32 maxIndex() const
+ {
+ return INT_MAX;
+ }
+ quint32 tooBigIndex() const
+ {
+ quint32 retn = 7;
+ retn += INT_MAX;
+ return retn;
+ }
+ qint32 negativeIndex() const
+ {
+ return -5;
+ }
+
QList<int> intListProperty() const { return m_intList; }
void setIntListProperty(const QList<int> &list) { m_intList = list; emit intListPropertyChanged(); }
QList<int> intListProperty2() const { return m_intList2; }
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
index 02f79d2dd7..354087da8a 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
@@ -170,6 +170,7 @@ private slots:
void sequenceConversionRead();
void sequenceConversionWrite();
void sequenceConversionArray();
+ void sequenceConversionIndexes();
void sequenceConversionThreads();
void sequenceConversionBindings();
void sequenceConversionCopy();
@@ -4608,6 +4609,30 @@ void tst_qdeclarativeecmascript::sequenceConversionArray()
delete object;
}
+
+void tst_qdeclarativeecmascript::sequenceConversionIndexes()
+{
+ // ensure that we gracefully fail if unsupported index values are specified.
+ // Qt container classes only support non-negative, signed integer index values.
+ QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
+ QDeclarativeComponent component(&engine, qmlFile);
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
+ QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
+ QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
+ QString w4 = qmlFile.toString() + QLatin1String(":78: std::bad_alloc during length set");
+ QString w5 = qmlFile.toString() + QLatin1String(":83: std::bad_alloc during indexed set");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w4));
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w5));
+ QMetaObject::invokeMethod(object, "indexedAccess");
+ QVERIFY(object->property("success").toBool());
+ delete object;
+}
+
void tst_qdeclarativeecmascript::sequenceConversionThreads()
{
// ensure that sequence conversion operations work correctly in a worker thread