Ensure that scarce resources work with var properties
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qdeclarativeecmascript / tst_qdeclarativeecmascript.cpp
index efa95d3..5e62ec6 100644 (file)
@@ -7,29 +7,29 @@
 ** This file is part of the test suite of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
 ** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
-** rights.  These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
 **
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
 **
 **
 **
@@ -38,7 +38,7 @@
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/
-#include <qtest.h>
+#include <QtTest/QtTest>
 #include <QtDeclarative/qdeclarativecomponent.h>
 #include <QtDeclarative/qdeclarativeengine.h>
 #include <QtDeclarative/qdeclarativeexpression.h>
 #include <QtCore/qdir.h>
 #include <QtCore/qnumeric.h>
 #include <private/qdeclarativeengine_p.h>
-#include <private/qdeclarativeglobalscriptclass_p.h>
-#include <private/qscriptdeclarativeclass_p.h>
+#include <private/qv8gccallback_p.h>
+#include <private/qdeclarativevmemetaobject_p.h>
+#include <private/qv4compiler_p.h>
 #include "testtypes.h"
 #include "testhttpserver.h"
-#include "../../../shared/util.h"
-
-#ifdef Q_OS_SYMBIAN
-// In Symbian OS test data is located in applications private dir
-#define SRCDIR "."
-#endif
+#include "../../shared/util.h"
 
 /*
 This test covers evaluation of ECMAScript expressions and bindings from within
@@ -68,8 +64,7 @@ Static QML language issues are covered in qmllanguage
 */
 inline QUrl TEST_FILE(const QString &filename)
 {
-    QFileInfo fileInfo(__FILE__);
-    return QUrl::fromLocalFile(fileInfo.absoluteDir().filePath("data/" + filename));
+    return QUrl::fromLocalFile(TESTDATA(filename));
 }
 
 inline QUrl TEST_FILE(const char *filename)
@@ -106,14 +101,17 @@ private slots:
     void constantsOverrideBindings();
     void outerBindingOverridesInnerBinding();
     void aliasPropertyAndBinding();
+    void aliasPropertyReset();
     void nonExistentAttachedObject();
     void scope();
+    void importScope();
     void signalParameterTypes();
     void objectsCompareAsEqual();
     void dynamicCreation_data();
     void dynamicCreation();
     void dynamicDestruction();
     void objectToString();
+    void objectHasOwnProperty();
     void selfDeletingBinding();
     void extendedObjectPropertyLookup();
     void scriptErrors();
@@ -130,6 +128,7 @@ private slots:
     void jsObject();
     void undefinedResetsProperty();
     void listToVariant();
+    void listAssignment();
     void multiEngineObject();
     void deletedObject();
     void attachedPropertyScope();
@@ -144,13 +143,50 @@ private slots:
     void numberAssignment();
     void propertySplicing();
     void signalWithUnknownTypes();
+    void signalWithJSValueInVariant_data();
+    void signalWithJSValueInVariant();
+    void signalWithJSValueInVariant_twoEngines_data();
+    void signalWithJSValueInVariant_twoEngines();
+    void moduleApi_data();
     void moduleApi();
+    void importScripts_data();
     void importScripts();
     void scarceResources();
+    void scarceResources_data();
+    void scarceResources_other();
+    void propertyChangeSlots();
+    void propertyVar_data();
+    void propertyVar();
+    void propertyVarCpp();
+    void propertyVarOwnership();
+    void propertyVarImplicitOwnership();
+    void propertyVarReparent();
+    void propertyVarReparentNullContext();
+    void propertyVarCircular();
+    void propertyVarCircular2();
+    void propertyVarInheritance();
+    void propertyVarInheritance2();
+    void elementAssign();
+    void objectPassThroughSignals();
+    void objectConversion();
+    void booleanConversion();
+    void handleReferenceManagement();
+    void stringArg();
+    void readonlyDeclaration();
+    void sequenceConversionRead();
+    void sequenceConversionWrite();
+    void sequenceConversionArray();
+    void sequenceConversionThreads();
+    void sequenceConversionBindings();
+    void sequenceConversionCopy();
+    void assignSequenceTypes();
+    void qtbug_22464();
+    void qtbug_21580();
 
     void bug1();
     void bug2();
     void dynamicCreationCrash();
+    void dynamicCreationOwnership();
     void regExpBug();
     void nullObjectBinding();
     void deletedEngine();
@@ -169,27 +205,44 @@ private slots:
     void qtbug_10696();
     void qtbug_11606();
     void qtbug_11600();
+    void qtbug_21864();
     void nonscriptable();
     void deleteLater();
     void in();
+    void typeOf();
     void sharedAttachedObject();
     void objectName();
     void writeRemovesBinding();
     void aliasBindingsAssignCorrectly();
     void aliasBindingsOverrideTarget();
     void aliasWritesOverrideBindings();
-    void pushCleanContext();
+    void aliasToCompositeElement();
     void realToInt();
-
+    void dynamicString();
     void include();
-
+    void signalHandlers();
+    void doubleEvaluate();
+    void forInLoop();
+    void nonNotifyable();
+    void deleteWhileBindingRunning();
     void callQtInvokables();
     void invokableObjectArg();
     void invokableObjectRet();
-
+    void qtbug_20344();
+    void qtbug_22679();
+    void qtbug_22843_data();
+    void qtbug_22843();
     void revisionErrors();
     void revision();
+
+    void automaticSemicolon();
+    void unaryExpression();
+    void switchStatement();
+    void withStatement();
+    void tryStatement();
+
 private:
+    static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
     QDeclarativeEngine engine;
 };
 
