diff options
Diffstat (limited to 'JavaScriptCore/qt')
-rw-r--r-- | JavaScriptCore/qt/ChangeLog | 112 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/QtScript.pro | 4 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptengine_p.cpp | 22 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptengine_p.h | 28 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h | 186 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptvalue_p.h | 29 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptvalueiterator.cpp | 226 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptvalueiterator.h | 63 | ||||
-rw-r--r-- | JavaScriptCore/qt/api/qscriptvalueiterator_p.h | 172 | ||||
-rw-r--r-- | JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.cpp | 24 | ||||
-rw-r--r-- | JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.h | 1 | ||||
-rw-r--r-- | JavaScriptCore/qt/tests/qscriptvalueiterator/qscriptvalueiterator.pro | 7 | ||||
-rw-r--r-- | JavaScriptCore/qt/tests/qscriptvalueiterator/tst_qscriptvalueiterator.cpp | 531 | ||||
-rw-r--r-- | JavaScriptCore/qt/tests/tests.pro | 1 |
14 files changed, 1353 insertions, 53 deletions
diff --git a/JavaScriptCore/qt/ChangeLog b/JavaScriptCore/qt/ChangeLog index 71b4a21..448a59e 100644 --- a/JavaScriptCore/qt/ChangeLog +++ b/JavaScriptCore/qt/ChangeLog @@ -1,3 +1,115 @@ +2010-07-14 Jedrzej Nowacki <jedrzej.nowacki@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + Introduce QScriptOriginalGlobalObject. + + QtScript exposes more functionality than JSC C API. Sometimes it is + necessary to take a shortcut in implementation. Really often we have + to use a standard JS function. These function could be changed or + even deleted by a script, so a backup of a reference to an object is needed. + + In them same time this is rather a workaround then real fix, so the code + should be separated and changed easily in future. It is why we need + the new internal class. + + The patch fixes a few crashes. + + [Qt] QScriptEngine should work correctly even after global object changes + https://bugs.webkit.org/show_bug.cgi?id=41839 + + * api/QtScript.pro: + * api/qscriptengine_p.cpp: + (QScriptEnginePrivate::QScriptEnginePrivate): + (QScriptEnginePrivate::~QScriptEnginePrivate): + * api/qscriptengine_p.h: + (QScriptEnginePrivate::isArray): + (QScriptEnginePrivate::isError): + (QScriptEnginePrivate::objectHasOwnProperty): + (QScriptEnginePrivate::objectGetOwnPropertyNames): + * api/qscriptoriginalglobalobject_p.h: Added. + (QScriptOriginalGlobalObject::QScriptOriginalGlobalObject): + (QScriptOriginalGlobalObject::initializeMember): + (QScriptOriginalGlobalObject::~QScriptOriginalGlobalObject): + (QScriptOriginalGlobalObject::objectHasOwnProperty): + (QScriptOriginalGlobalObject::objectGetOwnPropertyNames): + (QScriptOriginalGlobalObject::isArray): + (QScriptOriginalGlobalObject::isError): + (QScriptOriginalGlobalObject::isType): + * api/qscriptvalue_p.h: + (QScriptValuePrivate::isError): + (QScriptValuePrivate::hasOwnProperty): + * api/qscriptvalueiterator_p.h: + (QScriptValueIteratorPrivate::QScriptValueIteratorPrivate): + * tests/qscriptvalue/tst_qscriptvalue.cpp: + (tst_QScriptValue::globalObjectChanges): + * tests/qscriptvalue/tst_qscriptvalue.h: + +2010-07-13 Jedrzej Nowacki <jedrzej.nowacki@nokia.com> + + Reviewed by Simon Hausmann. + + Introduce QScriptValueIterator. + + The QScriptValueIterator class permits to iterate over a QScriptValue's properties. + + [Qt] QtScript should provide an API for enumerating a JS object's properties + https://bugs.webkit.org/show_bug.cgi?id=41680 + + * api/QtScript.pro: + * api/qscriptvalueiterator.cpp: Added. + (QScriptValueIterator::QScriptValueIterator): + (QScriptValueIterator::~QScriptValueIterator): + (QScriptValueIterator::hasNext): + (QScriptValueIterator::next): + (QScriptValueIterator::hasPrevious): + (QScriptValueIterator::previous): + (QScriptValueIterator::toFront): + (QScriptValueIterator::toBack): + (QScriptValueIterator::name): + (QScriptValueIterator::scriptName): + (QScriptValueIterator::value): + (QScriptValueIterator::setValue): + (QScriptValueIterator::remove): + (QScriptValueIterator::flags): + (QScriptValueIterator::operator=): + * api/qscriptvalueiterator.h: Added. + * api/qscriptvalueiterator_p.h: Added. + (QScriptValueIteratorPrivate::QScriptValueIteratorPrivate): + (QScriptValueIteratorPrivate::~QScriptValueIteratorPrivate): + (QScriptValueIteratorPrivate::hasNext): + (QScriptValueIteratorPrivate::next): + (QScriptValueIteratorPrivate::hasPrevious): + (QScriptValueIteratorPrivate::previous): + (QScriptValueIteratorPrivate::name): + (QScriptValueIteratorPrivate::scriptName): + (QScriptValueIteratorPrivate::value): + (QScriptValueIteratorPrivate::setValue): + (QScriptValueIteratorPrivate::remove): + (QScriptValueIteratorPrivate::toFront): + (QScriptValueIteratorPrivate::toBack): + (QScriptValueIteratorPrivate::flags): + (QScriptValueIteratorPrivate::isValid): + (QScriptValueIteratorPrivate::engine): + * tests/qscriptvalueiterator/qscriptvalueiterator.pro: Added. + * tests/qscriptvalueiterator/tst_qscriptvalueiterator.cpp: Added. + (tst_QScriptValueIterator::tst_QScriptValueIterator): + (tst_QScriptValueIterator::~tst_QScriptValueIterator): + (tst_QScriptValueIterator::iterateForward_data): + (tst_QScriptValueIterator::iterateForward): + (tst_QScriptValueIterator::iterateBackward_data): + (tst_QScriptValueIterator::iterateBackward): + (tst_QScriptValueIterator::iterateArray_data): + (tst_QScriptValueIterator::iterateArray): + (tst_QScriptValueIterator::iterateBackAndForth): + (tst_QScriptValueIterator::setValue): + (tst_QScriptValueIterator::remove): + (tst_QScriptValueIterator::removeMixed): + (tst_QScriptValueIterator::removeUndeletable): + (tst_QScriptValueIterator::iterateString): + (tst_QScriptValueIterator::assignObjectToIterator): + * tests/tests.pro: + 2010-07-09 Jedrzej Nowacki <jedrzej.nowacki@nokia.com> Reviewed by Simon Hausmann. diff --git a/JavaScriptCore/qt/api/QtScript.pro b/JavaScriptCore/qt/api/QtScript.pro index 3c2691e..c2c6f83 100644 --- a/JavaScriptCore/qt/api/QtScript.pro +++ b/JavaScriptCore/qt/api/QtScript.pro @@ -24,6 +24,7 @@ INCLUDEPATH += $$PWD/../../API SOURCES += $$PWD/qscriptengine.cpp \ $$PWD/qscriptengine_p.cpp \ $$PWD/qscriptvalue.cpp \ + $$PWD/qscriptvalueiterator.cpp \ $$PWD/qscriptstring.cpp \ $$PWD/qscriptprogram.cpp \ $$PWD/qscriptsyntaxcheckresult.cpp \ @@ -33,12 +34,15 @@ HEADERS += $$PWD/qtscriptglobal.h \ $$PWD/qscriptengine_p.h \ $$PWD/qscriptvalue.h \ $$PWD/qscriptvalue_p.h \ + $$PWD/qscriptvalueiterator.h \ + $$PWD/qscriptvalueiterator_p.h \ $$PWD/qscriptconverter_p.h \ $$PWD/qscriptstring.h \ $$PWD/qscriptstring_p.h \ $$PWD/qscriptprogram.h \ $$PWD/qscriptprogram_p.h \ $$PWD/qscriptsyntaxcheckresult.h \ + $$PWD/qscriptoriginalglobalobject_p.h \ !static: DEFINES += QT_MAKEDLL diff --git a/JavaScriptCore/qt/api/qscriptengine_p.cpp b/JavaScriptCore/qt/api/qscriptengine_p.cpp index 360de29..e3311ed 100644 --- a/JavaScriptCore/qt/api/qscriptengine_p.cpp +++ b/JavaScriptCore/qt/api/qscriptengine_p.cpp @@ -32,32 +32,12 @@ QScriptEnginePrivate::QScriptEnginePrivate(const QScriptEngine* engine) : q_ptr(const_cast<QScriptEngine*>(engine)) , m_context(JSGlobalContextCreate(0)) , m_exception(0) - , m_arrayConstructor(0) - , m_arrayPrototype(0) + , m_originalGlobalObject(m_context) { - JSObjectRef globalObject = JSContextGetGlobalObject(m_context); - - // Save references to the Array constructor and prototype. - JSRetainPtr<JSStringRef> arrayName(Adopt, JSStringCreateWithUTF8CString("Array")); - JSValueRef arrayConstructor = JSObjectGetProperty(m_context, globalObject, arrayName.get(), /* exception */ 0); - Q_ASSERT(JSValueIsObject(m_context, arrayConstructor)); - m_arrayConstructor = JSValueToObject(m_context, arrayConstructor, /* exception */ 0); - JSValueProtect(m_context, m_arrayConstructor); - - // Note that this is not the [[Prototype]] internal property (which we could - // get via JSObjectGetPrototype), but the Array.prototype, that will be set - // as [[Prototype]] of Array instances. - JSRetainPtr<JSStringRef> prototypeName(Adopt, JSStringCreateWithUTF8CString("prototype")); - JSValueRef arrayPrototype = JSObjectGetProperty(m_context, m_arrayConstructor, prototypeName.get(), /* exception */ 0); - Q_ASSERT(JSValueIsObject(m_context, arrayPrototype)); - m_arrayPrototype = arrayPrototype; - JSValueProtect(m_context, m_arrayPrototype); } QScriptEnginePrivate::~QScriptEnginePrivate() { - JSValueUnprotect(m_context, m_arrayConstructor); - JSValueUnprotect(m_context, m_arrayPrototype); if (m_exception) JSValueUnprotect(m_context, m_exception); JSGlobalContextRelease(m_context); diff --git a/JavaScriptCore/qt/api/qscriptengine_p.h b/JavaScriptCore/qt/api/qscriptengine_p.h index 401c051..d54cdcc 100644 --- a/JavaScriptCore/qt/api/qscriptengine_p.h +++ b/JavaScriptCore/qt/api/qscriptengine_p.h @@ -22,6 +22,7 @@ #include "qscriptconverter_p.h" #include "qscriptengine.h" +#include "qscriptoriginalglobalobject_p.h" #include "qscriptstring_p.h" #include "qscriptsyntaxcheckresult_p.h" #include "qscriptvalue.h" @@ -79,13 +80,15 @@ public: inline operator JSGlobalContextRef() const; inline bool isArray(JSValueRef value) const; + inline bool isError(JSValueRef value) const; + inline bool objectHasOwnProperty(JSObjectRef object, JSStringRef property) const; + inline QVector<JSStringRef> objectGetOwnPropertyNames(JSObjectRef object) const; private: QScriptEngine* q_ptr; JSGlobalContextRef m_context; JSValueRef m_exception; - JSObjectRef m_arrayConstructor; - JSValueRef m_arrayPrototype; + QScriptOriginalGlobalObject m_originalGlobalObject; }; @@ -218,9 +221,24 @@ QScriptEnginePrivate::operator JSGlobalContextRef() const bool QScriptEnginePrivate::isArray(JSValueRef value) const { - // JSC API doesn't export the [[Class]] information for the builtins. But we know that a value - // is an array if it was created with the Array constructor or if it is the Array.prototype. - return JSValueIsInstanceOfConstructor(m_context, value, m_arrayConstructor, /* exception */ 0) || JSValueIsStrictEqual(m_context, value, m_arrayPrototype); + return m_originalGlobalObject.isArray(value); +} + +bool QScriptEnginePrivate::isError(JSValueRef value) const +{ + return m_originalGlobalObject.isError(value); +} + +inline bool QScriptEnginePrivate::objectHasOwnProperty(JSObjectRef object, JSStringRef property) const +{ + // FIXME We need a JSC C API function for this. + return m_originalGlobalObject.objectHasOwnProperty(object, property); +} + +inline QVector<JSStringRef> QScriptEnginePrivate::objectGetOwnPropertyNames(JSObjectRef object) const +{ + // FIXME We can't use C API function JSObjectGetPropertyNames as it returns only enumerable properties. + return m_originalGlobalObject.objectGetOwnPropertyNames(object); } #endif diff --git a/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h b/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h new file mode 100644 index 0000000..8d080fb --- /dev/null +++ b/JavaScriptCore/qt/api/qscriptoriginalglobalobject_p.h @@ -0,0 +1,186 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef qscriptoriginalglobalobject_p_h +#define qscriptoriginalglobalobject_p_h + +#include <JavaScriptCore/JavaScript.h> +#include <JavaScriptCore/JSRetainPtr.h> +#include <QtCore/qvector.h> + +/*! + \internal + This class is a workaround for missing JSC C API functionality. This class keeps all important + properties of an original (default) global object, so we can use it even if the global object was + changed. + + FIXME this class is a container for workarounds :-) it should be replaced by proper JSC C API calls. + + The class have to be created on the QScriptEnginePrivate creation time (before any change got applied to + global object). +*/ +class QScriptOriginalGlobalObject { +public: + inline QScriptOriginalGlobalObject(JSGlobalContextRef context); + inline ~QScriptOriginalGlobalObject(); + + inline bool objectHasOwnProperty(JSObjectRef object, JSStringRef property) const; + inline QVector<JSStringRef> objectGetOwnPropertyNames(JSObjectRef object) const; + + inline bool isArray(JSValueRef value) const; + inline bool isError(JSValueRef value) const; +private: + inline bool isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const; + inline void initializeMember(JSObjectRef globalObject, JSStringRef prototypeName, const char* type, JSObjectRef& constructor, JSValueRef& prototype); + + // Copy of the global context reference (the same as in QScriptEnginePrivate). + JSGlobalContextRef m_context; + + // Copy of constructors and prototypes used in isType functions. + JSObjectRef m_arrayConstructor; + JSValueRef m_arrayPrototype; + JSObjectRef m_errorConstructor; + JSValueRef m_errorPrototype; + + // Reference to standard JS functions that are not exposed by JSC C API. + JSObjectRef m_hasOwnPropertyFunction; + JSObjectRef m_getOwnPropertyNamesFunction; +}; + +QScriptOriginalGlobalObject::QScriptOriginalGlobalObject(JSGlobalContextRef context) + : m_context(JSGlobalContextRetain(context)) +{ + JSObjectRef globalObject = JSContextGetGlobalObject(m_context); + JSValueRef exception = 0; + JSRetainPtr<JSStringRef> propertyName; + + propertyName.adopt(JSStringCreateWithUTF8CString("prototype")); + initializeMember(globalObject, propertyName.get(), "Array", m_arrayConstructor, m_arrayPrototype); + initializeMember(globalObject, propertyName.get(), "Error", m_errorConstructor, m_errorPrototype); + + propertyName.adopt(JSStringCreateWithUTF8CString("hasOwnProperty")); + m_hasOwnPropertyFunction = const_cast<JSObjectRef>(JSObjectGetProperty(m_context, globalObject, propertyName.get(), &exception)); + JSValueProtect(m_context, m_hasOwnPropertyFunction); + Q_ASSERT(JSValueIsObject(m_context, m_hasOwnPropertyFunction)); + Q_ASSERT(JSObjectIsFunction(m_context, m_hasOwnPropertyFunction)); + Q_ASSERT(!exception); + + propertyName.adopt(JSStringCreateWithUTF8CString("Object")); + JSObjectRef objectConstructor + = const_cast<JSObjectRef>(JSObjectGetProperty(m_context, globalObject, propertyName.get(), &exception)); + propertyName.adopt(JSStringCreateWithUTF8CString("getOwnPropertyNames")); + m_getOwnPropertyNamesFunction + = const_cast<JSObjectRef>(JSObjectGetProperty(m_context, objectConstructor, propertyName.get(), &exception)); + JSValueProtect(m_context, m_getOwnPropertyNamesFunction); + Q_ASSERT(JSValueIsObject(m_context, m_getOwnPropertyNamesFunction)); + Q_ASSERT(JSObjectIsFunction(m_context, m_getOwnPropertyNamesFunction)); + Q_ASSERT(!exception); +} + +inline void QScriptOriginalGlobalObject::initializeMember(JSObjectRef globalObject, JSStringRef prototypeName, const char* type, JSObjectRef& constructor, JSValueRef& prototype) +{ + JSRetainPtr<JSStringRef> typeName(Adopt, JSStringCreateWithUTF8CString(type)); + JSValueRef exception = 0; + + // Save references to the Type constructor and prototype. + JSValueRef typeConstructor = JSObjectGetProperty(m_context, globalObject, typeName.get(), &exception); + Q_ASSERT(JSValueIsObject(m_context, typeConstructor)); + constructor = JSValueToObject(m_context, typeConstructor, &exception); + JSValueProtect(m_context, constructor); + + // Note that this is not the [[Prototype]] internal property (which we could + // get via JSObjectGetPrototype), but the Type.prototype, that will be set + // as [[Prototype]] of Type instances. + prototype = JSObjectGetProperty(m_context, constructor, prototypeName, &exception); + Q_ASSERT(JSValueIsObject(m_context, prototype)); + JSValueProtect(m_context, prototype); + Q_ASSERT(!exception); +} + +QScriptOriginalGlobalObject::~QScriptOriginalGlobalObject() +{ + JSValueUnprotect(m_context, m_arrayConstructor); + JSValueUnprotect(m_context, m_arrayPrototype); + JSValueUnprotect(m_context, m_errorConstructor); + JSValueUnprotect(m_context, m_errorPrototype); + JSValueUnprotect(m_context, m_hasOwnPropertyFunction); + JSValueUnprotect(m_context, m_getOwnPropertyNamesFunction); + JSGlobalContextRelease(m_context); +} + +inline bool QScriptOriginalGlobalObject::objectHasOwnProperty(JSObjectRef object, JSStringRef property) const +{ + // FIXME This function should be replaced by JSC C API. + JSValueRef exception = 0; + JSValueRef propertyName[] = { JSValueMakeString(m_context, property) }; + JSValueRef result = JSObjectCallAsFunction(m_context, m_hasOwnPropertyFunction, object, 1, propertyName, &exception); + return exception ? false : JSValueToBoolean(m_context, result); +} + +/*! + \internal + This method gives ownership of all JSStringRefs. +*/ +inline QVector<JSStringRef> QScriptOriginalGlobalObject::objectGetOwnPropertyNames(JSObjectRef object) const +{ + JSValueRef exception = 0; + JSObjectRef propertyNames + = const_cast<JSObjectRef>(JSObjectCallAsFunction(m_context, + m_getOwnPropertyNamesFunction, + /* thisObject */ 0, + /* argumentCount */ 1, + &object, + &exception)); + Q_ASSERT(JSValueIsObject(m_context, propertyNames)); + Q_ASSERT(!exception); + JSStringRef lengthName = QScriptConverter::toString("length"); + int count = JSValueToNumber(m_context, JSObjectGetProperty(m_context, propertyNames, lengthName, &exception), &exception); + + Q_ASSERT(!exception); + QVector<JSStringRef> names; + names.reserve(count); + for (int i = 0; i < count; ++i) { + JSValueRef tmp = JSObjectGetPropertyAtIndex(m_context, propertyNames, i, &exception); + names.append(JSValueToStringCopy(m_context, tmp, &exception)); + Q_ASSERT(!exception); + } + return names; +} + +inline bool QScriptOriginalGlobalObject::isArray(JSValueRef value) const +{ + return isType(value, m_arrayConstructor, m_arrayPrototype); +} + +inline bool QScriptOriginalGlobalObject::isError(JSValueRef value) const +{ + return isType(value, m_errorConstructor, m_errorPrototype); +} + +inline bool QScriptOriginalGlobalObject::isType(JSValueRef value, JSObjectRef constructor, JSValueRef prototype) const +{ + // JSC API doesn't export the [[Class]] information for the builtins. But we know that a value + // is an object of the Type if it was created with the Type constructor or if it is the Type.prototype. + JSValueRef exception = 0; + bool result = JSValueIsInstanceOfConstructor(m_context, value, constructor, &exception) || JSValueIsStrictEqual(m_context, value, prototype); + Q_ASSERT(!exception); + return result; +} + +#endif // qscriptoriginalglobalobject_p_h diff --git a/JavaScriptCore/qt/api/qscriptvalue_p.h b/JavaScriptCore/qt/api/qscriptvalue_p.h index 1d319ba..6e93a07 100644 --- a/JavaScriptCore/qt/api/qscriptvalue_p.h +++ b/JavaScriptCore/qt/api/qscriptvalue_p.h @@ -184,7 +184,6 @@ private: Value(QString* string) : m_string(string) {} } u; - inline bool inherits(const char*); inline State refinedJSValue(); inline bool isJSBased() const; @@ -416,7 +415,7 @@ bool QScriptValuePrivate::isError() return false; // Fall-through. case JSObject: - return inherits("Error"); + return m_engine->isError(*this); default: return false; } @@ -868,13 +867,7 @@ inline bool QScriptValuePrivate::hasOwnProperty(quint32 property) inline bool QScriptValuePrivate::hasOwnProperty(JSStringRef property) { Q_ASSERT(isObject()); - // FIXME it could be faster, but JSC C API doesn't expose needed functionality. - JSRetainPtr<JSStringRef> hasOwnPropertyName(Adopt, JSStringCreateWithUTF8CString("hasOwnProperty")); - JSValueRef exception = 0; - JSValueRef hasOwnProperty = JSObjectGetProperty(*m_engine, *this, hasOwnPropertyName.get(), &exception); - JSValueRef propertyName[] = { JSValueMakeString(*m_engine, property) }; - JSValueRef result = JSObjectCallAsFunction(*m_engine, const_cast<JSObjectRef>(hasOwnProperty), *this, 1, propertyName, &exception); - return exception ? false : JSValueToBoolean(*m_engine, result); + return m_engine->objectHasOwnProperty(*this, property); } /*! @@ -1124,24 +1117,6 @@ QScriptValuePrivate::operator JSObjectRef() const /*! \internal - Returns true if QSV is created from constructor with the given \a name, it has to be a - built-in type. -*/ -bool QScriptValuePrivate::inherits(const char* name) -{ - Q_ASSERT(isJSBased()); - JSObjectRef globalObject = JSContextGetGlobalObject(*m_engine); - JSStringRef errorAttrName = QScriptConverter::toString(name); - JSValueRef exception = 0; - JSValueRef error = JSObjectGetProperty(*m_engine, globalObject, errorAttrName, &exception); - JSStringRelease(errorAttrName); - bool result = JSValueIsInstanceOfConstructor(*m_engine, *this, JSValueToObject(*m_engine, error, &exception), &exception); - m_engine->setException(exception); - return result; -} - -/*! - \internal Refines the state of this QScriptValuePrivate. Returns the new state. */ QScriptValuePrivate::State QScriptValuePrivate::refinedJSValue() diff --git a/JavaScriptCore/qt/api/qscriptvalueiterator.cpp b/JavaScriptCore/qt/api/qscriptvalueiterator.cpp new file mode 100644 index 0000000..f1caa61 --- /dev/null +++ b/JavaScriptCore/qt/api/qscriptvalueiterator.cpp @@ -0,0 +1,226 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include "qscriptvalueiterator.h" + +#include "qscriptvalue_p.h" +#include "qscriptvalueiterator_p.h" + +/*! + \class QScriptValueIterator + + \brief The QScriptValueIterator class provides a Java-style iterator for QScriptValue. + + \ingroup script + + + The QScriptValueIterator constructor takes a QScriptValue as + argument. After construction, the iterator is located at the very + beginning of the sequence of properties. Here's how to iterate over + all the properties of a QScriptValue: + + \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 0 + + The next() advances the iterator. The name(), value() and flags() + functions return the name, value and flags of the last item that was + jumped over. + + If you want to remove properties as you iterate over the + QScriptValue, use remove(). If you want to modify the value of a + property, use setValue(). + + Note that QScriptValueIterator only iterates over the QScriptValue's + own properties; i.e. it does not follow the prototype chain. You can + use a loop like this to follow the prototype chain: + + \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 1 + + Note that QScriptValueIterator will not automatically skip over + properties that have the QScriptValue::SkipInEnumeration flag set; + that flag only affects iteration in script code. If you want, you + can skip over such properties with code like the following: + + \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 2 + + \sa QScriptValue::property() +*/ + +/*! + Constructs an iterator for traversing \a object. The iterator is + set to be at the front of the sequence of properties (before the + first property). +*/ +QScriptValueIterator::QScriptValueIterator(const QScriptValue& object) + : d_ptr(new QScriptValueIteratorPrivate(QScriptValuePrivate::get(object))) +{} + +/*! + Destroys the iterator. +*/ +QScriptValueIterator::~QScriptValueIterator() +{} + +/*! + Returns true if there is at least one item ahead of the iterator + (i.e. the iterator is \e not at the back of the property sequence); + otherwise returns false. + + \sa next(), hasPrevious() +*/ +bool QScriptValueIterator::hasNext() const +{ + return d_ptr->hasNext(); +} + +/*! + Advances the iterator by one position. + + Calling this function on an iterator located at the back of the + container leads to undefined results. + + \sa hasNext(), previous(), name() +*/ +void QScriptValueIterator::next() +{ + d_ptr->next(); +} + +/*! + Returns true if there is at least one item behind the iterator + (i.e. the iterator is \e not at the front of the property sequence); + otherwise returns false. + + \sa previous(), hasNext() +*/ +bool QScriptValueIterator::hasPrevious() const +{ + return d_ptr->hasPrevious(); +} + +/*! + Moves the iterator back by one position. + + Calling this function on an iterator located at the front of the + container leads to undefined results. + + \sa hasPrevious(), next(), name() +*/ +void QScriptValueIterator::previous() +{ + d_ptr->previous(); +} + +/*! + Moves the iterator to the front of the QScriptValue (before the + first property). + + \sa toBack(), next() +*/ +void QScriptValueIterator::toFront() +{ + d_ptr->toFront(); +} + +/*! + Moves the iterator to the back of the QScriptValue (after the + last property). + + \sa toFront(), previous() +*/ +void QScriptValueIterator::toBack() +{ + d_ptr->toBack(); +} + +/*! + Returns the name of the last property that was jumped over using + next() or previous(). + + \sa value(), flags() +*/ +QString QScriptValueIterator::name() const +{ + return d_ptr->name(); +} + +/*! + Returns the name of the last property that was jumped over using + next() or previous(). +*/ +QScriptString QScriptValueIterator::scriptName() const +{ + return QScriptStringPrivate::get(d_ptr->scriptName()); +} + +/*! + Returns the value of the last property that was jumped over using + next() or previous(). + + \sa setValue(), name() +*/ +QScriptValue QScriptValueIterator::value() const +{ + return QScriptValuePrivate::get(d_ptr->value()); +} + +/*! + Sets the \a value of the last property that was jumped over using + next() or previous(). + + \sa value(), name() +*/ +void QScriptValueIterator::setValue(const QScriptValue& value) +{ + d_ptr->setValue(QScriptValuePrivate::get(value)); +} + +/*! + Removes the last property that was jumped over using next() + or previous(). + + \sa setValue() +*/ +void QScriptValueIterator::remove() +{ + d_ptr->remove(); +} + +/*! + Returns the flags of the last property that was jumped over using + next() or previous(). + + \sa value() +*/ +QScriptValue::PropertyFlags QScriptValueIterator::flags() const +{ + return d_ptr->flags(); +} + +/*! + Makes the iterator operate on \a object. The iterator is set to be + at the front of the sequence of properties (before the first + property). +*/ +QScriptValueIterator& QScriptValueIterator::operator=(QScriptValue& object) +{ + d_ptr = new QScriptValueIteratorPrivate(QScriptValuePrivate::get(object)); + return *this; +} diff --git a/JavaScriptCore/qt/api/qscriptvalueiterator.h b/JavaScriptCore/qt/api/qscriptvalueiterator.h new file mode 100644 index 0000000..0c90661 --- /dev/null +++ b/JavaScriptCore/qt/api/qscriptvalueiterator.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef qscriptvalueiterator_h +#define qscriptvalueiterator_h + +#include "qtscriptglobal.h" +#include "qscriptstring.h" +#include <QtCore/qshareddata.h> +#include "qscriptvalue.h" + +class QScriptValue; +class QScriptValueIteratorPrivate; + + +class Q_JAVASCRIPT_EXPORT QScriptValueIterator { +public: + QScriptValueIterator(const QScriptValue& value); + ~QScriptValueIterator(); + + bool hasNext() const; + void next(); + + bool hasPrevious() const; + void previous(); + + QString name() const; + QScriptString scriptName() const; + + QScriptValue value() const; + void setValue(const QScriptValue& value); + + void remove(); + QScriptValue::PropertyFlags flags() const; + + void toFront(); + void toBack(); + + QScriptValueIterator& operator=(QScriptValue& value); +private: + QExplicitlySharedDataPointer<QScriptValueIteratorPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QScriptValueIterator) + Q_DISABLE_COPY(QScriptValueIterator) +}; + +#endif // qscriptvalueiterator_h diff --git a/JavaScriptCore/qt/api/qscriptvalueiterator_p.h b/JavaScriptCore/qt/api/qscriptvalueiterator_p.h new file mode 100644 index 0000000..b93b518 --- /dev/null +++ b/JavaScriptCore/qt/api/qscriptvalueiterator_p.h @@ -0,0 +1,172 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef qscriptvalueiterator_p_h +#define qscriptvalueiterator_p_h + +#include "qscriptvalue_p.h" +#include <JavaScriptCore/JavaScript.h> +#include <JavaScriptCore/JSRetainPtr.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qvector.h> + +class QScriptValueIteratorPrivate: public QSharedData { +public: + inline QScriptValueIteratorPrivate(const QScriptValuePrivate* value); + inline ~QScriptValueIteratorPrivate(); + + inline bool hasNext(); + inline void next(); + + inline bool hasPrevious(); + inline void previous(); + + inline QString name() const; + inline QScriptStringPrivate* scriptName() const; + + inline QScriptValuePrivate* value() const; + inline void setValue(const QScriptValuePrivate* value); + + inline void remove(); + + inline void toFront(); + inline void toBack(); + + QScriptValue::PropertyFlags flags() const; + + inline bool isValid() const; +private: + inline QScriptEnginePrivate* engine() const; + + QExplicitlySharedDataPointer<QScriptValuePrivate> m_object; + QVector<JSStringRef> m_names; + QMutableVectorIterator<JSStringRef> m_idx; +}; + +inline QScriptValueIteratorPrivate::QScriptValueIteratorPrivate(const QScriptValuePrivate* value) + : m_object(const_cast<QScriptValuePrivate*>(value)) + , m_idx(m_names) +{ + if (m_object->isObject()) { + m_names = engine()->objectGetOwnPropertyNames(*m_object); + m_idx = m_names; + } else + m_object = 0; +} + +inline QScriptValueIteratorPrivate::~QScriptValueIteratorPrivate() +{ + QVector<JSStringRef>::const_iterator i = m_names.constBegin(); + for (; i != m_names.constEnd(); ++i) + JSStringRelease(*i); +} + +inline bool QScriptValueIteratorPrivate::hasNext() +{ + return m_idx.hasNext(); +} + +inline void QScriptValueIteratorPrivate::next() +{ + // FIXME (Qt5) This method should return a value (QTBUG-11226). + m_idx.next(); +} + +inline bool QScriptValueIteratorPrivate::hasPrevious() +{ + return m_idx.hasPrevious(); +} + +inline void QScriptValueIteratorPrivate::previous() +{ + m_idx.previous(); +} + +inline QString QScriptValueIteratorPrivate::name() const +{ + if (!isValid()) + return QString(); + return QScriptConverter::toString(m_idx.value()); +} + +inline QScriptStringPrivate* QScriptValueIteratorPrivate::scriptName() const +{ + if (!isValid()) + return new QScriptStringPrivate(); + return new QScriptStringPrivate(QScriptConverter::toString(m_idx.value())); +} + +inline QScriptValuePrivate* QScriptValueIteratorPrivate::value() const +{ + if (!isValid()) + return new QScriptValuePrivate(); + JSValueRef exception = 0; + JSValueRef value = m_object->property(m_idx.value(), &exception); + engine()->setException(exception); + return new QScriptValuePrivate(engine(), value); +} + +inline void QScriptValueIteratorPrivate::setValue(const QScriptValuePrivate* value) +{ + if (!isValid()) + return; + JSValueRef exception = 0; + m_object->setProperty(m_idx.value(), *value, /* flags */ 0, &exception); + engine()->setException(exception); +} + +inline void QScriptValueIteratorPrivate::remove() +{ + if (!isValid()) + return; + JSValueRef exception = 0; + m_object->deleteProperty(m_idx.value(), &exception); + engine()->setException(exception); + m_idx.remove(); +} + +inline void QScriptValueIteratorPrivate::toFront() +{ + m_idx.toFront(); +} + +inline void QScriptValueIteratorPrivate::toBack() +{ + m_idx.toBack(); +} + +QScriptValue::PropertyFlags QScriptValueIteratorPrivate::flags() const +{ + if (!isValid()) + return QScriptValue::PropertyFlags(0); + return m_object->propertyFlags(m_idx.value(), QScriptValue::ResolveLocal); +} + +inline bool QScriptValueIteratorPrivate::isValid() const +{ + return m_object; +} + +inline QScriptEnginePrivate* QScriptValueIteratorPrivate::engine() const +{ + Q_ASSERT(isValid()); + return m_object->engine(); +} + +#endif // qscriptvalueiterator_p_h diff --git a/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.cpp b/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.cpp index 73b57dc..a82347e 100644 --- a/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.cpp +++ b/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.cpp @@ -1261,4 +1261,28 @@ void tst_QScriptValue::propertyFlag() QVERIFY(!object.propertyFlags(protoNameHandle, QScriptValue::ResolveLocal)); } +void tst_QScriptValue::globalObjectChanges() +{ + // API functionality shouldn't depend on Global Object. + QScriptEngine engine; + QScriptValue array = engine.newArray(); + QScriptValue error = engine.evaluate("new Error"); + QScriptValue object = engine.newObject(); + + object.setProperty("foo", 512); + + // Remove properties form global object. + engine.evaluate("delete Object; delete Error; delete Array;"); + + QVERIFY(array.isArray()); + QVERIFY(error.isError()); + QVERIFY(object.isObject()); + + QVERIFY(object.property("foo").isValid()); + QVERIFY(object.property("foo", QScriptValue::ResolveLocal).isValid()); + object.setProperty("foo", QScriptValue()); + QVERIFY(!object.property("foo").isValid()); + QVERIFY(!object.property("foo", QScriptValue::ResolveLocal).isValid()); +} + QTEST_MAIN(tst_QScriptValue) diff --git a/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.h b/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.h index 6108423..41b99cd 100644 --- a/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.h +++ b/JavaScriptCore/qt/tests/qscriptvalue/tst_qscriptvalue.h @@ -60,6 +60,7 @@ private slots: void getPropertyResolveFlag(); void propertyFlag_data(); void propertyFlag(); + void globalObjectChanges(); // Generated test functions. void isBool_data(); diff --git a/JavaScriptCore/qt/tests/qscriptvalueiterator/qscriptvalueiterator.pro b/JavaScriptCore/qt/tests/qscriptvalueiterator/qscriptvalueiterator.pro new file mode 100644 index 0000000..5314ec9 --- /dev/null +++ b/JavaScriptCore/qt/tests/qscriptvalueiterator/qscriptvalueiterator.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = tst_qscriptvalueiterator +QT += testlib +include(../tests.pri) + +SOURCES += tst_qscriptvalueiterator.cpp + diff --git a/JavaScriptCore/qt/tests/qscriptvalueiterator/tst_qscriptvalueiterator.cpp b/JavaScriptCore/qt/tests/qscriptvalueiterator/tst_qscriptvalueiterator.cpp new file mode 100644 index 0000000..43d0042 --- /dev/null +++ b/JavaScriptCore/qt/tests/qscriptvalueiterator/tst_qscriptvalueiterator.cpp @@ -0,0 +1,531 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef tst_qscriptvalueiterator_h +#define tst_qscriptvalueiterator_h + +#include "qscriptengine.h" +#include "qscriptvalue.h" +#include "qscriptvalueiterator.h" +#include <QtCore/qhash.h> +#include <QtTest/QtTest> + +class tst_QScriptValueIterator : public QObject { + Q_OBJECT + +public: + tst_QScriptValueIterator(); + virtual ~tst_QScriptValueIterator(); + +private slots: + void iterateForward_data(); + void iterateForward(); + void iterateBackward_data(); + void iterateBackward(); + void iterateArray_data(); + void iterateArray(); + void iterateBackAndForth(); + void setValue(); + void remove(); + void removeMixed(); + void removeUndeletable(); + void iterateString(); + void assignObjectToIterator(); +}; + +tst_QScriptValueIterator::tst_QScriptValueIterator() +{ +} + +tst_QScriptValueIterator::~tst_QScriptValueIterator() +{ +} + +void tst_QScriptValueIterator::iterateForward_data() +{ + QTest::addColumn<QStringList>("propertyNames"); + QTest::addColumn<QStringList>("propertyValues"); + + QTest::newRow("no properties") + << QStringList() << QStringList(); + QTest::newRow("foo=bar") + << (QStringList() << "foo") + << (QStringList() << "bar"); + QTest::newRow("foo=bar, baz=123") + << (QStringList() << "foo" << "baz") + << (QStringList() << "bar" << "123"); + QTest::newRow("foo=bar, baz=123, rab=oof") + << (QStringList() << "foo" << "baz" << "rab") + << (QStringList() << "bar" << "123" << "oof"); +} + +void tst_QScriptValueIterator::iterateForward() +{ + QFETCH(QStringList, propertyNames); + QFETCH(QStringList, propertyValues); + QMap<QString, QString> pmap; + Q_ASSERT(propertyNames.size() == propertyValues.size()); + + QScriptEngine engine; + QScriptValue object = engine.newObject(); + for (int i = 0; i < propertyNames.size(); ++i) { + QString name = propertyNames.at(i); + QString value = propertyValues.at(i); + pmap.insert(name, value); + object.setProperty(name, QScriptValue(&engine, value)); + } + QScriptValue otherObject = engine.newObject(); + otherObject.setProperty("foo", QScriptValue(&engine, 123456)); + otherObject.setProperty("protoProperty", QScriptValue(&engine, 654321)); + object.setPrototype(otherObject); // should not affect iterator + + QStringList lst; + QScriptValueIterator it(object); + while (!pmap.isEmpty()) { + QCOMPARE(it.hasNext(), true); + QCOMPARE(it.hasNext(), true); + it.next(); + QString name = it.name(); + QCOMPARE(pmap.contains(name), true); + QCOMPARE(it.name(), name); + QCOMPARE(it.flags(), object.propertyFlags(name)); + QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, pmap.value(name))), true); + QCOMPARE(it.scriptName(), engine.toStringHandle(name)); + pmap.remove(name); + lst.append(name); + } + + QCOMPARE(it.hasNext(), false); + QCOMPARE(it.hasNext(), false); + + it.toFront(); + for (int i = 0; i < lst.count(); ++i) { + QCOMPARE(it.hasNext(), true); + it.next(); + QCOMPARE(it.name(), lst.at(i)); + } + + for (int i = 0; i < lst.count(); ++i) { + QCOMPARE(it.hasPrevious(), true); + it.previous(); + QCOMPARE(it.name(), lst.at(lst.count()-1-i)); + } + QCOMPARE(it.hasPrevious(), false); +} + +void tst_QScriptValueIterator::iterateBackward_data() +{ + iterateForward_data(); +} + +void tst_QScriptValueIterator::iterateBackward() +{ + QFETCH(QStringList, propertyNames); + QFETCH(QStringList, propertyValues); + QMap<QString, QString> pmap; + Q_ASSERT(propertyNames.size() == propertyValues.size()); + + QScriptEngine engine; + QScriptValue object = engine.newObject(); + for (int i = 0; i < propertyNames.size(); ++i) { + QString name = propertyNames.at(i); + QString value = propertyValues.at(i); + pmap.insert(name, value); + object.setProperty(name, QScriptValue(&engine, value)); + } + + QStringList lst; + QScriptValueIterator it(object); + it.toBack(); + while (!pmap.isEmpty()) { + QCOMPARE(it.hasPrevious(), true); + QCOMPARE(it.hasPrevious(), true); + it.previous(); + QString name = it.name(); + QCOMPARE(pmap.contains(name), true); + QCOMPARE(it.name(), name); + QCOMPARE(it.flags(), object.propertyFlags(name)); + QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, pmap.value(name))), true); + pmap.remove(name); + lst.append(name); + } + + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasPrevious(), false); + + it.toBack(); + for (int i = 0; i < lst.count(); ++i) { + QCOMPARE(it.hasPrevious(), true); + it.previous(); + QCOMPARE(it.name(), lst.at(i)); + } + + for (int i = 0; i < lst.count(); ++i) { + QCOMPARE(it.hasNext(), true); + it.next(); + QCOMPARE(it.name(), lst.at(lst.count()-1-i)); + } + QCOMPARE(it.hasNext(), false); +} + +void tst_QScriptValueIterator::iterateArray_data() +{ + QTest::addColumn<QStringList>("inputPropertyNames"); + QTest::addColumn<QStringList>("inputPropertyValues"); + QTest::addColumn<QStringList>("propertyNames"); + QTest::addColumn<QStringList>("propertyValues"); + QTest::newRow("no elements") << QStringList() << QStringList() << QStringList() << QStringList(); + + QTest::newRow("0=foo, 1=barr") + << (QStringList() << "0" << "1") + << (QStringList() << "foo" << "bar") + << (QStringList() << "0" << "1") + << (QStringList() << "foo" << "bar"); + + QTest::newRow("0=foo, 3=barr") + << (QStringList() << "0" << "1" << "2" << "3") + << (QStringList() << "foo" << "" << "" << "bar") + << (QStringList() << "0" << "1" << "2" << "3") + << (QStringList() << "foo" << "" << "" << "bar"); +} + +void tst_QScriptValueIterator::iterateArray() +{ + QFETCH(QStringList, inputPropertyNames); + QFETCH(QStringList, inputPropertyValues); + QFETCH(QStringList, propertyNames); + QFETCH(QStringList, propertyValues); + + QScriptEngine engine; + QScriptValue array = engine.newArray(); + for (int i = 0; i < inputPropertyNames.size(); ++i) + array.setProperty(inputPropertyNames.at(i), inputPropertyValues.at(i)); + + int length = array.property("length").toInt32(); + QCOMPARE(length, propertyNames.size()); + QScriptValueIterator it(array); + for (int i = 0; i < length; ++i) { + QCOMPARE(it.hasNext(), true); + it.next(); + QCOMPARE(it.name(), propertyNames.at(i)); + QCOMPARE(it.flags(), array.propertyFlags(propertyNames.at(i))); + QVERIFY(it.value().strictlyEquals(array.property(propertyNames.at(i)))); + QCOMPARE(it.value().toString(), propertyValues.at(i)); + } + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("length")); + QVERIFY(it.value().isNumber()); + QCOMPARE(it.value().toInt32(), length); + QCOMPARE(it.flags(), QScriptValue::PropertyFlags(QScriptValue::SkipInEnumeration | QScriptValue::Undeletable)); + + it.previous(); + QCOMPARE(it.hasPrevious(), length > 0); + for (int i = length - 1; i >= 0; --i) { + it.previous(); + QCOMPARE(it.name(), propertyNames.at(i)); + QCOMPARE(it.flags(), array.propertyFlags(propertyNames.at(i))); + QVERIFY(it.value().strictlyEquals(array.property(propertyNames.at(i)))); + QCOMPARE(it.value().toString(), propertyValues.at(i)); + QCOMPARE(it.hasPrevious(), i > 0); + } + QCOMPARE(it.hasPrevious(), false); + + // hasNext() and hasPrevious() cache their result; verify that the result is in sync + if (length > 1) { + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("0")); + QVERIFY(it.hasNext()); + it.previous(); + QCOMPARE(it.name(), QString::fromLatin1("0")); + QVERIFY(!it.hasPrevious()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("0")); + QVERIFY(it.hasPrevious()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("1")); + } + { + // same test as object: + QScriptValue originalArray = engine.newArray(); + for (int i = 0; i < inputPropertyNames.size(); ++i) + originalArray.setProperty(inputPropertyNames.at(i), inputPropertyValues.at(i)); + + QScriptValue array = originalArray.toObject(); + int length = array.property("length").toInt32(); + QCOMPARE(length, propertyNames.size()); + QScriptValueIterator it(array); + for (int i = 0; i < length; ++i) { + QCOMPARE(it.hasNext(), true); + it.next(); + QCOMPARE(it.name(), propertyNames.at(i)); + QCOMPARE(it.flags(), array.propertyFlags(propertyNames.at(i))); + QVERIFY(it.value().strictlyEquals(array.property(propertyNames.at(i)))); + QCOMPARE(it.value().toString(), propertyValues.at(i)); + } + QCOMPARE(it.hasNext(), true); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("length")); + } +} + +void tst_QScriptValueIterator::iterateBackAndForth() +{ + QScriptEngine engine; + { + QScriptValue object = engine.newObject(); + object.setProperty("foo", QScriptValue(&engine, "bar")); + object.setProperty("rab", QScriptValue(&engine, "oof"), + QScriptValue::SkipInEnumeration); // should not affect iterator + QScriptValueIterator it(object); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QLatin1String("foo")); + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QLatin1String("foo")); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QLatin1String("foo")); + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QLatin1String("foo")); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QLatin1String("foo")); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QLatin1String("rab")); + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QLatin1String("rab")); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QLatin1String("rab")); + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QLatin1String("rab")); + } + { + // hasNext() and hasPrevious() cache their result; verify that the result is in sync + QScriptValue object = engine.newObject(); + object.setProperty("foo", QScriptValue(&engine, "bar")); + object.setProperty("rab", QScriptValue(&engine, "oof")); + QScriptValueIterator it(object); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + QVERIFY(it.hasNext()); + it.previous(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + QVERIFY(!it.hasPrevious()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + QVERIFY(it.hasPrevious()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("rab")); + } +} + +void tst_QScriptValueIterator::setValue() +{ + QScriptEngine engine; + QScriptValue object = engine.newObject(); + object.setProperty("foo", QScriptValue(&engine, "bar")); + QScriptValueIterator it(object); + it.next(); + QCOMPARE(it.name(), QLatin1String("foo")); + it.setValue(QScriptValue(&engine, "baz")); + QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, QLatin1String("baz"))), true); + QCOMPARE(object.property("foo").toString(), QLatin1String("baz")); + it.setValue(QScriptValue(&engine, "zab")); + QCOMPARE(it.value().strictlyEquals(QScriptValue(&engine, QLatin1String("zab"))), true); + QCOMPARE(object.property("foo").toString(), QLatin1String("zab")); +} + +void tst_QScriptValueIterator::remove() +{ + QScriptEngine engine; + QScriptValue object = engine.newObject(); + object.setProperty("foo", QScriptValue(&engine, "bar"), + QScriptValue::SkipInEnumeration); // should not affect iterator + object.setProperty("rab", QScriptValue(&engine, "oof")); + QScriptValueIterator it(object); + it.next(); + QCOMPARE(it.name(), QLatin1String("foo")); + it.remove(); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(object.property("foo").isValid(), false); + QCOMPARE(object.property("rab").toString(), QLatin1String("oof")); + it.next(); + QCOMPARE(it.name(), QLatin1String("rab")); + QCOMPARE(it.value().toString(), QLatin1String("oof")); + QCOMPARE(it.hasNext(), false); + it.remove(); + QCOMPARE(object.property("rab").isValid(), false); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasNext(), false); +} + +void tst_QScriptValueIterator::removeMixed() +{ + // This test checks if QScriptValueIterator behaives correctly if an object's property got deleted + // in different way. + QScriptEngine engine; + QScriptValue object = engine.evaluate("o = new Object; o"); + object.setProperty("a", QScriptValue(124), QScriptValue::SkipInEnumeration); + object.setProperty("b", QScriptValue(816)); + object.setProperty("c", QScriptValue(3264)); + QScriptValueIterator it(object); + it.next(); + it.next(); + QCOMPARE(it.name(), QLatin1String("b")); + QCOMPARE(it.hasPrevious(), true); + QCOMPARE(it.hasNext(), true); + // Remove 'a' + object.setProperty("a", QScriptValue()); + QEXPECT_FAIL("", "That would be a significant behavioral and performance change, new QtScript API should be developed (QTBUG-12087)", Abort); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasNext(), true); + // Remove 'c' + engine.evaluate("delete o.c"); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasNext(), false); + // Remove 'b' + object.setProperty("b", QScriptValue()); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasNext(), false); + QCOMPARE(it.name(), QString()); + QCOMPARE(it.value().toString(), QString()); + + // Try to remove a removed property. + it.remove(); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasNext(), false); + QCOMPARE(it.name(), QString()); + QCOMPARE(it.value().toString(), QString()); + + for (int i = 0; i < 2; ++i) { + it.next(); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasNext(), false); + QCOMPARE(it.name(), QString()); + QCOMPARE(it.value().toString(), QString()); + } + + for (int i = 0; i < 2; ++i) { + it.previous(); + QCOMPARE(it.hasPrevious(), false); + QCOMPARE(it.hasNext(), false); + QCOMPARE(it.name(), QString()); + QCOMPARE(it.value().toString(), QString()); + } +} + +void tst_QScriptValueIterator::removeUndeletable() +{ + // Undeletable property can't be deleted via iterator. + QScriptEngine engine; + QScriptValue object = engine.evaluate("o = new Object; o"); + object.setProperty("a", QScriptValue(&engine, 124)); + object.setProperty("b", QScriptValue(&engine, 816), QScriptValue::Undeletable); + QVERIFY(object.property("b").isValid()); + QScriptValueIterator it(object); + it.next(); + it.next(); + it.remove(); + it.toFront(); + QVERIFY(it.hasNext()); + QVERIFY(object.property("b").isValid()); +} + +void tst_QScriptValueIterator::iterateString() +{ + QScriptEngine engine; + QScriptValue str = QScriptValue(&engine, QString::fromLatin1("ciao")); + QVERIFY(str.isString()); + QScriptValue obj = str.toObject(); + int length = obj.property("length").toInt32(); + QCOMPARE(length, 4); + QScriptValueIterator it(obj); + for (int i = 0; i < length; ++i) { + QCOMPARE(it.hasNext(), true); + QString indexStr = QScriptValue(&engine, i).toString(); + it.next(); + QCOMPARE(it.name(), indexStr); + QCOMPARE(it.flags(), obj.propertyFlags(indexStr)); + QCOMPARE(it.value().strictlyEquals(obj.property(indexStr)), true); + } + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("length")); + QVERIFY(it.value().isNumber()); + QCOMPARE(it.value().toInt32(), length); + QCOMPARE(it.flags(), QScriptValue::PropertyFlags(QScriptValue::ReadOnly | QScriptValue::SkipInEnumeration | QScriptValue::Undeletable)); + + it.previous(); + QCOMPARE(it.hasPrevious(), length > 0); + for (int i = length - 1; i >= 0; --i) { + it.previous(); + QString indexStr = QScriptValue(&engine, i).toString(); + QCOMPARE(it.name(), indexStr); + QCOMPARE(it.flags(), obj.propertyFlags(indexStr)); + QCOMPARE(it.value().strictlyEquals(obj.property(indexStr)), true); + QCOMPARE(it.hasPrevious(), i > 0); + } + QCOMPARE(it.hasPrevious(), false); +} + +void tst_QScriptValueIterator::assignObjectToIterator() +{ + QScriptEngine eng; + QScriptValue obj1 = eng.newObject(); + obj1.setProperty("foo", 123); + QScriptValue obj2 = eng.newObject(); + obj2.setProperty("bar", 456); + + QScriptValueIterator it(obj1); + QVERIFY(it.hasNext()); + it.next(); + it = obj2; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + + it = obj1; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + + it = obj2; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + + it = obj2; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); +} + +QTEST_MAIN(tst_QScriptValueIterator) +#include "tst_qscriptvalueiterator.moc" + +#endif // tst_qscriptvalueiterator_h diff --git a/JavaScriptCore/qt/tests/tests.pro b/JavaScriptCore/qt/tests/tests.pro index 7c3f590..6d5559b 100644 --- a/JavaScriptCore/qt/tests/tests.pro +++ b/JavaScriptCore/qt/tests/tests.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs SUBDIRS = qscriptengine \ qscriptvalue \ + qscriptvalueiterator \ qscriptstring |