diff options
author | Kent Hansen <kent.hansen@nokia.com> | 2010-06-17 10:27:43 (GMT) |
---|---|---|
committer | Kent Hansen <kent.hansen@nokia.com> | 2010-06-17 13:01:56 (GMT) |
commit | d8d547b92e02546cb030ac9d4a45fe0d89e0e7f6 (patch) | |
tree | dd95bb2244e0c44d66f1bb0cebd38fc71b5da140 /src | |
parent | 458a18284406dedcf0628c651fb3511553dbcb58 (diff) | |
download | Qt-d8d547b92e02546cb030ac9d4a45fe0d89e0e7f6.zip Qt-d8d547b92e02546cb030ac9d4a45fe0d89e0e7f6.tar.gz Qt-d8d547b92e02546cb030ac9d4a45fe0d89e0e7f6.tar.bz2 |
Usefully convert from QtScript object/array to QVariant
For arrays, the conversion would crash if the array was cyclic.
Introduce a set that keeps track of which objects are being
converted, and return an empty list when a cycle is detected.
For other types of objects, the object was previously attempted
to be converted to a primitive, which typically meant you would
get the string representation ("[object Object]"), since most
(practically all) objects can be converted to a string -- not
useful at all.
Change the conversion so it converts the object to a QVariantMap
instead. This was already done for slots that took a QVariantMap
as argument, but only one level deep. Make the conversion recursive,
using the same mechanism as for arrays to detect cycles.
This change also means that you get a meaningful
JS object => QVariant => JS object roundtrip.
It also aligns the behavior with the Qt WebKit bridge.
Update the documentation to describe the new behavior.
The 4.7 changelog will also be updated under "important behavioral
changes".
This change exposed an issue with one of the QML autotests: A JS
object was assigned to a QVariant property, which caused it to be
converted to a string (rather than a QVariantMap) -- just shows
that the previous behavior was unintuitive). Later, this variant
property is compared to another object, the intention being to
compare the _properties_ of the two objects; but because the variant
property contained a string, this would cause the other operand
(object) to be converted to a string as well ("[object Object]"),
causing a meaningless test pass.
Change the test to deserialize both objects using JSON.stringify,
and compare the resulting strings, so that actual
JS object => QVariant(Map) => JS object roundtrip is tested (the
intention).
Task-number: QTBUG-3511
Reviewed-by: Olivier Goffart
Diffstat (limited to 'src')
-rw-r--r-- | src/script/api/qscriptengine.cpp | 30 | ||||
-rw-r--r-- | src/script/api/qscriptengine_p.h | 6 | ||||
-rw-r--r-- | src/script/api/qscriptvalue.cpp | 4 |
3 files changed, 23 insertions, 17 deletions
diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index f02ea52..e2999c1 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -1011,12 +1011,17 @@ JSC::JSValue QScriptEnginePrivate::arrayFromVariantList(JSC::ExecState *exec, co return arr; } -QVariantList QScriptEnginePrivate::variantListFromArray(JSC::ExecState *exec, JSC::JSValue arr) +QVariantList QScriptEnginePrivate::variantListFromArray(JSC::ExecState *exec, JSC::JSArray *arr) { + QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(exec); + if (eng->visitedConversionObjects.contains(arr)) + return QVariantList(); // Avoid recursion. + eng->visitedConversionObjects.insert(arr); QVariantList lst; uint len = toUInt32(exec, property(exec, arr, exec->propertyNames().length)); for (uint i = 0; i < len; ++i) lst.append(toVariant(exec, property(exec, arr, i))); + eng->visitedConversionObjects.remove(arr); return lst; } @@ -1029,14 +1034,19 @@ JSC::JSValue QScriptEnginePrivate::objectFromVariantMap(JSC::ExecState *exec, co return obj; } -QVariantMap QScriptEnginePrivate::variantMapFromObject(JSC::ExecState *exec, JSC::JSValue obj) +QVariantMap QScriptEnginePrivate::variantMapFromObject(JSC::ExecState *exec, JSC::JSObject *obj) { + QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(exec); + if (eng->visitedConversionObjects.contains(obj)) + return QVariantMap(); // Avoid recursion. + eng->visitedConversionObjects.insert(obj); JSC::PropertyNameArray propertyNames(exec); - JSC::asObject(obj)->getOwnPropertyNames(exec, propertyNames, JSC::IncludeDontEnumProperties); + obj->getOwnPropertyNames(exec, propertyNames, JSC::IncludeDontEnumProperties); QVariantMap vmap; JSC::PropertyNameArray::const_iterator it = propertyNames.begin(); for( ; it != propertyNames.end(); ++it) vmap.insert(it->ustring(), toVariant(exec, property(exec, obj, *it))); + eng->visitedConversionObjects.remove(obj); return vmap; } @@ -1661,16 +1671,10 @@ QVariant QScriptEnginePrivate::toVariant(JSC::ExecState *exec, JSC::JSValue valu return QVariant(toRegExp(exec, value)); #endif else if (isArray(value)) - return variantListFromArray(exec, value); + return variantListFromArray(exec, JSC::asArray(value)); else if (QScriptDeclarativeClass *dc = declarativeClass(value)) return dc->toVariant(declarativeObject(value)); - // try to convert to primitive - JSC::JSValue savedException; - saveException(exec, &savedException); - JSC::JSValue prim = value.toPrimitive(exec); - restoreException(exec, savedException); - if (!prim.isObject()) - return toVariant(exec, prim); + return variantMapFromObject(exec, JSC::asObject(value)); } else if (value.isNumber()) { return QVariant(toNumber(exec, value)); } else if (value.isString()) { @@ -3129,12 +3133,12 @@ bool QScriptEnginePrivate::convertValue(JSC::ExecState *exec, JSC::JSValue value } break; case QMetaType::QVariantList: if (isArray(value)) { - *reinterpret_cast<QVariantList *>(ptr) = variantListFromArray(exec, value); + *reinterpret_cast<QVariantList *>(ptr) = variantListFromArray(exec, JSC::asArray(value)); return true; } break; case QMetaType::QVariantMap: if (isObject(value)) { - *reinterpret_cast<QVariantMap *>(ptr) = variantMapFromObject(exec, value); + *reinterpret_cast<QVariantMap *>(ptr) = variantMapFromObject(exec, JSC::asObject(value)); return true; } break; case QMetaType::QVariant: diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h index fd47208..56366e2 100644 --- a/src/script/api/qscriptengine_p.h +++ b/src/script/api/qscriptengine_p.h @@ -215,10 +215,10 @@ public: static QStringList stringListFromArray(JSC::ExecState*, JSC::JSValue arr); static JSC::JSValue arrayFromVariantList(JSC::ExecState*, const QVariantList &lst); - static QVariantList variantListFromArray(JSC::ExecState*, JSC::JSValue arr); + static QVariantList variantListFromArray(JSC::ExecState*, JSC::JSArray *arr); static JSC::JSValue objectFromVariantMap(JSC::ExecState*, const QVariantMap &vmap); - static QVariantMap variantMapFromObject(JSC::ExecState*, JSC::JSValue obj); + static QVariantMap variantMapFromObject(JSC::ExecState*, JSC::JSObject *obj); JSC::JSValue defaultPrototype(int metaTypeId) const; void setDefaultPrototype(int metaTypeId, JSC::JSValue prototype); @@ -378,6 +378,8 @@ public: QHash<intptr_t, QScript::UStringSourceProviderWithFeedback*> loadedScripts; QScriptValue m_currentException; + QSet<JSC::JSObject*> visitedConversionObjects; + #ifndef QT_NO_QOBJECT QHash<QObject*, QScript::QObjectData*> m_qobjectData; #endif diff --git a/src/script/api/qscriptvalue.cpp b/src/script/api/qscriptvalue.cpp index 1310c8c..451d1b0 100644 --- a/src/script/api/qscriptvalue.cpp +++ b/src/script/api/qscriptvalue.cpp @@ -1215,8 +1215,8 @@ qsreal QScriptValue::toInteger() const \row \o QObject Object \o A QVariant containing a pointer to the QObject. \row \o Date Object \o A QVariant containing the date value (toDateTime()). \row \o RegExp Object \o A QVariant containing the regular expression value (toRegExp()). - \row \o Array Object \o The array is converted to a QVariantList. - \row \o Object \o If the value is primitive, then the result is converted to a QVariant according to the above rules; otherwise, an invalid QVariant is returned. + \row \o Array Object \o The array is converted to a QVariantList. Each element is converted to a QVariant, recursively; cyclic references are not followed. + \row \o Object \o The object is converted to a QVariantMap. Each property is converted to a QVariant, recursively; cyclic references are not followed. \endtable \sa isVariant() |