@@ -375,7 +428,7 @@ void tst_qdeclarativeecmascript::methods()
 void tst_qdeclarativeecmascript::bindingLoop()
 {
     QDeclarativeComponent component(&engine, TEST_FILE("bindingLoop.qml"));
-    QString warning = component.url().toString() + ":9:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
+    QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
     QObject *object = component.create();
     QVERIFY(object != 0);
@@ -614,7 +667,7 @@ void tst_qdeclarativeecmascript::deferredPropertiesErrors()
     QVERIFY(object->objectProperty() == 0);
     QVERIFY(object->objectProperty2() == 0);
 
-    QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject* objectProperty";
+    QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*";
     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
 
     qmlExecuteDeferred(object);
@@ -680,6 +733,8 @@ void tst_qdeclarativeecmascript::attachedProperties()
         QCOMPARE(object->property("b").toInt(), 26);
         QCOMPARE(object->property("c").toInt(), 26);
         QCOMPARE(object->property("d").toInt(), 26);
+
+        delete object;
     }
 
     {
@@ -723,8 +778,8 @@ void tst_qdeclarativeecmascript::enums()
     {
     QDeclarativeComponent component(&engine, TEST_FILE("enums.2.qml"));
 
-    QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int a";
-    QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int b";
+    QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int";
+    QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int";
     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
 
@@ -862,7 +917,7 @@ void tst_qdeclarativeecmascript::nonExistentAttachedObject()
 {
     QDeclarativeComponent component(&engine, TEST_FILE("nonExistentAttachedObject.qml"));
 
-    QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString stringProperty";
+    QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString";
     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
 
     QObject *object = component.create();
@@ -958,6 +1013,19 @@ void tst_qdeclarativeecmascript::scope()
     }
 }
 
+// In 4.7, non-library javascript files that had no imports shared the imports of their
+// importing context
+void tst_qdeclarativeecmascript::importScope()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("importScope.qml"));
+    QObject *o = component.create();
+    QVERIFY(o != 0);
+
+    QCOMPARE(o->property("test").toInt(), 240);
+
+    delete o;
+}
+
 /*
 Tests that "any" type passes through a synthesized signal parameter.  This
 is essentially a test of QDeclarativeMetaType::copy()
@@ -1020,6 +1088,87 @@ void tst_qdeclarativeecmascript::aliasPropertyAndBinding()
     delete object;
 }
 
+/*
+Ensure that we can write undefined value to an alias property,
+and that the aliased property is reset correctly if possible.
+*/
+void tst_qdeclarativeecmascript::aliasPropertyReset()
+{
+    QObject *object = 0;
+
+    // test that a manual write (of undefined) to a resettable aliased property succeeds
+    QDeclarativeComponent c1(&engine, TEST_FILE("aliasreset/aliasPropertyReset.1.qml"));
+    object = c1.create();
+    QVERIFY(object != 0);
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
+    QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
+    QMetaObject::invokeMethod(object, "resetAliased");
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
+    QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
+    delete object;
+
+    // test that a manual write (of undefined) to a resettable alias property succeeds
+    QDeclarativeComponent c2(&engine, TEST_FILE("aliasreset/aliasPropertyReset.2.qml"));
+    object = c2.create();
+    QVERIFY(object != 0);
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
+    QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
+    QMetaObject::invokeMethod(object, "resetAlias");
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
+    QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
+    delete object;
+
+    // test that an alias to a bound property works correctly
+    QDeclarativeComponent c3(&engine, TEST_FILE("aliasreset/aliasPropertyReset.3.qml"));
+    object = c3.create();
+    QVERIFY(object != 0);
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
+    QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
+    QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
+    QMetaObject::invokeMethod(object, "resetAlias");
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
+    QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
+    QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
+    delete object;
+
+    // test that a manual write (of undefined) to a resettable alias property
+    // whose aliased property's object has been deleted, does not crash.
+    QDeclarativeComponent c4(&engine, TEST_FILE("aliasreset/aliasPropertyReset.4.qml"));
+    object = c4.create();
+    QVERIFY(object != 0);
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() != 0);
+    QObject *loader = object->findChild<QObject*>("loader");
+    QVERIFY(loader != 0);
+    delete loader;
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0); // deletion should have caused value unset.
+    QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
+    QMetaObject::invokeMethod(object, "setAlias");   // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0);
+    delete object;
+
+    // test that binding an alias property to an undefined value works correctly
+    QDeclarativeComponent c5(&engine, TEST_FILE("aliasreset/aliasPropertyReset.5.qml"));
+    object = c5.create();
+    QVERIFY(object != 0);
+    QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0); // bound to undefined value.
+    delete object;
+
+    // test that a manual write (of undefined) to a non-resettable property fails properly
+    QUrl url = TEST_FILE("aliasreset/aliasPropertyReset.error.1.qml");
+    QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
+    QDeclarativeComponent e1(&engine, url);
+    object = e1.create();
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("intAlias").value<int>(), 12);
+    QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
+    QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
+    QMetaObject::invokeMethod(object, "resetAlias");
+    QCOMPARE(object->property("intAlias").value<int>(), 12);
+    QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
+    delete object;
+}
+
 void tst_qdeclarativeecmascript::dynamicCreation_data()
 {
     QTest::addColumn<QString>("method");
@@ -1056,6 +1205,7 @@ void tst_qdeclarativeecmascript::dynamicCreation()
 */
 void tst_qdeclarativeecmascript::dynamicDestruction()
 {
+    {
     QDeclarativeComponent component(&engine, TEST_FILE("dynamicDeletion.qml"));
     QDeclarativeGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create());
     QVERIFY(object != 0);
@@ -1084,6 +1234,27 @@ void tst_qdeclarativeecmascript::dynamicDestruction()
     QTest::qWait(0);
     QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
     QVERIFY(!object);
+    }
+
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("dynamicDeletion.2.qml"));
+    QObject *o = component.create();
+    QVERIFY(o != 0);
+
+    QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
+
+    QMetaObject::invokeMethod(o, "create");
+
+    QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
+
+    QMetaObject::invokeMethod(o, "destroy");
+
+    QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
+
+    QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0);
+
+    delete o;
+    }
 }
 
 /*
@@ -1102,6 +1273,55 @@ void tst_qdeclarativeecmascript::objectToString()
 }
 
 /*
+  tests that id.hasOwnProperty() works
+*/
+void tst_qdeclarativeecmascript::objectHasOwnProperty()
+{
+    QUrl url = TEST_FILE("declarativeHasOwnProperty.qml");
+    QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
+    QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
+    QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
+
+    QDeclarativeComponent component(&engine, url);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    // test QObjects in QML
+    QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
+    QVERIFY(object->property("result").value<bool>() == true);
+    QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");
+    QVERIFY(object->property("result").value<bool>() == false);
+
+    // now test other types in QML
+    QObject *child = object->findChild<QObject*>("typeObj");
+    QVERIFY(child != 0);
+    QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess");
+    QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
+    QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
+    QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true);
+    QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true);
+
+    QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
+    QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne");
+    QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
+    QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
+    QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo");
+    QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false);
+    QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData());
+    QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree");
+    QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
+
+    delete object;
+}
+
+/*
 Tests bindings that indirectly cause their own deletion work.
 
 This test is best run under valgrind to ensure no invalid memory access occur.
@@ -1151,11 +1371,11 @@ void tst_qdeclarativeecmascript::scriptErrors()
     QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
     QString warning2 = url + ":5: ReferenceError: Can't find variable: a";
     QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
-    QString warning4 = url + ":10: ReferenceError: Can't find variable: a";
-    QString warning5 = url + ":8: ReferenceError: Can't find variable: a";
-    QString warning6 = url + ":7: Unable to assign [undefined] to int x";
-    QString warning7 = url + ":12: Error: Cannot assign to read-only property \"trueProperty\"";
-    QString warning8 = url + ":13: Error: Cannot assign to non-existent property \"fakeProperty\"";
+    QString warning4 = url + ":13: ReferenceError: Can't find variable: a";
+    QString warning5 = url + ":11: ReferenceError: Can't find variable: a";
+    QString warning6 = url + ":10: Unable to assign [undefined] to int";
+    QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\"";
+    QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
 
     QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
     QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData());
@@ -1194,11 +1414,15 @@ void tst_qdeclarativeecmascript::functionErrors()
     delete object;
 
     // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
-    QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml"));
+    QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceResourceFunctionFail.var.qml"));
     url = componentTwo.url().toString();
     object = componentTwo.create();
     QVERIFY(object != 0);
-    warning = url + QLatin1String(":16: TypeError: Result of expression 'scarceResourceProvider.scarceResource' [[object Object]] is not a function.");
+
+    QString srpname = object->property("srp_name").toString();
+    
+    warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname
+                  + QLatin1String(" is not a function");
     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
     QMetaObject::invokeMethod(object, "retrieveScarceResource");
     delete object;
@@ -1213,15 +1437,12 @@ void tst_qdeclarativeecmascript::propertyAssignmentErrors()
 
     QString url = component.url().toString();
 
-    QString warning1 = url + ":11:Error: Cannot assign [undefined] to int";
-    QString warning2 = url + ":17:Error: Cannot assign QString to int";
-
-    QTest::ignoreMessage(QtDebugMsg, warning1.toLatin1().constData());
-    QTest::ignoreMessage(QtDebugMsg, warning2.toLatin1().constData());
-
     QObject *object = component.create();
     QVERIFY(object != 0);
 
+    QCOMPARE(object->property("test1").toBool(), true);
+    QCOMPARE(object->property("test2").toBool(), true);
+
     delete object;
 }
     
@@ -1276,7 +1497,7 @@ void tst_qdeclarativeecmascript::exceptionClearsOnReeval()
     QDeclarativeComponent component(&engine, TEST_FILE("exceptionClearsOnReeval.qml"));
     QString url = component.url().toString();
 
-    QString warning = url + ":4: TypeError: Result of expression 'objectProperty' [null] is not an object.";
+    QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
 
     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
     MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
@@ -1382,6 +1603,7 @@ void tst_qdeclarativeecmascript::shutdownErrors()
 void tst_qdeclarativeecmascript::compositePropertyType()
 {
     QDeclarativeComponent component(&engine, TEST_FILE("compositePropertyType.qml"));
+
     QTest::ignoreMessage(QtDebugMsg, "hello world");
     QObject *object = qobject_cast<QObject *>(component.create());
     delete object;
@@ -1433,6 +1655,30 @@ void tst_qdeclarativeecmascript::undefinedResetsProperty()
     }
 }
 
+// Aliases to variant properties should work
+void tst_qdeclarativeecmascript::qtbug_22464()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("qtbug_22464.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::qtbug_21580()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("qtbug_21580.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
 // QTBUG-6781
 void tst_qdeclarativeecmascript::bug1()
 {
@@ -1479,6 +1725,40 @@ void tst_qdeclarativeecmascript::dynamicCreationCrash()
     delete object;
 }
 
+// ownership transferred to JS, ensure that GC runs the dtor
+void tst_qdeclarativeecmascript::dynamicCreationOwnership()
+{
+    int dtorCount = 0;
+    int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
+
+    // allow the engine to go out of scope too.
+    {
+        QDeclarativeEngine dcoEngine;
+        QDeclarativeComponent component(&dcoEngine, TEST_FILE("dynamicCreationOwnership.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
+        QVERIFY(mdcdo != 0);
+        mdcdo->setDtorCount(&dtorCount);
+
+        for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
+            QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
+            if (i % 90 == 0) {
+                // we do this once manually, but it should be done automatically
+                // when the engine goes out of scope (since it should gc in dtor)
+                QMetaObject::invokeMethod(object, "performGc");
+            }
+            if (i % 10 == 0) {
+                QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+            }
+        }
+
+        delete object;
+    }
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+    QCOMPARE(dtorCount, expectedDtorCount);
+}
+
 //QTBUG-9367
 void tst_qdeclarativeecmascript::regExpBug()
 {
@@ -1489,56 +1769,119 @@ void tst_qdeclarativeecmascript::regExpBug()
     delete object;
 }
 
+static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source)
+{
+    QString functionSource = QLatin1String("(function(object) { return ") + 
+                             QLatin1String(source) + QLatin1String(" })");
+    v8::TryCatch tc;
+    v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
+    if (tc.HasCaught())
+        return false;
+    v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
+    if (function.IsEmpty())
+        return false;
+    v8::Handle<v8::Value> args[] = { o };
+    function->Call(engine->global(), 1, args);
+    return tc.HasCaught();
+}
+
+static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o, 
+                                  const char *source, v8::Handle<v8::Value> result)
+{
+    QString functionSource = QLatin1String("(function(object) { return ") + 
+                             QLatin1String(source) + QLatin1String(" })");
+    v8::TryCatch tc;
+    v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
+    if (tc.HasCaught())
+        return false;
+    v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
+    if (function.IsEmpty())
+        return false;
+    v8::Handle<v8::Value> args[] = { o };
+
+    v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
+
+    if (tc.HasCaught())
+        return false;
+
+    return value->StrictEquals(result);
+}
+
+static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o, 
+                                             const char *source)
+{
+    QString functionSource = QLatin1String("(function(object) { return ") + 
+                             QLatin1String(source) + QLatin1String(" })");
+    v8::TryCatch tc;
+    v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource));
+    if (tc.HasCaught())
+        return v8::Handle<v8::Value>();
+    v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run());
+    if (function.IsEmpty())
+        return v8::Handle<v8::Value>();
+    v8::Handle<v8::Value> args[] = { o };
+
+    v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args);
+
+    if (tc.HasCaught())
+        return v8::Handle<v8::Value>();
+    return value;
+}
+
+#define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
+#define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
+#define EVALUATE(source) evaluate(engine, object, source)
+
 void tst_qdeclarativeecmascript::callQtInvokables()
 {
     MyInvokableObject o;
 
     QDeclarativeEngine qmlengine;
     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&qmlengine);
-    QScriptEngine *engine = &ep->scriptEngine;
+    
+    QV8Engine *engine = ep->v8engine();
 
-    QStringList names; QList<QScriptValue> values;
-    names << QLatin1String("object"); values << ep->objectClass->newQObject(&o);
-    names << QLatin1String("undefined"); values << engine->undefinedValue();
+    v8::HandleScope handle_scope;
+    v8::Context::Scope scope(engine->context());
 
-    ep->globalClass->explicitSetProperty(names, values);
+    v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject();
 
     // Non-existent methods
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_nonexistent()").isError(), true);
+    QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), -1);
     QCOMPARE(o.actuals().count(), 0);
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_nonexistent(10, 11)").isError(), true);
+    QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), -1);
     QCOMPARE(o.actuals().count(), 0);
 
     // Insufficient arguments
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int()").isError(), true);
+    QVERIFY(EVALUATE_ERROR("object.method_int()"));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), -1);
     QCOMPARE(o.actuals().count(), 0);
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_intint(10)").isError(), true);
+    QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), -1);
     QCOMPARE(o.actuals().count(), 0);
 
     // Excessive arguments
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int(10, 11)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 8);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(10));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_intint(10, 11, 12)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 9);
     QCOMPARE(o.actuals().count(), 2);
@@ -1547,27 +1890,28 @@ void tst_qdeclarativeecmascript::callQtInvokables()
 
     // Test return types
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_NoArgs()").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 0);
     QCOMPARE(o.actuals().count(), 0);
 
     o.reset();
-    QVERIFY(engine->evaluate("object.method_NoArgs_int()").strictlyEquals(QScriptValue(engine, 6)));
+    QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6)));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 1);
     QCOMPARE(o.actuals().count(), 0);
 
     o.reset();
-    QVERIFY(engine->evaluate("object.method_NoArgs_real()").strictlyEquals(QScriptValue(engine, 19.75)));
+    QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75)));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 2);
     QCOMPARE(o.actuals().count(), 0);
 
     o.reset();
     {
-    QScriptValue ret = engine->evaluate("object.method_NoArgs_QPointF()");
-    QCOMPARE(ret.toVariant(), QVariant(QPointF(123, 4.5)));
+    v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()");
+    QVERIFY(!ret.IsEmpty());
+    QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 3);
     QCOMPARE(o.actuals().count(), 0);
@@ -1575,81 +1919,80 @@ void tst_qdeclarativeecmascript::callQtInvokables()
 
     o.reset();
     {
-    QScriptValue ret = engine->evaluate("object.method_NoArgs_QObject()");
-    QVERIFY(ret.isQObject());
-    QCOMPARE(ret.toQObject(), (QObject *)&o);
+    v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()");
+    QCOMPARE(engine->toQObject(ret), (QObject *)&o);
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 4);
     QCOMPARE(o.actuals().count(), 0);
     }
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_NoArgs_unknown()").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 5);
     QCOMPARE(o.actuals().count(), 0);
 
     o.reset();
     {
-    QScriptValue ret = engine->evaluate("object.method_NoArgs_QScriptValue()");
-    QVERIFY(ret.isString());
-    QCOMPARE(ret.toString(), QString("Hello world"));
+    v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()");
+    QVERIFY(ret->IsString());
+    QCOMPARE(engine->toString(ret), QString("Hello world"));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 6);
     QCOMPARE(o.actuals().count(), 0);
     }
 
     o.reset();
-    QVERIFY(engine->evaluate("object.method_NoArgs_QVariant()").strictlyEquals(QScriptValue(engine, "QML rocks")));
+    QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks")));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 7);
     QCOMPARE(o.actuals().count(), 0);
 
     // Test arg types
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int(94)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 8);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(94));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int(\"94\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 8);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(94));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int(\"not a number\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 8);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int(null)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 8);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int(undefined)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 8);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_int(object)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 8);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_intint(122, 9)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 9);
     QCOMPARE(o.actuals().count(), 2);
@@ -1657,56 +2000,56 @@ void tst_qdeclarativeecmascript::callQtInvokables()
     QCOMPARE(o.actuals().at(1), QVariant(9));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_real(94.3)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 10);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(94.3));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_real(\"94.3\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 10);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(94.3));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_real(\"not a number\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 10);
     QCOMPARE(o.actuals().count(), 1);
     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_real(null)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 10);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_real(undefined)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 10);
     QCOMPARE(o.actuals().count(), 1);
     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_real(object)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 10);
     QCOMPARE(o.actuals().count(), 1);
     QVERIFY(qIsNaN(o.actuals().at(0).toDouble()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QString(\"Hello world\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 11);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant("Hello world"));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QString(19)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 11);
     QCOMPARE(o.actuals().count(), 1);
@@ -1715,7 +2058,7 @@ void tst_qdeclarativeecmascript::callQtInvokables()
     o.reset();
     {
     QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")";
-    QCOMPARE(engine->evaluate("object.method_QString(object)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 11);
     QCOMPARE(o.actuals().count(), 1);
@@ -1723,171 +2066,171 @@ void tst_qdeclarativeecmascript::callQtInvokables()
     }
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QString(null)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 11);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QString()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QString(undefined)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 11);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QString()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QPointF(0)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 12);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QPointF(null)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 12);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QPointF(undefined)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 12);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QPointF(object)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 12);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QPointF()));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QPointF(object.method_get_QPointF())").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 12);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2)));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QPointF(object.method_get_QPoint())").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 12);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12)));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QObject(0)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 13);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QObject(\"Hello world\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 13);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QObject(null)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 13);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QObject(undefined)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 13);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QObject(object)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 13);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QScriptValue(null)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 14);
     QCOMPARE(o.actuals().count(), 1);
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).isNull());
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull());
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QScriptValue(undefined)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 14);
     QCOMPARE(o.actuals().count(), 1);
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).isUndefined());
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined());
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QScriptValue(19)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 14);
     QCOMPARE(o.actuals().count(), 1);
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).strictlyEquals(QScriptValue(engine, 19)));
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19)));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QScriptValue([19, 20])").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 14);
     QCOMPARE(o.actuals().count(), 1);
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(0)).isArray());
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray());
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_intQScriptValue(4, null)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 15);
     QCOMPARE(o.actuals().count(), 2);
     QCOMPARE(o.actuals().at(0), QVariant(4));
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).isNull());
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull());
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_intQScriptValue(8, undefined)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 15);
     QCOMPARE(o.actuals().count(), 2);
     QCOMPARE(o.actuals().at(0), QVariant(8));
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).isUndefined());
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined());
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_intQScriptValue(3, 19)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 15);
     QCOMPARE(o.actuals().count(), 2);
     QCOMPARE(o.actuals().at(0), QVariant(3));
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).strictlyEquals(QScriptValue(engine, 19)));
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19)));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_intQScriptValue(44, [19, 20])").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 15);
     QCOMPARE(o.actuals().count(), 2);
     QCOMPARE(o.actuals().at(0), QVariant(44));
-    QVERIFY(qvariant_cast<QScriptValue>(o.actuals().at(1)).isArray());
+    QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray());
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_overload()").isError(), true);
+    QVERIFY(EVALUATE_ERROR("object.method_overload()"));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), -1);
     QCOMPARE(o.actuals().count(), 0);
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_overload(10)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 16);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(10));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_overload(10, 11)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 17);
     QCOMPARE(o.actuals().count(), 2);
@@ -1895,21 +2238,21 @@ void tst_qdeclarativeecmascript::callQtInvokables()
     QCOMPARE(o.actuals().at(1), QVariant(11));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_overload(\"Hello\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 18);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(QString("Hello")));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_with_enum(9)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 19);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(9));
 
     o.reset();
-    QVERIFY(engine->evaluate("object.method_default(10)").strictlyEquals(QScriptValue(19)));
+    QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19)));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 20);
     QCOMPARE(o.actuals().count(), 2);
@@ -1917,7 +2260,7 @@ void tst_qdeclarativeecmascript::callQtInvokables()
     QCOMPARE(o.actuals().at(1), QVariant(19));
 
     o.reset();
-    QVERIFY(engine->evaluate("object.method_default(10, 13)").strictlyEquals(QScriptValue(13)));
+    QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13)));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 20);
     QCOMPARE(o.actuals().count(), 2);
@@ -1925,14 +2268,14 @@ void tst_qdeclarativeecmascript::callQtInvokables()
     QCOMPARE(o.actuals().at(1), QVariant(13));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_inherited(9)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), -3);
     QCOMPARE(o.actuals().count(), 1);
     QCOMPARE(o.actuals().at(0), QVariant(9));
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QVariant(9)").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 21);
     QCOMPARE(o.actuals().count(), 2);
@@ -1940,7 +2283,7 @@ void tst_qdeclarativeecmascript::callQtInvokables()
     QCOMPARE(o.actuals().at(1), QVariant());
 
     o.reset();
-    QCOMPARE(engine->evaluate("object.method_QVariant(\"Hello\", \"World\")").isUndefined(), true);
+    QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined()));
     QCOMPARE(o.error(), false);
     QCOMPARE(o.invoked(), 21);
     QCOMPARE(o.actuals().count(), 2);
@@ -1993,6 +2336,21 @@ void tst_qdeclarativeecmascript::listToVariant()
     delete object;
 }
 
+// QTBUG-16316
+Q_DECLARE_METATYPE(QDeclarativeListProperty<MyQmlObject>)
+void tst_qdeclarativeecmascript::listAssignment()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("listAssignment.qml"));
+    QObject *obj = component.create();
+    QCOMPARE(obj->property("list1length").toInt(), 2);
+    QDeclarativeListProperty<MyQmlObject> list1 = obj->property("list1").value<QDeclarativeListProperty<MyQmlObject> >();
+    QDeclarativeListProperty<MyQmlObject> list2 = obj->property("list2").value<QDeclarativeListProperty<MyQmlObject> >();
+    QCOMPARE(list1.count(&list1), list2.count(&list2));
+    QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
+    QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
+    delete obj;
+}
+
 // QTBUG-7957
 void tst_qdeclarativeecmascript::multiEngineObject()
 {
@@ -2235,7 +2593,8 @@ void tst_qdeclarativeecmascript::ownership()
         QVERIFY(own.object != 0);
 
         QObject *object = component.create(context);
-        QDeclarativeEnginePrivate::getScriptEngine(&engine)->collectGarbage();
+
+        engine.collectGarbage();
 
         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
 
@@ -2252,7 +2611,8 @@ void tst_qdeclarativeecmascript::ownership()
         QVERIFY(own.object != 0);
 
         QObject *object = component.create(context);
-        QDeclarativeEnginePrivate::getScriptEngine(&engine)->collectGarbage();
+        
+        engine.collectGarbage();
 
         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
 
@@ -2272,16 +2632,12 @@ public:
     ~CppOwnershipReturnValue() { delete value; }
 
     Q_INVOKABLE QObject *create() {
-        Q_ASSERT(value == 0);
-
         value = new QObject;
         QDeclarativeEngine::setObjectOwnership(value, QDeclarativeEngine::CppOwnership);
         return value;
     }
 
     Q_INVOKABLE MyQmlObject *createQmlObject() {
-        Q_ASSERT(value == 0);
-
         MyQmlObject *rv = new MyQmlObject;
         value = rv;
         return rv;
@@ -2340,6 +2696,7 @@ void tst_qdeclarativeecmascript::ownershipCustomReturnValue()
     delete object;
     }
 
+    engine.collectGarbage();
     QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion);
 
     QVERIFY(source.value == 0);
@@ -2501,231 +2858,366 @@ void tst_qdeclarativeecmascript::signalWithUnknownTypes()
     delete object;
 }
 
-void tst_qdeclarativeecmascript::moduleApi()
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant_data()
 {
-    QDeclarativeComponent component(&engine, TEST_FILE("moduleApi.qml"));
-    QObject *object = component.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("existingUriTest").toInt(), 20);
-    QCOMPARE(object->property("scriptTest").toInt(), 13);
-    QCOMPARE(object->property("qobjectTest").toInt(), 20);
-    QCOMPARE(object->property("qobjectMinorVersionTest").toInt(), 20);
-    QCOMPARE(object->property("qobjectMajorVersionTest").toInt(), 20);
-    QCOMPARE(object->property("qobjectParentedTest").toInt(), 26);
-    delete object;
+    QTest::addColumn<QString>("expression");
+    QTest::addColumn<QString>("compare");
+
+    QString compareStrict("(function(a, b) { return a === b; })");
+    QTest::newRow("true") << "true" << compareStrict;
+    QTest::newRow("undefined") << "undefined" << compareStrict;
+    QTest::newRow("null") << "null" << compareStrict;
+    QTest::newRow("123") << "123" << compareStrict;
+    QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
+
+    QString comparePropertiesStrict(
+        "(function(a, b) {"
+        "  if (typeof b != 'object')"
+        "    return a === b;"
+        "  var props = Object.getOwnPropertyNames(b);"
+        "  for (var i = 0; i < props.length; ++i) {"
+        "    var p = props[i];"
+        "    return arguments.callee(a[p], b[p]);"
+        "  }"
+        "})");
+    QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })"  << comparePropertiesStrict;
+    QTest::newRow("[10,20,30]") << "[10,20,30]"  << comparePropertiesStrict;
+}
 
-    // test that caching of module apis works correctly.
-    QDeclarativeComponent componentTwo(&engine, TEST_FILE("moduleApiCaching.qml"));
-    object = componentTwo.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("existingUriTest").toInt(), 20);
-    QCOMPARE(object->property("scriptTest").toInt(), 13);            // shouldn't have incremented.
-    QCOMPARE(object->property("qobjectParentedTest").toInt(), 26);   // shouldn't have incremented.
-    delete object;
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant()
+{
+    QFETCH(QString, expression);
+    QFETCH(QString, compare);
 
-    // test that writing to a property of module apis works correctly.
-    QDeclarativeComponent componentThree(&engine, TEST_FILE("moduleApiWriting.qml"));
-    QString expectedWarning = QLatin1String("file://") + TEST_FILE("moduleApiWriting.qml").toLocalFile() + QLatin1String(":15: Error: Cannot assign to read-only property \"qobjectTestProperty\"");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
-    object = componentThree.create();
+    QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
+    QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
     QVERIFY(object != 0);
-    QCOMPARE(object->property("readOnlyProperty").toInt(), 20);
-    QCOMPARE(object->property("writableProperty").toInt(), 50);
-    QVERIFY(object->setProperty("firstProperty", QVariant(30))); // shouldn't affect value of readOnlyProperty
-    QVERIFY(object->setProperty("writableProperty", QVariant(30))); // SHOULD affect value of writableProperty
-    QCOMPARE(object->property("readOnlyProperty").toInt(), 20);
-    QCOMPARE(object->property("writableProperty").toInt(), 30);
-    delete object;
 
-    QDeclarativeComponent failOne(&engine, TEST_FILE("moduleApiMajorVersionFail.qml"));
-    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
-    object = failOne.create();
-    QVERIFY(object == 0); // should have failed: invalid major version
+    QJSValue value = engine.evaluate(expression);
+    QVERIFY(!engine.hasUncaughtException());
+    object->setProperty("expression", expression);
+    object->setProperty("compare", compare);
+    object->setProperty("pass", false);
 
-    QDeclarativeComponent failTwo(&engine, TEST_FILE("moduleApiMinorVersionFail.qml"));
-    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
-    object = failTwo.create();
-    QVERIFY(object == 0); // should have failed: invalid minor version
+    emit object->signalWithVariant(QVariant::fromValue(value));
+    QVERIFY(object->property("pass").toBool());
 }
 
-void tst_qdeclarativeecmascript::importScripts()
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines_data()
 {
-    QObject *object = 0;
+    signalWithJSValueInVariant_data();
+}
 
-    // first, ensure that the required behaviour works.
-    QDeclarativeComponent component(&engine, TEST_FILE("jsimport/testImport.qml"));
-    object = component.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("importedScriptStringValue"), QVariant(QString(QLatin1String("Hello, World!"))));
-    QCOMPARE(object->property("importedScriptFunctionValue"), QVariant(20));
-    QCOMPARE(object->property("importedModuleAttachedPropertyValue"), QVariant(19));
-    QCOMPARE(object->property("importedModuleEnumValue"), QVariant(2));
-    delete object;
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines()
+{
+    QFETCH(QString, expression);
+    QFETCH(QString, compare);
 
-    QDeclarativeComponent componentTwo(&engine, TEST_FILE("jsimport/testImportScoping.qml"));
-    object = componentTwo.create();
+    QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
+    QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
     QVERIFY(object != 0);
-    QCOMPARE(object->property("componentError"), QVariant(5));
-    delete object;
 
-    // then, ensure that unintended behaviour does not work.
-    QDeclarativeComponent failOneComponent(&engine, TEST_FILE("jsimportfail/failOne.qml"));
-    QString expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Result of expression 'TestScriptImport.ImportOneJs' [undefined] is not an object.");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
-    object = failOneComponent.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("importScriptFunctionValue").toString().isEmpty());
-    delete object;
-    QDeclarativeComponent failTwoComponent(&engine, TEST_FILE("jsimportfail/failTwo.qml"));
-    expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
-    object = failTwoComponent.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("importScriptFunctionValue").toString().isEmpty());
-    delete object;
-    QDeclarativeComponent failThreeComponent(&engine, TEST_FILE("jsimportfail/failThree.qml"));
-    expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Result of expression 'testQtObject.TestModuleImport.JsQtTest' [undefined] is not an object.");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
-    object = failThreeComponent.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("importedModuleAttachedPropertyValue"), QVariant(false));
-    delete object;
-    QDeclarativeComponent failFourComponent(&engine, TEST_FILE("jsimportfail/failFour.qml"));
-    expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
-    object = failFourComponent.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("importedModuleEnumValue"), QVariant(0));
-    delete object;
-    QDeclarativeComponent failFiveComponent(&engine, TEST_FILE("jsimportfail/failFive.qml"));
-    expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
-    expectedWarning = QLatin1String("file://") + TEST_FILE("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData());
-    object = failFiveComponent.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("componentError"), QVariant(0));
-    delete object;
+    QJSEngine engine2;
+    QJSValue value = engine2.evaluate(expression);
+    QVERIFY(!engine2.hasUncaughtException());
+    object->setProperty("expression", expression);
+    object->setProperty("compare", compare);
+    object->setProperty("pass", false);
 
-    // also, test that importing scripts with .pragma library works as required
-    QDeclarativeComponent pragmaLibraryComponent(&engine, TEST_FILE("jsimport/testImportPragmaLibrary.qml"));
-    object = pragmaLibraryComponent.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("testValue"), QVariant(31));
-    delete object;
+    QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
+    emit object->signalWithVariant(QVariant::fromValue(value));
+    QVERIFY(!object->property("pass").toBool());
+}
 
-    // and that .pragma library scripts don't inherit imports from any .qml file
-    QDeclarativeComponent pragmaLibraryComponentTwo(&engine, TEST_FILE("jsimportfail/testImportPragmaLibrary.qml"));
-    object = pragmaLibraryComponentTwo.create();
-    QVERIFY(object != 0);
-    QCOMPARE(object->property("testValue"), QVariant(0));
-    delete object;
+void tst_qdeclarativeecmascript::moduleApi_data()
+{
+    QTest::addColumn<QUrl>("testfile");
+    QTest::addColumn<QString>("errorMessage");
+    QTest::addColumn<QStringList>("warningMessages");
+    QTest::addColumn<QStringList>("readProperties");
+    QTest::addColumn<QVariantList>("readExpectedValues");
+    QTest::addColumn<QStringList>("writeProperties");
+    QTest::addColumn<QVariantList>("writeValues");
+    QTest::addColumn<QStringList>("readBackProperties");
+    QTest::addColumn<QVariantList>("readBackExpectedValues");
+
+    QTest::newRow("qobject, register + read + method")
+            << TEST_FILE("moduleapi/qobjectModuleApi.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
+                   << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest")
+            << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26)
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList();
+
+    QTest::newRow("script, register + read")
+            << TEST_FILE("moduleapi/scriptModuleApi.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << "scriptTest")
+            << (QVariantList() << 13)
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList();
+
+    QTest::newRow("qobject, caching + read")
+            << TEST_FILE("moduleapi/qobjectModuleApiCaching.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << "existingUriTest" << "qobjectParentedTest")
+            << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27.
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList();
+
+    QTest::newRow("script, caching + read")
+            << TEST_FILE("moduleapi/scriptModuleApiCaching.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << "scriptTest")
+            << (QVariantList() << 13) // 13, shouldn't have incremented to 14.
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList();
+
+    QTest::newRow("qobject, writing + readonly constraints")
+            << TEST_FILE("moduleapi/qobjectModuleApiWriting.qml")
+            << QString()
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/qobjectModuleApiWriting.qml").toLocalFile() + QLatin1String(":14: Error: Cannot assign to read-only property \"qobjectTestProperty\"")))
+            << (QStringList() << "readOnlyProperty" << "writableProperty")
+            << (QVariantList() << 20 << 50)
+            << (QStringList() << "firstProperty" << "writableProperty")
+            << (QVariantList() << 30 << 30)
+            << (QStringList() << "readOnlyProperty" << "writableProperty")
+            << (QVariantList() << 20 << 30);
+
+    QTest::newRow("script, writing + readonly constraints")
+            << TEST_FILE("moduleapi/scriptModuleApiWriting.qml")
+            << QString()
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("moduleapi/scriptModuleApiWriting.qml").toLocalFile() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\"")))
+            << (QStringList() << "readBack" << "unchanged")
+            << (QVariantList() << 13 << 42)
+            << (QStringList() << "firstProperty" << "secondProperty")
+            << (QVariantList() << 30 << 30)
+            << (QStringList() << "readBack" << "unchanged")
+            << (QVariantList() << 30 << 42);
+
+    QTest::newRow("qobject module API enum values in JS")
+            << TEST_FILE("moduleapi/qobjectModuleApiEnums.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << "enumValue" << "enumMethod")
+            << (QVariantList() << 42 << 30)
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList();
+
+    QTest::newRow("qobject, invalid major version fail")
+            << TEST_FILE("moduleapi/moduleApiMajorVersionFail.qml")
+            << QString("QDeclarativeComponent: Component is not ready")
+            << QStringList()
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList();
+
+    QTest::newRow("qobject, invalid minor version fail")
+            << TEST_FILE("moduleapi/moduleApiMinorVersionFail.qml")
+            << QString("QDeclarativeComponent: Component is not ready")
+            << QStringList()
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList()
+            << QStringList()
+            << QVariantList();
 }
 
-void tst_qdeclarativeecmascript::scarceResources()
+void tst_qdeclarativeecmascript::moduleApi()
 {
-    QPixmap origPixmap(100, 100);
-    origPixmap.fill(Qt::blue);
+    QFETCH(QUrl, testfile);
+    QFETCH(QString, errorMessage);
+    QFETCH(QStringList, warningMessages);
+    QFETCH(QStringList, readProperties);
+    QFETCH(QVariantList, readExpectedValues);
+    QFETCH(QStringList, writeProperties);
+    QFETCH(QVariantList, writeValues);
+    QFETCH(QStringList, readBackProperties);
+    QFETCH(QVariantList, readBackExpectedValues);
 
-    QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
-    ScarceResourceObject *eo = 0;
-    QObject *object = 0;
+    QDeclarativeComponent component(&engine, testfile);
 
-    // in the following three cases, the instance created from the component
-    // has a property which is a copy of the scarce resource; hence, the
-    // resource should NOT be detached prior to deletion of the object instance,
-    // unless the resource is destroyed explicitly.
-    QDeclarativeComponent component(&engine, TEST_FILE("scarceresources/scarceResourceCopy.qml"));
-    object = component.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("scarceResourceCopy").isValid());
-    QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
-    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
-    QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
-    delete object;
+    if (!errorMessage.isEmpty())
+        QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
 
-    QDeclarativeComponent componentTwo(&engine, TEST_FILE("scarceresources/scarceResourceCopyFromJs.qml"));
-    object = componentTwo.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("scarceResourceCopy").isValid());
-    QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
-    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
-    QVERIFY(!eo->scarceResourceIsDetached()); // there are two copies of it in existence: the property of object, and the property of eo.
-    delete object;
+    if (warningMessages.size())
+        foreach (const QString &warning, warningMessages)
+            QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
 
-    QDeclarativeComponent componentThree(&engine, TEST_FILE("scarceresources/scarceResourceDestroyedCopy.qml"));
-    object = componentThree.create();
-    QVERIFY(object != 0);
-    QVERIFY(!(object->property("scarceResourceCopy").isValid())); // was manually released prior to being returned.
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
-    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
-    QVERIFY(eo->scarceResourceIsDetached()); // should have explicitly been released during the evaluation of the binding.
-    delete object;
+    QObject *object = component.create();
+    if (!errorMessage.isEmpty()) {
+        QVERIFY(object == 0);
+    } else {
+        QVERIFY(object != 0);
+        for (int i = 0; i < readProperties.size(); ++i)
+            QCOMPARE(object->property(readProperties.at(i).toAscii().constData()), readExpectedValues.at(i));
+        for (int i = 0; i < writeProperties.size(); ++i)
+            QVERIFY(object->setProperty(writeProperties.at(i).toAscii().constData(), writeValues.at(i)));
+        for (int i = 0; i < readBackProperties.size(); ++i)
+            QCOMPARE(object->property(readBackProperties.at(i).toAscii().constData()), readBackExpectedValues.at(i));
+        delete object;
+    }
+}
 
-    // in the following three cases, no other copy should exist in memory,
-    // and so it should be detached (unless explicitly preserved).
-    QDeclarativeComponent componentFour(&engine, TEST_FILE("scarceresources/scarceResourceTest.qml"));
-    object = componentFour.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("scarceResourceTest").isValid());
-    QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
-    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
-    QVERIFY(eo->scarceResourceIsDetached()); // the resource should have been released after the binding was evaluated.
-    delete object;
+void tst_qdeclarativeecmascript::importScripts_data()
+{
+    QTest::addColumn<QUrl>("testfile");
+    QTest::addColumn<QString>("errorMessage");
+    QTest::addColumn<QStringList>("warningMessages");
+    QTest::addColumn<QStringList>("propertyNames");
+    QTest::addColumn<QVariantList>("propertyValues");
+
+    QTest::newRow("basic functionality")
+            << TEST_FILE("jsimport/testImport.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << QLatin1String("importedScriptStringValue")
+                              << QLatin1String("importedScriptFunctionValue")
+                              << QLatin1String("importedModuleAttachedPropertyValue")
+                              << QLatin1String("importedModuleEnumValue"))
+            << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
+                               << QVariant(20)
+                               << QVariant(19)
+                               << QVariant(2));
+
+    QTest::newRow("import scoping")
+            << TEST_FILE("jsimport/testImportScoping.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << QLatin1String("componentError"))
+            << (QVariantList() << QVariant(5));
+
+    QTest::newRow("parent scope shouldn't be inherited by import with imports")
+            << TEST_FILE("jsimportfail/failOne.qml")
+            << QString()
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
+            << (QStringList() << QLatin1String("importScriptFunctionValue"))
+            << (QVariantList() << QVariant(QString()));
+
+    QTest::newRow("javascript imports in an import should be private to the import scope")
+            << TEST_FILE("jsimportfail/failTwo.qml")
+            << QString()
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs")))
+            << (QStringList() << QLatin1String("importScriptFunctionValue"))
+            << (QVariantList() << QVariant(QString()));
+
+    QTest::newRow("module imports in an import should be private to the import scope")
+            << TEST_FILE("jsimportfail/failThree.qml")
+            << QString()
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
+            << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
+            << (QVariantList() << QVariant(false));
+
+    QTest::newRow("typenames in an import should be private to the import scope")
+            << TEST_FILE("jsimportfail/failFour.qml")
+            << QString()
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest")))
+            << (QStringList() << QLatin1String("importedModuleEnumValue"))
+            << (QVariantList() << QVariant(0));
+
+    QTest::newRow("import with imports has it's own activation scope")
+            << TEST_FILE("jsimportfail/failFive.qml")
+            << QString()
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component"))
+                              << QString(QLatin1String("file://") + TEST_FILE("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component")))
+            << (QStringList() << QLatin1String("componentError"))
+            << (QVariantList() << QVariant(0));
+
+    QTest::newRow("import pragma library script")
+            << TEST_FILE("jsimport/testImportPragmaLibrary.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << QLatin1String("testValue"))
+            << (QVariantList() << QVariant(31));
+
+    QTest::newRow("pragma library imports shouldn't inherit parent imports or scope")
+            << TEST_FILE("jsimportfail/testImportPragmaLibrary.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << QLatin1String("testValue"))
+            << (QVariantList() << QVariant(0));
+
+    QTest::newRow("import pragma library script which has an import")
+            << TEST_FILE("jsimport/testImportPragmaLibraryWithImports.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << QLatin1String("testValue"))
+            << (QVariantList() << QVariant(55));
+
+    QTest::newRow("import pragma library script which has a pragma library import")
+            << TEST_FILE("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
+            << QString()
+            << QStringList()
+            << (QStringList() << QLatin1String("testValue"))
+            << (QVariantList() << QVariant(18));
+}
 
-    QDeclarativeComponent componentFive(&engine, TEST_FILE("scarceresources/scarceResourceTestPreserve.qml"));
-    object = componentFive.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("scarceResourceTest").isValid());
-    QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
-    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
-    QVERIFY(!eo->scarceResourceIsDetached()); // this won't be detached since we explicitly preserved it.
-    delete object;
+void tst_qdeclarativeecmascript::importScripts()
+{
+    QFETCH(QUrl, testfile);
+    QFETCH(QString, errorMessage);
+    QFETCH(QStringList, warningMessages);
+    QFETCH(QStringList, propertyNames);
+    QFETCH(QVariantList, propertyValues);
 
-    QDeclarativeComponent componentSix(&engine, TEST_FILE("scarceresources/scarceResourceTestMultiple.qml"));
-    object = componentSix.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("scarceResourceTest").isValid());
-    QCOMPARE(object->property("scarceResourceTest").toInt(), 100);
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
-    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
-    QVERIFY(eo->scarceResourceIsDetached()); // all resources were released manually or automatically released.
-    delete object;
+    QDeclarativeComponent component(&engine, testfile);
 
-    // test that scarce resources are handled correctly for imports
-    QDeclarativeComponent componentSeven(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportNoBinding.qml"));
-    object = componentSeven.create();
-    QVERIFY(object != 0); // the import should have caused the addition of a resource to the ScarceResources list
-    QVERIFY(ep->scarceResources == 0); // but they should have been released by this point.
-    delete object;
+    if (!errorMessage.isEmpty())
+        QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData());
 
-    QDeclarativeComponent componentEight(&engine, TEST_FILE("scarceresources/scarceResourceCopyImportFail.qml"));
-    object = componentEight.create();
-    QVERIFY(object != 0);
-    QVERIFY(!object->property("scarceResourceCopy").isValid()); // wasn't preserved, so shouldn't be valid.
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
-    delete object;
+    if (warningMessages.size())
+        foreach (const QString &warning, warningMessages)
+            QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
 
-    QDeclarativeComponent componentNine(&engine, TEST_FILE("scarceresources/scarceResourceCopyImport.qml"));
-    object = componentNine.create();
-    QVERIFY(object != 0);
-    QVERIFY(object->property("scarceResourceCopy").isValid()); // preserved, so should be valid.
-    QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
-    QVERIFY(object->property("scarceResourceAssignedCopyOne").isValid()); // assigned before destroy(), so should be valid.
-    QCOMPARE(object->property("scarceResourceAssignedCopyOne").value<QPixmap>(), origPixmap);
-    QVERIFY(!object->property("scarceResourceAssignedCopyTwo").isValid()); // assigned after destroy(), so should be invalid.
-    QVERIFY(ep->scarceResources == 0); // this will still be zero, because "preserve()" REMOVES it from this list.
-    delete object;
+    QObject *object = component.create();
+    if (!errorMessage.isEmpty()) {
+        QVERIFY(object == 0);
+    } else {
+        QVERIFY(object != 0);
+        for (int i = 0; i < propertyNames.size(); ++i)
+            QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
+        delete object;
+    }
+}
+
+void tst_qdeclarativeecmascript::scarceResources_other()
+{
+    /* These tests require knowledge of state, since we test values after
+       performing signal or function invocation. */
+
+    QPixmap origPixmap(100, 100);
+    origPixmap.fill(Qt::blue);
+    QString srp_name, expectedWarning;
+    QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
+    ScarceResourceObject *eo = 0;
+    QObject *srsc = 0;
+    QObject *object = 0;
+
+    /* property var semantics */
 
     // test that scarce resources are handled properly in signal invocation
