tests/auto/qscriptextqobject/tst_qscriptextqobject.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qscriptextqobject/tst_qscriptextqobject.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,2937 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** 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.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qscriptengine.h>
+#include <qscriptcontext.h>
+#include <qscriptvalueiterator.h>
+#include <qwidget.h>
+#include <qpushbutton.h>
+#include <qlineedit.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=script/qscriptextqobject_p.h script/qscriptextqobject.cpp
+
+struct CustomType
+{
+#if defined (Q_CC_NOKIAX86)
+    // Compiler crash workaround
+    CustomType() {}
+#endif
+    QString string;
+};
+Q_DECLARE_METATYPE(CustomType)
+
+Q_DECLARE_METATYPE(QBrush*)
+Q_DECLARE_METATYPE(QObjectList)
+Q_DECLARE_METATYPE(QList<int>)
+Q_DECLARE_METATYPE(Qt::BrushStyle)
+Q_DECLARE_METATYPE(QDir)
+
+static void dirFromScript(const QScriptValue &in, QDir &out)
+{
+    QScriptValue path = in.property("path");
+    if (!path.isValid())
+        in.engine()->currentContext()->throwError("No path");
+    else
+        out.setPath(path.toString());
+}
+
+namespace MyNS
+{
+    class A : public QObject
+    {
+        Q_OBJECT
+    public:
+        enum Type {
+            Foo,
+            Bar
+        };
+        Q_ENUMS(Type)
+    public Q_SLOTS:
+        int slotTakingScopedEnumArg(MyNS::A::Type t) {
+            return t;
+        }
+    };
+}
+
+class MyQObject : public QObject
+{
+    Q_OBJECT
+
+    Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty)
+    Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty)
+    Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty)
+    Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
+    Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty)
+    Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty)
+    Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty)
+    Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false)
+    Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty)
+    Q_PROPERTY(int readOnlyProperty READ readOnlyProperty)
+    Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
+    Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType)
+    Q_PROPERTY(Policy enumProperty READ enumProperty WRITE setEnumProperty)
+    Q_ENUMS(Policy Strategy)
+    Q_FLAGS(Ability)
+
+public:
+    enum Policy {
+        FooPolicy = 0,
+        BarPolicy,
+        BazPolicy
+    };
+
+    enum Strategy {
+        FooStrategy = 10,
+        BarStrategy,
+        BazStrategy
+    };
+
+    enum AbilityFlag {
+        NoAbility  = 0x000,
+        FooAbility = 0x001,
+        BarAbility = 0x080,
+        BazAbility = 0x200,
+        AllAbility = FooAbility | BarAbility | BazAbility
+    };
+
+    Q_DECLARE_FLAGS(Ability, AbilityFlag)
+
+    MyQObject(QObject *parent = 0)
+        : QObject(parent),
+          m_intValue(123),
+          m_variantValue(QLatin1String("foo")),
+          m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))),
+          m_stringValue(QLatin1String("bar")),
+          m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")),
+          m_brushValue(QColor(10, 20, 30, 40)),
+          m_hiddenValue(456.0),
+          m_writeOnlyValue(789),
+          m_readOnlyValue(987),
+          m_enumValue(BarPolicy),
+          m_qtFunctionInvoked(-1)
+        { }
+
+    int intProperty() const
+        { return m_intValue; }
+    void setIntProperty(int value)
+        { m_intValue = value; }
+
+    QVariant variantProperty() const
+        { return m_variantValue; }
+    void setVariantProperty(const QVariant &value)
+        { m_variantValue = value; }
+
+    QVariantList variantListProperty() const
+        { return m_variantListValue; }
+    void setVariantListProperty(const QVariantList &value)
+        { m_variantListValue = value; }
+
+    QString stringProperty() const
+        { return m_stringValue; }
+    void setStringProperty(const QString &value)
+        { m_stringValue = value; }
+
+    QStringList stringListProperty() const
+        { return m_stringListValue; }
+    void setStringListProperty(const QStringList &value)
+        { m_stringListValue = value; }
+
+    QByteArray byteArrayProperty() const
+        { return m_byteArrayValue; }
+    void setByteArrayProperty(const QByteArray &value)
+        { m_byteArrayValue = value; }
+
+    QBrush brushProperty() const
+        { return m_brushValue; }
+    Q_INVOKABLE void setBrushProperty(const QBrush &value)
+        { m_brushValue = value; }
+
+    double hiddenProperty() const
+        { return m_hiddenValue; }
+    void setHiddenProperty(double value)
+        { m_hiddenValue = value; }
+
+    int writeOnlyProperty() const
+        { return m_writeOnlyValue; }
+    void setWriteOnlyProperty(int value)
+        { m_writeOnlyValue = value; }
+
+    int readOnlyProperty() const
+        { return m_readOnlyValue; }
+
+    QKeySequence shortcut() const
+        { return m_shortcut; }
+    void setShortcut(const QKeySequence &seq)
+        { m_shortcut = seq; }
+
+    CustomType propWithCustomType() const
+        { return m_customType; }
+    void setPropWithCustomType(const CustomType &c)
+        { m_customType = c; }
+
+    Policy enumProperty() const
+        { return m_enumValue; }
+    void setEnumProperty(Policy policy)
+        { m_enumValue = policy; }
+
+    int qtFunctionInvoked() const
+        { return m_qtFunctionInvoked; }
+
+    QVariantList qtFunctionActuals() const
+        { return m_actuals; }
+
+    void resetQtFunctionInvoked()
+        { m_qtFunctionInvoked = -1; m_actuals.clear(); }
+
+    void clearConnectedSignal()
+        { m_connectedSignal = QByteArray(); }
+    void clearDisconnectedSignal()
+        { m_disconnectedSignal = QByteArray(); }
+    QByteArray connectedSignal() const
+        { return m_connectedSignal; }
+    QByteArray disconnectedSignal() const
+        { return m_disconnectedSignal; }
+
+    Q_INVOKABLE void myInvokable()
+        { m_qtFunctionInvoked = 0; }
+    Q_INVOKABLE void myInvokableWithIntArg(int arg)
+        { m_qtFunctionInvoked = 1; m_actuals << arg; }
+    Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg)
+        { m_qtFunctionInvoked = 2; m_actuals << arg; }
+    Q_INVOKABLE void myInvokableWithFloatArg(float arg)
+        { m_qtFunctionInvoked = 3; m_actuals << arg; }
+    Q_INVOKABLE void myInvokableWithDoubleArg(double arg)
+        { m_qtFunctionInvoked = 4; m_actuals << arg; }
+    Q_INVOKABLE void myInvokableWithStringArg(const QString &arg)
+        { m_qtFunctionInvoked = 5; m_actuals << arg; }
+    Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2)
+        { m_qtFunctionInvoked = 6; m_actuals << arg1 << arg2; }
+    Q_INVOKABLE int myInvokableReturningInt()
+        { m_qtFunctionInvoked = 7; return 123; }
+    Q_INVOKABLE qlonglong myInvokableReturningLongLong()
+        { m_qtFunctionInvoked = 39; return 456; }
+    Q_INVOKABLE QString myInvokableReturningString()
+        { m_qtFunctionInvoked = 8; return QLatin1String("ciao"); }
+    Q_INVOKABLE QVariant myInvokableReturningVariant()
+        { m_qtFunctionInvoked = 60; return 123; }
+    Q_INVOKABLE QScriptValue myInvokableReturningScriptValue()
+        { m_qtFunctionInvoked = 61; return 456; }
+    Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) // overload
+        { m_qtFunctionInvoked = 9; m_actuals << arg1 << arg2; }
+    Q_INVOKABLE void myInvokableWithEnumArg(Policy policy)
+        { m_qtFunctionInvoked = 10; m_actuals << policy; }
+    Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy)
+        { m_qtFunctionInvoked = 36; m_actuals << policy; }
+    Q_INVOKABLE Policy myInvokableReturningEnum()
+        { m_qtFunctionInvoked = 37; return BazPolicy; }
+    Q_INVOKABLE MyQObject::Strategy myInvokableReturningQualifiedEnum()
+        { m_qtFunctionInvoked = 38; return BazStrategy; }
+    Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt()
+        { m_qtFunctionInvoked = 11; return QVector<int>(); }
+    Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &)
+        { m_qtFunctionInvoked = 12; }
+    Q_INVOKABLE QObject *myInvokableReturningQObjectStar()
+        { m_qtFunctionInvoked = 13; return this; }
+    Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst)
+        { m_qtFunctionInvoked = 14; m_actuals << qVariantFromValue(lst); return lst; }
+    Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v)
+        { m_qtFunctionInvoked = 15; m_actuals << v; return v; }
+    Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm)
+        { m_qtFunctionInvoked = 16; m_actuals << vm; return vm; }
+    Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst)
+        { m_qtFunctionInvoked = 17; m_actuals << qVariantFromValue(lst); return lst; }
+    Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject *obj)
+        { m_qtFunctionInvoked = 18; m_actuals << qVariantFromValue(obj); return obj; }
+    Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush)
+        { m_qtFunctionInvoked = 19; m_actuals << qVariantFromValue(brush); return brush; }
+    Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style)
+        { m_qtFunctionInvoked = 43; m_actuals << qVariantFromValue(style); }
+    Q_INVOKABLE void myInvokableWithVoidStarArg(void *arg)
+        { m_qtFunctionInvoked = 44; m_actuals << qVariantFromValue(arg); }
+    Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg)
+        { m_qtFunctionInvoked = 45; m_actuals << qVariantFromValue(arg); }
+    Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg)
+        { m_qtFunctionInvoked = 46; m_actuals << qVariantFromValue(arg); }
+    Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "")
+        { m_qtFunctionInvoked = 47; m_actuals << qVariantFromValue(arg1) << qVariantFromValue(arg2); }
+    Q_INVOKABLE QObject& myInvokableReturningRef()
+        { m_qtFunctionInvoked = 48; return *this; }
+    Q_INVOKABLE const QObject& myInvokableReturningConstRef() const
+        { const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49; return *this; }
+    Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg)
+        { const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50; m_actuals << qVariantFromValue(arg); }
+    Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg)
+        { const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51; m_actuals << qVariantFromValue(arg); }
+    Q_INVOKABLE void myInvokableWithMyQObjectArg(MyQObject *arg)
+        { m_qtFunctionInvoked = 52; m_actuals << qVariantFromValue((QObject*)arg); }
+    Q_INVOKABLE MyQObject* myInvokableReturningMyQObject()
+        { m_qtFunctionInvoked = 53; return this; }
+    Q_INVOKABLE void myInvokableWithConstMyQObjectArg(const MyQObject *arg)
+        { m_qtFunctionInvoked = 54; m_actuals << qVariantFromValue((QObject*)arg); }
+    Q_INVOKABLE void myInvokableWithQDirArg(const QDir &arg)
+        { m_qtFunctionInvoked = 55; m_actuals << qVariantFromValue(arg); }
+    Q_INVOKABLE QScriptValue myInvokableWithScriptValueArg(const QScriptValue &arg)
+        { m_qtFunctionInvoked = 56; return arg; }
+    Q_INVOKABLE QObject* myInvokableReturningMyQObjectAsQObject()
+        { m_qtFunctionInvoked = 57; return this; }
+
+    Q_INVOKABLE QObjectList findObjects() const
+    {  return findChildren<QObject *>();  }
+    Q_INVOKABLE QList<int> myInvokableNumbers() const
+    {  return QList<int>() << 1 << 2 << 3; }
+
+    void emitMySignal()
+        { emit mySignal(); }
+    void emitMySignalWithIntArg(int arg)
+        { emit mySignalWithIntArg(arg); }
+    void emitMySignal2(bool arg)
+        { emit mySignal2(arg); }
+    void emitMySignal2()
+        { emit mySignal2(); }
+    void emitMyOverloadedSignal(int arg)
+        { emit myOverloadedSignal(arg); }
+    void emitMyOverloadedSignal(const QString &arg)
+        { emit myOverloadedSignal(arg); }
+    void emitMyOtherOverloadedSignal(const QString &arg)
+        { emit myOtherOverloadedSignal(arg); }
+    void emitMyOtherOverloadedSignal(int arg)
+        { emit myOtherOverloadedSignal(arg); }
+    void emitMySignalWithDefaultArgWithArg(int arg)
+        { emit mySignalWithDefaultArg(arg); }
+    void emitMySignalWithDefaultArg()
+        { emit mySignalWithDefaultArg(); }
+    void emitMySignalWithVariantArg(const QVariant &arg)
+        { emit mySignalWithVariantArg(arg); }
+    void emitMySignalWithScriptEngineArg(QScriptEngine *arg)
+        { emit mySignalWithScriptEngineArg(arg); }
+
+public Q_SLOTS:
+    void mySlot()
+        { m_qtFunctionInvoked = 20; }
+    void mySlotWithIntArg(int arg)
+        { m_qtFunctionInvoked = 21; m_actuals << arg; }
+    void mySlotWithDoubleArg(double arg)
+        { m_qtFunctionInvoked = 22; m_actuals << arg; }
+    void mySlotWithStringArg(const QString &arg)
+        { m_qtFunctionInvoked = 23; m_actuals << arg; }
+
+    void myOverloadedSlot()
+        { m_qtFunctionInvoked = 24; }
+    void myOverloadedSlot(QObject *arg)
+        { m_qtFunctionInvoked = 41; m_actuals << qVariantFromValue(arg); }
+    void myOverloadedSlot(bool arg)
+        { m_qtFunctionInvoked = 25; m_actuals << arg; }
+    void myOverloadedSlot(const QStringList &arg)
+        { m_qtFunctionInvoked = 42; m_actuals << arg; }
+    void myOverloadedSlot(double arg)
+        { m_qtFunctionInvoked = 26; m_actuals << arg; }
+    void myOverloadedSlot(float arg)
+        { m_qtFunctionInvoked = 27; m_actuals << arg; }
+    void myOverloadedSlot(int arg)
+        { m_qtFunctionInvoked = 28; m_actuals << arg; }
+    void myOverloadedSlot(const QString &arg)
+        { m_qtFunctionInvoked = 29; m_actuals << arg; }
+    void myOverloadedSlot(const QColor &arg)
+        { m_qtFunctionInvoked = 30; m_actuals << arg; }
+    void myOverloadedSlot(const QBrush &arg)
+        { m_qtFunctionInvoked = 31; m_actuals << arg; }
+    void myOverloadedSlot(const QDateTime &arg)
+        { m_qtFunctionInvoked = 32; m_actuals << arg; }
+    void myOverloadedSlot(const QDate &arg)
+        { m_qtFunctionInvoked = 33; m_actuals << arg; }
+    void myOverloadedSlot(const QRegExp &arg)
+        { m_qtFunctionInvoked = 34; m_actuals << arg; }
+    void myOverloadedSlot(const QVariant &arg)
+        { m_qtFunctionInvoked = 35; m_actuals << arg; }
+
+    virtual int myVirtualSlot(int arg)
+        { m_qtFunctionInvoked = 58; return arg; }
+
+    void qscript_call(int arg)
+        { m_qtFunctionInvoked = 40; m_actuals << arg; }
+
+protected Q_SLOTS:
+    void myProtectedSlot() { m_qtFunctionInvoked = 36; }
+
+private Q_SLOTS:
+    void myPrivateSlot() { }
+
+Q_SIGNALS:
+    void mySignal();
+    void mySignalWithIntArg(int arg);
+    void mySignalWithDoubleArg(double arg);
+    void mySignal2(bool arg = false);
+    void myOverloadedSignal(int arg);
+    void myOverloadedSignal(const QString &arg);
+    void myOtherOverloadedSignal(const QString &arg);
+    void myOtherOverloadedSignal(int arg);
+    void mySignalWithDefaultArg(int arg = 123);
+    void mySignalWithVariantArg(const QVariant &arg);
+    void mySignalWithScriptEngineArg(QScriptEngine *arg);
+
+protected:
+    void connectNotify(const char *signal) {
+        m_connectedSignal = signal;
+    }
+    void disconnectNotify(const char *signal) {
+        m_disconnectedSignal = signal;
+    }
+
+protected:
+    int m_intValue;
+    QVariant m_variantValue;
+    QVariantList m_variantListValue;
+    QString m_stringValue;
+    QStringList m_stringListValue;
+    QByteArray m_byteArrayValue;
+    QBrush m_brushValue;
+    double m_hiddenValue;
+    int m_writeOnlyValue;
+    int m_readOnlyValue;
+    QKeySequence m_shortcut;
+    CustomType m_customType;
+    Policy m_enumValue;
+    int m_qtFunctionInvoked;
+    QVariantList m_actuals;
+    QByteArray m_connectedSignal;
+    QByteArray m_disconnectedSignal;
+};
+
+Q_DECLARE_METATYPE(MyQObject*)
+Q_DECLARE_METATYPE(MyQObject::Policy)
+
+class MyOtherQObject : public MyQObject
+{
+    Q_OBJECT
+public:
+    MyOtherQObject(QObject *parent = 0)
+        : MyQObject(parent)
+        { }
+public Q_SLOTS:
+    virtual int myVirtualSlot(int arg)
+        { m_qtFunctionInvoked = 59; return arg; }
+};
+
+class MyEnumTestQObject : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QString p1 READ p1)
+    Q_PROPERTY(QString p2 READ p2)
+    Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false)
+    Q_PROPERTY(QString p4 READ p4)
+    Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false)
+    Q_PROPERTY(QString p6 READ p6)
+public:
+    MyEnumTestQObject(QObject *parent = 0)
+        : QObject(parent) { }
+    QString p1() const { return QLatin1String("p1"); }
+    QString p2() const { return QLatin1String("p2"); }
+    QString p3() const { return QLatin1String("p3"); }
+    QString p4() const { return QLatin1String("p4"); }
+    QString p5() const { return QLatin1String("p5"); }
+    QString p6() const { return QLatin1String("p5"); }
+public Q_SLOTS:
+    void mySlot() { }
+    void myOtherSlot() { }
+Q_SIGNALS:
+    void mySignal();
+};
+
+class tst_QScriptExtQObject : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_QScriptExtQObject();
+    virtual ~tst_QScriptExtQObject();
+
+public slots:
+    void init();
+    void cleanup();
+
+protected slots:
+    void onSignalHandlerException(const QScriptValue &exception)
+    {
+        m_signalHandlerException = exception;
+    }
+
+private slots:
+    void registeredTypes();
+    void getSetStaticProperty();
+    void getSetDynamicProperty();
+    void getSetChildren();
+    void callQtInvokable();
+    void connectAndDisconnect();
+    void connectAndDisconnectWithBadArgs();
+    void cppConnectAndDisconnect();
+    void classEnums();
+    void classConstructor();
+    void overrideInvokable();
+    void transferInvokable();
+    void findChild();
+    void findChildren();
+    void overloadedSlots();
+    void enumerate_data();
+    void enumerate();
+    void enumerateSpecial();
+    void wrapOptions();
+    void prototypes();
+    void objectDeleted();
+    void connectToDestroyedSignal();
+
+private:
+    QScriptEngine *m_engine;
+    MyQObject *m_myObject;
+    QScriptValue m_signalHandlerException;
+};
+
+tst_QScriptExtQObject::tst_QScriptExtQObject()
+{
+}
+
+tst_QScriptExtQObject::~tst_QScriptExtQObject()
+{
+}
+
+void tst_QScriptExtQObject::init()
+{
+    m_engine = new QScriptEngine();
+    m_myObject = new MyQObject();
+    m_engine->globalObject().setProperty("myObject", m_engine->newQObject(m_myObject));
+    m_engine->globalObject().setProperty("global", m_engine->globalObject());
+}
+
+void tst_QScriptExtQObject::cleanup()
+{
+    delete m_engine;
+    delete m_myObject;
+}
+
+// this test has to be first and test that some types are automatically registered
+void tst_QScriptExtQObject::registeredTypes()
+{
+    QScriptEngine e;
+    QObject *t = new MyQObject;
+    QObject *c = new QObject(t);
+    c->setObjectName ("child1");
+
+    e.globalObject().setProperty("MyTest", e.newQObject(t));
+
+    QScriptValue v1 = e.evaluate("MyTest.findObjects()[0].objectName;");
+    QCOMPARE(v1.toString(), c->objectName());
+
+    QScriptValue v2 = e.evaluate("MyTest.myInvokableNumbers()");
+    QCOMPARE(qscriptvalue_cast<QList<int> >(v2), (QList<int>() << 1 << 2 << 3));
+}
+
+
+static QScriptValue getSetProperty(QScriptContext *ctx, QScriptEngine *)
+{
+    if (ctx->argumentCount() != 0)
+        ctx->callee().setProperty("value", ctx->argument(0));
+    return ctx->callee().property("value");
+}
+
+static QScriptValue policyToScriptValue(QScriptEngine *engine, const MyQObject::Policy &policy)
+{
+    return qScriptValueFromValue(engine, policy);
+}
+
+static void policyFromScriptValue(const QScriptValue &value, MyQObject::Policy &policy)
+{
+    QString str = value.toString();
+    if (str == QLatin1String("red"))
+        policy = MyQObject::FooPolicy;
+    else if (str == QLatin1String("green"))
+        policy = MyQObject::BarPolicy;
+    else if (str == QLatin1String("blue"))
+        policy = MyQObject::BazPolicy;
+    else
+        policy = (MyQObject::Policy)-1;
+}
+
+void tst_QScriptExtQObject::getSetStaticProperty()
+{
+    QCOMPARE(m_engine->evaluate("myObject.noSuchProperty").isUndefined(), true);
+
+    // initial value (set in MyQObject constructor)
+    QCOMPARE(m_engine->evaluate("myObject.intProperty")
+             .strictlyEquals(QScriptValue(m_engine, 123.0)), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantProperty")
+             .toVariant(), QVariant(QLatin1String("foo")));
+    QCOMPARE(m_engine->evaluate("myObject.stringProperty")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("bar"))), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty").isArray(), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty.length")
+             .strictlyEquals(QScriptValue(m_engine, 2)), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty[0]")
+             .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty[1]")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("foo"))), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty").isArray(), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty.length")
+             .strictlyEquals(QScriptValue(m_engine, 2)), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").isString(), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").toString(),
+             QLatin1String("zig"));
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").isString(), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").toString(),
+             QLatin1String("zag"));
+
+    // default flags for "normal" properties
+    {
+        QScriptValue mobj = m_engine->globalObject().property("myObject");
+        QVERIFY(!(mobj.propertyFlags("intProperty") & QScriptValue::ReadOnly));
+        QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::Undeletable);
+        QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::PropertyGetter);
+        QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::PropertySetter);
+        QVERIFY(!(mobj.propertyFlags("intProperty") & QScriptValue::SkipInEnumeration));
+        QVERIFY(mobj.propertyFlags("intProperty") & QScriptValue::QObjectMember);
+
+        QVERIFY(!(mobj.propertyFlags("mySlot") & QScriptValue::ReadOnly));
+        QVERIFY(!(mobj.propertyFlags("mySlot") & QScriptValue::Undeletable));
+        QVERIFY(!(mobj.propertyFlags("mySlot") & QScriptValue::SkipInEnumeration));
+        QVERIFY(mobj.propertyFlags("mySlot") & QScriptValue::QObjectMember);
+
+        // signature-based property
+        QVERIFY(!(mobj.propertyFlags("mySlot()") & QScriptValue::ReadOnly));
+        QVERIFY(!(mobj.propertyFlags("mySlot()") & QScriptValue::Undeletable));
+        QVERIFY(!(mobj.propertyFlags("mySlot()") & QScriptValue::SkipInEnumeration));
+        QVERIFY(mobj.propertyFlags("mySlot()") & QScriptValue::QObjectMember);
+    }
+
+    // property change in C++ should be reflected in script
+    m_myObject->setIntProperty(456);
+    QCOMPARE(m_engine->evaluate("myObject.intProperty")
+             .strictlyEquals(QScriptValue(m_engine, 456)), true);
+    m_myObject->setIntProperty(789);
+    QCOMPARE(m_engine->evaluate("myObject.intProperty")
+             .strictlyEquals(QScriptValue(m_engine, 789)), true);
+
+    m_myObject->setVariantProperty(QLatin1String("bar"));
+    QVERIFY(m_engine->evaluate("myObject.variantProperty")
+            .strictlyEquals(QScriptValue(m_engine, QLatin1String("bar"))));
+    m_myObject->setVariantProperty(42);
+    QCOMPARE(m_engine->evaluate("myObject.variantProperty")
+             .toVariant(), QVariant(42));
+    m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
+    QVERIFY(m_engine->evaluate("myObject.variantProperty").isVariant());
+
+    m_myObject->setStringProperty(QLatin1String("baz"));
+    QCOMPARE(m_engine->evaluate("myObject.stringProperty")
+             .equals(QScriptValue(m_engine, QLatin1String("baz"))), true);
+    m_myObject->setStringProperty(QLatin1String("zab"));
+    QCOMPARE(m_engine->evaluate("myObject.stringProperty")
+             .equals(QScriptValue(m_engine, QLatin1String("zab"))), true);
+
+    // property change in script should be reflected in C++
+    QCOMPARE(m_engine->evaluate("myObject.intProperty = 123")
+             .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    QCOMPARE(m_engine->evaluate("myObject.intProperty")
+             .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    QCOMPARE(m_myObject->intProperty(), 123);
+    QCOMPARE(m_engine->evaluate("myObject.intProperty = 'ciao!';"
+                                "myObject.intProperty")
+             .strictlyEquals(QScriptValue(m_engine, 0)), true);
+    QCOMPARE(m_myObject->intProperty(), 0);
+    QCOMPARE(m_engine->evaluate("myObject.intProperty = '123';"
+                                "myObject.intProperty")
+             .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    QCOMPARE(m_myObject->intProperty(), 123);
+
+    QCOMPARE(m_engine->evaluate("myObject.stringProperty = 'ciao'")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("ciao"))), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringProperty")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("ciao"))), true);
+    QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao"));
+    QCOMPARE(m_engine->evaluate("myObject.stringProperty = 123;"
+                                "myObject.stringProperty")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("123"))), true);
+    QCOMPARE(m_myObject->stringProperty(), QLatin1String("123"));
+    QVERIFY(m_engine->evaluate("myObject.stringProperty = null;"
+                               "myObject.stringProperty")
+            .strictlyEquals(QScriptValue(m_engine, QString())));
+    QCOMPARE(m_myObject->stringProperty(), QString());
+    QVERIFY(m_engine->evaluate("myObject.stringProperty = undefined;"
+                               "myObject.stringProperty")
+            .strictlyEquals(QScriptValue(m_engine, QString())));
+    QCOMPARE(m_myObject->stringProperty(), QString());
+
+    QCOMPARE(m_engine->evaluate("myObject.variantProperty = 'foo';"
+                                "myObject.variantProperty.valueOf()").toString(), QLatin1String("foo"));
+    QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo")));
+    QVERIFY(m_engine->evaluate("myObject.variantProperty = undefined;"
+                               "myObject.variantProperty").isUndefined());
+    QVERIFY(!m_myObject->variantProperty().isValid());
+    QVERIFY(m_engine->evaluate("myObject.variantProperty = null;"
+                               "myObject.variantProperty").isUndefined());
+    QVERIFY(!m_myObject->variantProperty().isValid());
+    QCOMPARE(m_engine->evaluate("myObject.variantProperty = 42;"
+                                "myObject.variantProperty").toNumber(), 42.0);
+    QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0);
+
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty = [1, 'two', true];"
+                                "myObject.variantListProperty.length")
+             .strictlyEquals(QScriptValue(m_engine, 3)), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty[0]")
+             .strictlyEquals(QScriptValue(m_engine, 1)), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty[1]")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("two"))), true);
+    QCOMPARE(m_engine->evaluate("myObject.variantListProperty[2]")
+             .strictlyEquals(QScriptValue(m_engine, true)), true);
+    {
+        QVariantList vl = qscriptvalue_cast<QVariantList>(m_engine->evaluate("myObject.variantListProperty"));
+        QCOMPARE(vl, QVariantList()
+                 << QVariant(1)
+                 << QVariant(QLatin1String("two"))
+                 << QVariant(true));
+    }
+
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty = [1, 'two', true];"
+                                "myObject.stringListProperty.length")
+             .strictlyEquals(QScriptValue(m_engine, 3)), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").isString(), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[0]").toString(),
+             QLatin1String("1"));
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").isString(), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[1]").toString(),
+             QLatin1String("two"));
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[2]").isString(), true);
+    QCOMPARE(m_engine->evaluate("myObject.stringListProperty[2]").toString(),
+             QLatin1String("true"));
+    {
+        QStringList sl = qscriptvalue_cast<QStringList>(m_engine->evaluate("myObject.stringListProperty"));
+        QCOMPARE(sl, QStringList()
+                 << QLatin1String("1")
+                 << QLatin1String("two")
+                 << QLatin1String("true"));
+    }
+
+    // test setting properties where we can't convert the type natively but where the
+    // types happen to be compatible variant types already
+    {
+        QKeySequence sequence(Qt::ControlModifier + Qt::AltModifier + Qt::Key_Delete);
+        QScriptValue mobj = m_engine->globalObject().property("myObject");
+
+        QVERIFY(m_myObject->shortcut().isEmpty());
+        mobj.setProperty("shortcut", m_engine->newVariant(sequence));
+        QVERIFY(m_myObject->shortcut() == sequence);
+    }
+    {
+        CustomType t; t.string = "hello";
+        QScriptValue mobj = m_engine->globalObject().property("myObject");
+
+        QVERIFY(m_myObject->propWithCustomType().string.isEmpty());
+        mobj.setProperty("propWithCustomType", m_engine->newVariant(qVariantFromValue(t)));
+        QVERIFY(m_myObject->propWithCustomType().string == t.string);
+    }
+
+    // test that we do value conversion if necessary when setting properties
+    {
+        QScriptValue br = m_engine->evaluate("myObject.brushProperty");
+        QVERIFY(br.isVariant());
+        QVERIFY(!br.strictlyEquals(m_engine->evaluate("myObject.brushProperty")));
+        QCOMPARE(qscriptvalue_cast<QBrush>(br), m_myObject->brushProperty());
+        QCOMPARE(qscriptvalue_cast<QColor>(br), m_myObject->brushProperty().color());
+
+        QColor newColor(40, 30, 20, 10);
+        QScriptValue val = qScriptValueFromValue(m_engine, newColor);
+        m_engine->globalObject().setProperty("myColor", val);
+        QScriptValue ret = m_engine->evaluate("myObject.brushProperty = myColor");
+        QCOMPARE(ret.strictlyEquals(val), true);
+        br = m_engine->evaluate("myObject.brushProperty");
+        QCOMPARE(qscriptvalue_cast<QBrush>(br), QBrush(newColor));
+        QCOMPARE(qscriptvalue_cast<QColor>(br), newColor);
+
+        m_engine->globalObject().setProperty("myColor", QScriptValue());
+    }
+
+    // try to delete
+    QCOMPARE(m_engine->evaluate("delete myObject.intProperty").toBoolean(), false);
+    QCOMPARE(m_engine->evaluate("myObject.intProperty").toNumber(), 123.0);
+
+    QCOMPARE(m_engine->evaluate("delete myObject.variantProperty").toBoolean(), false);
+    QCOMPARE(m_engine->evaluate("myObject.variantProperty").toNumber(), 42.0);
+
+    // non-scriptable property
+    QCOMPARE(m_myObject->hiddenProperty(), 456.0);
+    QCOMPARE(m_engine->evaluate("myObject.hiddenProperty").isUndefined(), true);
+    QCOMPARE(m_engine->evaluate("myObject.hiddenProperty = 123;"
+                                "myObject.hiddenProperty").toInt32(), 123);
+    QCOMPARE(m_myObject->hiddenProperty(), 456.0);
+
+    // write-only property
+    QCOMPARE(m_myObject->writeOnlyProperty(), 789);
+    QCOMPARE(m_engine->evaluate("myObject.writeOnlyProperty").isUndefined(), true);
+    QCOMPARE(m_engine->evaluate("myObject.writeOnlyProperty = 123;"
+                                "myObject.writeOnlyProperty").isUndefined(), true);
+    QCOMPARE(m_myObject->writeOnlyProperty(), 123);
+
+    // read-only property
+    QCOMPARE(m_myObject->readOnlyProperty(), 987);
+    QCOMPARE(m_engine->evaluate("myObject.readOnlyProperty").toInt32(), 987);
+    QCOMPARE(m_engine->evaluate("myObject.readOnlyProperty = 654;"
+                                "myObject.readOnlyProperty").toInt32(), 987);
+    QCOMPARE(m_myObject->readOnlyProperty(), 987);
+    {
+        QScriptValue mobj = m_engine->globalObject().property("myObject");
+        QCOMPARE(mobj.propertyFlags("readOnlyProperty") & QScriptValue::ReadOnly,
+                 QScriptValue::ReadOnly);
+    }
+
+    // enum property
+    QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy);
+    {
+        QScriptValue val = m_engine->evaluate("myObject.enumProperty");
+        QVERIFY(val.isNumber());
+        QCOMPARE(val.toInt32(), (int)MyQObject::BarPolicy);
+    }
+    m_engine->evaluate("myObject.enumProperty = 2");
+    QCOMPARE(m_myObject->enumProperty(), MyQObject::BazPolicy);
+    m_engine->evaluate("myObject.enumProperty = 'BarPolicy'");
+    QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy);
+    m_engine->evaluate("myObject.enumProperty = 'ScoobyDoo'");
+    // ### ouch! Shouldn't QMetaProperty::write() rather not change the value...?
+    QCOMPARE(m_myObject->enumProperty(), (MyQObject::Policy)-1);
+    // enum property with custom conversion
+    qScriptRegisterMetaType<MyQObject::Policy>(m_engine, policyToScriptValue, policyFromScriptValue);
+    m_engine->evaluate("myObject.enumProperty = 'red'");
+    QCOMPARE(m_myObject->enumProperty(), MyQObject::FooPolicy);
+    m_engine->evaluate("myObject.enumProperty = 'green'");
+    QCOMPARE(m_myObject->enumProperty(), MyQObject::BarPolicy);
+    m_engine->evaluate("myObject.enumProperty = 'blue'");
+    QCOMPARE(m_myObject->enumProperty(), MyQObject::BazPolicy);
+    m_engine->evaluate("myObject.enumProperty = 'nada'");
+    QCOMPARE(m_myObject->enumProperty(), (MyQObject::Policy)-1);
+
+    // auto-dereferencing of pointers
+    {
+        QBrush b = QColor(0xCA, 0xFE, 0xBA, 0xBE);
+        QBrush *bp = &b;
+        QScriptValue bpValue = m_engine->newVariant(qVariantFromValue(bp));
+        m_engine->globalObject().setProperty("brushPointer", bpValue);
+        {
+            QScriptValue ret = m_engine->evaluate("myObject.setBrushProperty(brushPointer)");
+            QCOMPARE(ret.isUndefined(), true);
+            QCOMPARE(qscriptvalue_cast<QBrush>(m_engine->evaluate("myObject.brushProperty")), b);
+        }
+        {
+            b = QColor(0xDE, 0xAD, 0xBE, 0xEF);
+            QScriptValue ret = m_engine->evaluate("myObject.brushProperty = brushPointer");
+            QCOMPARE(ret.strictlyEquals(bpValue), true);
+            QCOMPARE(qscriptvalue_cast<QBrush>(m_engine->evaluate("myObject.brushProperty")), b);
+        }
+        m_engine->globalObject().setProperty("brushPointer", QScriptValue());
+    }
+
+    // install custom property getter+setter
+    {
+        QScriptValue mobj = m_engine->globalObject().property("myObject");
+        mobj.setProperty("intProperty", m_engine->newFunction(getSetProperty),
+                         QScriptValue::PropertyGetter | QScriptValue::PropertySetter);
+        QVERIFY(mobj.property("intProperty").toInt32() != 321);
+        mobj.setProperty("intProperty", 321);
+        QCOMPARE(mobj.property("intProperty").toInt32(), 321);
+    }
+
+    // method properties are persistent
+    {
+        QScriptValue slot = m_engine->evaluate("myObject.mySlot");
+        QVERIFY(slot.isFunction());
+        QScriptValue sameSlot = m_engine->evaluate("myObject.mySlot");
+        QVERIFY(sameSlot.strictlyEquals(slot));
+        sameSlot = m_engine->evaluate("myObject[mySlot()]");
+        QEXPECT_FAIL("", "Signature-based method lookup creates new function wrapper object", Continue);
+        QVERIFY(sameSlot.strictlyEquals(slot));
+    }
+}
+
+void tst_QScriptExtQObject::getSetDynamicProperty()
+{
+    // initially the object does not have the property
+    QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('dynamicProperty')")
+             .strictlyEquals(QScriptValue(m_engine, false)), true);
+
+    // add a dynamic property in C++
+    QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false);
+    QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('dynamicProperty')")
+             .strictlyEquals(QScriptValue(m_engine, true)), true);
+    QCOMPARE(m_engine->evaluate("myObject.dynamicProperty")
+             .strictlyEquals(QScriptValue(m_engine, 123)), true);
+
+    // check the flags
+    {
+        QScriptValue mobj = m_engine->globalObject().property("myObject");
+        QVERIFY(!(mobj.propertyFlags("dynamicProperty") & QScriptValue::ReadOnly));
+        QVERIFY(!(mobj.propertyFlags("dynamicProperty") & QScriptValue::Undeletable));
+        QVERIFY(!(mobj.propertyFlags("dynamicProperty") & QScriptValue::SkipInEnumeration));
+        QVERIFY(mobj.propertyFlags("dynamicProperty") & QScriptValue::QObjectMember);
+    }
+
+    // property change in script should be reflected in C++
+    QCOMPARE(m_engine->evaluate("myObject.dynamicProperty = 'foo';"
+                                "myObject.dynamicProperty")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("foo"))), true);
+    QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo"));
+
+    // delete the property
+    QCOMPARE(m_engine->evaluate("delete myObject.dynamicProperty").toBoolean(), true);
+    QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false);
+    QCOMPARE(m_engine->evaluate("myObject.dynamicProperty").isUndefined(), true);
+    QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('dynamicProperty')").toBoolean(), false);
+}
+
+void tst_QScriptExtQObject::getSetChildren()
+{
+    QScriptValue mobj = m_engine->evaluate("myObject");
+
+    // initially the object does not have the child
+    QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('child')")
+             .strictlyEquals(QScriptValue(m_engine, false)), true);
+
+    // add a child
+    MyQObject *child = new MyQObject(m_myObject);
+    child->setObjectName("child");
+    QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('child')")
+             .strictlyEquals(QScriptValue(m_engine, true)), true);
+
+    QVERIFY(mobj.propertyFlags("child") & QScriptValue::ReadOnly);
+    QVERIFY(mobj.propertyFlags("child") & QScriptValue::Undeletable);
+    QVERIFY(mobj.propertyFlags("child") & QScriptValue::SkipInEnumeration);
+    QVERIFY(!(mobj.propertyFlags("child") & QScriptValue::QObjectMember));
+
+    {
+        QScriptValue scriptChild = m_engine->evaluate("myObject.child");
+        QVERIFY(scriptChild.isQObject());
+        QCOMPARE(scriptChild.toQObject(), (QObject*)child);
+        QScriptValue sameChild = m_engine->evaluate("myObject.child");
+        QVERIFY(sameChild.strictlyEquals(scriptChild));
+    }
+
+    // add a grandchild
+    MyQObject *grandChild = new MyQObject(child);
+    grandChild->setObjectName("grandChild");
+    QCOMPARE(m_engine->evaluate("myObject.child.hasOwnProperty('grandChild')")
+             .strictlyEquals(QScriptValue(m_engine, true)), true);
+
+    // delete grandchild
+    delete grandChild;
+    QCOMPARE(m_engine->evaluate("myObject.child.hasOwnProperty('grandChild')")
+             .strictlyEquals(QScriptValue(m_engine, false)), true);
+
+    // delete child
+    delete child;
+    QCOMPARE(m_engine->evaluate("myObject.hasOwnProperty('child')")
+             .strictlyEquals(QScriptValue(m_engine, false)), true);
+
+}
+
+Q_DECLARE_METATYPE(QVector<int>)
+Q_DECLARE_METATYPE(QVector<double>)
+Q_DECLARE_METATYPE(QVector<QString>)
+
+template <class T>
+static QScriptValue qobjectToScriptValue(QScriptEngine *engine, T* const &in)
+{ return engine->newQObject(in); }
+
+template <class T>
+static void qobjectFromScriptValue(const QScriptValue &object, T* &out)
+{ out = qobject_cast<T*>(object.toQObject()); }
+
+void tst_QScriptExtQObject::callQtInvokable()
+{
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokable()").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
+    QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
+
+    // extra arguments should silently be ignored
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokable(10, 20, 30)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
+    QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArg(123)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArg('123')").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithLonglongArg(123)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 2);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123));
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithFloatArg(123.5)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 3);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithDoubleArg(123.5)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 4);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5);
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithStringArg('ciao')").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao"));
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithStringArg(123)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
+
+    m_myObject->resetQtFunctionInvoked();
+    QVERIFY(m_engine->evaluate("myObject.myInvokableWithStringArg(null)").isUndefined());
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::String);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
+
+    m_myObject->resetQtFunctionInvoked();
+    QVERIFY(m_engine->evaluate("myObject.myInvokableWithStringArg(undefined)").isUndefined());
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 5);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).type(), QVariant::String);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString());
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArgs(123, 456)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 6);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningInt()")
+             .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 7);
+    QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningLongLong()")
+             .strictlyEquals(QScriptValue(m_engine, 456)), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 39);
+    QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningString()")
+             .strictlyEquals(QScriptValue(m_engine, QLatin1String("ciao"))), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 8);
+    QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList());
+
+    m_myObject->resetQtFunctionInvoked();
+    QVERIFY(m_engine->evaluate("myObject.myInvokableReturningVariant()")
+             .strictlyEquals(QScriptValue(m_engine, 123)));
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 60);
+
+    m_myObject->resetQtFunctionInvoked();
+    QVERIFY(m_engine->evaluate("myObject.myInvokableReturningScriptValue()")
+             .strictlyEquals(QScriptValue(m_engine, 456)));
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 61);
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithIntArg(123, 456)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 9);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456);
+
+    m_myObject->resetQtFunctionInvoked();
+    QVERIFY(m_engine->evaluate("myObject.myInvokableWithVoidStarArg(null)").isUndefined());
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 44);
+    m_myObject->resetQtFunctionInvoked();
+    QVERIFY(m_engine->evaluate("myObject.myInvokableWithVoidStarArg(123)").isError());
+    QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithAmbiguousArg(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n    myInvokableWithAmbiguousArg(int)\n    myInvokableWithAmbiguousArg(uint)"));
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithDefaultArgs(123, 'hello')");
+        QVERIFY(ret.isUndefined());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello"));
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithDefaultArgs(456)");
+        QVERIFY(ret.isUndefined());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 47);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 2);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString());
+    }
+
+    {
+        QScriptValue fun = m_engine->evaluate("myObject.myInvokableWithPointArg");
+        QVERIFY(fun.isFunction());
+        m_myObject->resetQtFunctionInvoked();
+        {
+            QScriptValue ret = fun.call(m_engine->evaluate("myObject"),
+                                        QScriptValueList() << qScriptValueFromValue(m_engine, QPoint(10, 20)));
+            QVERIFY(ret.isUndefined());
+            QCOMPARE(m_myObject->qtFunctionInvoked(), 50);
+            QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+            QCOMPARE(m_myObject->qtFunctionActuals().at(0).toPoint(), QPoint(10, 20));
+        }
+        m_myObject->resetQtFunctionInvoked();
+        {
+            QScriptValue ret = fun.call(m_engine->evaluate("myObject"),
+                                        QScriptValueList() << qScriptValueFromValue(m_engine, QPointF(30, 40)));
+            QVERIFY(ret.isUndefined());
+            QCOMPARE(m_myObject->qtFunctionInvoked(), 51);
+            QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+            QCOMPARE(m_myObject->qtFunctionActuals().at(0).toPointF(), QPointF(30, 40));
+        }
+    }
+
+    // calling function that returns (const)ref
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningRef()");
+        QVERIFY(ret.isUndefined());
+        QVERIFY(!m_engine->hasUncaughtException());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 48);
+    }
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningConstRef()");
+        QVERIFY(ret.isUndefined());
+        QVERIFY(!m_engine->hasUncaughtException());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 49);
+    }
+
+    // first time we expect failure because the metatype is not registered
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableReturningVectorOfInt()").isError(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithVectorOfIntArg(0)").isError(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+
+    // now we register it, and it should work
+    qScriptRegisterSequenceMetaType<QVector<int> >(m_engine);
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningVectorOfInt()");
+        QCOMPARE(ret.isArray(), true);
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 11);
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVectorOfIntArg(myObject.myInvokableReturningVectorOfInt())");
+        QCOMPARE(ret.isUndefined(), true);
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 12);
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningQObjectStar()");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 13);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
+        QCOMPARE(ret.isQObject(), true);
+        QCOMPARE(ret.toQObject(), (QObject *)m_myObject);
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQObjectListArg([myObject])");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 14);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(ret.isArray(), true);
+        QCOMPARE(ret.property(QLatin1String("length"))
+                 .strictlyEquals(QScriptValue(m_engine, 1)), true);
+        QCOMPARE(ret.property(QLatin1String("0")).isQObject(), true);
+        QCOMPARE(ret.property(QLatin1String("0")).toQObject(), (QObject *)m_myObject);
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        m_myObject->setVariantProperty(QVariant(123));
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(myObject.variantProperty)");
+        QVERIFY(ret.isNumber());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty());
+        QVERIFY(ret.strictlyEquals(QScriptValue(m_engine, 123)));
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        m_myObject->setVariantProperty(qVariantFromValue(QBrush()));
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(myObject.variantProperty)");
+        QVERIFY(ret.isVariant());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0));
+        QCOMPARE(ret.toVariant(), m_myObject->variantProperty());
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(123)");
+        QVERIFY(ret.isNumber());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123));
+        QVERIFY(ret.strictlyEquals(QScriptValue(m_engine, 123)));
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg('ciao')");
+        QVERIFY(ret.isString());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(QString::fromLatin1("ciao")));
+        QVERIFY(ret.strictlyEquals(QScriptValue(m_engine, QString::fromLatin1("ciao"))));
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(null)");
+        QVERIFY(ret.isUndefined()); // invalid QVariant is converted to Undefined
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantArg(undefined)");
+        QVERIFY(ret.isUndefined());
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 15);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant());
+    }
+
+    m_engine->globalObject().setProperty("fishy", m_engine->newVariant(123));
+    m_engine->evaluate("myObject.myInvokableWithStringArg(fishy)");
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 16);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QVariant v = m_myObject->qtFunctionActuals().at(0);
+        QCOMPARE(v.userType(), int(QMetaType::QVariantMap));
+        QVariantMap vmap = qvariant_cast<QVariantMap>(v);
+        QCOMPARE(vmap.keys().size(), 2);
+        QCOMPARE(vmap.keys().at(0), QLatin1String("a"));
+        QCOMPARE(vmap.value("a"), QVariant(123));
+        QCOMPARE(vmap.keys().at(1), QLatin1String("b"));
+        QCOMPARE(vmap.value("b"), QVariant("ciao"));
+
+        QCOMPARE(ret.isObject(), true);
+        QCOMPARE(ret.property("a").strictlyEquals(QScriptValue(m_engine, 123)), true);
+        QCOMPARE(ret.property("b").strictlyEquals(QScriptValue(m_engine, "ciao")), true);
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithListOfIntArg([1, 5])");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 17);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QVariant v = m_myObject->qtFunctionActuals().at(0);
+        QCOMPARE(v.userType(), qMetaTypeId<QList<int> >());
+        QList<int> ilst = qvariant_cast<QList<int> >(v);
+        QCOMPARE(ilst.size(), 2);
+        QCOMPARE(ilst.at(0), 1);
+        QCOMPARE(ilst.at(1), 5);
+
+        QCOMPARE(ret.isArray(), true);
+        QCOMPARE(ret.property("0").strictlyEquals(QScriptValue(m_engine, 1)), true);
+        QCOMPARE(ret.property("1").strictlyEquals(QScriptValue(m_engine, 5)), true);
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQObjectStarArg(myObject)");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 18);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QVariant v = m_myObject->qtFunctionActuals().at(0);
+        QCOMPARE(v.userType(), int(QMetaType::QObjectStar));
+        QCOMPARE(qvariant_cast<QObject*>(v), (QObject *)m_myObject);
+
+        QCOMPARE(ret.isQObject(), true);
+        QCOMPARE(qscriptvalue_cast<QObject*>(ret), (QObject *)m_myObject);
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        // no implicit conversion from integer to QObject*
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQObjectStarArg(123)");
+        QCOMPARE(ret.isError(), true);
+    }
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue fun = m_engine->evaluate("myObject.myInvokableWithQBrushArg");
+        QVERIFY(fun.isFunction());
+        QColor color(10, 20, 30, 40);
+        // QColor should be converted to a QBrush
+        QScriptValue ret = fun.call(QScriptValue(), QScriptValueList()
+                                    << qScriptValueFromValue(m_engine, color));
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 19);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QVariant v = m_myObject->qtFunctionActuals().at(0);
+        QCOMPARE(v.userType(), int(QMetaType::QBrush));
+        QCOMPARE(qvariant_cast<QColor>(v), color);
+
+        QCOMPARE(qscriptvalue_cast<QColor>(ret), color);
+    }
+
+    // private slots should not be part of the QObject binding
+    QCOMPARE(m_engine->evaluate("myObject.myPrivateSlot").isUndefined(), true);
+
+    // protected slots should be fine
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myProtectedSlot()");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
+
+    // call with too few arguments
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithIntArg()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n    myInvokableWithIntArg(int,int)\n    myInvokableWithIntArg(int)"));
+    }
+
+    // call function where not all types have been registered
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithBrushStyleArg(0)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): argument 1 has unknown type `Qt::BrushStyle' (register the type with qScriptRegisterMetaType())"));
+        QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+    }
+
+    // call function with incompatible argument type
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQBrushArg(null)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n    myInvokableWithQBrushArg(QBrush)"));
+        QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+    }
+
+    // ability to call a slot with QObject-based arguments, even if those types haven't been registered
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithMyQObjectArg(myObject)");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 52);
+        QVERIFY(ret.isUndefined());
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(qvariant_cast<QObject*>(m_myObject->qtFunctionActuals().at(0)), (QObject*)m_myObject);
+    }
+
+    // inability to call a slot returning QObject-based type, when that type hasn't been registered
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningMyQObject()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: cannot call myInvokableReturningMyQObject(): unknown return type `MyQObject*' (register the type with qScriptRegisterMetaType())"));
+    }
+
+    // ability to call a slot returning QObject-based type when that type has been registered
+    qRegisterMetaType<MyQObject*>("MyQObject*");
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningMyQObject()");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 53);
+        QVERIFY(ret.isVariant());
+        QCOMPARE(*reinterpret_cast<MyQObject* const *>(ret.toVariant().constData()), m_myObject);
+    }
+
+    // ability to call a slot with QObject-based argument, when the argument is const
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithConstMyQObjectArg(myObject)");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 54);
+        QVERIFY(ret.isUndefined());
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+        QCOMPARE(qvariant_cast<QObject*>(m_myObject->qtFunctionActuals().at(0)), (QObject*)m_myObject);
+    }
+
+    // QScriptValue arguments should be passed on without conversion
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithScriptValueArg(123)");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 56);
+        QVERIFY(ret.isNumber());
+        QCOMPARE(ret.toInt32(), 123);
+    }
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithScriptValueArg('ciao')");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 56);
+        QVERIFY(ret.isString());
+        QCOMPARE(ret.toString(), QString::fromLatin1("ciao"));
+    }
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithScriptValueArg(this)");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 56);
+        QVERIFY(ret.isObject());
+        QVERIFY(ret.strictlyEquals(m_engine->globalObject()));
+    }
+
+    // the prototype specified by a conversion function should not be "down-graded"
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue qobjectProto = m_engine->newObject();
+        qScriptRegisterMetaType<QObject*>(m_engine, qobjectToScriptValue,
+                                          qobjectFromScriptValue, qobjectProto);
+        QScriptValue myQObjectProto = m_engine->newObject();
+        myQObjectProto.setPrototype(qobjectProto);
+        qScriptRegisterMetaType<MyQObject*>(m_engine, qobjectToScriptValue,
+                                          qobjectFromScriptValue, myQObjectProto);
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningMyQObjectAsQObject()");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 57);
+        QVERIFY(ret.isQObject());
+        QVERIFY(ret.prototype().strictlyEquals(myQObjectProto));
+
+        qScriptRegisterMetaType<QObject*>(m_engine, 0, 0, QScriptValue());
+        qScriptRegisterMetaType<MyQObject*>(m_engine, 0, 0, QScriptValue());
+    }
+
+    // detect exceptions during argument conversion
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue (*dummy)(QScriptEngine *, const QDir &) = 0;
+        qScriptRegisterMetaType<QDir>(m_engine, dummy, dirFromScript);
+        {
+            QVERIFY(!m_engine->hasUncaughtException());
+            QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQDirArg({})");
+            QVERIFY(m_engine->hasUncaughtException());
+            QVERIFY(ret.isError());
+            QCOMPARE(ret.toString(), QString::fromLatin1("Error: No path"));
+            QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+        }
+        m_engine->clearExceptions();
+        {
+            QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQDirArg({path:'.'})");
+            QVERIFY(!m_engine->hasUncaughtException());
+            QVERIFY(ret.isUndefined());
+            QCOMPARE(m_myObject->qtFunctionInvoked(), 55);
+        }
+    }
+
+    // qscript_call()
+    {
+        m_myObject->resetQtFunctionInvoked();
+        QScriptValue ret = m_engine->evaluate("new myObject(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'myObject' [MyQObject(name = \"\")] is not a constructor."));
+    }
+    {
+        m_myObject->resetQtFunctionInvoked();
+        QScriptValue ret = m_engine->evaluate("myObject(123)");
+        QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'myObject' [MyQObject(name = \"\")] is not a function."));
+    }
+
+    // task 233624
+    {
+        MyNS::A a;
+        m_engine->globalObject().setProperty("anObject", m_engine->newQObject(&a));
+        QScriptValue ret = m_engine->evaluate("anObject.slotTakingScopedEnumArg(1)");
+        QVERIFY(!ret.isError());
+        QVERIFY(ret.isNumber());
+        QCOMPARE(ret.toInt32(), 1);
+        m_engine->globalObject().setProperty("anObject", QScriptValue());
+    }
+
+    // virtual slot redeclared in subclass (task 236467)
+    {
+        MyOtherQObject moq;
+        m_engine->globalObject().setProperty("myOtherQObject", m_engine->newQObject(&moq));
+        moq.resetQtFunctionInvoked();
+        QScriptValue ret = m_engine->evaluate("myOtherQObject.myVirtualSlot(123)");
+        QCOMPARE(moq.qtFunctionInvoked(), 59);
+        QVERIFY(!ret.isError());
+        QVERIFY(ret.isNumber());
+        QCOMPARE(ret.toInt32(), 123);
+    }
+}
+
+void tst_QScriptExtQObject::connectAndDisconnect()
+{
+    // connect(function)
+    QCOMPARE(m_engine->evaluate("myObject.mySignal.connect(123)").isError(), true);
+
+    m_engine->evaluate("myHandler = function() { global.gotSignal = true; global.signalArgs = arguments; global.slotThisObject = this; }");
+
+    m_myObject->clearConnectedSignal();
+    QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myHandler)").isUndefined());
+    QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignal()));
+
+    m_engine->evaluate("gotSignal = false");
+    m_engine->evaluate("myObject.mySignal()");
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 0.0);
+    QVERIFY(m_engine->evaluate("slotThisObject").strictlyEquals(m_engine->globalObject()));
+
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignal();
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 0.0);
+
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myHandler)").isUndefined());
+
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignalWithIntArg(123);
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0);
+    QCOMPARE(m_engine->evaluate("signalArgs[0]").toNumber(), 123.0);
+
+    m_myObject->clearDisconnectedSignal();
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myHandler)").isUndefined());
+    QCOMPARE(m_myObject->disconnectedSignal().constData(), SIGNAL(mySignal()));
+
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myHandler)").isError());
+
+    QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(myHandler)").isUndefined());
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myHandler)").isUndefined());
+
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignal2(false);
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0);
+    QCOMPARE(m_engine->evaluate("signalArgs[0]").toBoolean(), false);
+
+    m_engine->evaluate("gotSignal = false");
+    QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(myHandler)").isUndefined());
+    m_myObject->emitMySignal2(true);
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0);
+    QCOMPARE(m_engine->evaluate("signalArgs[0]").toBoolean(), true);
+
+    QVERIFY(m_engine->evaluate("myObject.mySignal2.disconnect(myHandler)").isUndefined());
+
+    QVERIFY(m_engine->evaluate("myObject['mySignal2()'].connect(myHandler)").isUndefined());
+
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignal2();
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+
+    QVERIFY(m_engine->evaluate("myObject['mySignal2()'].disconnect(myHandler)").isUndefined());
+
+    // connecting to signal with default args should pick the most generic version (i.e. with all args)
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithDefaultArg.connect(myHandler)").isUndefined());
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignalWithDefaultArgWithArg(456);
+    QVERIFY(m_engine->evaluate("gotSignal").toBoolean());
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toInt32(), 1);
+    QCOMPARE(m_engine->evaluate("signalArgs[0]").toInt32(), 456);
+
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignalWithDefaultArg();
+    QVERIFY(m_engine->evaluate("gotSignal").toBoolean());
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toInt32(), 1);
+    QCOMPARE(m_engine->evaluate("signalArgs[0]").toInt32(), 123);
+
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithDefaultArg.disconnect(myHandler)").isUndefined());
+
+    m_engine->evaluate("gotSignal = false");
+    // connecting to overloaded signal should throw an error
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myOverloadedSignal.connect(myHandler)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QString::fromLatin1("Error: Function.prototype.connect: ambiguous connect to MyQObject::myOverloadedSignal(); candidates are\n"
+                                                     "    myOverloadedSignal(int)\n"
+                                                     "    myOverloadedSignal(QString)\n"
+                                                     "Use e.g. object['myOverloadedSignal(QString)'].connect() to connect to a particular overload"));
+    }
+    m_myObject->emitMyOverloadedSignal(123);
+    QVERIFY(!m_engine->evaluate("gotSignal").toBoolean());
+    m_myObject->emitMyOverloadedSignal("ciao");
+    QVERIFY(!m_engine->evaluate("gotSignal").toBoolean());
+
+    m_engine->evaluate("gotSignal = false");
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myOtherOverloadedSignal.connect(myHandler)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QString::fromLatin1("Error: Function.prototype.connect: ambiguous connect to MyQObject::myOtherOverloadedSignal(); candidates are\n"
+                                                     "    myOtherOverloadedSignal(QString)\n"
+                                                     "    myOtherOverloadedSignal(int)\n"
+                                                     "Use e.g. object['myOtherOverloadedSignal(int)'].connect() to connect to a particular overload"));
+    }
+    m_myObject->emitMyOtherOverloadedSignal("ciao");
+    QVERIFY(!m_engine->evaluate("gotSignal").toBoolean());
+    m_myObject->emitMyOtherOverloadedSignal(123);
+    QVERIFY(!m_engine->evaluate("gotSignal").toBoolean());
+
+    // signal with QVariant arg: argument conversion should work
+    m_myObject->clearConnectedSignal();
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.connect(myHandler)").isUndefined());
+    QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignalWithVariantArg(QVariant)));
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignalWithVariantArg(123);
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0);
+    QVERIFY(m_engine->evaluate("signalArgs[0]").isNumber());
+    QCOMPARE(m_engine->evaluate("signalArgs[0]").toNumber(), 123.0);
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithVariantArg.disconnect(myHandler)").isUndefined());
+
+    // signal with argument type that's unknown to the meta-type system
+    m_myObject->clearConnectedSignal();
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.connect(myHandler)").isUndefined());
+    QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignalWithScriptEngineArg(QScriptEngine*)));
+    m_engine->evaluate("gotSignal = false");
+    QTest::ignoreMessage(QtWarningMsg, "QScriptEngine: Unable to handle unregistered datatype 'QScriptEngine*' when invoking handler of signal MyQObject::mySignalWithScriptEngineArg(QScriptEngine*)");
+    m_myObject->emitMySignalWithScriptEngineArg(m_engine);
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0);
+    QVERIFY(m_engine->evaluate("signalArgs[0]").isUndefined());
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithScriptEngineArg.disconnect(myHandler)").isUndefined());
+
+    // connect(object, function)
+    m_engine->evaluate("otherObject = { name:'foo' }");
+    QVERIFY(m_engine->evaluate("myObject.mySignal.connect(otherObject, myHandler)").isUndefined());
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(otherObject, myHandler)").isUndefined());
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignal();
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), false);
+
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(otherObject, myHandler)").isError());
+
+    QVERIFY(m_engine->evaluate("myObject.mySignal.connect(otherObject, myHandler)").isUndefined());
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignal();
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 0.0);
+    QVERIFY(m_engine->evaluate("slotThisObject").strictlyEquals(m_engine->evaluate("otherObject")));
+    QCOMPARE(m_engine->evaluate("slotThisObject").property("name").toString(), QLatin1String("foo"));
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(otherObject, myHandler)").isUndefined());
+
+    m_engine->evaluate("yetAnotherObject = { name:'bar', func : function() { } }");
+    QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(yetAnotherObject, myHandler)").isUndefined());
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignal2(true);
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0);
+    QVERIFY(m_engine->evaluate("slotThisObject").strictlyEquals(m_engine->evaluate("yetAnotherObject")));
+    QCOMPARE(m_engine->evaluate("slotThisObject").property("name").toString(), QLatin1String("bar"));
+    QVERIFY(m_engine->evaluate("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)").isUndefined());
+
+    QVERIFY(m_engine->evaluate("myObject.mySignal2.connect(myObject, myHandler)").isUndefined());
+    m_engine->evaluate("gotSignal = false");
+    m_myObject->emitMySignal2(true);
+    QCOMPARE(m_engine->evaluate("gotSignal").toBoolean(), true);
+    QCOMPARE(m_engine->evaluate("signalArgs.length").toNumber(), 1.0);
+    QCOMPARE(m_engine->evaluate("slotThisObject").toQObject(), (QObject *)m_myObject);
+    QVERIFY(m_engine->evaluate("myObject.mySignal2.disconnect(myObject, myHandler)").isUndefined());
+
+    // connect(obj, string)
+    QVERIFY(m_engine->evaluate("myObject.mySignal.connect(yetAnotherObject, 'func')").isUndefined());
+    QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject, 'mySlot')").isUndefined());
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(yetAnotherObject, 'func')").isUndefined());
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myObject, 'mySlot')").isUndefined());
+
+    // check that emitting signals from script works
+
+    // no arguments
+    QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject.mySlot)").isUndefined());
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.mySignal()").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
+    QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myObject.mySlot)").isUndefined());
+
+    // one argument
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)").isUndefined());
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 21);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)").isUndefined());
+
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)").isUndefined());
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 22);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0);
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)").isUndefined());
+
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)").isUndefined());
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 23);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123"));
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)").isUndefined());
+
+    // connecting to overloaded slot
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)").isUndefined());
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(123)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123);
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)").isUndefined());
+
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])").isUndefined());
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.mySignalWithIntArg(456)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
+    QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])").isUndefined());
+
+    // when the wrapper dies, the connection stays alive
+    QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject.mySlot)").isUndefined());
+    m_myObject->resetQtFunctionInvoked();
+    m_myObject->emitMySignal();
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
+    m_engine->evaluate("myObject = null");
+    m_engine->collectGarbage();
+    m_myObject->resetQtFunctionInvoked();
+    m_myObject->emitMySignal();
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 20);
+}
+
+void tst_QScriptExtQObject::connectAndDisconnectWithBadArgs()
+{
+    {
+        QScriptValue ret = m_engine->evaluate("(function() { }).connect()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.connect: no arguments given"));
+    }
+    {
+        QScriptValue ret = m_engine->evaluate("var o = { }; o.connect = Function.prototype.connect;  o.connect()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.connect: no arguments given"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("(function() { }).connect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: this object is not a signal"));
+    }
+    {
+        QScriptValue ret = m_engine->evaluate("var o = { }; o.connect = Function.prototype.connect;  o.connect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: this object is not a signal"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokable.connect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: MyQObject::myInvokable() is not a signal"));
+    }
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokable.connect(function() { })");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: MyQObject::myInvokable() is not a signal"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.mySignal.connect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.connect: target is not a function"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("(function() { }).disconnect()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.disconnect: no arguments given"));
+    }
+    {
+        QScriptValue ret = m_engine->evaluate("var o = { }; o.disconnect = Function.prototype.disconnect;  o.disconnect()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.disconnect: no arguments given"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("(function() { }).disconnect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: this object is not a signal"));
+    }
+    {
+        QScriptValue ret = m_engine->evaluate("var o = { }; o.disconnect = Function.prototype.disconnect;  o.disconnect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: this object is not a signal"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokable.disconnect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: MyQObject::myInvokable() is not a signal"));
+    }
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokable.disconnect(function() { })");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: MyQObject::myInvokable() is not a signal"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.mySignal.disconnect(123)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Function.prototype.disconnect: target is not a function"));
+    }
+
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.mySignal.disconnect(function() { })");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: Function.prototype.disconnect: failed to disconnect from MyQObject::mySignal()"));
+    }
+}
+
+void tst_QScriptExtQObject::cppConnectAndDisconnect()
+{
+    QScriptEngine eng;
+    QLineEdit edit;
+    QLineEdit edit2;
+    QScriptValue fun = eng.evaluate("function fun(text) { signalObject = this; signalArg = text; }; fun");
+    QVERIFY(fun.isFunction());
+    for (int z = 0; z < 2; ++z) {
+        QScriptValue receiver;
+        if (z == 0)
+            receiver = QScriptValue();
+        else
+            receiver = eng.newObject();
+        for (int y = 0; y < 2; ++y) {
+            QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), receiver, fun));
+            QVERIFY(qScriptConnect(&edit2, SIGNAL(textChanged(const QString &)), receiver, fun));
+            // check signal emission
+            for (int x = 0; x < 4; ++x) {
+                QLineEdit *ed = (x < 2) ? &edit : &edit2;
+                ed->setText((x % 2) ? "foo" : "bar");
+                {
+                    QScriptValue ret = eng.globalObject().property("signalObject");
+                    if (receiver.isObject())
+                        QVERIFY(ret.strictlyEquals(receiver));
+                    else
+                        QVERIFY(ret.strictlyEquals(eng.globalObject()));
+                }
+                {
+                    QScriptValue ret = eng.globalObject().property("signalArg");
+                    QVERIFY(ret.isString());
+                    QCOMPARE(ret.toString(), ed->text());
+                }
+                eng.collectGarbage();
+            }
+
+            // check disconnect
+            QVERIFY(qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), receiver, fun));
+            eng.globalObject().setProperty("signalObject", QScriptValue());
+            eng.globalObject().setProperty("signalArg", QScriptValue());
+            edit.setText("something else");
+            QVERIFY(!eng.globalObject().property("signalObject").isValid());
+            QVERIFY(!eng.globalObject().property("signalArg").isValid());
+            QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), receiver, fun));
+
+            // other object's connection should remain
+            edit2.setText(edit.text());
+            {
+                QScriptValue ret = eng.globalObject().property("signalObject");
+                if (receiver.isObject())
+                    QVERIFY(ret.strictlyEquals(receiver));
+                else
+                    QVERIFY(ret.strictlyEquals(eng.globalObject()));
+            }
+            {
+                QScriptValue ret = eng.globalObject().property("signalArg");
+                QVERIFY(ret.isString());
+                QCOMPARE(ret.toString(), edit2.text());
+            }
+
+            // disconnect other object too
+            QVERIFY(qScriptDisconnect(&edit2, SIGNAL(textChanged(const QString &)), receiver, fun));
+            eng.globalObject().setProperty("signalObject", QScriptValue());
+            eng.globalObject().setProperty("signalArg", QScriptValue());
+            edit2.setText("even more different");
+            QVERIFY(!eng.globalObject().property("signalObject").isValid());
+            QVERIFY(!eng.globalObject().property("signalArg").isValid());
+            QVERIFY(!qScriptDisconnect(&edit2, SIGNAL(textChanged(const QString &)), receiver, fun));
+        }
+    }
+
+    // make sure we don't crash when engine is deleted
+    {
+        QScriptEngine *eng2 = new QScriptEngine;
+        QScriptValue fun2 = eng2->evaluate("(function(text) { signalObject = this; signalArg = text; })");
+        QVERIFY(fun2.isFunction());
+        QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun2));
+        delete eng2;
+        edit.setText("ciao");
+        QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun2));
+    }
+
+    // mixing script-side and C++-side connect
+    {
+        eng.globalObject().setProperty("edit", eng.newQObject(&edit));
+        QVERIFY(eng.evaluate("edit.textChanged.connect(fun)").isUndefined());
+        QVERIFY(qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun));
+
+        QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun));
+        QVERIFY(eng.evaluate("edit.textChanged.disconnect(fun)").isUndefined());
+    }
+
+    // signalHandlerException()
+    {
+        connect(&eng, SIGNAL(signalHandlerException(QScriptValue)),
+                this, SLOT(onSignalHandlerException(QScriptValue)));
+
+        eng.globalObject().setProperty("edit", eng.newQObject(&edit));
+        QScriptValue fun = eng.evaluate("(function() { nonExistingFunction(); })");
+        QVERIFY(fun.isFunction());
+        QVERIFY(qScriptConnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun));
+
+        m_signalHandlerException = QScriptValue();
+        QScriptValue ret = eng.evaluate("edit.text = 'trigger a signal handler exception from script'");
+        QVERIFY(ret.isError());
+        QVERIFY(m_signalHandlerException.strictlyEquals(ret));
+
+        m_signalHandlerException = QScriptValue();
+        edit.setText("trigger a signal handler exception from C++");
+        QVERIFY(m_signalHandlerException.isError());
+
+        QVERIFY(qScriptDisconnect(&edit, SIGNAL(textChanged(const QString &)), QScriptValue(), fun));
+
+        m_signalHandlerException = QScriptValue();
+        eng.evaluate("edit.text = 'no more exception from script'");
+        QVERIFY(!m_signalHandlerException.isValid());
+        edit.setText("no more exception from C++");
+        QVERIFY(!m_signalHandlerException.isValid());
+
+        disconnect(&eng, SIGNAL(signalHandlerException(QScriptValue)),
+                   this, SLOT(onSignalHandlerException(QScriptValue)));
+    }
+
+    // check that connectNotify() and disconnectNotify() are called (task 232987)
+    {
+        m_myObject->clearConnectedSignal();
+        QVERIFY(qScriptConnect(m_myObject, SIGNAL(mySignal()), QScriptValue(), fun));
+        QCOMPARE(m_myObject->connectedSignal().constData(), SIGNAL(mySignal()));
+
+        m_myObject->clearDisconnectedSignal();
+        QVERIFY(qScriptDisconnect(m_myObject, SIGNAL(mySignal()), QScriptValue(), fun));
+        QCOMPARE(m_myObject->disconnectedSignal().constData(), SIGNAL(mySignal()));
+    }
+
+    // bad args
+    QVERIFY(!qScriptConnect(0, SIGNAL(foo()), QScriptValue(), fun));
+    QVERIFY(!qScriptConnect(&edit, 0, QScriptValue(), fun));
+    QVERIFY(!qScriptConnect(&edit, SIGNAL(foo()), QScriptValue(), fun));
+    QVERIFY(!qScriptConnect(&edit, SIGNAL(textChanged(QString)), QScriptValue(), QScriptValue()));
+    QVERIFY(!qScriptDisconnect(0, SIGNAL(foo()), QScriptValue(), fun));
+    QVERIFY(!qScriptDisconnect(&edit, 0, QScriptValue(), fun));
+    QVERIFY(!qScriptDisconnect(&edit, SIGNAL(foo()), QScriptValue(), fun));
+    QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(QString)), QScriptValue(), QScriptValue()));
+    {
+        QScriptEngine eng2;
+        QScriptValue receiverInDifferentEngine = eng2.newObject();
+        QVERIFY(!qScriptConnect(&edit, SIGNAL(textChanged(QString)), receiverInDifferentEngine, fun));
+        QVERIFY(!qScriptDisconnect(&edit, SIGNAL(textChanged(QString)), receiverInDifferentEngine, fun));
+    }
+}
+
+void tst_QScriptExtQObject::classEnums()
+{
+    QScriptValue myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue());
+    m_engine->globalObject().setProperty("MyQObject", myClass);
+
+    QCOMPARE(static_cast<MyQObject::Policy>(m_engine->evaluate("MyQObject.FooPolicy").toInt32()),
+             MyQObject::FooPolicy);
+    QCOMPARE(static_cast<MyQObject::Policy>(m_engine->evaluate("MyQObject.BarPolicy").toInt32()),
+             MyQObject::BarPolicy);
+    QCOMPARE(static_cast<MyQObject::Policy>(m_engine->evaluate("MyQObject.BazPolicy").toInt32()),
+             MyQObject::BazPolicy);
+
+    QCOMPARE(static_cast<MyQObject::Strategy>(m_engine->evaluate("MyQObject.FooStrategy").toInt32()),
+             MyQObject::FooStrategy);
+    QCOMPARE(static_cast<MyQObject::Strategy>(m_engine->evaluate("MyQObject.BarStrategy").toInt32()),
+             MyQObject::BarStrategy);
+    QCOMPARE(static_cast<MyQObject::Strategy>(m_engine->evaluate("MyQObject.BazStrategy").toInt32()),
+             MyQObject::BazStrategy);
+
+    QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.NoAbility").toInt32()),
+             MyQObject::NoAbility);
+    QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.FooAbility").toInt32()),
+             MyQObject::FooAbility);
+    QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.BarAbility").toInt32()),
+             MyQObject::BarAbility);
+    QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.BazAbility").toInt32()),
+             MyQObject::BazAbility);
+    QCOMPARE(MyQObject::Ability(m_engine->evaluate("MyQObject.AllAbility").toInt32()),
+             MyQObject::AllAbility);
+
+    QScriptValue::PropertyFlags expectedEnumFlags = QScriptValue::ReadOnly | QScriptValue::Undeletable;
+    QCOMPARE(myClass.propertyFlags("FooPolicy"), expectedEnumFlags);
+    QCOMPARE(myClass.propertyFlags("BarPolicy"), expectedEnumFlags);
+    QCOMPARE(myClass.propertyFlags("BazPolicy"), expectedEnumFlags);
+
+    // enums from Qt are inherited through prototype
+    QCOMPARE(static_cast<Qt::FocusPolicy>(m_engine->evaluate("MyQObject.StrongFocus").toInt32()),
+             Qt::StrongFocus);
+    QCOMPARE(static_cast<Qt::Key>(m_engine->evaluate("MyQObject.Key_Left").toInt32()),
+             Qt::Key_Left);
+
+    QCOMPARE(m_engine->evaluate("MyQObject.className()").toString(), QLatin1String("MyQObject"));
+
+    qRegisterMetaType<MyQObject::Policy>("Policy");
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 10);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithEnumArg('BarPolicy')").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 10);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BarPolicy));
+
+    m_myObject->resetQtFunctionInvoked();
+    QVERIFY(m_engine->evaluate("myObject.myInvokableWithEnumArg('NoSuchPolicy')").isError());
+    QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(m_engine->evaluate("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)").isUndefined(), true);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 36);
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy));
+
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningEnum()");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 37);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
+        QCOMPARE(ret.isVariant(), true);
+    }
+    m_myObject->resetQtFunctionInvoked();
+    {
+        QScriptValue ret = m_engine->evaluate("myObject.myInvokableReturningQualifiedEnum()");
+        QCOMPARE(m_myObject->qtFunctionInvoked(), 38);
+        QCOMPARE(m_myObject->qtFunctionActuals().size(), 0);
+        QCOMPARE(ret.isNumber(), true);
+    }
+
+    // enum properties are not deletable or writable
+    QVERIFY(!m_engine->evaluate("delete MyQObject.BazPolicy").toBool());
+    myClass.setProperty("BazPolicy", QScriptValue());
+    QCOMPARE(static_cast<MyQObject::Policy>(myClass.property("BazPolicy").toInt32()),
+             MyQObject::BazPolicy);
+    myClass.setProperty("BazPolicy", MyQObject::FooPolicy);
+    QCOMPARE(static_cast<MyQObject::Policy>(myClass.property("BazPolicy").toInt32()),
+             MyQObject::BazPolicy);
+}
+
+QT_BEGIN_NAMESPACE
+Q_SCRIPT_DECLARE_QMETAOBJECT(MyQObject, QObject*)
+Q_SCRIPT_DECLARE_QMETAOBJECT(QObject, QObject*)
+QT_END_NAMESPACE
+
+class ConstructorTest : public QObject
+{
+    Q_OBJECT
+public:
+    Q_INVOKABLE ConstructorTest(QObject *parent)
+        : QObject(parent)
+    {
+        setProperty("ctorIndex", 0);
+    }
+    Q_INVOKABLE ConstructorTest(int arg, QObject *parent = 0)
+        : QObject(parent)
+    {
+        setProperty("ctorIndex", 1);
+        setProperty("arg", arg);
+    }
+    Q_INVOKABLE ConstructorTest(const QString &arg, QObject *parent = 0)
+        : QObject(parent)
+    {
+        setProperty("ctorIndex", 2);
+        setProperty("arg", arg);
+    }
+    Q_INVOKABLE ConstructorTest(int arg, const QString &arg2, QObject *parent = 0)
+        : QObject(parent)
+    {
+        setProperty("ctorIndex", 3);
+        setProperty("arg", arg);
+        setProperty("arg2", arg2);
+    }
+    Q_INVOKABLE ConstructorTest(const QBrush &arg, QObject *parent = 0)
+        : QObject(parent)
+    {
+        setProperty("ctorIndex", 4);
+        setProperty("arg", arg);
+    }
+};
+
+void tst_QScriptExtQObject::classConstructor()
+{
+    QScriptValue myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine);
+    m_engine->globalObject().setProperty("MyQObject", myClass);
+
+    QScriptValue myObj = m_engine->evaluate("myObj = MyQObject()");
+    QObject *qobj = myObj.toQObject();
+    QVERIFY(qobj != 0);
+    QCOMPARE(qobj->metaObject()->className(), "MyQObject");
+    QCOMPARE(qobj->parent(), (QObject *)0);
+
+    QScriptValue qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine);
+    m_engine->globalObject().setProperty("QObject", qobjectClass);
+
+    QScriptValue otherObj = m_engine->evaluate("otherObj = QObject(myObj)");
+    QObject *qqobj = otherObj.toQObject();
+    QVERIFY(qqobj != 0);
+    QCOMPARE(qqobj->metaObject()->className(), "QObject");
+    QCOMPARE(qqobj->parent(), qobj);
+
+    delete qobj;
+
+    // Q_INVOKABLE constructors
+    {
+        QScriptValue klazz = m_engine->newQMetaObject(&ConstructorTest::staticMetaObject);
+        {
+            QScriptValue obj = klazz.construct();
+            QVERIFY(obj.isError());
+            QCOMPARE(obj.toString(), QString::fromLatin1("SyntaxError: too few arguments in call to ConstructorTest(); candidates are\n"
+                                                         "    ConstructorTest(QBrush)\n"
+                                                         "    ConstructorTest(QBrush,QObject*)\n"
+                                                         "    ConstructorTest(int,QString)\n"
+                                                         "    ConstructorTest(int,QString,QObject*)\n"
+                                                         "    ConstructorTest(QString)\n"
+                                                         "    ConstructorTest(QString,QObject*)\n"
+                                                         "    ConstructorTest(int)\n"
+                                                         "    ConstructorTest(int,QObject*)\n"
+                                                         "    ConstructorTest(QObject*)"));
+        }
+        {
+            QObject objobj;
+            QScriptValue arg = m_engine->newQObject(&objobj);
+            QScriptValue obj = klazz.construct(QScriptValueList() << arg);
+            QVERIFY(!obj.isError());
+            QVERIFY(obj.instanceOf(klazz));
+            QVERIFY(obj.property("ctorIndex").isNumber());
+            QCOMPARE(obj.property("ctorIndex").toInt32(), 0);
+        }
+        {
+            int arg = 123;
+            QScriptValue obj = klazz.construct(QScriptValueList() << arg);
+            QVERIFY(!obj.isError());
+            QVERIFY(obj.instanceOf(klazz));
+            QVERIFY(obj.property("ctorIndex").isNumber());
+            QCOMPARE(obj.property("ctorIndex").toInt32(), 1);
+            QVERIFY(obj.property("arg").isNumber());
+            QCOMPARE(obj.property("arg").toInt32(), arg);
+        }
+        {
+            QString arg = "foo";
+            QScriptValue obj = klazz.construct(QScriptValueList() << arg);
+            QVERIFY(!obj.isError());
+            QVERIFY(obj.instanceOf(klazz));
+            QVERIFY(obj.property("ctorIndex").isNumber());
+            QCOMPARE(obj.property("ctorIndex").toInt32(), 2);
+            QVERIFY(obj.property("arg").isString());
+            QCOMPARE(obj.property("arg").toString(), arg);
+        }
+        {
+            int arg = 123;
+            QString arg2 = "foo";
+            QScriptValue obj = klazz.construct(QScriptValueList() << arg << arg2);
+            QVERIFY(!obj.isError());
+            QVERIFY(obj.instanceOf(klazz));
+            QVERIFY(obj.property("ctorIndex").isNumber());
+            QCOMPARE(obj.property("ctorIndex").toInt32(), 3);
+            QVERIFY(obj.property("arg").isNumber());
+            QCOMPARE(obj.property("arg").toInt32(), arg);
+            QVERIFY(obj.property("arg2").isString());
+            QCOMPARE(obj.property("arg2").toString(), arg2);
+        }
+        {
+            QBrush arg(Qt::red);
+            QScriptValue obj = klazz.construct(QScriptValueList() << qScriptValueFromValue(m_engine, arg));
+            QVERIFY(!obj.isError());
+            QVERIFY(obj.instanceOf(klazz));
+            QVERIFY(obj.property("ctorIndex").isNumber());
+            QCOMPARE(obj.property("ctorIndex").toInt32(), 4);
+            QVERIFY(obj.property("arg").isVariant());
+            QCOMPARE(qvariant_cast<QBrush>(obj.property("arg").toVariant()), arg);
+        }
+        {
+            QDir arg;
+            QScriptValue obj = klazz.construct(QScriptValueList()
+                                               << qScriptValueFromValue(m_engine, arg));
+            QVERIFY(obj.isError());
+            QCOMPARE(obj.toString(), QString::fromLatin1("TypeError: ambiguous call of overloaded function ConstructorTest(); candidates were\n"
+                                                         "    ConstructorTest(int)\n"
+                                                         "    ConstructorTest(QString)"));
+        }
+    }
+}
+
+void tst_QScriptExtQObject::overrideInvokable()
+{
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myInvokable()");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
+
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myInvokable = function() { global.a = 123; }");
+    m_engine->evaluate("myObject.myInvokable()");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+    QCOMPARE(m_engine->evaluate("global.a").toNumber(), 123.0);
+
+    m_engine->evaluate("myObject.myInvokable = function() { global.a = 456; }");
+    m_engine->evaluate("myObject.myInvokable()");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), -1);
+    QCOMPARE(m_engine->evaluate("global.a").toNumber(), 456.0);
+
+    m_engine->evaluate("delete myObject.myInvokable");
+    m_engine->evaluate("myObject.myInvokable()");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
+
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myInvokable = myObject.myInvokableWithIntArg");
+    m_engine->evaluate("myObject.myInvokable(123)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
+
+    m_engine->evaluate("delete myObject.myInvokable");
+    m_myObject->resetQtFunctionInvoked();
+    // this form (with the '()') is read-only
+    m_engine->evaluate("myObject['myInvokable()'] = function() { global.a = 123; }");
+    m_engine->evaluate("myObject.myInvokable()");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
+}
+
+void tst_QScriptExtQObject::transferInvokable()
+{
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.foozball = myObject.myInvokable");
+    m_engine->evaluate("myObject.foozball()");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 0);
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.foozball = myObject.myInvokableWithIntArg");
+    m_engine->evaluate("myObject.foozball(123)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myInvokable = myObject.myInvokableWithIntArg");
+    m_engine->evaluate("myObject.myInvokable(123)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 1);
+
+    MyOtherQObject other;
+    m_engine->globalObject().setProperty(
+        "myOtherObject", m_engine->newQObject(&other));
+    m_engine->evaluate("myOtherObject.foo = myObject.foozball");
+    other.resetQtFunctionInvoked();
+    m_engine->evaluate("myOtherObject.foo(456)");
+    QCOMPARE(other.qtFunctionInvoked(), 1);
+}
+
+void tst_QScriptExtQObject::findChild()
+{
+    QObject *child = new QObject(m_myObject);
+    child->setObjectName(QLatin1String("myChildObject"));
+
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChild('noSuchChild')");
+        QCOMPARE(result.isNull(), true);
+    }
+
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChild('myChildObject')");
+        QCOMPARE(result.isQObject(), true);
+        QCOMPARE(result.toQObject(), child);
+    }
+
+    delete child;
+}
+
+void tst_QScriptExtQObject::findChildren()
+{
+    QObject *child = new QObject(m_myObject);
+    child->setObjectName(QLatin1String("myChildObject"));
+
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren('noSuchChild')");
+        QCOMPARE(result.isArray(), true);
+        QCOMPARE(result.property(QLatin1String("length")).toNumber(), 0.0);
+    }
+
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren('myChildObject')");
+        QCOMPARE(result.isArray(), true);
+        QCOMPARE(result.property(QLatin1String("length")).toNumber(), 1.0);
+        QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
+    }
+
+    QObject *namelessChild = new QObject(m_myObject);
+
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren('myChildObject')");
+        QCOMPARE(result.isArray(), true);
+        QCOMPARE(result.property(QLatin1String("length")).toNumber(), 1.0);
+        QCOMPARE(result.property(QLatin1String("0")).toQObject(), child);
+    }
+
+    QObject *anotherChild = new QObject(m_myObject);
+    anotherChild->setObjectName(QLatin1String("anotherChildObject"));
+
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren('anotherChildObject')");
+        QCOMPARE(result.isArray(), true);
+        QCOMPARE(result.property(QLatin1String("length")).toNumber(), 1.0);
+        QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild);
+    }
+
+    anotherChild->setObjectName(QLatin1String("myChildObject"));
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren('myChildObject')");
+        QCOMPARE(result.isArray(), true);
+        QCOMPARE(result.property(QLatin1String("length")).toNumber(), 2.0);
+        QObject *o1 = result.property(QLatin1String("0")).toQObject();
+        QObject *o2 = result.property(QLatin1String("1")).toQObject();
+        if (o1 != child) {
+            QCOMPARE(o1, anotherChild);
+            QCOMPARE(o2, child);
+        } else {
+            QCOMPARE(o1, child);
+            QCOMPARE(o2, anotherChild);
+        }
+    }
+
+    // find all
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren()");
+        QVERIFY(result.isArray());
+        int count = 3;
+        QCOMPARE(result.property("length").toInt32(), count);
+        for (int i = 0; i < 3; ++i) {
+            QObject *o = result.property(i).toQObject();
+            if (o == namelessChild || o == child || o == anotherChild)
+                --count;
+        }
+        QVERIFY(count == 0);
+    }
+
+    // matchall regexp
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren(/.*/)");
+        QVERIFY(result.isArray());
+        int count = 3;
+        QCOMPARE(result.property("length").toInt32(), count);
+        for (int i = 0; i < 3; ++i) {
+            QObject *o = result.property(i).toQObject();
+            if (o == namelessChild || o == child || o == anotherChild)
+                --count;
+        }
+        QVERIFY(count == 0);
+    }
+
+    // matchall regexp my*
+    {
+        QScriptValue result = m_engine->evaluate("myObject.findChildren(new RegExp(\"^my.*\"))");
+        QCOMPARE(result.isArray(), true);
+        QCOMPARE(result.property(QLatin1String("length")).toNumber(), 2.0);
+        QObject *o1 = result.property(QLatin1String("0")).toQObject();
+        QObject *o2 = result.property(QLatin1String("1")).toQObject();
+        if (o1 != child) {
+            QCOMPARE(o1, anotherChild);
+            QCOMPARE(o2, child);
+        } else {
+            QCOMPARE(o1, child);
+            QCOMPARE(o2, anotherChild);
+        }
+    }
+    delete anotherChild;
+    delete namelessChild;
+    delete child;
+}
+
+void tst_QScriptExtQObject::overloadedSlots()
+{
+    // should pick myOverloadedSlot(double)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(10)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
+
+    // should pick myOverloadedSlot(double)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(10.0)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 26);
+
+    // should pick myOverloadedSlot(QString)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot('10')");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 29);
+
+    // should pick myOverloadedSlot(bool)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(true)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 25);
+
+    // should pick myOverloadedSlot(QDateTime)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(new Date())");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 32);
+
+    // should pick myOverloadedSlot(QRegExp)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(new RegExp())");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 34);
+
+    // should pick myOverloadedSlot(QVariant)
+    m_myObject->resetQtFunctionInvoked();
+    QScriptValue f = m_engine->evaluate("myObject.myOverloadedSlot");
+    f.call(QScriptValue(), QScriptValueList() << m_engine->newVariant(QVariant("ciao")));
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 35);
+
+    // should pick myOverloadedSlot(QObject*)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(myObject)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
+
+    // should pick myOverloadedSlot(QObject*)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(null)");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 41);
+
+    // should pick myOverloadedSlot(QStringList)
+    m_myObject->resetQtFunctionInvoked();
+    m_engine->evaluate("myObject.myOverloadedSlot(['hello'])");
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 42);
+}
+
+void tst_QScriptExtQObject::enumerate_data()
+{
+    QTest::addColumn<int>("wrapOptions");
+    QTest::addColumn<QStringList>("expectedNames");
+
+    QTest::newRow( "enumerate all" )
+        << 0
+        << (QStringList()
+            // meta-object-defined properties:
+            //   inherited
+            << "objectName"
+            //   non-inherited
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3"
+            // inherited slots
+            << "destroyed(QObject*)" << "destroyed()"
+            << "deleteLater()"
+            // not included because it's private:
+            // << "_q_reregisterTimers(void*)"
+            // signals
+            << "mySignal()"
+            // slots
+            << "mySlot()" << "myOtherSlot()");
+
+    QTest::newRow( "don't enumerate inherited properties" )
+        << int(QScriptEngine::ExcludeSuperClassProperties)
+        << (QStringList()
+            // meta-object-defined properties:
+            //   non-inherited
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3"
+            // inherited slots
+            << "destroyed(QObject*)" << "destroyed()"
+            << "deleteLater()"
+            // not included because it's private:
+            // << "_q_reregisterTimers(void*)"
+            // signals
+            << "mySignal()"
+            // slots
+            << "mySlot()" << "myOtherSlot()");
+
+    QTest::newRow( "don't enumerate inherited methods" )
+        << int(QScriptEngine::ExcludeSuperClassMethods)
+        << (QStringList()
+            // meta-object-defined properties:
+            //   inherited
+            << "objectName"
+            //   non-inherited
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3"
+            // signals
+            << "mySignal()"
+            // slots
+            << "mySlot()" << "myOtherSlot()");
+
+    QTest::newRow( "don't enumerate inherited members" )
+        << int(QScriptEngine::ExcludeSuperClassMethods
+               | QScriptEngine::ExcludeSuperClassProperties)
+        << (QStringList()
+            // meta-object-defined properties
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3"
+            // signals
+            << "mySignal()"
+            // slots
+            << "mySlot()" << "myOtherSlot()");
+
+    QTest::newRow( "enumerate properties, not methods" )
+        << int(QScriptEngine::SkipMethodsInEnumeration)
+        << (QStringList()
+            // meta-object-defined properties:
+            //   inherited
+            << "objectName"
+            //   non-inherited
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3");
+
+    QTest::newRow( "don't enumerate inherited properties + methods" )
+        << int(QScriptEngine::ExcludeSuperClassProperties
+            | QScriptEngine::SkipMethodsInEnumeration)
+        << (QStringList()
+            // meta-object-defined properties:
+            //   non-inherited
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3");
+
+    QTest::newRow( "don't enumerate inherited members" )
+        << int(QScriptEngine::ExcludeSuperClassContents)
+        << (QStringList()
+            // meta-object-defined properties
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3"
+            // signals
+            << "mySignal()"
+            // slots
+            << "mySlot()" << "myOtherSlot()");
+
+    QTest::newRow( "don't enumerate deleteLater()" )
+        << int(QScriptEngine::ExcludeDeleteLater)
+        << (QStringList()
+            // meta-object-defined properties:
+            //   inherited
+            << "objectName"
+            //   non-inherited
+            << "p1" << "p2" << "p4" << "p6"
+            // dynamic properties
+            << "dp1" << "dp2" << "dp3"
+            // inherited slots
+            << "destroyed(QObject*)" << "destroyed()"
+            // not included because it's private:
+            // << "_q_reregisterTimers(void*)"
+            // signals
+            << "mySignal()"
+            // slots
+            << "mySlot()" << "myOtherSlot()");
+}
+
+void tst_QScriptExtQObject::enumerate()
+{
+    QFETCH( int, wrapOptions );
+    QFETCH( QStringList, expectedNames );
+
+    QScriptEngine eng;
+    MyEnumTestQObject enumQObject;
+    // give it some dynamic properties
+    enumQObject.setProperty("dp1", "dp1");
+    enumQObject.setProperty("dp2", "dp2");
+    enumQObject.setProperty("dp3", "dp3");
+    QScriptValue obj = eng.newQObject(&enumQObject, QScriptEngine::QtOwnership,
+                                      QScriptEngine::QObjectWrapOptions(wrapOptions));
+
+    // enumerate in script
+    {
+        eng.globalObject().setProperty("myEnumObject", obj);
+        eng.evaluate("var enumeratedProperties = []");
+        eng.evaluate("for (var p in myEnumObject) { enumeratedProperties.push(p); }");
+        QStringList result = qscriptvalue_cast<QStringList>(eng.evaluate("enumeratedProperties"));
+        QCOMPARE(result.size(), expectedNames.size());
+        for (int i = 0; i < expectedNames.size(); ++i)
+            QCOMPARE(result.at(i), expectedNames.at(i));
+    }
+    // enumerate in C++
+    {
+        QScriptValueIterator it(obj);
+        QStringList result;
+        while (it.hasNext()) {
+            it.next();
+            QCOMPARE(it.flags(), obj.propertyFlags(it.name()));
+            result.append(it.name());
+        }
+        QCOMPARE(result.size(), expectedNames.size());
+        for (int i = 0; i < expectedNames.size(); ++i)
+            QCOMPARE(result.at(i), expectedNames.at(i));
+    }
+}
+
+class SpecialEnumTestObject : public QObject
+{
+    Q_OBJECT
+    // overriding a property in the super-class to make it non-scriptable
+    Q_PROPERTY(QString objectName READ objectName SCRIPTABLE false)
+public:
+    SpecialEnumTestObject(QObject *parent = 0)
+        : QObject(parent) {}
+};
+
+class SpecialEnumTestObject2 : public QObject
+{
+    Q_OBJECT
+    // overriding a property in the super-class to make it non-designable (but still scriptable)
+    Q_PROPERTY(QString objectName READ objectName DESIGNABLE false)
+public:
+    SpecialEnumTestObject2(QObject *parent = 0)
+        : QObject(parent) {}
+};
+
+void tst_QScriptExtQObject::enumerateSpecial()
+{
+    QScriptEngine eng;
+    {
+        SpecialEnumTestObject testObj;
+        QScriptValueIterator it(eng.newQObject(&testObj));
+        bool objectNameEncountered = false;
+        while (it.hasNext()) {
+            it.next();
+            if (it.name() == QLatin1String("objectName")) {
+                objectNameEncountered = true;
+                break;
+            }
+        }
+        QVERIFY(!objectNameEncountered);
+    }
+    {
+        SpecialEnumTestObject2 testObj;
+        testObj.setObjectName("foo");
+        QScriptValueList values;
+        QScriptValueIterator it(eng.newQObject(&testObj));
+        while (it.hasNext()) {
+            it.next();
+            if (it.name() == "objectName")
+                values.append(it.value());
+        }
+        QCOMPARE(values.size(), 1);
+        QCOMPARE(values.at(0).toString(), QString::fromLatin1("foo"));
+    }
+}
+
+void tst_QScriptExtQObject::wrapOptions()
+{
+    QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false);
+    MyQObject *child = new MyQObject(m_myObject);
+    child->setObjectName("child");
+    // exclude child objects
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership,
+                                                QScriptEngine::ExcludeChildObjects);
+        QCOMPARE(obj.property("child").isValid(), false);
+        obj.setProperty("child", QScriptValue(m_engine, 123));
+        QCOMPARE(obj.property("child")
+                 .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    }
+    // don't auto-create dynamic properties
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject);
+        QVERIFY(!m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty"));
+        obj.setProperty("anotherDynamicProperty", QScriptValue(m_engine, 123));
+        QVERIFY(!m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty"));
+        QCOMPARE(obj.property("anotherDynamicProperty")
+                 .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    }
+    // auto-create dynamic properties
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership,
+                                                QScriptEngine::AutoCreateDynamicProperties);
+        QVERIFY(!m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty"));
+        obj.setProperty("anotherDynamicProperty", QScriptValue(m_engine, 123));
+        QVERIFY(m_myObject->dynamicPropertyNames().contains("anotherDynamicProperty"));
+        QCOMPARE(obj.property("anotherDynamicProperty")
+                 .strictlyEquals(QScriptValue(m_engine, 123)), true);
+        // task 236685
+        {
+            QScriptValue obj2 = m_engine->newObject();
+            obj2.setProperty("notADynamicProperty", 456);
+            obj.setPrototype(obj2);
+            QScriptValue ret = obj.property("notADynamicProperty");
+            QVERIFY(ret.isNumber());
+            QVERIFY(ret.strictlyEquals(obj2.property("notADynamicProperty")));
+        }
+    }
+    // don't exclude super-class properties
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject);
+        QVERIFY(obj.property("objectName").isValid());
+        QVERIFY(obj.propertyFlags("objectName") & QScriptValue::QObjectMember);
+    }
+    // exclude super-class properties
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership,
+                                                QScriptEngine::ExcludeSuperClassProperties);
+        QVERIFY(!obj.property("objectName").isValid());
+        QVERIFY(!(obj.propertyFlags("objectName") & QScriptValue::QObjectMember));
+        QVERIFY(obj.property("intProperty").isValid());
+        QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember);
+    }
+    // don't exclude super-class methods
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject);
+        QVERIFY(obj.property("deleteLater").isValid());
+        QVERIFY(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember);
+    }
+    // exclude super-class methods
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership,
+                                                QScriptEngine::ExcludeSuperClassMethods);
+        QVERIFY(!obj.property("deleteLater").isValid());
+        QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember));
+        QVERIFY(obj.property("mySlot").isValid());
+        QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember);
+    }
+    // exclude all super-class contents
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership,
+                                                QScriptEngine::ExcludeSuperClassContents);
+        QVERIFY(!obj.property("deleteLater").isValid());
+        QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember));
+        QVERIFY(obj.property("mySlot").isValid());
+        QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember);
+
+        QVERIFY(!obj.property("objectName").isValid());
+        QVERIFY(!(obj.propertyFlags("objectName") & QScriptValue::QObjectMember));
+        QVERIFY(obj.property("intProperty").isValid());
+        QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember);
+    }
+    // exclude deleteLater()
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership,
+                                                QScriptEngine::ExcludeDeleteLater);
+        QVERIFY(!obj.property("deleteLater").isValid());
+        QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember));
+        QVERIFY(obj.property("mySlot").isValid());
+        QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember);
+
+        QVERIFY(obj.property("objectName").isValid());
+        QVERIFY(obj.propertyFlags("objectName") & QScriptValue::QObjectMember);
+        QVERIFY(obj.property("intProperty").isValid());
+        QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember);
+    }
+    // exclude all that we can
+    {
+        QScriptValue obj = m_engine->newQObject(m_myObject, QScriptEngine::QtOwnership,
+                                                QScriptEngine::ExcludeSuperClassMethods
+                                                | QScriptEngine::ExcludeSuperClassProperties
+                                                | QScriptEngine::ExcludeChildObjects);
+        QVERIFY(!obj.property("deleteLater").isValid());
+        QVERIFY(!(obj.propertyFlags("deleteLater") & QScriptValue::QObjectMember));
+        QVERIFY(obj.property("mySlot").isValid());
+        QVERIFY(obj.propertyFlags("mySlot") & QScriptValue::QObjectMember);
+
+        QVERIFY(!obj.property("objectName").isValid());
+        QVERIFY(!(obj.propertyFlags("objectName") & QScriptValue::QObjectMember));
+        QVERIFY(obj.property("intProperty").isValid());
+        QVERIFY(obj.propertyFlags("intProperty") & QScriptValue::QObjectMember);
+
+        QCOMPARE(obj.property("child").isValid(), false);
+        obj.setProperty("child", QScriptValue(m_engine, 123));
+        QCOMPARE(obj.property("child")
+                 .strictlyEquals(QScriptValue(m_engine, 123)), true);
+    }
+
+    delete child;
+}
+
+Q_DECLARE_METATYPE(QWidget*)
+Q_DECLARE_METATYPE(QPushButton*)
+
+void tst_QScriptExtQObject::prototypes()
+{
+    QScriptEngine eng;
+    QScriptValue widgetProto = eng.newQObject(new QWidget(), QScriptEngine::ScriptOwnership);
+    eng.setDefaultPrototype(qMetaTypeId<QWidget*>(), widgetProto);
+    QPushButton *pbp = new QPushButton();
+    QScriptValue buttonProto = eng.newQObject(pbp, QScriptEngine::ScriptOwnership);
+    buttonProto.setPrototype(widgetProto);
+    eng.setDefaultPrototype(qMetaTypeId<QPushButton*>(), buttonProto);
+    QPushButton *pb = new QPushButton();
+    QScriptValue button = eng.newQObject(pb, QScriptEngine::ScriptOwnership);
+    QVERIFY(button.prototype().strictlyEquals(buttonProto));
+
+    buttonProto.setProperty("text", QScriptValue(&eng, "prototype button"));
+    QCOMPARE(pbp->text(), QLatin1String("prototype button"));
+    button.setProperty("text", QScriptValue(&eng, "not the prototype button"));
+    QCOMPARE(pb->text(), QLatin1String("not the prototype button"));
+    QCOMPARE(pbp->text(), QLatin1String("prototype button"));
+
+    buttonProto.setProperty("objectName", QScriptValue(&eng, "prototype button"));
+    QCOMPARE(pbp->objectName(), QLatin1String("prototype button"));
+    button.setProperty("objectName", QScriptValue(&eng, "not the prototype button"));
+    QCOMPARE(pb->objectName(), QLatin1String("not the prototype button"));
+    QCOMPARE(pbp->objectName(), QLatin1String("prototype button"));
+}
+
+void tst_QScriptExtQObject::objectDeleted()
+{
+    QScriptEngine eng;
+    MyQObject *qobj = new MyQObject();
+    QScriptValue v = eng.newQObject(qobj);
+    v.setProperty("objectName", QScriptValue(&eng, "foo"));
+    QCOMPARE(qobj->objectName(), QLatin1String("foo"));
+    v.setProperty("intProperty", QScriptValue(&eng, 123));
+    QCOMPARE(qobj->intProperty(), 123);
+    qobj->resetQtFunctionInvoked();
+    v.property("myInvokable").call(v);
+    QCOMPARE(qobj->qtFunctionInvoked(), 0);
+
+    // now delete the object
+    delete qobj;
+
+    // the documented behavior is: isQObject() should still return true,
+    // but toQObject() should return 0
+    QVERIFY(v.isQObject());
+    QCOMPARE(v.toQObject(), (QObject *)0);
+
+    // any attempt to access properties of the object should result in an exception
+    {
+        QScriptValue ret = v.property("objectName");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
+    }
+    {
+        eng.evaluate("Object");
+        QVERIFY(!eng.hasUncaughtException());
+        v.setProperty("objectName", QScriptValue(&eng, "foo"));
+        QVERIFY(eng.hasUncaughtException());
+        QVERIFY(eng.uncaughtException().isError());
+        QCOMPARE(eng.uncaughtException().toString(), QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
+    }
+
+    {
+        QScriptValue ret = v.call();
+        QVERIFY(!ret.isValid());
+    }
+
+    // myInvokableWithIntArg is not stored in member table (since we've not accessed it)
+    {
+        QScriptValue ret = v.property("myInvokableWithIntArg");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
+    }
+
+    // access from script
+    eng.globalObject().setProperty("o", v);
+    {
+        QScriptValue ret = eng.evaluate("o()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("TypeError: Result of expression 'o' [] is not a function."));
+    }
+    {
+        QScriptValue ret = eng.evaluate("o.objectName");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `objectName' of deleted QObject"));
+    }
+    {
+        QScriptValue ret = eng.evaluate("o.myInvokable()");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `myInvokable' of deleted QObject"));
+    }
+    {
+        QScriptValue ret = eng.evaluate("o.myInvokableWithIntArg(10)");
+        QVERIFY(ret.isError());
+        QCOMPARE(ret.toString(), QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject"));
+    }
+}
+
+void tst_QScriptExtQObject::connectToDestroyedSignal()
+{
+    // ### the following test currently depends on signal emission order
+#if 0
+    {
+        // case 1: deleted when the engine is not doing GC
+        QScriptEngine eng;
+        QObject *obj = new QObject();
+        eng.globalObject().setProperty("o", eng.newQObject(obj, QScriptEngine::QtOwnership));
+        eng.evaluate("o.destroyed.connect(function() { wasDestroyed = true; })");
+        eng.evaluate("wasDestroyed = false");
+        delete obj;
+        QVERIFY(eng.evaluate("wasDestroyed").toBoolean());
+    }
+    {
+        // case 2: deleted when the engine is doing GC
+        QScriptEngine eng;
+        QObject *obj = new QObject();
+        eng.globalObject().setProperty("o", eng.newQObject(obj, QScriptEngine::ScriptOwnership));
+        eng.evaluate("o.destroyed.connect(function() { wasDestroyed = true; })");
+        eng.evaluate("wasDestroyed = false");
+        eng.evaluate("o = null");
+        eng.collectGarbage();
+        QVERIFY(eng.evaluate("wasDestroyed").toBoolean());
+    }
+    {
+        // case 3: deleted when the engine is destroyed
+        QScriptEngine eng;
+        QObject *obj = new QObject();
+        eng.globalObject().setProperty("o", eng.newQObject(obj, QScriptEngine::ScriptOwnership));
+        eng.evaluate("o.destroyed.connect(function() { })");
+        // the signal handler won't get called -- we don't want to crash
+    }
+#endif
+}
+
+QTEST_MAIN(tst_QScriptExtQObject)
+#include "tst_qscriptextqobject.moc"