-    QDeclarativeComponent componentTen(&engine, TEST_FILE("scarceresources/scarceResourceSignal.qml"));
-    object = componentTen.create();
-    QVERIFY(object != 0);
-    QObject *srsc = object->findChild<QObject*>("srsc");
+    QDeclarativeComponent varComponentTen(&engine, TEST_FILE("scarceResourceSignal.var.qml"));
+    object = varComponentTen.create();
+    srsc = object->findChild<QObject*>("srsc");
     QVERIFY(srsc);
     QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
     QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
@@ -2741,12 +3233,12 @@ void tst_qdeclarativeecmascript::scarceResources()
     QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
     QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+    QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
     delete object;
 
     // test that scarce resources are handled properly from js functions in qml files
-    QDeclarativeComponent componentEleven(&engine, TEST_FILE("scarceresources/scarceResourceFunction.qml"));
-    object = componentEleven.create();
+    QDeclarativeComponent varComponentEleven(&engine, TEST_FILE("scarceResourceFunction.var.qml"));
+    object = varComponentEleven.create();
     QVERIFY(object != 0);
     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
@@ -2760,47 +3252,1431 @@ void tst_qdeclarativeecmascript::scarceResources()
     QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+    QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
     delete object;
 
     // test that if an exception occurs while invoking js function from cpp, that the resources are released.
-    QDeclarativeComponent componentTwelve(&engine, TEST_FILE("scarceresources/scarceResourceFunctionFail.qml"));
-    object = componentTwelve.create();
+    QDeclarativeComponent varComponentTwelve(&engine, TEST_FILE("scarceResourceFunctionFail.var.qml"));
+    object = varComponentTwelve.create();
     QVERIFY(object != 0);
     QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
-    QString expectedWarning = QLatin1String("file://") + TEST_FILE("scarceresources/scarceResourceFunctionFail.qml").toLocalFile() + QLatin1String(":16: TypeError: Result of expression 'scarceResourceProvider.scarceResource' [[object Object]] is not a function.");
-    QTest::ignoreMessage(QtWarningMsg, expectedWarning.toAscii().constData()); // we expect a meaningful warning to be printed.
+    srp_name = object->property("srp_name").toString();
+    expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
+    QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
     QMetaObject::invokeMethod(object, "retrieveScarceResource");
     QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
     eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
     QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
-    QVERIFY(ep->scarceResources == 0); // should have been released by this point.
+    QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
     delete object;
-}
-
-// Test that assigning a null object works 
-// Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
-void tst_qdeclarativeecmascript::nullObjectBinding()
-{
-    QDeclarativeComponent component(&engine, TEST_FILE("nullObjectBinding.qml"));
 
-    QObject *object = component.create();
+    // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
+    // that the scarce resource is removed from the engine's list of scarce resources to clean up.
+    QDeclarativeComponent varComponentThirteen(&engine, TEST_FILE("scarceResourceObjectGc.var.qml"));
+    object = varComponentThirteen.create();
     QVERIFY(object != 0);
-
-    QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
-
+    QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
+    QMetaObject::invokeMethod(object, "assignVarProperty");
+    QVERIFY(ep->scarceResources.isEmpty());             // the scarce resource is a VME property.
+    QMetaObject::invokeMethod(object, "deassignVarProperty");
+    QVERIFY(ep->scarceResources.isEmpty());             // should still be empty; the resource should have been released on gc.
     delete object;
-}
 
-// Test that bindings don't evaluate once the engine has been destroyed
-void tst_qdeclarativeecmascript::deletedEngine()
-{
-    QDeclarativeEngine *engine = new QDeclarativeEngine;
-    QDeclarativeComponent component(engine, TEST_FILE("deletedEngine.qml"));
+    /* property variant semantics */
 
-    QObject *object = component.create();
+    // test that scarce resources are handled properly in signal invocation
+    QDeclarativeComponent variantComponentTen(&engine, TEST_FILE("scarceResourceSignal.variant.qml"));
+    object = variantComponentTen.create();
+    QVERIFY(object != 0);
+    srsc = object->findChild<QObject*>("srsc");
+    QVERIFY(srsc);
+    QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
+    QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+    QMetaObject::invokeMethod(srsc, "testSignal");
+    QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
+    QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
+    QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
+    QVERIFY(srsc->property("scarceResourceCopy").isValid());
+    QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
+    QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
+    delete object;
+
+    // test that scarce resources are handled properly from js functions in qml files
+    QDeclarativeComponent variantComponentEleven(&engine, TEST_FILE("scarceResourceFunction.variant.qml"));
+    object = variantComponentEleven.create();
+    QVERIFY(object != 0);
+    QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+    QMetaObject::invokeMethod(object, "retrieveScarceResource");
+    QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
+    QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
+    QMetaObject::invokeMethod(object, "releaseScarceResource");
+    QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+    QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
+    delete object;
+
+    // test that if an exception occurs while invoking js function from cpp, that the resources are released.
+    QDeclarativeComponent variantComponentTwelve(&engine, TEST_FILE("scarceResourceFunctionFail.variant.qml"));
+    object = variantComponentTwelve.create();
+    QVERIFY(object != 0);
+    QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+    srp_name = object->property("srp_name").toString();
+    expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function");
+    QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
+    QMetaObject::invokeMethod(object, "retrieveScarceResource");
+    QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
+    eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+    QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
+    QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point.
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::scarceResources_data()
+{
+    QTest::addColumn<QUrl>("qmlFile");
+    QTest::addColumn<bool>("readDetachStatus");
+    QTest::addColumn<bool>("expectedDetachStatus");
+    QTest::addColumn<QStringList>("propertyNames");
+    QTest::addColumn<QVariantList>("expectedValidity");
+    QTest::addColumn<QVariantList>("expectedValues");
+    QTest::addColumn<QStringList>("expectedErrors");
+
+    QPixmap origPixmap(100, 100);
+    origPixmap.fill(Qt::blue);
+
+    /* property var semantics */
+
+    // in the following three cases, the instance created from the component
+    // has a property which is a copy of the scarce resource; hence, the
+    // resource should NOT be detached prior to deletion of the object instance,
+    // unless the resource is destroyed explicitly.
+    QTest::newRow("var: import scarce resource copy directly")
+        << TEST_FILE("scarceResourceCopy.var.qml")
+        << true
+        << false // won't be detached, because assigned to property and not explicitly released
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << origPixmap)
+        << QStringList();
+
+    QTest::newRow("var: import scarce resource copy from JS")
+        << TEST_FILE("scarceResourceCopyFromJs.var.qml")
+        << true
+        << false // won't be detached, because assigned to property and not explicitly released
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << origPixmap)
+        << QStringList();
+
+    QTest::newRow("var: import released scarce resource copy from JS")
+        << TEST_FILE("scarceResourceDestroyedCopy.var.qml")
+        << true
+        << true // explicitly released, so it will be detached
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << false)
+        << (QList<QVariant>() << QVariant())
+        << QStringList();
+
+    // in the following three cases, no other copy should exist in memory,
+    // and so it should be detached (unless explicitly preserved).
+    QTest::newRow("var: import auto-release SR from JS in binding side-effect")
+        << TEST_FILE("scarceResourceTest.var.qml")
+        << true
+        << true // auto released, so it will be detached
+        << (QStringList() << QLatin1String("scarceResourceTest"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << QVariant(100))
+        << QStringList();
+    QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
+        << TEST_FILE("scarceResourceTestPreserve.var.qml")
+        << true
+        << false // won't be detached because we explicitly preserve it
+        << (QStringList() << QLatin1String("scarceResourceTest"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << QVariant(100))
+        << QStringList();
+    QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect")
+        << TEST_FILE("scarceResourceTestMultiple.var.qml")
+        << true
+        << true // will be detached because all resources were released manually or automatically.
+        << (QStringList() << QLatin1String("scarceResourceTest"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << QVariant(100))
+        << QStringList();
+
+    // In the following three cases, test that scarce resources are handled
+    // correctly for imports.
+    QTest::newRow("var: import with no binding")
+        << TEST_FILE("scarceResourceCopyImportNoBinding.var.qml")
+        << false // cannot check detach status.
+        << false
+        << QStringList()
+        << QList<QVariant>()
+        << QList<QVariant>()
+        << QStringList();
+    QTest::newRow("var: import with binding without explicit preserve")
+        << TEST_FILE("scarceResourceCopyImportNoBinding.var.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
+        << (QList<QVariant>() << QVariant())
+        << QStringList();
+    QTest::newRow("var: import with explicit release after binding evaluation")
+        << TEST_FILE("scarceResourceCopyImport.var.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
+        << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
+        << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
+        << QStringList();
+    QTest::newRow("var: import with different js objects")
+        << TEST_FILE("scarceResourceCopyImportDifferent.var.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
+        << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
+        << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
+        << QStringList();
+    QTest::newRow("var: import with different js objects and explicit release")
+        << TEST_FILE("scarceResourceMultipleDifferentNoBinding.var.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
+        << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
+        << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
+        << QStringList();
+    QTest::newRow("var: import with same js objects and explicit release")
+        << TEST_FILE("scarceResourceMultipleSameNoBinding.var.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
+        << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
+        << (QList<QVariant>() << QVariant() << QVariant())
+        << QStringList();
+    QTest::newRow("var: binding with same js objects and explicit release")
+        << TEST_FILE("scarceResourceMultipleSameWithBinding.var.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
+        << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
+        << (QList<QVariant>() << QVariant() << QVariant())
+        << QStringList();
+
+
+    /* property variant semantics */
+
+    // in the following three cases, the instance created from the component
+    // has a property which is a copy of the scarce resource; hence, the
+    // resource should NOT be detached prior to deletion of the object instance,
+    // unless the resource is destroyed explicitly.
+    QTest::newRow("variant: import scarce resource copy directly")
+        << TEST_FILE("scarceResourceCopy.variant.qml")
+        << true
+        << false // won't be detached, because assigned to property and not explicitly released
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << origPixmap)
+        << QStringList();
+
+    QTest::newRow("variant: import scarce resource copy from JS")
+        << TEST_FILE("scarceResourceCopyFromJs.variant.qml")
+        << true
+        << false // won't be detached, because assigned to property and not explicitly released
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << origPixmap)
+        << QStringList();
+
+    QTest::newRow("variant: import released scarce resource copy from JS")
+        << TEST_FILE("scarceResourceDestroyedCopy.variant.qml")
+        << true
+        << true // explicitly released, so it will be detached
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << false)
+        << (QList<QVariant>() << QVariant())
+        << QStringList();
+
+    // in the following three cases, no other copy should exist in memory,
+    // and so it should be detached (unless explicitly preserved).
+    QTest::newRow("variant: import auto-release SR from JS in binding side-effect")
+        << TEST_FILE("scarceResourceTest.variant.qml")
+        << true
+        << true // auto released, so it will be detached
+        << (QStringList() << QLatin1String("scarceResourceTest"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << QVariant(100))
+        << QStringList();
+    QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect")
+        << TEST_FILE("scarceResourceTestPreserve.variant.qml")
+        << true
+        << false // won't be detached because we explicitly preserve it
+        << (QStringList() << QLatin1String("scarceResourceTest"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << QVariant(100))
+        << QStringList();
+    QTest::newRow("variant: import multiple scarce resources")
+        << TEST_FILE("scarceResourceTestMultiple.variant.qml")
+        << true
+        << true // will be detached because all resources were released manually or automatically.
+        << (QStringList() << QLatin1String("scarceResourceTest"))
+        << (QList<QVariant>() << true)
+        << (QList<QVariant>() << QVariant(100))
+        << QStringList();
+
+    // In the following three cases, test that scarce resources are handled
+    // correctly for imports.
+    QTest::newRow("variant: import with no binding")
+        << TEST_FILE("scarceResourceCopyImportNoBinding.variant.qml")
+        << false // cannot check detach status.
+        << false
+        << QStringList()
+        << QList<QVariant>()
+        << QList<QVariant>()
+        << QStringList();
+    QTest::newRow("variant: import with binding without explicit preserve")
+        << TEST_FILE("scarceResourceCopyImportNoBinding.variant.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("scarceResourceCopy"))
+        << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
+        << (QList<QVariant>() << QVariant())
+        << QStringList();
+    QTest::newRow("variant: import with explicit release after binding evaluation")
+        << TEST_FILE("scarceResourceCopyImport.variant.qml")
+        << false
+        << false
+        << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
+        << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
+        << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
+        << QStringList();
+}
+
+void tst_qdeclarativeecmascript::scarceResources()
+{
+    QFETCH(QUrl, qmlFile);
+    QFETCH(bool, readDetachStatus);
+    QFETCH(bool, expectedDetachStatus);
+    QFETCH(QStringList, propertyNames);
+    QFETCH(QVariantList, expectedValidity);
+    QFETCH(QVariantList, expectedValues);
+    QFETCH(QStringList, expectedErrors);
+
+    QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&engine);
+    ScarceResourceObject *eo = 0;
+    QObject *object = 0;
+
+    QDeclarativeComponent c(&engine, qmlFile);
+    object = c.create();
+    QVERIFY(object != 0);
+    for (int i = 0; i < propertyNames.size(); ++i) {
+        QString prop = propertyNames.at(i);
+        bool validity = expectedValidity.at(i).toBool();
+        QVariant value = expectedValues.at(i);
+
+        QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
+        if (value.type() == QVariant::Int) {
+            QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
+        } else if (value.type() == QVariant::Pixmap) {
+            QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
+        }
+    }
+
+    if (readDetachStatus) {
+        eo = qobject_cast<ScarceResourceObject*>(QDeclarativeProperty::read(object, "a").value<QObject*>());
+        QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
+    }
+
+    QVERIFY(ep->scarceResources.isEmpty());
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyChangeSlots()
+{
+    // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
+    QDeclarativeComponent component(&engine, TEST_FILE("changeslots/propertyChangeSlots.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    delete object;
+
+    // ensure that invalid property names fail properly.
+    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+    QDeclarativeComponent e1(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.1.qml"));
+    QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
+    QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
+    object = e1.create();
+    QVERIFY(object == 0);
+    delete object;
+
+    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+    QDeclarativeComponent e2(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.2.qml"));
+    expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
+    QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
+    object = e2.create();
+    QVERIFY(object == 0);
+    delete object;
+
+    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+    QDeclarativeComponent e3(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.3.qml"));
+    expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
+    QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
+    object = e3.create();
+    QVERIFY(object == 0);
+    delete object;
+
+    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready");
+    QDeclarativeComponent e4(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.4.qml"));
+    expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
+    QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
+    object = e4.create();
+    QVERIFY(object == 0);
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVar_data()
+{
+    QTest::addColumn<QUrl>("qmlFile");
+
+    // valid
+    QTest::newRow("non-bindable object subproperty changed") << TEST_FILE("propertyVar.1.qml");
+    QTest::newRow("non-bindable object changed") << TEST_FILE("propertyVar.2.qml");
+    QTest::newRow("primitive changed") << TEST_FILE("propertyVar.3.qml");
+    QTest::newRow("javascript array modification") << TEST_FILE("propertyVar.4.qml");
+    QTest::newRow("javascript map modification") << TEST_FILE("propertyVar.5.qml");
+    QTest::newRow("javascript array assignment") << TEST_FILE("propertyVar.6.qml");
+    QTest::newRow("javascript map assignment") << TEST_FILE("propertyVar.7.qml");
+    QTest::newRow("literal property assignment") << TEST_FILE("propertyVar.8.qml");
+    QTest::newRow("qobject property assignment") << TEST_FILE("propertyVar.9.qml");
+}
+
+void tst_qdeclarativeecmascript::propertyVar()
+{
+    QFETCH(QUrl, qmlFile);
+
+    QDeclarativeComponent component(&engine, qmlFile);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
+// Tests that we can write QVariant values to var properties from C++
+void tst_qdeclarativeecmascript::propertyVarCpp()
+{
+    QObject *object = 0;
+
+    // ensure that writing to and reading from a var property from cpp works as required.
+    // Literal values stored in var properties can be read and written as QVariants
+    // of a specific type, whereas object values are read as QVariantMaps.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarCpp.qml"));
+    object = component.create();
+    QVERIFY(object != 0);
+    // assign int to property var that currently has int assigned
+    QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
+    QCOMPARE(object->property("varBound"), QVariant(15));
+    QCOMPARE(object->property("intBound"), QVariant(15));
+    QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
+    QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
+    // assign string to property var that current has bool assigned
+    QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
+    QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
+    QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
+    QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
+    // now enforce behaviour when accessing JavaScript objects from cpp.
+    QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
+    delete object;
+}
+
+static void gc(QDeclarativeEngine &engine)
+{
+    engine.collectGarbage();
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+}
+
+void tst_qdeclarativeecmascript::propertyVarOwnership()
+{
+    // Referenced JS objects are not collected
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("test").toBool(), false);
+    QMetaObject::invokeMethod(object, "runTest");
+    QCOMPARE(object->property("test").toBool(), true);
+    delete object;
+    }
+    // Referenced JS objects are not collected
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.2.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("test").toBool(), false);
+    QMetaObject::invokeMethod(object, "runTest");
+    QCOMPARE(object->property("test").toBool(), true);
+    delete object;
+    }
+    // Qt objects are not collected until they've been dereferenced
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.3.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test2").toBool(), false);
+    QCOMPARE(object->property("test2").toBool(), false);
+
+    QMetaObject::invokeMethod(object, "runTest");
+    QCOMPARE(object->property("test1").toBool(), true);
+
+    QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
+    QVERIFY(!referencedObject.isNull());
+    gc(engine);
+    QVERIFY(!referencedObject.isNull());
+
+    QMetaObject::invokeMethod(object, "runTest2");
+    QCOMPARE(object->property("test2").toBool(), true);
+    gc(engine);
+    QVERIFY(referencedObject.isNull());
+
+    delete object;
+    }
+    // Self reference does not prevent Qt object collection
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.4.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
+    QVERIFY(!referencedObject.isNull());
+    gc(engine);
+    QVERIFY(!referencedObject.isNull());
+
+    QMetaObject::invokeMethod(object, "runTest");
+    gc(engine);
+    QVERIFY(referencedObject.isNull());
+
+    delete object;
+    }
+}
+
+void tst_qdeclarativeecmascript::propertyVarImplicitOwnership()
+{
+    // The childObject has a reference to a different QObject.  We want to ensure
+    // that the different item will not be cleaned up until required.  IE, the childObject
+    // has implicit ownership of the constructed QObject.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarImplicitOwnership.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rootObject = object->property("vp").value<QObject*>();
+    QVERIFY(rootObject != 0);
+    QObject *childObject = rootObject->findChild<QObject*>("text");
+    QVERIFY(childObject != 0);
+    QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
+    QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    QMetaObject::invokeMethod(childObject, "constructQObject");    // creates a reference to a constructed QObject.
+    QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
+    QVERIFY(!qobjectGuard.isNull());
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(!qobjectGuard.isNull());
+    QMetaObject::invokeMethod(object, "deassignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(qobjectGuard.isNull());                                // should have been collected now.
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarReparent()
+{
+    // ensure that nothing breaks if we re-parent objects
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignVarProp");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rect = object->property("vp").value<QObject*>();
+    QObject *text = rect->findChild<QObject*>("textOne");
+    QObject *text2 = rect->findChild<QObject*>("textTwo");
+    QWeakPointer<QObject> rectGuard(rect);
+    QWeakPointer<QObject> textGuard(text);
+    QWeakPointer<QObject> text2Guard(text2);
+    QVERIFY(!rectGuard.isNull());
+    QVERIFY(!textGuard.isNull());
+    QVERIFY(!text2Guard.isNull());
+    QCOMPARE(text->property("textCanary").toInt(), 11);
+    QCOMPARE(text2->property("textCanary").toInt(), 12);
+    // now construct an image which we will reparent.
+    QMetaObject::invokeMethod(text2, "constructQObject");
+    QObject *image = text2->property("vp").value<QObject*>();
+    QWeakPointer<QObject> imageGuard(image);
+    QVERIFY(!imageGuard.isNull());
+    QCOMPARE(image->property("imageCanary").toInt(), 13);
+    // now reparent the "Image" object (currently, it has JS ownership)
+    image->setParent(text);                                        // shouldn't be collected after deassignVp now, since has a parent.
+    QMetaObject::invokeMethod(text2, "deassignVp");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QCOMPARE(text->property("textCanary").toInt(), 11);
+    QCOMPARE(text2->property("textCanary").toInt(), 22);
+    QVERIFY(!imageGuard.isNull());                                 // should still be alive.
+    QCOMPARE(image->property("imageCanary").toInt(), 13);          // still able to access var properties
+    QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(imageGuard.isNull());                                  // should now have been deleted, due to parent being deleted.
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarReparentNullContext()
+{
+    // sometimes reparenting can cause problems
+    // (eg, if the ctxt is collected, varproperties are no longer available)
+    // this test ensures that no crash occurs in that situation.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignVarProp");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rect = object->property("vp").value<QObject*>();
+    QObject *text = rect->findChild<QObject*>("textOne");
+    QObject *text2 = rect->findChild<QObject*>("textTwo");
+    QWeakPointer<QObject> rectGuard(rect);
+    QWeakPointer<QObject> textGuard(text);
+    QWeakPointer<QObject> text2Guard(text2);
+    QVERIFY(!rectGuard.isNull());
+    QVERIFY(!textGuard.isNull());
+    QVERIFY(!text2Guard.isNull());
+    QCOMPARE(text->property("textCanary").toInt(), 11);
+    QCOMPARE(text2->property("textCanary").toInt(), 12);
+    // now construct an image which we will reparent.
+    QMetaObject::invokeMethod(text2, "constructQObject");
+    QObject *image = text2->property("vp").value<QObject*>();
+    QWeakPointer<QObject> imageGuard(image);
+    QVERIFY(!imageGuard.isNull());
+    QCOMPARE(image->property("imageCanary").toInt(), 13);
+    // now reparent the "Image" object (currently, it has JS ownership)
+    image->setParent(object);                                      // reparented to base object.  after deassignVarProp, the ctxt will be invalid.
+    QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(!imageGuard.isNull());                                 // should still be alive.
+    QVERIFY(!image->property("imageCanary").isValid());            // but varProperties won't be available (null context).
+    delete object;
+    QVERIFY(imageGuard.isNull());                                  // should now be dead.
+}
+
+void tst_qdeclarativeecmascript::propertyVarCircular()
+{
+    // enforce behaviour regarding circular references - ensure qdvmemo deletion.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QCOMPARE(object->property("canaryInt"), QVariant(5));
+    QVariant canaryResourceVariant = object->property("canaryResource");
+    QVERIFY(canaryResourceVariant.isValid());
+    QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
+    canaryResourceVariant = QVariant();                            // invalidate it to remove one copy of the pixmap from memory.
+    QMetaObject::invokeMethod(object, "deassignCanaryResource");   // remove one copy of the pixmap from memory
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(!canaryResourcePixmap.isDetached());                   // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
+    QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QCOMPARE(object->property("canaryInt"), QVariant(2));
+    QCOMPARE(object->property("canaryResource"), QVariant(1));
+    QVERIFY(canaryResourcePixmap.isDetached());                    // now detached, since orig copy was member of qdvmemo which was deleted.
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarCircular2()
+{
+    // track deletion of JS-owned parent item with Cpp-owned child
+    // where the child has a var property referencing its parent.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rootObject = object->property("vp").value<QObject*>();
+    QVERIFY(rootObject != 0);
+    QObject *childObject = rootObject->findChild<QObject*>("text");
+    QVERIFY(childObject != 0);
+    QWeakPointer<QObject> rootObjectTracker(rootObject);
+    QVERIFY(!rootObjectTracker.isNull());
+    QWeakPointer<QObject> childObjectTracker(childObject);
+    QVERIFY(!childObjectTracker.isNull());
+    gc(engine);
+    QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
+    QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    QMetaObject::invokeMethod(object, "deassignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(rootObjectTracker.isNull());                           // should have been collected
+    QVERIFY(childObjectTracker.isNull());                          // should have been collected
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
+{
+    *(int*)(parameter) += 1;
+    qPersistentDispose(object);
+}
+
+void tst_qdeclarativeecmascript::propertyVarInheritance()
+{
+    int propertyVarWeakRefCallbackCount = 0;
+
+    // enforce behaviour regarding element inheritance - ensure handle disposal.
+    // The particular component under test here has a chain of references.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.inherit.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    // we want to be able to track when the varProperties array of the last metaobject is disposed
+    QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
+    QObject *ico5 = object->property("varProperty").value<QObject*>()->property("inheritanceVarProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
+    QDeclarativeVMEMetaObject *icovmemo = ((QDeclarativeVMEMetaObject *)(ico5->metaObject()));
+    QDeclarativeVMEMetaObject *ccovmemo = ((QDeclarativeVMEMetaObject *)(cco5->metaObject()));
+    v8::Persistent<v8::Value> icoCanaryHandle;
+    v8::Persistent<v8::Value> ccoCanaryHandle;
+    {
+        v8::HandleScope hs;
+        // XXX NOTE: this is very implementation dependent.  QDVMEMO->vmeProperty() is the only
+        // public function which can return us a handle to something in the varProperties array.
+        icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(41));
+        ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(41));
+        // we make them weak and invoke the gc, but we should not hit the weak-callback yet
+        // as the varproperties array of each vmemo still references the resource.
+        icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
+        ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
+        gc(engine);
+        QVERIFY(propertyVarWeakRefCallbackCount == 0);
+    }
+    // now we deassign the var prop, which should trigger collection of item subtrees.
+    QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    // ensure that there are only weak handles to the underlying varProperties array remaining.
+    gc(engine);
+    QCOMPARE(propertyVarWeakRefCallbackCount, 2);                  // should have been called for both, since all refs should be weak.
+    delete object;
+    // since there are no parent vmemo's to keep implicit references alive, and the only handles
+    // to what remains are weak, all varProperties arrays must have been collected.
+}
+
+void tst_qdeclarativeecmascript::propertyVarInheritance2()
+{
+    int propertyVarWeakRefCallbackCount = 0;
+
+    // The particular component under test here does NOT have a chain of references; the
+    // only link between rootObject and childObject is that rootObject is the parent of childObject.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rootObject = object->property("vp").value<QObject*>();
+    QVERIFY(rootObject != 0);
+    QObject *childObject = rootObject->findChild<QObject*>("text");
+    QVERIFY(childObject != 0);
+    QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
+    QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
+    {
+        v8::HandleScope hs;
+        propertyVarWeakRefCallbackCount = 0;                           // reset callback count.
+        childObjectVarArrayValueHandle = qPersistentNew(((QDeclarativeVMEMetaObject *)(childObject->metaObject()))->vmeProperty(58));
+        childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
+        gc(engine);
+        QVERIFY(propertyVarWeakRefCallbackCount == 0);                 // should not have been collected yet.
+        QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    }
+    QMetaObject::invokeMethod(object, "deassignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(propertyVarWeakRefCallbackCount == 1);                 // should have been collected now.
+    delete object;
+}
+
+// Ensure that QObject type conversion works on binding assignment
+void tst_qdeclarativeecmascript::elementAssign()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("elementAssign.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
+// QTBUG-12457
+void tst_qdeclarativeecmascript::objectPassThroughSignals()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("objectsPassThroughSignals.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
+// QTBUG-21626
+void tst_qdeclarativeecmascript::objectConversion()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("objectConversion.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QVariant retn;
+    QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn));
+    QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100));
+
+    delete object;
+}
+
+
+// QTBUG-20242
+void tst_qdeclarativeecmascript::booleanConversion()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("booleanConversion.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test_true1").toBool(), true);
+    QCOMPARE(object->property("test_true2").toBool(), true);
+    QCOMPARE(object->property("test_true3").toBool(), true);
+    QCOMPARE(object->property("test_true4").toBool(), true);
+    QCOMPARE(object->property("test_true5").toBool(), true);
+
+    QCOMPARE(object->property("test_false1").toBool(), false);
+    QCOMPARE(object->property("test_false2").toBool(), false);
+    QCOMPARE(object->property("test_false3").toBool(), false);
+
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::handleReferenceManagement()
+{
+
+    int dtorCount = 0;
+    {
+        // Linear QObject reference
+        QDeclarativeEngine hrmEngine;
+        QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.1.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
+        cro->setDtorCount(&dtorCount);
+        QMetaObject::invokeMethod(object, "createReference");
+        gc(engine);
+        QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
+        delete object;
+        hrmEngine.collectGarbage();
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 3);
+    }
+
+    dtorCount = 0;
+    {
+        // Circular QObject reference
+        QDeclarativeEngine hrmEngine;
+        QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.2.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
+        cro->setDtorCount(&dtorCount);
+        QMetaObject::invokeMethod(object, "circularReference");
+        gc(engine);
+        QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
+        delete object;
+        hrmEngine.collectGarbage();
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 3);
+    }
+
+    dtorCount = 0;
+    {
+        // Linear handle reference
+        QDeclarativeEngine hrmEngine;
+        QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
+        QVERIFY(crh != 0);
+        crh->setDtorCount(&dtorCount);
+        QMetaObject::invokeMethod(object, "createReference");
+        CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
+        QVERIFY(first != 0);
+        QVERIFY(second != 0);
+        first->addReference(QDeclarativeData::get(second)->v8object); // create reference
+        // now we have to reparent second and make second owned by JS.
+        second->setParent(0);
+        QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
+        gc(engine);
+        QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
+        delete object;
+        hrmEngine.collectGarbage();
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 3);
+    }
+
+    dtorCount = 0;
+    {
+        // Circular handle reference
+        QDeclarativeEngine hrmEngine;
+        QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.2.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
+        QVERIFY(crh != 0);
+        crh->setDtorCount(&dtorCount);
+        QMetaObject::invokeMethod(object, "circularReference");
+        CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
+        QVERIFY(first != 0);
+        QVERIFY(second != 0);
+        first->addReference(QDeclarativeData::get(second)->v8object); // create circular reference
+        second->addReference(QDeclarativeData::get(first)->v8object); // note: must be weak.
+        // now we have to reparent and change ownership.
+        first->setParent(0);
+        second->setParent(0);
+        QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership);
+        QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
+        gc(engine);
+        QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
+        delete object;
+        hrmEngine.collectGarbage();
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 3);
+    }
+
+    dtorCount = 0;
+    {
+        // multiple engine interaction - linear reference
+        QDeclarativeEngine hrmEngine1;
+        QDeclarativeEngine hrmEngine2;
+        QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+        QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+        QObject *object1 = component1.create();
+        QObject *object2 = component2.create();
+        QVERIFY(object1 != 0);
+        QVERIFY(object2 != 0);
+        CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
+        CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
+        QVERIFY(crh1 != 0);
+        QVERIFY(crh2 != 0);
+        crh1->setDtorCount(&dtorCount);
+        crh2->setDtorCount(&dtorCount);
+        QMetaObject::invokeMethod(object1, "createReference");
+        QMetaObject::invokeMethod(object2, "createReference");
+        CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
+        QVERIFY(first1 != 0);
+        QVERIFY(second1 != 0);
+        QVERIFY(first2 != 0);
+        QVERIFY(second2 != 0);
+        first1->addReference(QDeclarativeData::get(second2)->v8object); // create reference across engines
+        // now we have to reparent second2 and make second2 owned by JS.
+        second2->setParent(0);
+        QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
+        gc(engine);
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
+        delete object1;
+        delete object2;
+        hrmEngine1.collectGarbage();
+        hrmEngine2.collectGarbage();
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 6);
+    }
+
+    dtorCount = 0;
+    {
+        // multiple engine interaction - circular reference
+        QDeclarativeEngine hrmEngine1;
+        QDeclarativeEngine hrmEngine2;
+        QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+        QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+        QObject *object1 = component1.create();
+        QObject *object2 = component2.create();
+        QVERIFY(object1 != 0);
+        QVERIFY(object2 != 0);
+        CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
+        CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
+        QVERIFY(crh1 != 0);
+        QVERIFY(crh2 != 0);
+        crh1->setDtorCount(&dtorCount);
+        crh2->setDtorCount(&dtorCount);
+        QMetaObject::invokeMethod(object1, "createReference");
+        QMetaObject::invokeMethod(object2, "createReference");
+        CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
+        QVERIFY(first1 != 0);
+        QVERIFY(second1 != 0);
+        QVERIFY(first2 != 0);
+        QVERIFY(second2 != 0);
+        first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
+        second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
+        second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
+        first2->addReference(QDeclarativeData::get(first1)->v8object);   // close the loop - circular ref across engines
+        // now we have to reparent and change ownership to JS.
+        first1->setParent(0);
+        second1->setParent(0);
+        first2->setParent(0);
+        second2->setParent(0);
+        QDeclarativeEngine::setObjectOwnership(first1, QDeclarativeEngine::JavaScriptOwnership);
+        QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
+        QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
+        QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
+        gc(engine);
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
+        delete object1;
+        delete object2;
+        hrmEngine1.collectGarbage();
+        hrmEngine2.collectGarbage();
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 6);
+    }
+
+    dtorCount = 0;
+    {
+        // multiple engine interaction - linear reference with engine deletion
+        QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine;
+        QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine;
+        QDeclarativeComponent component1(hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+        QDeclarativeComponent component2(hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+        QObject *object1 = component1.create();
+        QObject *object2 = component2.create();
+        QVERIFY(object1 != 0);
+        QVERIFY(object2 != 0);
+        CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
+        CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
+        QVERIFY(crh1 != 0);
+        QVERIFY(crh2 != 0);
+        crh1->setDtorCount(&dtorCount);
+        crh2->setDtorCount(&dtorCount);
+        QMetaObject::invokeMethod(object1, "createReference");
+        QMetaObject::invokeMethod(object2, "createReference");
+        CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
+        CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
+        QVERIFY(first1 != 0);
+        QVERIFY(second1 != 0);
+        QVERIFY(first2 != 0);
+        QVERIFY(second2 != 0);
+        first1->addReference(QDeclarativeData::get(second1)->v8object);  // create linear reference within engine1
+        second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
+        second2->addReference(QDeclarativeData::get(first2)->v8object);  // create linear reference within engine2
+        // now we have to reparent and change ownership to JS.
+        first1->setParent(crh1);
+        second1->setParent(0);
+        first2->setParent(0);
+        second2->setParent(0);
+        QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
+        QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
+        QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
+        gc(engine);
+        QCOMPARE(dtorCount, 0);
+        delete hrmEngine2;
+        gc(engine);
+        QCOMPARE(dtorCount, 0);
+        delete object1;
+        delete object2;
+        hrmEngine1->collectGarbage();
+        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        QCOMPARE(dtorCount, 6);
+        delete hrmEngine1;
+    }
+}
+
+void tst_qdeclarativeecmascript::stringArg()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("stringArg.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "success");
+    QVERIFY(object->property("returnValue").toBool());
+
+    QString w1 = TEST_FILE("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
+    QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData());
+    QMetaObject::invokeMethod(object, "failure");
+    QVERIFY(object->property("returnValue").toBool());
+
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::readonlyDeclaration()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("readonlyDeclaration.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
+Q_DECLARE_METATYPE(QList<int>)
+Q_DECLARE_METATYPE(QList<qreal>)
+Q_DECLARE_METATYPE(QList<bool>)
+Q_DECLARE_METATYPE(QList<QString>)
+Q_DECLARE_METATYPE(QList<QUrl>)
+void tst_qdeclarativeecmascript::sequenceConversionRead()
+{
+    {
+        QUrl qmlFile = TEST_FILE("sequenceConversion.read.qml");
+        QDeclarativeComponent component(&engine, qmlFile);
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
+        QVERIFY(seq != 0);
+
+        QMetaObject::invokeMethod(object, "readSequences");
+        QList<int> intList; intList << 1 << 2 << 3 << 4;
+        QCOMPARE(object->property("intListLength").toInt(), intList.length());
+        QCOMPARE(object->property("intList").value<QList<int> >(), intList);
+        QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
+        QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
+        QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
+        QList<bool> boolList; boolList << true << false << true << false;
+        QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
+        QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
+        QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
+        QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
+        QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
+        QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
+        QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
+        QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
+        QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
+        QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
+        QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
+
+        QMetaObject::invokeMethod(object, "readSequenceElements");
+        QCOMPARE(object->property("intVal").toInt(), 2);
+        QCOMPARE(object->property("qrealVal").toReal(), 2.2);
+        QCOMPARE(object->property("boolVal").toBool(), false);
+        QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
+        QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
+        QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
+
+        QMetaObject::invokeMethod(object, "enumerateSequenceElements");
+        QCOMPARE(object->property("enumerationMatches").toBool(), true);
+
+        intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
+        QDeclarativeProperty seqProp(seq, "intListProperty");
+        QCOMPARE(seqProp.read().value<QList<int> >(), intList);
+        QDeclarativeProperty seqProp2(seq, "intListProperty", &engine);
+        QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
+
+        QMetaObject::invokeMethod(object, "testReferenceDeletion");
+        QCOMPARE(object->property("referenceDeletion").toBool(), true);
+
+        delete object;
+    }
+
+    {
+        QUrl qmlFile = TEST_FILE("sequenceConversion.read.error.qml");
+        QDeclarativeComponent component(&engine, qmlFile);
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
+        QVERIFY(seq != 0);
+
+        // we haven't registered QList<QPoint> as a sequence type.
+        QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
+        QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
+        QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
+        QTest::ignoreMessage(QtWarningMsg, warningTwo.toAscii().constData());
+
+        QMetaObject::invokeMethod(object, "performTest");
+
+        // QList<QPoint> has not been registered as a sequence type.
+        QCOMPARE(object->property("pointListLength").toInt(), 0);
+        QVERIFY(!object->property("pointList").isValid());
+        QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'");
+        QDeclarativeProperty seqProp(seq, "pointListProperty", &engine);
+        QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
+
+        delete object;
+    }
+}
+
+void tst_qdeclarativeecmascript::sequenceConversionWrite()
+{
+    {
+        QUrl qmlFile = TEST_FILE("sequenceConversion.write.qml");
+        QDeclarativeComponent component(&engine, qmlFile);
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
+        QVERIFY(seq != 0);
+
+        QMetaObject::invokeMethod(object, "writeSequences");
+        QCOMPARE(object->property("success").toBool(), true);
+
+        QMetaObject::invokeMethod(object, "writeSequenceElements");
+        QCOMPARE(object->property("success").toBool(), true);
+
+        QMetaObject::invokeMethod(object, "writeOtherElements");
+        QCOMPARE(object->property("success").toBool(), true);
+
+        QMetaObject::invokeMethod(object, "testReferenceDeletion");
+        QCOMPARE(object->property("referenceDeletion").toBool(), true);
+
+        delete object;
+    }
+
+    {
+        QUrl qmlFile = TEST_FILE("sequenceConversion.write.error.qml");
+        QDeclarativeComponent component(&engine, qmlFile);
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
+        QVERIFY(seq != 0);
+
+        // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
+        QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to void");
+        QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData());
+
+        QMetaObject::invokeMethod(object, "performTest");
+
+        QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
+        QCOMPARE(seq->pointListProperty(), pointList);
+
+        delete object;
+    }
+}
+
+void tst_qdeclarativeecmascript::sequenceConversionArray()
+{
+    // ensure that in JS the returned sequences act just like normal JS Arrays.
+    QUrl qmlFile = TEST_FILE("sequenceConversion.array.qml");
+    QDeclarativeComponent component(&engine, qmlFile);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "indexedAccess");
+    QVERIFY(object->property("success").toBool());
+    QMetaObject::invokeMethod(object, "arrayOperations");
+    QVERIFY(object->property("success").toBool());
+    QMetaObject::invokeMethod(object, "testEqualitySemantics");
+    QVERIFY(object->property("success").toBool());
+    QMetaObject::invokeMethod(object, "testReferenceDeletion");
+    QCOMPARE(object->property("referenceDeletion").toBool(), true);
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::sequenceConversionThreads()
+{
+    // ensure that sequence conversion operations work correctly in a worker thread
+    // and that serialisation between the main and worker thread succeeds.
+    QUrl qmlFile = TEST_FILE("sequenceConversion.threads.qml");
+    QDeclarativeComponent component(&engine, qmlFile);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QMetaObject::invokeMethod(object, "testIntSequence");
+    QTRY_VERIFY(object->property("finished").toBool());
+    QVERIFY(object->property("success").toBool());
+
+    QMetaObject::invokeMethod(object, "testQrealSequence");
+    QTRY_VERIFY(object->property("finished").toBool());
+    QVERIFY(object->property("success").toBool());
+
+    QMetaObject::invokeMethod(object, "testBoolSequence");
+    QTRY_VERIFY(object->property("finished").toBool());
+    QVERIFY(object->property("success").toBool());
+
+    QMetaObject::invokeMethod(object, "testStringSequence");
+    QTRY_VERIFY(object->property("finished").toBool());
+    QVERIFY(object->property("success").toBool());
+
+    QMetaObject::invokeMethod(object, "testQStringSequence");
+    QTRY_VERIFY(object->property("finished").toBool());
+    QVERIFY(object->property("success").toBool());
+
+    QMetaObject::invokeMethod(object, "testUrlSequence");
+    QTRY_VERIFY(object->property("finished").toBool());
+    QVERIFY(object->property("success").toBool());
+
+    QMetaObject::invokeMethod(object, "testVariantSequence");
+    QTRY_VERIFY(object->property("finished").toBool());
+    QVERIFY(object->property("success").toBool());
+
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::sequenceConversionBindings()
+{
+    {
+        QUrl qmlFile = TEST_FILE("sequenceConversion.bindings.qml");
+        QDeclarativeComponent component(&engine, qmlFile);
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
+        QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
+        QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
+        QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
+        QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
+        delete object;
+    }
+
+    {
+        QUrl qmlFile = TEST_FILE("sequenceConversion.bindings.error.qml");
+        QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString());
+        QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
+        QDeclarativeComponent component(&engine, qmlFile);
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        delete object;
+    }
+}
+
+void tst_qdeclarativeecmascript::sequenceConversionCopy()
+{
+    QUrl qmlFile = TEST_FILE("sequenceConversion.copy.qml");
+    QDeclarativeComponent component(&engine, qmlFile);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "testCopySequences");
+    QCOMPARE(object->property("success").toBool(), true);
+    QMetaObject::invokeMethod(object, "readSequenceCopyElements");
+    QCOMPARE(object->property("success").toBool(), true);
+    QMetaObject::invokeMethod(object, "testEqualitySemantics");
+    QCOMPARE(object->property("success").toBool(), true);
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::assignSequenceTypes()
+{
+    // test binding array to sequence type property
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.1.qml"));
+    MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
+    QVERIFY(object != 0);
+    QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
+    QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
+    QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
+    QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
+    QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
+    QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
+    delete object;
+    }
+
+    // test binding literal to sequence type property
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.2.qml"));
+    MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
+    QVERIFY(object != 0);
+    QCOMPARE(object->intListProperty(), (QList<int>() << 1));
+    QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
+    QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
+    QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
+    QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
+    QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
+    delete object;
+    }
+
+    // test binding single value to sequence type property
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.3.qml"));
+    MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
+    QVERIFY(object != 0);
+    QCOMPARE(object->intListProperty(), (QList<int>() << 1));
+    QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
+    QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
+    QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(TEST_FILE("example.html"))));
+    delete object;
+    }
+
+    // test assigning array to sequence type property in js function
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.4.qml"));
+    MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
+    QVERIFY(object != 0);
+    QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
+    QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2));
+    QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
+    QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
+    QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
+    QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
+    delete object;
+    }
+
+    // test assigning literal to sequence type property in js function
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.5.qml"));
+    MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
+    QVERIFY(object != 0);
+    QCOMPARE(object->intListProperty(), (QList<int>() << 1));
+    QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
+    QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
+    QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
+    QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
+    QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
+    delete object;
+    }
+
+    // test assigning single value to sequence type property in js function
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("assignSequenceTypes.6.qml"));
+    MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create());
+    QVERIFY(object != 0);
+    QCOMPARE(object->intListProperty(), (QList<int>() << 1));
+    QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
+    QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
+    QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(TEST_FILE("example.html"))));
+    delete object;
+    }
+}
+
+// Test that assigning a null object works 
+// Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
+void tst_qdeclarativeecmascript::nullObjectBinding()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("nullObjectBinding.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0));
+
+    delete object;
+}
+
+// Test that bindings don't evaluate once the engine has been destroyed
+void tst_qdeclarativeecmascript::deletedEngine()
+{
+    QDeclarativeEngine *engine = new QDeclarativeEngine;
+    QDeclarativeComponent component(engine, TEST_FILE("deletedEngine.qml"));
+
+    QObject *object = component.create();
     QVERIFY(object != 0);
 
     QCOMPARE(object->property("a").toInt(), 39);
@@ -3020,11 +4896,11 @@ void tst_qdeclarativeecmascript::functionAssignmentfromJS_invalid()
     QVERIFY(!o->property("a").isValid());
 
     QString url = component.url().toString();
-    QString warning = url + ":63: Unable to assign QString to int";
+    QString warning = url + ":67: Unable to assign QString to int";
     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
     o->setProperty("assignWrongType", true);
 
-    warning = url + ":70: Unable to assign QString to int";
+    warning = url + ":71: Unable to assign QString to int";
     QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
     o->setProperty("assignWrongTypeToValueType", true);
 
@@ -3126,7 +5002,7 @@ void tst_qdeclarativeecmascript::include()
     {
     TestHTTPServer server(8111);
     QVERIFY(server.isValid());
-    server.serveDirectory(SRCDIR "/data");
+    server.serveDirectory(TESTDATA(""));
 
     QDeclarativeComponent component(&engine, TEST_FILE("include_remote.qml"));
     QObject *o = component.create();
@@ -3154,7 +5030,7 @@ void tst_qdeclarativeecmascript::include()
     {
     TestHTTPServer server(8111);
     QVERIFY(server.isValid());
-    server.serveDirectory(SRCDIR "/data");
+    server.serveDirectory(TESTDATA(""));
 
     QDeclarativeComponent component(&engine, TEST_FILE("include_remote_missing.qml"));
     QObject *o = component.create();
@@ -3170,6 +5046,36 @@ void tst_qdeclarativeecmascript::include()
     }
 }
 
+void tst_qdeclarativeecmascript::signalHandlers()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("signalHandlers.qml"));
+    QObject *o = component.create();
+    QVERIFY(o != 0);
+
+    QVERIFY(o->property("count").toInt() == 0);
+    QMetaObject::invokeMethod(o, "testSignalCall");
+    QCOMPARE(o->property("count").toInt(), 1);
+
+    QMetaObject::invokeMethod(o, "testSignalHandlerCall");
+    QCOMPARE(o->property("count").toInt(), 1);
+    QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
+
+    QVERIFY(o->property("funcCount").toInt() == 0);
+    QMetaObject::invokeMethod(o, "testSignalConnection");
+    QCOMPARE(o->property("funcCount").toInt(), 1);
+
+    QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
+    QCOMPARE(o->property("funcCount").toInt(), 2);
+
+    QMetaObject::invokeMethod(o, "testSignalDefined");
+    QCOMPARE(o->property("definedResult").toBool(), true);
+
+    QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
+    QCOMPARE(o->property("definedHandlerResult").toBool(), true);
+
+    delete o;
+}
+
 void tst_qdeclarativeecmascript::qtbug_10696()
 {
     QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml"));
@@ -3196,6 +5102,15 @@ void tst_qdeclarativeecmascript::qtbug_11600()
     delete o;
 }
 
+void tst_qdeclarativeecmascript::qtbug_21864()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("qtbug_21864.qml"));
+    QObject *o = component.create();
+    QVERIFY(o != 0);
+    QCOMPARE(o->property("test").toBool(), true);
+    delete o;
+}
+
 // Reading and writing non-scriptable properties should fail
 void tst_qdeclarativeecmascript::nonscriptable()
 {
@@ -3227,6 +5142,34 @@ void tst_qdeclarativeecmascript::in()
     delete o;
 }
 
+void tst_qdeclarativeecmascript::typeOf()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("typeOf.qml"));
+
+    // These warnings should not happen once QTBUG-21864 is fixed
+    QString warning1 = component.url().toString() + QLatin1String(":16: Error: Cannot assign [undefined] to QString");
+    QString warning2 = component.url().resolved(QUrl("typeOf.js")).toString() + QLatin1String(":1: ReferenceError: Can't find variable: a");
+
+    QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
+    QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
+
+    QObject *o = component.create();
+    QVERIFY(o != 0);
+
+    QEXPECT_FAIL("", "QTBUG-21864", Abort);
+    QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
+    QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
+    QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
+    QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
+    QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
+    QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
+    QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
+    QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
+    QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
+
+    delete o;
+}
+
 void tst_qdeclarativeecmascript::sharedAttachedObject()
 {
     QDeclarativeComponent component(&engine, TEST_FILE("sharedAttachedObject.qml"));
@@ -3346,6 +5289,31 @@ void tst_qdeclarativeecmascript::aliasWritesOverrideBindings()
     }
 }
 
+// Allow an alais to a composite element
+// QTBUG-20200
+void tst_qdeclarativeecmascript::aliasToCompositeElement()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("aliasToCompositeElement.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::qtbug_20344()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("qtbug_20344.qml"));
+
+    QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
+    QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    delete object;
+}
+
 void tst_qdeclarativeecmascript::revisionErrors()
 {
     {
@@ -3440,54 +5408,365 @@ void tst_qdeclarativeecmascript::revision()
     }
 }
 
-// Test for QScriptDeclarativeClass::pushCleanContext()
-void tst_qdeclarativeecmascript::pushCleanContext()
+void tst_qdeclarativeecmascript::realToInt()
 {
-    QScriptEngine engine;
-    engine.globalObject().setProperty("a", 6);
-    QCOMPARE(engine.evaluate("a").toInt32(), 6);
+    QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml"));
+    MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+    QVERIFY(object != 0);
 
-    // First confirm pushContext() behaves as we expect
-    QScriptValue object = engine.newObject();
-    object.setProperty("a", 15);
-    QScriptContext *context1 = engine.pushContext();
-    context1->pushScope(object);
-    QCOMPARE(engine.evaluate("a").toInt32(), 15);
+    QMetaObject::invokeMethod(object, "test1");
+    QCOMPARE(object->value(), int(4));
+    QMetaObject::invokeMethod(object, "test2");
+    QCOMPARE(object->value(), int(8));
+}
+void tst_qdeclarativeecmascript::dynamicString()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("dynamicString.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("stringProperty").toString(),
+             QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
+}
 
-    QScriptContext *context2 = engine.pushContext();
-    Q_UNUSED(context2);
-    QCOMPARE(engine.evaluate("a").toInt32(), 15);
-    QScriptValue func1 = engine.evaluate("(function() { return a; })");
+void tst_qdeclarativeecmascript::automaticSemicolon()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("automaticSemicolon.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+}
 
-    // Now check that pushCleanContext() works
-    QScriptDeclarativeClass::pushCleanContext(&engine);
-    QCOMPARE(engine.evaluate("a").toInt32(), 6);
-    QScriptValue func2 = engine.evaluate("(function() { return a; })");
+void tst_qdeclarativeecmascript::unaryExpression()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("unaryExpression.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+}
 
-    engine.popContext();
-    QCOMPARE(engine.evaluate("a").toInt32(), 15);
+// Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
+void tst_qdeclarativeecmascript::doubleEvaluate()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("doubleEvaluate.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    WriteCounter *wc = qobject_cast<WriteCounter *>(object);
+    QVERIFY(wc != 0);
+    QCOMPARE(wc->count(), 1);
 
-    engine.popContext();
-    QCOMPARE(engine.evaluate("a").toInt32(), 15);
+    wc->setProperty("x", 9);
 
-    engine.popContext();
-    QCOMPARE(engine.evaluate("a").toInt32(), 6);
+    QCOMPARE(wc->count(), 2);
 
-    // Check that function objects created in these contexts work
-    QCOMPARE(func1.call().toInt32(), 15);
-    QCOMPARE(func2.call().toInt32(), 6);
+    delete object;
 }
 
-void tst_qdeclarativeecmascript::realToInt()
+static QStringList messages;
+static void captureMsgHandler(QtMsgType, const char *msg)
 {
-    QDeclarativeComponent component(&engine, TEST_FILE("realToInt.qml"));
-    MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+    messages.append(QLatin1String(msg));
+}
+
+void tst_qdeclarativeecmascript::nonNotifyable()
+{
+    QV4Compiler::enableV4(false);
+    QDeclarativeComponent component(&engine, TEST_FILE("nonNotifyable.qml"));
+    QV4Compiler::enableV4(true);
+
+    QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
+    messages.clear();
+    QObject *object = component.create();
+    qInstallMsgHandler(old);
+
     QVERIFY(object != 0);
 
-    QMetaObject::invokeMethod(object, "test1");
-    QCOMPARE(object->value(), int(4));
-    QMetaObject::invokeMethod(object, "test2");
-    QCOMPARE(object->value(), int(8));
+    QString expected1 = QLatin1String("QDeclarativeExpression: Expression ") +
+                        component.url().toString() +
+                        QLatin1String(":5 depends on non-NOTIFYable properties:");
+    QString expected2 = QLatin1String("    ") +
+                        QLatin1String(object->metaObject()->className()) +
+                        QLatin1String("::value");
+
+    QCOMPARE(messages.length(), 2);
+    QCOMPARE(messages.at(0), expected1);
+    QCOMPARE(messages.at(1), expected2);
+
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::forInLoop()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("forInLoop.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QMetaObject::invokeMethod(object, "listProperty");
+
+    QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts);
+    QCOMPARE(r.size(), 3);
+    QCOMPARE(r[0],QLatin1String("0=obj1"));
+    QCOMPARE(r[1],QLatin1String("1=obj2"));
+    QCOMPARE(r[2],QLatin1String("2=obj3"));
+
+    //TODO: should test for in loop for other objects (such as QObjects) as well.
+
+    delete object;
+}
+
+// An object the binding depends on is deleted while the binding is still running
+void tst_qdeclarativeecmascript::deleteWhileBindingRunning()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("deleteWhileBindingRunning.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::qtbug_22679()
+{
+    MyQmlObject object;
+    object.setStringProperty(QLatin1String("Please work correctly"));
+    engine.rootContext()->setContextProperty("contextProp", &object);
+
+    QDeclarativeComponent component(&engine, TEST_FILE("qtbug_22679.qml"));
+    qRegisterMetaType<QList<QDeclarativeError> >("QList<QDeclarativeError>");
+    QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QDeclarativeError>)));
+
+    QObject *o = component.create();
+    QVERIFY(o != 0);
+    QCOMPARE(warningsSpy.count(), 0);
+    delete o;
+}
+
+void tst_qdeclarativeecmascript::qtbug_22843_data()
+{
+    QTest::addColumn<bool>("library");
+
+    QTest::newRow("without .pragma library") << false;
+    QTest::newRow("with .pragma library") << true;
+}
+
+void tst_qdeclarativeecmascript::qtbug_22843()
+{
+    QFETCH(bool, library);
+
+    QString fileName("qtbug_22843");
+    if (library)
+        fileName += QLatin1String(".library");
+    fileName += QLatin1String(".qml");
+
+    QDeclarativeComponent component(&engine, TEST_FILE(fileName));
+    QString url = component.url().toString();
+    QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )");
+    QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'");
+
+    qRegisterMetaType<QList<QDeclarativeError> >("QList<QDeclarativeError>");
+    QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QDeclarativeError>)));
+    for (int x = 0; x < 3; ++x) {
+        warningsSpy.clear();
+        // For libraries, only the first import attempt should produce a
+        // SyntaxError warning; subsequent component creation should not
+        // attempt to reload the script.
+        bool expectSyntaxError = !library || (x == 0);
+        if (expectSyntaxError)
+            QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
+        QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0));
+        delete object;
+    }
+}
+
+
+void tst_qdeclarativeecmascript::switchStatement()
+{
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.1.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        // `object->value()' is the number of executed statements
+
+        object->setStringProperty("A");
+        QCOMPARE(object->value(), 5);
+
+        object->setStringProperty("S");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("D");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("F");
+        QCOMPARE(object->value(), 4);
+
+        object->setStringProperty("something else");
+        QCOMPARE(object->value(), 1);
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.2.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        // `object->value()' is the number of executed statements
+
+        object->setStringProperty("A");
+        QCOMPARE(object->value(), 5);
+
+        object->setStringProperty("S");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("D");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("F");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("something else");
+        QCOMPARE(object->value(), 4);
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.3.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        // `object->value()' is the number of executed statements
+
+        object->setStringProperty("A");
+        QCOMPARE(object->value(), 5);
+
+        object->setStringProperty("S");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("D");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("F");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("something else");
+        QCOMPARE(object->value(), 6);
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.4.qml"));
+
+        QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int";
+        QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        // `object->value()' is the number of executed statements
+
+        object->setStringProperty("A");
+        QCOMPARE(object->value(), 5);
+
+        object->setStringProperty("S");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("D");
+        QCOMPARE(object->value(), 3);
+
+        object->setStringProperty("F");
+        QCOMPARE(object->value(), 3);
+
+        QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+
+        object->setStringProperty("something else");
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.5.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        // `object->value()' is the number of executed statements
+
+        object->setStringProperty("A");
+        QCOMPARE(object->value(), 1);
+
+        object->setStringProperty("S");
+        QCOMPARE(object->value(), 1);
+
+        object->setStringProperty("D");
+        QCOMPARE(object->value(), 1);
+
+        object->setStringProperty("F");
+        QCOMPARE(object->value(), 1);
+
+        object->setStringProperty("something else");
+        QCOMPARE(object->value(), 1);
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("switchStatement.6.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        // `object->value()' is the number of executed statements
+
+        object->setStringProperty("A");
+        QCOMPARE(object->value(), 123);
+
+        object->setStringProperty("S");
+        QCOMPARE(object->value(), 123);
+
+        object->setStringProperty("D");
+        QCOMPARE(object->value(), 321);
+
+        object->setStringProperty("F");
+        QCOMPARE(object->value(), 321);
+
+        object->setStringProperty("something else");
+        QCOMPARE(object->value(), 0);
+    }
+}
+
+void tst_qdeclarativeecmascript::withStatement()
+{
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("withStatement.1.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        QCOMPARE(object->value(), 123);
+    }
+}
+
+void tst_qdeclarativeecmascript::tryStatement()
+{
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.1.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        QCOMPARE(object->value(), 123);
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.2.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        QCOMPARE(object->value(), 321);
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.3.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        QCOMPARE(object->value(), 1);
+    }
+
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("tryStatement.4.qml"));
+        MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+        QVERIFY(object != 0);
+
+        QCOMPARE(object->value(), 1);
+    }
 }
 
 QTEST_MAIN(tst_qdeclarativeecmascript)