Michael Goddard <michael.goddard@trolltech.com>
authorhausmann@webkit.org <hausmann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Jan 2008 09:39:04 +0000 (09:39 +0000)
committerhausmann@webkit.org <hausmann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Jan 2008 09:39:04 +0000 (09:39 +0000)
Reworked the JavaScriptCore Qt bindings:

* Add initial support for string and variant arrays, as well
  as sub QObjects in the JS bindings.

* Don't expose fields marked as not scriptable by moc.

* Add support for dynamic properties and accessing named
  QObject children of an object (like QtScript and older
  IE DOM style JS).
* Add support for custom toString methods.

* Fine tune some bindings to be closer to QtScript.
  Make void functions return undefined, and empty/
  null QStrings return a zero length string.

* Create framework for allowing more direct method calls.
  Since RuntimeMethod doesn't allow us to add additional
  methods/properties to a function, add these classes.
  Start prototyping object.signal.connect(...).

* Add signal support to the Qt bindings.
  Allow connecting to signals (object.signal.connect(slot)),
  disconnecting, and emitting signals.  Currently chooses
  the first signal that matches the name, so this will need
  improvement.

* Add property names, and resolve signals closer to use.
  Enumerating properties now returns some of the Qt properties
  and signals.  Slots and methods aren't quite present.  Also,
  resolve signal connections etc. closer to the time of use, so
  we can do more dynamic resolution based on argument type etc.
  Still picks the first one with the same name, at the moment.

* Make signature comparison code consistent.
  Use the same code for checking meta signatures in
  the method and fallback getters, and avoid a
  QByteArray construction when we can.

* Fix minor memory leak, and handle pointers better.
  Delete the private object in the dtors, and use RefPtrs
  for holding Instances etc.

* Handle method lookup better.
  Allow invocation time method lookup based on the arguments,
  which is closer to QtScript behaviour.  Also, cache the
  method lists and delete them in the QtClass dtor (stops
  a memory leak).

* Improve JS to Qt data type conversions.
  Add some support for Date & RegExp JS objects,
  and provide some metrics on the quality of the
  conversion.

* A couple of fixes for autotest failures.
  Better support for converting lists, read/write only
  QMetaProperty support, modified slot search order...)

* Update JS DRT controller for Qt JS binding changes.
  There were two functions that needed some changes
  so that the layout tests would work, so this makes
  a few tests pass again.

* Bump the timeout for layout tests up to 11s.
  At least some tests have an internal timeout of
  10 seconds, so make the waitUntilDone approach
  wait at least 11s.  fast/dom/open-and-close-by-DOM.html
  is one of these - now the failure message is more
  accurate.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@29729 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/bindings/qt/qt_class.cpp
JavaScriptCore/bindings/qt/qt_class.h
JavaScriptCore/bindings/qt/qt_instance.cpp
JavaScriptCore/bindings/qt/qt_instance.h
JavaScriptCore/bindings/qt/qt_runtime.cpp
JavaScriptCore/bindings/qt/qt_runtime.h
JavaScriptCore/bindings/runtime.cpp
JavaScriptCore/bindings/runtime.h
WebKitTools/ChangeLog
WebKitTools/DumpRenderTree/qt/jsobjects.cpp
WebKitTools/DumpRenderTree/qt/jsobjects.h

index ab9f72b8e16cb5966e3dc54d02ff97c72d748b6f..d894021f2059d854b09f5fda4db337d14177c235 100644 (file)
@@ -1,3 +1,169 @@
+2008-01-23  Michael Goddard <michael.goddard@trolltech.com>
+
+        Reviewed by Lars Knoll <lars@trolltech.com>.
+
+        Reworked the JavaScriptCore Qt bindings:
+        
+        * Add initial support for string and variant arrays, as well
+        as sub QObjects in the JS bindings.
+        
+        * Don't expose fields marked as not scriptable by moc.
+        
+        * Add support for dynamic properties and accessing named
+        QObject children of an object (like QtScript and older
+        IE DOM style JS).
+        * Add support for custom toString methods.
+        
+        * Fine tune some bindings to be closer to QtScript.
+        Make void functions return undefined, and empty/
+        null QStrings return a zero length string.
+        
+        * Create framework for allowing more direct method calls.
+        Since RuntimeMethod doesn't allow us to add additional
+        methods/properties to a function, add these classes.
+        Start prototyping object.signal.connect(...).
+        
+        * Add signal support to the Qt bindings.
+        Allow connecting to signals (object.signal.connect(slot)),
+        disconnecting, and emitting signals.  Currently chooses
+        the first signal that matches the name, so this will need
+        improvement.
+        
+        * Add property names, and resolve signals closer to use.
+        Enumerating properties now returns some of the Qt properties
+        and signals.  Slots and methods aren't quite present.  Also,
+        resolve signal connections etc. closer to the time of use, so
+        we can do more dynamic resolution based on argument type etc.
+        Still picks the first one with the same name, at the moment.
+        
+        * Make signature comparison code consistent.
+        Use the same code for checking meta signatures in
+        the method and fallback getters, and avoid a
+        QByteArray construction when we can.
+        
+        * Fix minor memory leak, and handle pointers better.
+        Delete the private object in the dtors, and use RefPtrs
+        for holding Instances etc.
+        
+        * Handle method lookup better.
+        Allow invocation time method lookup based on the arguments,
+        which is closer to QtScript behaviour.  Also, cache the
+        method lists and delete them in the QtClass dtor (stops
+        a memory leak).
+        
+        * Improve JS to Qt data type conversions.
+        Add some support for Date & RegExp JS objects,
+        and provide some metrics on the quality of the
+        conversion.
+        
+        * A couple of fixes for autotest failures.
+        Better support for converting lists, read/write only
+        QMetaProperty support, modified slot search order...)
+        
+
+        * bindings/qt/qt_class.cpp:
+        (KJS::Bindings::QtClass::QtClass):
+        (KJS::Bindings::QtClass::~QtClass):
+        (KJS::Bindings::QtClass::name):
+        (KJS::Bindings::QtClass::fallbackObject):
+        (KJS::Bindings::QtClass::methodsNamed):
+        (KJS::Bindings::QtClass::fieldNamed):
+        * bindings/qt/qt_class.h:
+        * bindings/qt/qt_instance.cpp:
+        (KJS::Bindings::QtInstance::QtInstance):
+        (KJS::Bindings::QtInstance::~QtInstance):
+        (KJS::Bindings::QtInstance::getRuntimeObject):
+        (KJS::Bindings::QtInstance::getClass):
+        (KJS::Bindings::QtInstance::implementsCall):
+        (KJS::Bindings::QtInstance::getPropertyNames):
+        (KJS::Bindings::QtInstance::invokeMethod):
+        (KJS::Bindings::QtInstance::invokeDefaultMethod):
+        (KJS::Bindings::QtInstance::stringValue):
+        (KJS::Bindings::QtInstance::booleanValue):
+        (KJS::Bindings::QtInstance::valueOf):
+        (KJS::Bindings::QtField::name):
+        (KJS::Bindings::QtField::valueFromInstance):
+        (KJS::Bindings::QtField::setValueToInstance):
+        * bindings/qt/qt_instance.h:
+        (KJS::Bindings::QtInstance::getBindingLanguage):
+        (KJS::Bindings::QtInstance::getObject):
+        * bindings/qt/qt_runtime.cpp:
+        (KJS::Bindings::QWKNoDebug::QWKNoDebug):
+        (KJS::Bindings::QWKNoDebug::~QWKNoDebug):
+        (KJS::Bindings::QWKNoDebug::operator<<):
+        (KJS::Bindings::):
+        (KJS::Bindings::valueRealType):
+        (KJS::Bindings::convertValueToQVariant):
+        (KJS::Bindings::convertQVariantToValue):
+        (KJS::Bindings::QtRuntimeMethod::QtRuntimeMethod):
+        (KJS::Bindings::QtRuntimeMethod::~QtRuntimeMethod):
+        (KJS::Bindings::QtRuntimeMethod::codeType):
+        (KJS::Bindings::QtRuntimeMethod::execute):
+        (KJS::Bindings::QtRuntimeMethodData::~QtRuntimeMethodData):
+        (KJS::Bindings::QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData):
+        (KJS::Bindings::QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData):
+        (KJS::Bindings::QtMethodMatchType::):
+        (KJS::Bindings::QtMethodMatchType::QtMethodMatchType):
+        (KJS::Bindings::QtMethodMatchType::kind):
+        (KJS::Bindings::QtMethodMatchType::isValid):
+        (KJS::Bindings::QtMethodMatchType::isVariant):
+        (KJS::Bindings::QtMethodMatchType::isMetaType):
+        (KJS::Bindings::QtMethodMatchType::isUnresolved):
+        (KJS::Bindings::QtMethodMatchType::isMetaEnum):
+        (KJS::Bindings::QtMethodMatchType::enumeratorIndex):
+        (KJS::Bindings::QtMethodMatchType::variant):
+        (KJS::Bindings::QtMethodMatchType::metaType):
+        (KJS::Bindings::QtMethodMatchType::metaEnum):
+        (KJS::Bindings::QtMethodMatchType::unresolved):
+        (KJS::Bindings::QtMethodMatchType::typeId):
+        (KJS::Bindings::QtMethodMatchType::name):
+        (KJS::Bindings::QtMethodMatchData::QtMethodMatchData):
+        (KJS::Bindings::QtMethodMatchData::isValid):
+        (KJS::Bindings::QtMethodMatchData::firstUnresolvedIndex):
+        (KJS::Bindings::indexOfMetaEnum):
+        (KJS::Bindings::findMethodIndex):
+        (KJS::Bindings::findSignalIndex):
+        (KJS::Bindings::QtRuntimeMetaMethod::QtRuntimeMetaMethod):
+        (KJS::Bindings::QtRuntimeMetaMethod::mark):
+        (KJS::Bindings::QtRuntimeMetaMethod::callAsFunction):
+        (KJS::Bindings::QtRuntimeMetaMethod::getOwnPropertySlot):
+        (KJS::Bindings::QtRuntimeMetaMethod::lengthGetter):
+        (KJS::Bindings::QtRuntimeMetaMethod::connectGetter):
+        (KJS::Bindings::QtRuntimeMetaMethod::disconnectGetter):
+        (KJS::Bindings::QtRuntimeConnectionMethod::QtRuntimeConnectionMethod):
+        (KJS::Bindings::QtRuntimeConnectionMethod::callAsFunction):
+        (KJS::Bindings::QtRuntimeConnectionMethod::getOwnPropertySlot):
+        (KJS::Bindings::QtRuntimeConnectionMethod::lengthGetter):
+        (KJS::Bindings::QtConnectionObject::QtConnectionObject):
+        (KJS::Bindings::QtConnectionObject::~QtConnectionObject):
+        (KJS::Bindings::QtConnectionObject::metaObject):
+        (KJS::Bindings::QtConnectionObject::qt_metacast):
+        (KJS::Bindings::QtConnectionObject::qt_metacall):
+        (KJS::Bindings::QtConnectionObject::execute):
+        (KJS::Bindings::QtConnectionObject::match):
+        (KJS::Bindings::::QtArray):
+        (KJS::Bindings::::~QtArray):
+        (KJS::Bindings::::rootObject):
+        (KJS::Bindings::::setValueAt):
+        (KJS::Bindings::::valueAt):
+        * bindings/qt/qt_runtime.h:
+        (KJS::Bindings::QtField::):
+        (KJS::Bindings::QtField::QtField):
+        (KJS::Bindings::QtField::fieldType):
+        (KJS::Bindings::QtMethod::QtMethod):
+        (KJS::Bindings::QtMethod::name):
+        (KJS::Bindings::QtMethod::numParameters):
+        (KJS::Bindings::QtArray::getLength):
+        (KJS::Bindings::QtRuntimeMethod::d_func):
+        (KJS::Bindings::QtRuntimeMetaMethod::d_func):
+        (KJS::Bindings::QtRuntimeConnectionMethod::d_func):
+        (KJS::Bindings::):
+        * bindings/runtime.cpp:
+        (KJS::Bindings::Instance::createBindingForLanguageInstance):
+        (KJS::Bindings::Instance::createRuntimeObject):
+        (KJS::Bindings::Instance::reallyCreateRuntimeObject):
+        * bindings/runtime.h:
+
 2008-01-22  Anders Carlsson  <andersca@apple.com>
 
         Reviewed by Darin and Adam.
index 51b701dff5ab576c6a2760e5a13e561a5102a1ca..26d381b5f79404ae16eed43348a40d7fb5e4cd4e 100644 (file)
@@ -31,14 +31,14 @@ namespace KJS {
 namespace Bindings {
 
 QtClass::QtClass(const QMetaObject* mo)
-    : metaObject(mo)
+    : m_metaObject(mo)
 {
 }
 
 QtClass::~QtClass()
 {
 }
-    
+
 typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject;
 static ClassesByMetaObject* classesByMetaObject = 0;
 
@@ -59,42 +59,163 @@ QtClass* QtClass::classForObject(QObject* o)
 
 const char* QtClass::name() const
 {
-    return metaObject->className();
+    return m_metaObject->className();
 }
 
-MethodList QtClass::methodsNamed(const Identifier& identifier, Instance*) const
+// We use this to get at signals (so we can return a proper function object,
+// and not get wrapped in RuntimeMethod).  Also, use this for methods,
+// so we can cache the JSValue* and return the same JSValue for the same
+// identifier...
+//
+// Unfortunately... we need to gcProtect our JSValues, since we don't have
+// access to an actual JS class that can mark() our JSValues.
+//
+JSValue* QtClass::fallbackObject(ExecState *exec, Instance *inst, const Identifier &identifier)
 {
-    MethodList methodList;
-
-    const char* name = identifier.ascii();
+    QtInstance* qtinst = static_cast<QtInstance*>(inst);
+
+    QByteArray name(identifier.ascii());
+
+    // First see if we have a cache hit
+    JSValue* val = qtinst->m_methods.value(name);
+    if (val)
+        return val;
+
+    // Nope, create an entry
+    QByteArray normal = QMetaObject::normalizedSignature(name.constData());
+
+    // See if there is an exact match
+    int index = -1;
+    if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) {
+        QMetaMethod m = m_metaObject->method(index);
+        if (m.access() != QMetaMethod::Private) {
+            JSValue *val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal);
+            gcProtect(val);
+            qtinst->m_methods.insert(name, val);
+            return val;
+        }
+    }
 
-    int count = metaObject->methodCount();
-    for (int i = 0; i < count; ++i) {
-        const QMetaMethod m = metaObject->method(i);
-        if (m.methodType() == QMetaMethod::Signal)
-            continue;
-        if (m.access() != QMetaMethod::Public)
+    // Nope.. try a basename match
+    int count = m_metaObject->methodCount();
+    for (index = count - 1; index >= 0; --index) {
+        const QMetaMethod m = m_metaObject->method(index);
+        if (m.access() == QMetaMethod::Private)
             continue;
+
         QByteArray signature = m.signature();
         signature.truncate(signature.indexOf('('));
-        if (signature == name) {
-            Method* method = new QtMethod(metaObject, i, signature, m.parameterTypes().size()); 
-            methodList.append(method);
-            break;
+
+        if (normal == signature) {
+            JSValue* val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal);
+            gcProtect(val);
+            qtinst->m_methods.insert(name, val);
+            return val;
         }
     }
-    
-    return methodList;
+
+    return jsUndefined();
 }
 
+// This functionality is handled by the fallback case above...
+MethodList QtClass::methodsNamed(const Identifier&, Instance*) const
+{
+    return MethodList();
+}
 
-Field* QtClass::fieldNamed(const Identifier& identifier, Instance*) const
+// ### we may end up with a different search order than QtScript by not
+// folding this code into the fallbackMethod above, but Fields propagate out
+// of the binding code
+Field* QtClass::fieldNamed(const Identifier& identifier, Instance* instance) const
 {
-    int index = metaObject->indexOfProperty(identifier.ascii());
-    if (index < 0)
-        return 0;
+    // Check static properties first
+    QtInstance* qtinst = static_cast<QtInstance*>(instance);
+
+    QObject* obj = qtinst->getObject();
+    UString ustring = identifier.ustring();
+    QString objName(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()));
+    QByteArray ba = objName.toAscii();
+
+    // First check for a cached field
+    QtField* f = qtinst->m_fields.value(objName);
+
+    if (obj) {
+        if (f) {
+            // We only cache real metaproperties, but we do store the
+            // other types so we can delete them later
+            if (f->fieldType() == QtField::MetaProperty)
+                return f;
+            else if (f->fieldType() == QtField::DynamicProperty) {
+                if (obj->dynamicPropertyNames().indexOf(ba) >= 0)
+                    return f;
+                else {
+                    // Dynamic property that disappeared
+                    qtinst->m_fields.remove(objName);
+                    delete f;
+                }
+            } else {
+                QList<QObject*> children = obj->children();
+                for (int index = 0; index < children.count(); ++index) {
+                    QObject *child = children.at(index);
+                    if (child->objectName() == objName)
+                        return f;
+                }
+
+                // Didn't find it, delete it from the cache
+                qtinst->m_fields.remove(objName);
+                delete f;
+            }
+        }
 
-    return new QtField(metaObject->property(index));
+        int index = m_metaObject->indexOfProperty(identifier.ascii());
+        if (index >= 0) {
+            QMetaProperty prop = m_metaObject->property(index);
+
+            if (prop.isScriptable(obj)) {
+                f = new QtField(prop);
+                qtinst->m_fields.insert(objName, f);
+                return f;
+            }
+        }
+
+        // Dynamic properties
+        index = obj->dynamicPropertyNames().indexOf(ba);
+        if (index >= 0) {
+            f = new QtField(ba);
+            qtinst->m_fields.insert(objName, f);
+            return f;
+        }
+
+        // Child objects
+
+        QList<QObject*> children = obj->children();
+        for (index = 0; index < children.count(); ++index) {
+            QObject *child = children.at(index);
+            if (child->objectName() == objName) {
+                f = new QtField(child);
+                qtinst->m_fields.insert(objName, f);
+                return f;
+            }
+        }
+
+        // Nothing named this
+        return 0;
+    } else {
+        QByteArray ba(identifier.ascii());
+        // For compatibility with qtscript, cached methods don't cause
+        // errors until they are accessed, so don't blindly create an error
+        // here.
+        if (qtinst->m_methods.contains(ba))
+            return 0;
+
+        // deleted qobject, but can't throw an error from here (no exec)
+        // create a fake QtField that will throw upon access
+        if (!f) {
+            f = new QtField(ba);
+            qtinst->m_fields.insert(objName, f);
+        }
+        return f;
+    }
 }
 
 }
index b30449b9625993beb9c57d94db57e9505087c345..c3e59ec4106b06b143381b188c5c8923d2961da8 100644 (file)
@@ -27,23 +27,26 @@ class QMetaObject;
 namespace KJS {
 namespace Bindings {
 
+
 class QtClass : public Class {
 protected:
     QtClass(const QMetaObject*);
-    
+
 public:
     static QtClass* classForObject(QObject*);
     virtual ~QtClass();
 
-    virtual const char* name() const;    
+    virtual const char* name() const;
     virtual MethodList methodsNamed(const Identifier&, Instance*) const;
     virtual Field* fieldNamed(const Identifier&, Instance*) const;
 
+    virtual JSValue* fallbackObject(ExecState*, Instance*, const Identifier&);
+
 private:
     QtClass(const QtClass&); // prohibit copying
     QtClass& operator=(const QtClass&); // prohibit assignment
 
-    const QMetaObject* metaObject;
+    const QMetaObject* m_metaObject;
 };
 
 } // namespace Bindings
index c6eec0af9a15263fe30b24d45ab4e93a34b5c748..1ab39d3c82bb48e4a0677e0789a0053ab1fdd849 100644 (file)
 #include "list.h"
 #include "qt_class.h"
 #include "qt_runtime.h"
+#include "PropertyNameArray.h"
 #include "runtime_object.h"
 
 #include <qmetaobject.h>
 #include <qdebug.h>
+#include <qmetatype.h>
 #include <qhash.h>
 
 namespace KJS {
@@ -40,54 +42,31 @@ static QObjectInstanceMap cachedInstances;
 typedef QHash<QtInstance*, JSObject*> InstanceJSObjectMap;
 static InstanceJSObjectMap cachedObjects;
 
-// Derived RuntimeObject
-class QtRuntimeObjectImp : public RuntimeObjectImp {
-    public:
-        QtRuntimeObjectImp(Instance*);
-        ~QtRuntimeObjectImp();
-        virtual void invalidate();
-    protected:
-        void removeFromCache();
-};
-
-QtRuntimeObjectImp::QtRuntimeObjectImp(Instance* instance)
-    : RuntimeObjectImp(instance)
-{
-}
-
-QtRuntimeObjectImp::~QtRuntimeObjectImp()
-{
-    removeFromCache();
-}
-
-void QtRuntimeObjectImp::invalidate()
-{
-    removeFromCache();
-    RuntimeObjectImp::invalidate();
-}
-
-void QtRuntimeObjectImp::removeFromCache()
-{
-    JSLock lock;
-    QtInstance* key = cachedObjects.key(this);
-    if (key)
-        cachedObjects.remove(key);
-}
-
-// QtInstance
 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
     : Instance(rootObject)
-    , _class(0)
-    , _object(o)
-    , _hashkey(o)
+    , m_class(0)
+    , m_object(o)
+    , m_hashkey(o)
 {
 }
 
 QtInstance::~QtInstance()
 {
     JSLock lock;
+
     cachedObjects.remove(this);
-    cachedInstances.remove(_hashkey);
+    cachedInstances.remove(m_hashkey);
+
+    // clean up (unprotect from gc) the JSValues we've created
+    foreach(JSValue* val, m_methods.values()) {
+        gcUnprotect(val);
+    }
+    m_methods.clear();
+
+    foreach(QtField* f, m_fields.values()) {
+        delete f;
+    }
+    m_fields.clear();
 }
 
 QtInstance* QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
@@ -110,7 +89,7 @@ JSObject* QtInstance::getRuntimeObject(QtInstance* instance)
     JSLock lock;
     JSObject* ret = cachedObjects.value(instance);
     if (!ret) {
-        ret = new QtRuntimeObjectImp(instance);
+        ret = Instance::reallyCreateRuntimeObject(instance);
         cachedObjects.insert(instance, ret);
     }
     return ret;
@@ -118,9 +97,9 @@ JSObject* QtInstance::getRuntimeObject(QtInstance* instance)
 
 Class* QtInstance::getClass() const
 {
-    if (!_class)
-        _class = QtClass::classForObject(_object);
-    return _class;
+    if (!m_class)
+        m_class = QtClass::classForObject(m_object);
+    return m_class;
 }
 
 void QtInstance::begin()
@@ -135,69 +114,56 @@ void QtInstance::end()
 
 bool QtInstance::implementsCall() const
 {
-    return true;
+    // typeof object that implements call == function
+    return false;
 }
 
-QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QVariant::Type hint); 
-JSValue* convertQVariantToValue(ExecState* exec, const QVariant& variant);
-    
-JSValue* QtInstance::invokeMethod(ExecState* exec, const MethodList& methodList, const List& args)
+void QtInstance::getPropertyNames(ExecState* , PropertyNameArray& array)
 {
-    // ### Should we support overloading methods?
-    ASSERT(methodList.size() == 1);
-
-    QtMethod* method = static_cast<QtMethod*>(methodList[0]);
-
-    if (method->metaObject != _object->metaObject()) 
-        return jsUndefined();
-
-    QMetaMethod metaMethod = method->metaObject->method(method->index);
-    QList<QByteArray> argTypes = metaMethod.parameterTypes();
-    if (argTypes.count() != args.size()) 
-        return jsUndefined();
-
-    // only void methods work currently
-    if (args.size() > 10) 
-        return jsUndefined();
-
-    QVariant vargs[11];
-    void *qargs[11];
+    // This is the enumerable properties, so put:
+    // properties
+    // dynamic properties
+    // slots
+    QObject* obj = getObject();
+    if (obj) {
+        const QMetaObject* meta = obj->metaObject();
+
+        int i;
+        for (i=0; i < meta->propertyCount(); i++) {
+            QMetaProperty prop = meta->property(i);
+            if (prop.isScriptable()) {
+                array.add(Identifier(prop.name()));
+            }
+        }
 
-    QVariant::Type returnType = (QVariant::Type)QMetaType::type(metaMethod.typeName());
-    if (!returnType && qstrlen(metaMethod.typeName())) {
-        qCritical("QtInstance::invokeMethod: Return type %s of method %s is not registered with QMetaType!", metaMethod.typeName(), metaMethod.signature());
-        return jsUndefined();
-    }
-    vargs[0] = QVariant(returnType, (void*)0);
-    qargs[0] = vargs[0].data();
-
-    for (int i = 0; i < args.size(); ++i) {
-        QVariant::Type type = (QVariant::Type) QMetaType::type(argTypes.at(i));
-        if (!type) {
-            qCritical("QtInstance::invokeMethod: Method %s has argument %s which is not registered with QMetaType!", metaMethod.signature(), argTypes.at(i).constData());
-            return jsUndefined();
+        QList<QByteArray> dynProps = obj->dynamicPropertyNames();
+        foreach(QByteArray ba, dynProps) {
+            array.add(Identifier(ba.constData()));
         }
-        vargs[i+1] = convertValueToQVariant(exec, args[i], type);
-        if (vargs[i+1].type() == QVariant::Invalid)
-            return jsUndefined();
 
-        qargs[i+1] = vargs[i+1].data();
+        for (i=0; i < meta->methodCount(); i++) {
+            QMetaMethod method = meta->method(i);
+            if (method.access() != QMetaMethod::Private) {
+                array.add(Identifier(method.signature()));
+            }
+        }
     }
-    if (_object->qt_metacall(QMetaObject::InvokeMetaMethod, method->index, qargs) >= 0) 
-        return jsUndefined();
-
-
-    if (vargs[0].isValid())
-        return convertQVariantToValue(exec, vargs[0]);
-    return jsNull();
 }
 
-
-JSValue* QtInstance::invokeDefaultMethod(ExecState* exec, const List& args)
+JSValue* QtInstance::invokeMethod(ExecState*, const MethodList&, const List&)
 {
+    // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
     return jsUndefined();
 }
 
+JSValue* QtInstance::invokeDefaultMethod(ExecState* exec, const List& )
+{
+    // ### QtScript tries to invoke a meta method qscript_call
+    if (!getObject()) {
+        return throwError(exec, GeneralError, "cannot call function of deleted QObject");
+    }
+    return jsUndefined();
+}
 
 JSValue* QtInstance::defaultValue(JSType hint) const
 {
@@ -212,12 +178,45 @@ JSValue* QtInstance::defaultValue(JSType hint) const
 
 JSValue* QtInstance::stringValue() const
 {
+    // Hmm.. see if there is a toString defined
     QByteArray buf;
-    buf = "QObject ";
-    buf.append(QByteArray::number(quintptr(_object.operator->())));
-    buf.append(" (");
-    buf.append(_object->metaObject()->className());
-    buf.append(")");
+    bool useDefault = true;
+    getClass();
+    QObject* obj = getObject();
+    if (m_class && obj) {
+        // Cheat and don't use the full name resolution
+        int index = obj->metaObject()->indexOfMethod("toString()");
+        if (index >= 0) {
+            QMetaMethod m = obj->metaObject()->method(index);
+            // Check to see how much we can call it
+            if (m.access() != QMetaMethod::Private
+                && m.methodType() != QMetaMethod::Signal
+                && m.parameterTypes().count() == 0) {
+                const char* retsig = m.typeName();
+                if (retsig && *retsig) {
+                    QVariant ret(QMetaType::type(retsig), (void*)0);
+                    void * qargs[1];
+                    qargs[0] = ret.data();
+
+                    if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
+                        if (ret.isValid() && ret.canConvert(QVariant::String)) {
+                            buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
+                            useDefault = false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if (useDefault) {
+        const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
+        QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
+        QString str = QString::fromUtf8("%0(name = \"%1\")")
+                      .arg(QLatin1String(meta->className())).arg(name);
+
+        buf = str.toLatin1();
+    }
     return jsString(buf.constData());
 }
 
@@ -228,14 +227,79 @@ JSValue* QtInstance::numberValue() const
 
 JSValue* QtInstance::booleanValue() const
 {
-    // FIXME: Implement something sensible.
-    return jsBoolean(false);
+    // ECMA 9.2
+    return jsBoolean(true);
 }
 
-JSValue* QtInstance::valueOf() const 
+JSValue* QtInstance::valueOf() const
 {
     return stringValue();
 }
 
+// In qt_runtime.cpp
+JSValue* convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant);
+QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance);
+
+const char* QtField::name() const
+{
+    if (m_type == MetaProperty)
+        return m_property.name();
+    else if (m_type == ChildObject && m_childObject)
+        return m_childObject->objectName().toLatin1();
+    else if (m_type == DynamicProperty)
+        return m_dynamicProperty.constData();
+    return ""; // deleted child object
+}
+
+JSValue* QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
+{
+    const QtInstance* instance = static_cast<const QtInstance*>(inst);
+    QObject* obj = instance->getObject();
+
+    if (obj) {
+        QVariant val;
+        if (m_type == MetaProperty) {
+            if (m_property.isReadable())
+                val = m_property.read(obj);
+            else
+                return jsUndefined();
+        } else if (m_type == ChildObject)
+            val = QVariant::fromValue((QObject*) m_childObject);
+        else if (m_type == DynamicProperty)
+            val = obj->property(m_dynamicProperty);
+
+        return convertQVariantToValue(exec, inst->rootObject(), val);
+    } else {
+        QString msg = QString("cannot access member `%1' of deleted QObject").arg(name());
+        return throwError(exec, GeneralError, msg.toLatin1().constData());
+    }
+}
+
+void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue* aValue) const
+{
+    if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
+        return;
+
+    const QtInstance* instance = static_cast<const QtInstance*>(inst);
+    QObject* obj = instance->getObject();
+    if (obj) {
+        QMetaType::Type argtype = QMetaType::Void;
+        if (m_type == MetaProperty)
+            argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
+
+        // dynamic properties just get any QVariant
+        QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
+        if (m_type == MetaProperty) {
+            if (m_property.isWritable())
+                m_property.write(obj, val);
+        } else if (m_type == DynamicProperty)
+            obj->setProperty(m_dynamicProperty.constData(), val);
+    } else {
+        QString msg = QString("cannot access member `%1' of deleted QObject").arg(name());
+        throwError(exec, GeneralError, msg.toLatin1().constData());
+    }
+}
+
+
 }
 }
index 5449d583b3c5550894a218209ac5ee4569e31cdb..5aa79da411f605350e3806cb2de78cf5cde30c19 100644 (file)
@@ -23,6 +23,7 @@
 #include "runtime.h"
 #include "runtime_root.h"
 #include <qpointer.h>
+#include <qhash.h>
 
 class QObject;
 
@@ -31,41 +32,47 @@ namespace KJS {
 namespace Bindings {
 
 class QtClass;
+class QtField;
 
 class QtInstance : public Instance
 {
 public:
     ~QtInstance ();
-    
+
     virtual Class* getClass() const;
-    
+
     virtual void begin();
     virtual void end();
-    
+
     virtual JSValue* valueOf() const;
     virtual JSValue* defaultValue (JSType hint) const;
 
     virtual bool implementsCall() const;
-    
+
     virtual JSValue* invokeMethod (ExecState *exec, const MethodList &method, const List &args);
     virtual JSValue* invokeDefaultMethod (ExecState *exec, const List &args);
 
+    virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+
+    virtual BindingLanguage getBindingLanguage() const {return QtLanguage;}
+
     JSValue* stringValue() const;
     JSValue* numberValue() const;
     JSValue* booleanValue() const;
-    
-    QObject* getObject() const { return _object; }
 
-    virtual BindingLanguage getBindingLanguage() const { return QtLanguage; }
+    QObject* getObject() const { return m_object; }
 
     static QtInstance* getQtInstance(QObject*, PassRefPtr<RootObject>);
     static JSObject* getRuntimeObject(QtInstance*);
 
 private:
+    friend class QtClass;
     QtInstance(QObject*, PassRefPtr<RootObject>); // Factory produced only..
-    mutable QtClass* _class;
-    QPointer<QObject> _object;
-    QObject* _hashkey;
+    mutable QtClass* m_class;
+    QPointer<QObject> m_object;
+    QObject* m_hashkey;
+    mutable QHash<QByteArray,JSValue*> m_methods;
+    mutable QHash<QString,QtField*> m_fields;
 };
 
 } // namespace Bindings
index af3d89f531c4d7c74f8e8208579b452549d06d22..71963a40fccfaf7c4b51922ecb3a2d52f553d0cf 100644 (file)
 #include "qt_instance.h"
 #include "object.h"
 #include "array_instance.h"
-
+#include "date_object.h"
+#include "DateMath.h"
+#include "regexp_object.h"
+#include <runtime_object.h>
+#include <runtime_array.h>
+#include <function.h>
+#include "PropertyNameArray.h"
+#include "qmetatype.h"
 #include "qmetaobject.h"
 #include "qobject.h"
 #include "qstringlist.h"
 #include "qdebug.h"
+#include "qvarlengtharray.h"
+#include "qdatetime.h"
+#include <limits.h>
+
+// QtScript has these
+Q_DECLARE_METATYPE(QObjectList);
+Q_DECLARE_METATYPE(QList<int>);
+Q_DECLARE_METATYPE(QVariant);
+
 
 namespace KJS {
 namespace Bindings {
 
-// Variant value must be released with NPReleaseVariantValue()
-QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QVariant::Type hint)
+// Debugging
+//#define QTWK_RUNTIME_CONVERSION_DEBUG
+//#define QTWK_RUNTIME_MATCH_DEBUG
+
+class QWKNoDebug
+{
+public:
+    inline QWKNoDebug(){}
+    inline ~QWKNoDebug(){}
+
+    template<typename T>
+    inline QWKNoDebug &operator<<(const T &) { return *this; }
+};
+
+#ifdef QTWK_RUNTIME_CONVERSION_DEBUG
+#define qConvDebug() qDebug()
+#else
+#define qConvDebug() QWKNoDebug()
+#endif
+
+#ifdef QTWK_RUNTIME_MATCH_DEBUG
+#define qMatchDebug() qDebug()
+#else
+#define qMatchDebug() QWKNoDebug()
+#endif
+
+typedef enum {
+    Variant,
+    Number,
+    Boolean,
+    String,
+    Date,
+    RegExp,
+    Array,
+    QObj,
+    Object,
+    Null
+} JSRealType;
+
+static JSRealType valueRealType(ExecState* exec, JSValue* val)
+{
+    if (val->isNumber())
+        return Number;
+    else if (val->isString())
+        return String;
+    else if (val->isBoolean())
+        return Boolean;
+    else if (val->isNull())
+        return Null;
+    else if (val->isObject()) {
+        JSObject *object = val->toObject(exec);
+        if (object->inherits(&ArrayInstance::info))
+            return Array;
+        else if (object->inherits(&DateInstance::info))
+            return Date;
+        else if (object->inherits(&RegExpImp::info))
+            return RegExp;
+        else if (object->inherits(&RuntimeObjectImp::info))
+            return QObj;
+        return Object;
+    }
+
+    return String; // I don't know.
+}
+
+QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance)
 {
     // check magic pointer values before dereferencing value
-    if (value == jsNull() || value == jsNaN() || value == jsUndefined())
+    if (value == jsNaN() || value == jsUndefined()) {
+        if (distance)
+            *distance = -1;
         return QVariant();
+    }
 
     JSLock lock;
-    JSType type = value->type();
-    if (hint == QVariant::Invalid) {
+    JSRealType type = valueRealType(exec, value);
+    if (hint == QMetaType::Void) {
         switch(type) {
-        case NullType:
-        case UndefinedType:
-        case UnspecifiedType:
-        case StringType:
-        case GetterSetterType:
-            hint = QVariant::String;
+            case Number:
+                hint = QMetaType::Double;
+                break;
+            case Boolean:
+                hint = QMetaType::Bool;
+                break;
+            case String:
+            default:
+                hint = QMetaType::QString;
+                break;
+            case Date:
+                hint = QMetaType::QDateTime;
+                break;
+            case RegExp:
+                hint = QMetaType::QRegExp;
+                break;
+            case QObj:
+                hint = QMetaType::QObjectStar;
+                break;
+            case Array:
+                hint = QMetaType::QVariantList;
+                break;
+        }
+    }
+
+    if (value == jsNull() 
+        && hint != QMetaType::QObjectStar
+        && hint != QMetaType::VoidStar) {
+        if (distance)
+            *distance = -1;
+        return QVariant();
+    }
+
+    QVariant ret;
+    int dist = -1;
+    switch (hint) {
+        case QMetaType::Bool:
+            ret = QVariant(value->toBoolean(exec));
+            if (type == Boolean)
+                dist = 0;
+            else
+                dist = 10;
             break;
-        case NumberType:
-            hint = QVariant::Double;
+
+        case QMetaType::Int:
+        case QMetaType::UInt:
+        case QMetaType::Long:
+        case QMetaType::ULong:
+        case QMetaType::LongLong:
+        case QMetaType::ULongLong:
+        case QMetaType::Short:
+        case QMetaType::UShort:
+        case QMetaType::Float:
+        case QMetaType::Double:
+            ret = QVariant(value->toNumber(exec));
+            ret.convert((QVariant::Type)hint);
+            if (type == Number) {
+                switch (hint) {
+                case QMetaType::Double:
+                    dist = 0;
+                    break;
+                case QMetaType::Float:
+                    dist = 1;
+                    break;
+                case QMetaType::LongLong:
+                case QMetaType::ULongLong:
+                    dist = 2;
+                    break;
+                case QMetaType::Long:
+                case QMetaType::ULong:
+                    dist = 3;
+                    break;
+                case QMetaType::Int:
+                case QMetaType::UInt:
+                    dist = 4;
+                    break;
+                case QMetaType::Short:
+                case QMetaType::UShort:
+                    dist = 5;
+                    break;
+                    break;
+                default:
+                    dist = 10;
+                    break;
+                }
+            } else {
+                dist = 10;
+            }
             break;
-        case BooleanType:
-            hint = QVariant::Bool;
+
+        case QMetaType::QChar:
+            if (type == Number || type == Boolean) {
+                ret = QVariant(QChar((ushort)value->toNumber(exec)));
+                if (type == Boolean)
+                    dist = 3;
+                else
+                    dist = 6;
+            } else {
+                UString str = value->toString(exec);
+                ret = QVariant(QChar(str.size() ? *(const ushort*)str.rep()->data() : 0));
+                if (type == String)
+                    dist = 3;
+                else
+                    dist = 10;
+            }
             break;
-        case ObjectType: {
-            JSObject *object = value->toObject(exec);
-            if (object->inherits(&ArrayInstance::info)) 
-                hint = QVariant::List;
+
+        case QMetaType::QString: {
+            UString ustring = value->toString(exec);
+            ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()));
+            if (type == String)
+                dist = 0;
             else
-                hint = QVariant::String;
+                dist = 10;
+            break;
         }
+
+        case QMetaType::QVariantMap: 
+            if (type == Object || type == Array) {
+                // Enumerate the contents of the object
+                JSObject* object = value->toObject(exec);
+
+                PropertyNameArray properties;
+                object->getPropertyNames(exec, properties);
+                PropertyNameArray::const_iterator it = properties.begin();
+
+                QVariantMap result;
+                int objdist = 0;
+                while(it != properties.end()) {
+                    if (object->propertyIsEnumerable(exec, *it)) {
+                        JSValue* val = object->get(exec, *it);
+                        QVariant v = convertValueToQVariant(exec, val, QMetaType::Void, &objdist);
+                        if (objdist >= 0) {
+                            UString ustring = (*it).ustring();
+                            QString id = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+                            result.insert(id, v);
+                        }
+                    }
+                    ++it;
+                }
+                dist = 1;
+                ret = QVariant(result);
+            }
+            break;
+
+        case QMetaType::QVariantList:
+            if (type == Array) {
+                JSObject* object = value->toObject(exec);
+                ArrayInstance* array = static_cast<ArrayInstance*>(object);
+
+                QVariantList result;
+                int len = array->getLength();
+                int objdist = 0;
+                for (int i = 0; i < len; ++i) {
+                    JSValue *val = array->getItem(i);
+                    result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist));
+                    if (objdist == -1)
+                        break; // Failed converting a list entry, so fail the array
+                }
+                if (objdist != -1) {
+                    dist = 5;
+                    ret = QVariant(result);
+                }
+            } else {
+                // Make a single length array
+                QVariantList result;
+                int objdist;
+                result.append(convertValueToQVariant(exec, value, QMetaType::Void, &objdist));
+                if (objdist != -1) {
+                    ret = QVariant(result);
+                    dist = 10;
+                }
+            }
+            break;
+
+        case QMetaType::QStringList: {
+            if (type == Array) {
+                JSObject* object = value->toObject(exec);
+                ArrayInstance* array = static_cast<ArrayInstance*>(object);
+
+                QStringList result;
+                int len = array->getLength();
+                for (int i = 0; i < len; ++i) {
+                    JSValue* val = array->getItem(i);
+                    UString ustring = val->toString(exec);
+                    QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+
+                    result.append(qstring);
+                }
+                dist = 5;
+                ret = QVariant(result);
+            } else {
+                // Make a single length array
+                UString ustring = value->toString(exec);
+                QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+                QStringList result;
+                result.append(qstring);
+                ret = QVariant(result);
+                dist = 10;
+            }
+            break;
+        }
+
+        case QMetaType::QByteArray: {
+            UString ustring = value->toString(exec);
+            ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()).toLatin1());
+            if (type == String)
+                dist = 5;
+            else
+                dist = 10;
+            break;
         }
+
+        case QMetaType::QDateTime:
+        case QMetaType::QDate:
+        case QMetaType::QTime:
+            if (type == Date) {
+                JSObject* object = value->toObject(exec);
+                DateInstance* date = static_cast<DateInstance*>(object);
+                GregorianDateTime gdt;
+                date->getUTCTime(gdt);
+                if (hint == QMetaType::QDateTime) {
+                    ret = QDateTime(QDate(gdt.year, gdt.month, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
+                    dist = 0;
+                } else if (hint == QMetaType::QDate) {
+                    ret = QDate(gdt.year, gdt.month, gdt.monthDay);
+                    dist = 1;
+                } else {
+                    ret = QTime(gdt.hour, gdt.minute, gdt.second);
+                    dist = 2;
+                }
+            } else if (type == Number) {
+                double b = value->toNumber(exec);
+                GregorianDateTime gdt;
+                msToGregorianDateTime(b, true, gdt);
+                if (hint == QMetaType::QDateTime) {
+                    ret = QDateTime(QDate(gdt.year, gdt.month, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
+                    dist = 2;
+                } else if (hint == QMetaType::QDate) {
+                    ret = QDate(gdt.year, gdt.month, gdt.monthDay);
+                    dist = 3;
+                } else {
+                    ret = QTime(gdt.hour, gdt.minute, gdt.second);
+                    dist = 4;
+                }
+            } else if (type == String) {
+                UString ustring = value->toString(exec);
+                QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+
+                if (hint == QMetaType::QDateTime) {
+                    QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate);
+                    if (!dt.isValid())
+                        dt = QDateTime::fromString(qstring, Qt::TextDate);
+                    if (!dt.isValid())
+                        dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate);
+                    if (!dt.isValid())
+                        dt = QDateTime::fromString(qstring, Qt::LocaleDate);
+                    if (dt.isValid()) {
+                        ret = dt;
+                        dist = 2;
+                    }
+                } else if (hint == QMetaType::QDate) {
+                    QDate dt = QDate::fromString(qstring, Qt::ISODate);
+                    if (!dt.isValid())
+                        dt = QDate::fromString(qstring, Qt::TextDate);
+                    if (!dt.isValid())
+                        dt = QDate::fromString(qstring, Qt::SystemLocaleDate);
+                    if (!dt.isValid())
+                        dt = QDate::fromString(qstring, Qt::LocaleDate);
+                    if (dt.isValid()) {
+                        ret = dt;
+                        dist = 3;
+                    }
+                } else {
+                    QTime dt = QTime::fromString(qstring, Qt::ISODate);
+                    if (!dt.isValid())
+                        dt = QTime::fromString(qstring, Qt::TextDate);
+                    if (!dt.isValid())
+                        dt = QTime::fromString(qstring, Qt::SystemLocaleDate);
+                    if (!dt.isValid())
+                        dt = QTime::fromString(qstring, Qt::LocaleDate);
+                    if (dt.isValid()) {
+                        ret = dt;
+                        dist = 3;
+                    }
+                }
+            }
+            break;
+
+        case QMetaType::QRegExp:
+            if (type == RegExp) {
+/*                JSObject *object = value->toObject(exec);
+                RegExpImp *re = static_cast<RegExpImp*>(object);
+*/
+                // Attempt to convert.. a bit risky
+                UString ustring = value->toString(exec);
+                QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+
+                // this is of the form '/xxxxxx/i'
+                int firstSlash = qstring.indexOf('/');
+                int lastSlash = qstring.lastIndexOf('/');
+                if (firstSlash >=0 && lastSlash > firstSlash) {
+                    QRegExp realRe;
+
+                    realRe.setPattern(qstring.mid(firstSlash + 1, lastSlash - firstSlash - 1));
+
+                    if (qstring.mid(lastSlash + 1).contains('i'))
+                        realRe.setCaseSensitivity(Qt::CaseInsensitive);
+
+                    ret = qVariantFromValue(realRe);
+                    dist = 0;
+                } else {
+                    qConvDebug() << "couldn't parse a JS regexp";
+                }
+            } else if (type == String) {
+                UString ustring = value->toString(exec);
+                QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+
+                QRegExp re(qstring);
+                if (re.isValid()) {
+                    ret = qVariantFromValue(re);
+                    dist = 10;
+                }
+            }
+            break;
+
+        case QMetaType::QObjectStar:
+            if (type == QObj) {
+                JSObject* object = value->toObject(exec);
+                QtInstance* qtinst = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage));
+                if (qtinst) {
+                    if (qtinst->getObject()) {
+                        qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
+                        ret = qVariantFromValue(qtinst->getObject());
+                        qConvDebug() << ret;
+                        dist = 0;
+                    } else {
+                        qConvDebug() << "can't convert deleted qobject";
+                    }
+                } else {
+                    qConvDebug() << "wasn't a qtinstance";
+                }
+            } else if (type == Null) {
+                QObject* nullobj = 0;
+                ret = qVariantFromValue(nullobj);
+                dist = 0;
+            } else {
+                qConvDebug() << "previous type was not an object:" << type;
+            }
+            break;
+
+        case QMetaType::VoidStar:
+            if (type == QObj) {
+                JSObject* object = value->toObject(exec);
+                QtInstance* qtinst = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage));
+                if (qtinst) {
+                    if (qtinst->getObject()) {
+                        qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
+                        ret = qVariantFromValue((void *)qtinst->getObject());
+                        qConvDebug() << ret;
+                        dist = 0;
+                    } else {
+                        qConvDebug() << "can't convert deleted qobject";
+                    }
+                } else {
+                    qConvDebug() << "wasn't a qtinstance";
+                }
+            } else if (type == Null) {
+                ret = qVariantFromValue((void*)0);
+                dist = 0;
+            } else if (type == Number) {
+                // I don't think that converting a double to a pointer is a wise
+                // move.  Except maybe 0.
+                qConvDebug() << "got number for void * - not converting, seems unsafe:" << value->toNumber(exec);
+            } else {
+                qConvDebug() << "void* - unhandled type" << type;
+            }
+            break;
+
+        default:
+            // Non const type ids
+            if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>())
+            {
+                if (type == Array) {
+                    JSObject* object = value->toObject(exec);
+                    ArrayInstance* array = static_cast<ArrayInstance *>(object);
+
+                    QObjectList result;
+                    int len = array->getLength();
+                    for (int i = 0; i < len; ++i) {
+                        JSValue *val = array->getItem(i);
+                        int itemdist = -1;
+                        QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist);
+                        if (itemdist >= 0)
+                            result.append(item.value<QObject*>());
+                        else
+                            break;
+                    }
+                    // If we didn't fail conversion
+                    if (result.count() == len) {
+                        dist = 5;
+                        ret = QVariant::fromValue(result);
+                    } else {
+                        qConvDebug() << "type conversion failed (wanted" << len << ", got " << result.count() << ")";
+                    }
+                } else {
+                    // Make a single length array
+                    QObjectList result;
+                    int itemdist = -1;
+                    QVariant item = convertValueToQVariant(exec, value, QMetaType::QObjectStar, &itemdist);
+                    if (itemdist >= 0) {
+                        result.append(item.value<QObject*>());
+                        dist = 10;
+                        ret = QVariant::fromValue(result);
+                    }
+                }
+                break;
+            } else if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) {
+                if (type == Array) {
+                    JSObject* object = value->toObject(exec);
+                    ArrayInstance* array = static_cast<ArrayInstance *>(object);
+
+                    QList<int> result;
+                    int len = array->getLength();
+                    for (int i = 0; i < len; ++i) {
+                        JSValue* val = array->getItem(i);
+                        int itemdist = -1;
+                        QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist);
+                        if (itemdist >= 0)
+                            result.append(item.value<int>());
+                        else
+                            break;
+                    }
+                    // If we didn't fail conversion
+                    if (result.count() == len) {
+                        dist = 5;
+                        ret = QVariant::fromValue(result);
+                    } else {
+                        qConvDebug() << "type conversion failed (wanted" << len << ", got " << result.count() << ")";
+                    }
+                } else {
+                    // Make a single length array
+                    QList<int> result;
+                    int itemdist = -1;
+                    QVariant item = convertValueToQVariant(exec, value, QMetaType::Int, &itemdist);
+                    if (itemdist >= 0) {
+                        result.append(item.value<int>());
+                        dist = 10;
+                        ret = QVariant::fromValue(result);
+                    }
+                }
+                break;
+            } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) {
+                // Well.. we can do anything... just recurse with the autodetect flag
+                ret = convertValueToQVariant(exec, value, QMetaType::Void, distance);
+                dist = 10;
+                break;
+            }
+
+            dist = 10;
+            break;
     }
 
-    switch (hint) {
-    case QVariant::Bool:
-        return value->toBoolean(exec);
-    case QVariant::Int:
-    case QVariant::UInt:
-    case QVariant::LongLong:
-    case QVariant::ULongLong:
-    case QVariant::Double: {
-        QVariant v(value->toNumber(exec));
-        v.convert(hint);
-        return v;
-    }
-    case QVariant::Char:
-        if (type == NumberType || type == BooleanType) {
-            return QChar((ushort)value->toNumber(exec));
-        } else {
-            UString str = value->toString(exec);
-            return QChar(str.size() ? *(const ushort*)str.rep()->data() : 0);
-        }
-    
-//     case QVariant::Map:
-//     case QVariant::List:
-    case QVariant::String: {
-        UString ustring = value->toString(exec);
-        return QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
-    }
-    case QVariant::StringList: {
-        if (type != ObjectType)
-            return QVariant();
-        JSObject *object = value->toObject(exec);
-        if (!object->inherits(&ArrayInstance::info))
-            return QVariant();
-        ArrayInstance *array = static_cast<ArrayInstance *>(object);
-
-        QStringList result;
-        int len = array->getLength();
-        for (int i = 0; i < len; ++i) {
-            JSValue *val = array->getItem(i);
-            UString ustring = val->toString(exec);
-            QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
-            
-            result.append(qstring);
-        }
-        return result;
-    }        
-    case QVariant::ByteArray: {
-        UString ustring = value->toString(exec);
-        return QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()).toLatin1();
-    }
-    default:
-        break;
-    }
-    return QVariant();
-}
-
-JSValue* convertQVariantToValue(ExecState*, const QVariant& variant)
-{
-    if (variant.isNull())
+    if (!ret.isValid())
+        dist = -1;
+    if (distance)
+        *distance = dist;
+
+    return ret;
+}
+
+JSValue* convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant)
+{
+    // Variants with QObject * can be isNull but not a null pointer
+    // An empty QString variant is also null
+    QMetaType::Type type = (QMetaType::Type) variant.userType();
+    if (variant.isNull() &&
+        type != QMetaType::QObjectStar &&
+        type != QMetaType::VoidStar &&
+        type != QMetaType::QWidgetStar &&
+        type != QMetaType::QString) {
         return jsNull();
+    }
 
     JSLock lock;
-    QVariant::Type type = variant.type();
 
-    if (type == QVariant::Bool)
+    if (type == QMetaType::Bool)
         return jsBoolean(variant.toBool());
-    if (type == QVariant::Invalid)
-        return jsUndefined();
-    if (type == QVariant::Int ||
-        type == QVariant::UInt ||
-        type == QVariant::LongLong ||
-        type == QVariant::ULongLong ||
-        type == QVariant::Double)
+
+    if (type == QMetaType::Int ||
+        type == QMetaType::UInt ||
+        type == QMetaType::Long ||
+        type == QMetaType::ULong ||
+        type == QMetaType::LongLong ||
+        type == QMetaType::ULongLong ||
+        type == QMetaType::Short ||
+        type == QMetaType::UShort ||
+        type == QMetaType::Float ||
+        type == QMetaType::Double)
         return jsNumber(variant.toDouble());
+
+    if (type == QMetaType::QRegExp) {
+        QRegExp re = variant.value<QRegExp>();
+
+        if (re.isValid()) {
+            RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor());
+            List args;
+            UString uflags;
+
+            if (re.caseSensitivity() == Qt::CaseInsensitive)
+                uflags = "i"; // ### Can't do g or m
+            UString ustring((KJS::UChar*)re.pattern().utf16(), re.pattern().length());
+            args.append(jsString(ustring));
+            args.append(jsString(uflags));
+            return regExpObj->construct(exec, args);
+        }
+    }
+
+    if (type == QMetaType::QDateTime ||
+        type == QMetaType::QDate ||
+        type == QMetaType::QTime) {
+        DateObjectImp *dateObj = static_cast<DateObjectImp*>(exec->lexicalGlobalObject()->dateConstructor());
+        List args;
+
+        QDate date = QDate::currentDate();
+        QTime time(0,0,0); // midnight
+
+        if (type == QMetaType::QDate)
+            date = variant.value<QDate>();
+        else if (type == QMetaType::QTime)
+            time = variant.value<QTime>();
+        else {
+            QDateTime dt = variant.value<QDateTime>();
+            date = dt.date();
+            time = dt.time();
+        }
+
+        // ### There's probably a UTC bug here.
+        args.append(jsNumber(date.year()));
+        args.append(jsNumber(date.month()));
+        args.append(jsNumber(date.day()));
+        args.append(jsNumber(time.hour()));
+        args.append(jsNumber(time.minute()));
+        args.append(jsNumber(time.second()));
+        args.append(jsNumber(time.msec()));
+        return dateObj->construct(exec, args);
+    }
+
+    if (type == QMetaType::QByteArray) {
+        QByteArray ba = variant.value<QByteArray>();
+        UString ustring(ba.constData());
+        return jsString(ustring);
+    }
+
+    if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
+        QObject* obj = variant.value<QObject*>();
+        return Instance::createRuntimeObject(Instance::QtLanguage, obj, root);
+    }
+
+    if (type == QMetaType::QVariantMap) {
+        // create a new object, and stuff properties into it
+        JSObject* ret = new JSObject(exec->lexicalGlobalObject()->objectPrototype());
+        QVariantMap map = variant.value<QVariantMap>();
+        QVariantMap::const_iterator i = map.constBegin();
+        while (i != map.constEnd()) {
+            QString s = i.key();
+            JSValue* val = convertQVariantToValue(exec, root, i.value());
+            if (val)
+                ret->put(exec, Identifier((const UChar *)s.constData(), s.length()), val);
+            // ### error case?
+            ++i;
+        }
+
+        return ret;
+    }
+
+    // List types
+    if (type == QMetaType::QVariantList) {
+        QVariantList vl = variant.toList();
+        return new RuntimeArray(exec, new QtArray<QVariant>(vl, QMetaType::Void, root));
+    } else if (type == QMetaType::QStringList) {
+        QStringList sl = variant.value<QStringList>();
+        return new RuntimeArray(exec, new QtArray<QString>(sl, QMetaType::QString, root));
+    } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
+        QObjectList ol= variant.value<QObjectList>();
+        return new RuntimeArray(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root));
+    } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) {
+        QList<int> il= variant.value<QList<int> >();
+        return new RuntimeArray(exec, new QtArray<int>(il, QMetaType::Int, root));
+    }
+
+    if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) {
+        QVariant real = variant.value<QVariant>();
+        qConvDebug() << "real variant is:" << real;
+        return convertQVariantToValue(exec, root, real);
+    }
+
+    qConvDebug() << "fallback path for" << variant << variant.userType();
+
     QString string = variant.toString();
-    if (string.isNull())
-        return jsUndefined();
-    
     UString ustring((KJS::UChar*)string.utf16(), string.length());
     return jsString(ustring);
 }
-    
-const char* QtField::name() const
+
+// ===============
+
+// Qt-like macros
+#define QW_D(Class) Class##Data* d = d_func()
+#define QW_DS(Class,Instance) Class##Data* d = Instance->d_func()
+
+QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState *exec, const Identifier &ident, PassRefPtr<QtInstance> inst)
+    : InternalFunctionImp (static_cast<FunctionPrototype*>(exec->lexicalGlobalObject()->functionPrototype()), ident)
+    , d_ptr(dd)
+{
+    QW_D(QtRuntimeMethod);
+    d->m_instance = inst;
+}
+
+QtRuntimeMethod::~QtRuntimeMethod()
+{
+    delete d_ptr;
+}
+
+CodeType QtRuntimeMethod::codeType() const
+{
+    return FunctionCode;
+}
+
+Completion QtRuntimeMethod::execute(ExecState*)
+{
+    return Completion(Normal, jsUndefined());
+}
+
+// ===============
+
+QtRuntimeMethodData::~QtRuntimeMethodData()
+{
+}
+
+QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData()
+{
+
+}
+
+QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData()
+{
+
+}
+
+// ===============
+
+// Type conversion metadata (from QtScript originally)
+class QtMethodMatchType
+{
+public:
+    enum Kind {
+        Invalid,
+        Variant,
+        MetaType,
+        Unresolved,
+        MetaEnum
+    };
+
+
+    QtMethodMatchType()
+        : m_kind(Invalid) { }
+
+    Kind kind() const
+    { return m_kind; }
+
+    QMetaType::Type typeId() const;
+
+    bool isValid() const
+    { return (m_kind != Invalid); }
+
+    bool isVariant() const
+    { return (m_kind == Variant); }
+
+    bool isMetaType() const
+    { return (m_kind == MetaType); }
+
+    bool isUnresolved() const
+    { return (m_kind == Unresolved); }
+
+    bool isMetaEnum() const
+    { return (m_kind == MetaEnum); }
+
+    QByteArray name() const;
+
+    int enumeratorIndex() const
+    { Q_ASSERT(isMetaEnum()); return m_typeId; }
+
+    static QtMethodMatchType variant()
+    { return QtMethodMatchType(Variant); }
+
+    static QtMethodMatchType metaType(int typeId, const QByteArray &name)
+    { return QtMethodMatchType(MetaType, typeId, name); }
+
+    static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name)
+    { return QtMethodMatchType(MetaEnum, enumIndex, name); }
+
+    static QtMethodMatchType unresolved(const QByteArray &name)
+    { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); }
+
+private:
+    QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray())
+        : m_kind(kind), m_typeId(typeId), m_name(name) { }
+
+    Kind m_kind;
+    int m_typeId;
+    QByteArray m_name;
+};
+
+QMetaType::Type QtMethodMatchType::typeId() const
 {
-    return property.name();
+    if (isVariant())
+        return (QMetaType::Type) QMetaType::type("QVariant");
+    return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId);
 }
 
-JSValue* QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
+QByteArray QtMethodMatchType::name() const
 {
-    qDebug() << "valueFromInstance";
-    const QtInstance* instance = static_cast<const QtInstance*>(inst);
-    QObject* obj = instance->getObject();
-    QVariant val = property.read(obj);
-    return convertQVariantToValue(exec, val);
+    if (!m_name.isEmpty())
+        return m_name;
+    else if (m_kind == Variant)
+        return "QVariant";
+    return QByteArray();
 }
 
-void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue* aValue) const
+struct QtMethodMatchData
+{
+    int matchDistance;
+    int index;
+    QVector<QtMethodMatchType> types;
+    QVarLengthArray<QVariant, 10> args;
+
+    QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs,
+                                const QVarLengthArray<QVariant, 10> &as)
+        : matchDistance(dist), index(idx), types(typs), args(as) { }
+    QtMethodMatchData()
+        : index(-1) { }
+
+    bool isValid() const
+    { return (index != -1); }
+
+    int firstUnresolvedIndex() const
+    {
+        for (int i=0; i < types.count(); i++) {
+            if (types.at(i).isUnresolved())
+                return i;
+        }
+        return -1;
+    }
+};
+
+static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str)
 {
-    qDebug() << "setValueToInstance";
-    const QtInstance* instance = static_cast<const QtInstance*>(inst);
-    QObject* obj = instance->getObject();
-    
-    QVariant val = convertValueToQVariant(exec, aValue, QVariant::Invalid);
-    property.write(obj, val);
+    QByteArray scope;
+    QByteArray name;
+    int scopeIdx = str.indexOf("::");
+    if (scopeIdx != -1) {
+        scope = str.left(scopeIdx);
+        name = str.mid(scopeIdx + 2);
+    } else {
+        name = str;
+    }
+    for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
+        QMetaEnum m = meta->enumerator(i);
+        if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/)
+            return i;
+    }
+    return -1;
 }
 
+// Helper function for resolving methods
+// Largely based on code in QtScript for compatibility reasons
+static int findMethodIndex(ExecState* exec,
+                           const QMetaObject* meta,
+                           const QByteArray& signature,
+                           const List& jsArgs,
+                           QVarLengthArray<QVariant, 10> &vars,
+                           void** vvars,
+                           JSObject **pError)
+{
+    QList<int> matchingIndices;
+
+    bool overloads = !signature.contains('(');
+
+    int count = meta->methodCount();
+    for (int i = count - 1; i >= 0; --i) {
+        const QMetaMethod m = meta->method(i);
+
+        // Don't choose private methods
+        if (m.access() == QMetaMethod::Private)
+            continue;
+
+        // try and find all matching named methods
+        if (m.signature() == signature)
+            matchingIndices.append(i);
+        else if (overloads) {
+            QByteArray rawsignature = m.signature();
+            rawsignature.truncate(rawsignature.indexOf('('));
+            if (rawsignature == signature)
+                matchingIndices.append(i);
+        }
+    }
+
+    int chosenIndex = -1;
+    *pError = 0;
+    QVector<QtMethodMatchType> chosenTypes;
+
+    QVarLengthArray<QVariant, 10> args;
+    QVector<QtMethodMatchData> candidates;
+    QVector<QtMethodMatchData> unresolved;
+    QVector<int> tooFewArgs;
+    QVector<int> conversionFailed;
+
+    foreach(int index, matchingIndices) {
+        QMetaMethod method = meta->method(index);
+
+        QVector<QtMethodMatchType> types;
+        bool unresolvedTypes = false;
+
+        // resolve return type
+        QByteArray returnTypeName = method.typeName();
+        int rtype = QMetaType::type(returnTypeName);
+        if ((rtype == 0) && !returnTypeName.isEmpty()) {
+            if (returnTypeName == "QVariant") {
+                types.append(QtMethodMatchType::variant());
+            } else if (returnTypeName.endsWith('*')) {
+                types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName));
+            } else {
+                int enumIndex = indexOfMetaEnum(meta, returnTypeName);
+                if (enumIndex != -1)
+                    types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName));
+                else {
+                    unresolvedTypes = true;
+                    types.append(QtMethodMatchType::unresolved(returnTypeName));
+                }
+            }
+        } else {
+            if (returnTypeName == "QVariant")
+                types.append(QtMethodMatchType::variant());
+            else
+                types.append(QtMethodMatchType::metaType(rtype, returnTypeName));
+        }
+
+        // resolve argument types
+        QList<QByteArray> parameterTypeNames = method.parameterTypes();
+        for (int i = 0; i < parameterTypeNames.count(); ++i) {
+            QByteArray argTypeName = parameterTypeNames.at(i);
+            int atype = QMetaType::type(argTypeName);
+            if (atype == 0) {
+                if (argTypeName == "QVariant") {
+                    types.append(QtMethodMatchType::variant());
+                } else {
+                    int enumIndex = indexOfMetaEnum(meta, argTypeName);
+                    if (enumIndex != -1)
+                        types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName));
+                    else {
+                        unresolvedTypes = true;
+                        types.append(QtMethodMatchType::unresolved(argTypeName));
+                    }
+                }
+            } else {
+                if (argTypeName == "QVariant")
+                    types.append(QtMethodMatchType::variant());
+                else
+                    types.append(QtMethodMatchType::metaType(atype, argTypeName));
+            }
+        }
+
+        if (jsArgs.size() < (types.count() - 1)) {
+            qMatchDebug() << "Match:too few args for" << method.signature();
+            tooFewArgs.append(index);
+            continue;
+        }
+
+        if (unresolvedTypes) {
+            qMatchDebug() << "Match:unresolved arg types for" << method.signature();
+            // remember it so we can give an error message later, if necessary
+            unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index,
+                                                   types, QVarLengthArray<QVariant, 10>()));
+            continue;
+        }
+
+        // Now convert arguments
+        if (args.count() != types.count())
+            args.resize(types.count());
+
+        QtMethodMatchType retType = types[0];
+        args[0] = QVariant(retType.typeId(), (void *)0); // the return value
+
+        bool converted = true;
+        int matchDistance = 0;
+        for (int i = 0; converted && i < types.count() - 1; ++i) {
+            JSValue* arg = i < jsArgs.size() ? jsArgs[i] : jsUndefined();
+
+            int argdistance = -1;
+            QVariant v = convertValueToQVariant(exec, arg, types.at(i+1).typeId(), &argdistance);
+            if (argdistance >= 0) {
+                matchDistance += argdistance;
+                args[i+1] = v;
+            } else {
+                qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId());
+                converted = false;
+            }
+        }
+
+        qMatchDebug() << "Match: " << method.signature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance;
+
+        if (converted) {
+            if ((jsArgs.size() == types.count() - 1)
+                && (matchDistance == 0)) {
+                // perfect match, use this one
+                chosenIndex = index;
+                break;
+            } else {
+                QtMethodMatchData metaArgs(matchDistance, index, types, args);
+                if (candidates.isEmpty()) {
+                    candidates.append(metaArgs);
+                } else {
+                    QtMethodMatchData otherArgs = candidates.at(0);
+                    if ((args.count() > otherArgs.args.count())
+                        || ((args.count() == otherArgs.args.count())
+                            && (matchDistance <= otherArgs.matchDistance))) {
+                        candidates.prepend(metaArgs);
+                    } else {
+                        candidates.append(metaArgs);
+                    }
+                }
+            }
+        } else {
+            conversionFailed.append(index);
+        }
+
+        if (!overloads)
+            break;
+    }
+
+    if (chosenIndex == -1 && candidates.count() == 0) {
+        // No valid functions at all - format an error message
+        if (!conversionFailed.isEmpty()) {
+            QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n")
+                              .arg(QLatin1String(signature));
+            for (int i = 0; i < conversionFailed.size(); ++i) {
+                if (i > 0)
+                    message += QLatin1String("\n");
+                QMetaMethod mtd = meta->method(conversionFailed.at(i));
+                message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.signature()));
+            }
+            *pError = throwError(exec, TypeError, message.toLatin1().constData());
+        } else if (!unresolved.isEmpty()) {
+            QtMethodMatchData argsInstance = unresolved.first();
+            int unresolvedIndex = argsInstance.firstUnresolvedIndex();
+            Q_ASSERT(unresolvedIndex != -1);
+            QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex);
+            QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'")
+                .arg(QString::fromLatin1(signature))
+                .arg(QLatin1String(unresolvedType.name()));
+            *pError = throwError(exec, TypeError, message.toLatin1().constData());
+        } else {
+            QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n")
+                              .arg(QLatin1String(signature));
+            for (int i = 0; i < tooFewArgs.size(); ++i) {
+                if (i > 0)
+                    message += QLatin1String("\n");
+                QMetaMethod mtd = meta->method(tooFewArgs.at(i));
+                message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.signature()));
+            }
+            *pError = throwError(exec, SyntaxError, message.toLatin1().constData());
+        }
+    }
+
+    if (chosenIndex == -1 && candidates.count() > 0) {
+        QtMethodMatchData metaArgs = candidates.at(0);
+        if ((candidates.size() > 1)
+            && (metaArgs.args.count() == candidates.at(1).args.count())
+            && (metaArgs.matchDistance == candidates.at(1).matchDistance)) {
+            // ambiguous call
+            QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n")
+                                .arg(QLatin1String(signature));
+            for (int i = 0; i < candidates.size(); ++i) {
+                if (i > 0)
+                    message += QLatin1String("\n");
+                QMetaMethod mtd = meta->method(candidates.at(i).index);
+                message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.signature()));
+            }
+            *pError = throwError(exec, TypeError, message.toLatin1().constData());
+        } else {
+            chosenIndex = metaArgs.index;
+            args = metaArgs.args;
+        }
+    }
+
+    if (chosenIndex != -1) {
+        /* Copy the stuff over */
+        int i;
+        vars.resize(args.count());
+        for (i=0; i < args.count(); i++) {
+            vars[i] = args[i];
+            vvars[i] = vars[i].data();
+        }
+    }
+
+    return chosenIndex;
+}
+
+// Signals are not fuzzy matched as much as methods
+static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature)
+{
+    int index = initialIndex;
+    QMetaMethod method = meta->method(index);
+    bool overloads = !signature.contains('(');
+    if (overloads && (method.attributes() & QMetaMethod::Cloned)) {
+        // find the most general method
+        do {
+            method = meta->method(--index);
+        } while (method.attributes() & QMetaMethod::Cloned);
+    }
+    return index;
+}
+
+QtRuntimeMetaMethod::QtRuntimeMetaMethod(ExecState* exec, const Identifier& ident, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature)
+    : QtRuntimeMethod (new QtRuntimeMetaMethodData(), exec, ident, inst)
+{
+    QW_D(QtRuntimeMetaMethod);
+    d->m_signature = signature;
+    d->m_index = index;
+    d->m_connect = 0;
+    d->m_disconnect = 0;
+}
+
+void QtRuntimeMetaMethod::mark()
+{
+    QtRuntimeMethod::mark();
+    QW_D(QtRuntimeMetaMethod);
+    if (d->m_connect)
+        d->m_connect->mark();
+    if (d->m_disconnect)
+        d->m_disconnect->mark();
+}
+
+JSValue* QtRuntimeMetaMethod::callAsFunction(ExecState* exec, JSObject*, const List& args)
+{
+    QW_D(QtRuntimeMetaMethod);
+
+    // We're limited to 10 args
+    if (args.size() > 10)
+        return jsUndefined();
+
+    // We have to pick a method that matches..
+    JSLock lock;
+
+    QObject *obj = d->m_instance->getObject();
+    if (obj) {
+        QVarLengthArray<QVariant, 10> vargs;
+        void *qargs[11];
+
+        int methodIndex;
+        JSObject* errorObj = 0;
+        if ((methodIndex = findMethodIndex(exec, obj->metaObject(), d->m_signature, args, vargs, (void**)qargs, &errorObj)) != -1) {
+            if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0)
+                return jsUndefined();
+
+            if (vargs[0].isValid())
+                return convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0]);
+        }
+
+        if (errorObj)
+            return errorObj;
+    } else {
+        return throwError(exec, GeneralError, "cannot call function of deleted QObject");
+    }
+
+    // void functions return undefined
+    return jsUndefined();
+}
+
+bool QtRuntimeMetaMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
+{
+    if (propertyName == "connect") {
+        slot.setCustom(this, connectGetter);
+        return true;
+    } else if (propertyName == "disconnect") {
+        slot.setCustom(this, disconnectGetter);
+        return true;
+    } else if (propertyName == exec->propertyNames().length) {
+        slot.setCustom(this, lengthGetter);
+        return true;
+    }
+
+    return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot);
+}
+
+JSValue *QtRuntimeMetaMethod::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&)
+{
+    // QtScript always returns 0
+    return jsNumber(0);
+}
+
+JSValue *QtRuntimeMetaMethod::connectGetter(ExecState* exec, JSObject*, const Identifier& ident, const PropertySlot& slot)
+{
+    QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(slot.slotBase());
+    QW_DS(QtRuntimeMetaMethod, thisObj);
+
+    if (!d->m_connect)
+        d->m_connect = new QtRuntimeConnectionMethod(exec, ident, true, d->m_instance, d->m_index, d->m_signature);
+    return d->m_connect;
+}
+
+JSValue* QtRuntimeMetaMethod::disconnectGetter(ExecState* exec, JSObject*, const Identifier& ident, const PropertySlot& slot)
+{
+    QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(slot.slotBase());
+    QW_DS(QtRuntimeMetaMethod, thisObj);
+
+    if (!d->m_disconnect)
+        d->m_disconnect = new QtRuntimeConnectionMethod(exec, ident, false, d->m_instance, d->m_index, d->m_signature);
+    return d->m_disconnect;
+}
+
+// ===============
+
+QMultiMap<QObject*, QtConnectionObject*> QtRuntimeConnectionMethod::connections;
+
+QtRuntimeConnectionMethod::QtRuntimeConnectionMethod(ExecState* exec, const Identifier& ident, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature)
+    : QtRuntimeMethod (new QtRuntimeConnectionMethodData(), exec, ident, inst)
+{
+    QW_D(QtRuntimeConnectionMethod);
+
+    d->m_signature = signature;
+    d->m_index = index;
+    d->m_isConnect = isConnect;
+}
+
+JSValue *QtRuntimeConnectionMethod::callAsFunction(ExecState* exec, JSObject*, const List& args)
+{
+    QW_D(QtRuntimeConnectionMethod);
+
+    JSLock lock;
+
+    QObject* sender = d->m_instance->getObject();
+
+    if (sender) {
+
+        JSObject* thisObject = exec->lexicalGlobalObject();
+        JSObject* funcObject = 0;
+
+        // QtScript checks signalness first, arguments second
+        int signalIndex = -1;
+
+        // Make sure the initial index is a signal
+        QMetaMethod m = sender->metaObject()->method(d->m_index);
+        if (m.methodType() == QMetaMethod::Signal)
+            signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_signature);
+
+        if (signalIndex != -1) {
+            if (args.size() == 1) {
+                funcObject = args[0]->toObject(exec);
+                if (!funcObject->implementsCall()) {
+                    if (d->m_isConnect)
+                        return throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function");
+                    else
+                        return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function");
+                }
+            } else if (args.size() >= 2) {
+                if (args[0]->type() == ObjectType) {
+                    thisObject = args[0]->toObject(exec);
+
+                    // Get the actual function to call
+                    JSObject *asObj = args[1]->toObject(exec);
+                    if (asObj->implementsCall()) {
+                        // Function version
+                        funcObject = asObj;
+                    } else {
+                        // Convert it to a string
+                        UString funcName = args[1]->toString(exec);
+                        Identifier funcIdent(funcName);
+
+                        // ### DropAllLocks
+                        // This is resolved at this point in QtScript
+                        JSValue* val = thisObject->get(exec, funcIdent);
+                        JSObject* asFuncObj = val->toObject(exec);
+
+                        if (asFuncObj->implementsCall()) {
+                            funcObject = asFuncObj;
+                        } else {
+                            if (d->m_isConnect)
+                                return throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function");
+                            else
+                                return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function");
+                        }
+                    }
+                } else {
+                    if (d->m_isConnect)
+                        return throwError(exec, TypeError, "QtMetaMethod.connect: thisObject is not an object");
+                    else
+                        return throwError(exec, TypeError, "QtMetaMethod.disconnect: thisObject is not an object");
+                }
+            } else {
+                if (d->m_isConnect)
+                    return throwError(exec, GeneralError, "QtMetaMethod.connect: no arguments given");
+                else
+                    return throwError(exec, GeneralError, "QtMetaMethod.disconnect: no arguments given");
+            }
+
+            if (d->m_isConnect) {
+                // to connect, we need:
+                //  target object [from ctor]
+                //  target signal index etc. [from ctor]
+                //  receiver function [from arguments]
+                //  receiver this object [from arguments]
+
+                QtConnectionObject* conn = new QtConnectionObject(d->m_instance, signalIndex, thisObject, funcObject);
+                bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
+                if (!ok) {
+                    delete conn;
+                    QString msg = QString("QtMetaMethod.connect: failed to connect to %1::%2()")
+                            .arg(sender->metaObject()->className())
+                            .arg(QLatin1String(d->m_signature));
+                    return throwError(exec, GeneralError, msg.toLatin1().constData());
+                }
+                else {
+                    // Store connection
+                    connections.insert(sender, conn);
+                }
+            } else {
+                // Now to find our previous connection object. Hmm.
+                QList<QtConnectionObject*> conns = connections.values(sender);
+                bool ret = false;
+
+                foreach(QtConnectionObject* conn, conns) {
+                    // Is this the right connection?
+                    if (conn->match(sender, signalIndex, thisObject, funcObject)) {
+                        // Yep, disconnect it
+                        QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
+                        delete conn; // this will also remove it from the map
+                        ret = true;
+                        break;
+                    }
+                }
+
+                if (!ret) {
+                    QString msg = QString("QtMetaMethod.disconnect: failed to disconnect from %1::%2()")
+                            .arg(sender->metaObject()->className())
+                            .arg(QLatin1String(d->m_signature));
+                    return throwError(exec, GeneralError, msg.toLatin1().constData());
+                }
+            }
+        } else {
+            QString msg = QString("QtMetaMethod.%1: %2::%3() is not a signal")
+                    .arg(d->m_isConnect ? "connect": "disconnect")
+                    .arg(sender->metaObject()->className())
+                    .arg(QLatin1String(d->m_signature));
+            return throwError(exec, TypeError, msg.toLatin1().constData());
+        }
+    } else {
+        return throwError(exec, GeneralError, "cannot call function of deleted QObject");
+    }
+
+    return jsUndefined();
+}
+
+bool QtRuntimeConnectionMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
+{
+    if (propertyName == exec->propertyNames().length) {
+        slot.setCustom(this, lengthGetter);
+        return true;
+    }
+
+    return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot);
+}
+
+JSValue *QtRuntimeConnectionMethod::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&)
+{
+    // we have one formal argument, and one optional
+    return jsNumber(1);
+}
+
+// ===============
+
+QtConnectionObject::QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject)
+    : m_instance(instance)
+    , m_signalIndex(signalIndex)
+    , m_originalObject(m_instance->getObject())
+    , m_thisObject(thisObject)
+    , m_funcObject(funcObject)
+{
+    setParent(m_originalObject);
+    ASSERT(JSLock::currentThreadIsHoldingLock()); // so our ProtectedPtrs are safe
+}
+
+QtConnectionObject::~QtConnectionObject()
+{
+    // Remove us from the map of active connections
+    QtRuntimeConnectionMethod::connections.remove(m_originalObject, this);
+}
+
+static const uint qt_meta_data_QtConnectionObject[] = {
+
+ // content:
+       1,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   10, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+
+ // slots: signature, parameters, type, tag, flags
+      28,   27,   27,   27, 0x0a,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_QtConnectionObject[] = {
+    "KJS::Bindings::QtConnectionObject\0\0execute()\0"
+};
+
+const QMetaObject QtConnectionObject::staticMetaObject = {
+    { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject,
+      qt_meta_data_QtConnectionObject, 0 }
+};
+
+const QMetaObject *QtConnectionObject::metaObject() const
+{
+    return &staticMetaObject;
+}
+
+void *QtConnectionObject::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject))
+        return static_cast<void*>(const_cast<QtConnectionObject*>(this));
+    return QObject::qt_metacast(_clname);
+}
+
+int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QObject::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        switch (_id) {
+        case 0: execute(_a); break;
+        }
+        _id -= 1;
+    }
+    return _id;
+}
+
+void QtConnectionObject::execute(void **argv)
+{
+    QObject* obj = m_instance->getObject();
+    if (obj) {
+        const QMetaObject* meta = obj->metaObject();
+        const QMetaMethod method = meta->method(m_signalIndex);
+
+        QList<QByteArray> parameterTypes = method.parameterTypes();
+
+        int argc = parameterTypes.count();
+
+        JSLock lock;
+
+        // ### Should the Interpreter/ExecState come from somewhere else?
+        RefPtr<RootObject> ro = m_instance->rootObject();
+        if (ro) {
+            JSGlobalObject* globalobj = ro->globalObject();
+            if (globalobj) {
+                ExecState* exec = globalobj->globalExec();
+                if (exec) {
+                    // Build the argument list (up to the formal argument length of the slot)
+                    List l;
+                    // ### DropAllLocks?
+                    int funcArgC = m_funcObject->get(exec, exec->propertyNames().length)->toInt32(exec);
+                    int argTotal = qMax(funcArgC, argc);
+                    for(int i=0; i < argTotal; i++) {
+                        if (i < argc) {
+                            int argType = QMetaType::type(parameterTypes.at(i));
+                            l.append(convertQVariantToValue(exec, ro, QVariant(argType, argv[i+1])));
+                        } else {
+                            l.append(jsUndefined());
+                        }
+                    }
+                    // Stuff in the __qt_sender property, if we can
+                    if (m_funcObject->inherits(&FunctionImp::info)) {
+                        FunctionImp* fimp = static_cast<FunctionImp*>(m_funcObject.get());
+
+                        JSObject* qt_sender = Instance::createRuntimeObject(Instance::QtLanguage, sender(), ro);
+                        JSObject* wrapper = new JSObject();
+                        wrapper->put(exec, "__qt_sender__", qt_sender);
+                        ScopeChain oldsc = fimp->scope();
+                        ScopeChain sc = oldsc;
+                        sc.push(wrapper);
+                        fimp->setScope(sc);
+                        fimp->call(exec, m_thisObject, l);
+                        fimp->setScope(oldsc);
+                    } else
+                        m_funcObject->call(exec, m_thisObject, l);
+                }
+            }
+        }
+    } else {
+        // A strange place to be - a deleted object emitted a signal here.
+        qWarning() << "sender deleted, cannot deliver signal";
+    }
+}
+
+bool QtConnectionObject::match(QObject* sender, int signalIndex, JSObject* thisObject, JSObject *funcObject)
+{
+    if (m_originalObject == sender && m_signalIndex == signalIndex
+        && thisObject == (JSObject*)m_thisObject && funcObject == (JSObject*)m_funcObject)
+        return true;
+    return false;
+}
+
+// ===============
+
+template <typename T> QtArray<T>::QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject> rootObject)
+    : Array(rootObject)
+    , m_list(list)
+    , m_type(type)
+{
+    m_length = m_list.count();
+}
+
+template <typename T> QtArray<T>::~QtArray ()
+{
+}
+
+template <typename T> RootObject* QtArray<T>::rootObject() const
+{
+    return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0;
+}
+
+template <typename T> void QtArray<T>::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const
+{
+    // QtScript sets the value, but doesn't forward it to the original source
+    // (e.g. if you do 'object.intList[5] = 6', the object is not updated, but the
+    // copy of the list is).
+    int dist = -1;
+    QVariant val = convertValueToQVariant(exec, aValue, m_type, &dist);
+
+    if (dist >= 0) {
+        m_list[index] = val.value<T>();
+    }
+}
+
+
+template <typename T> JSValue* QtArray<T>::valueAt(ExecState *exec, unsigned int index) const
+{
+    if (index < m_length) {
+        T val = m_list.at(index);
+        return convertQVariantToValue(exec, rootObject(), QVariant::fromValue(val));
+    }
+
+    return jsUndefined();
+}
+
+// ===============
+
 } }
index 91fb4d8fa42151f5e9e3415d8471c86329c5b7c3..af239d59fa6616821e0c66493112580a241bcd90 100644 (file)
 #define BINDINGS_QT_RUNTIME_H_
 
 #include "runtime.h"
+#include "runtime_method.h"
+#include "protect.h"
+
 #include <qbytearray.h>
 #include <qmetaobject.h>
+#include <qpointer.h>
 
 namespace KJS {
 namespace Bindings {
@@ -31,16 +35,34 @@ class QtInstance;
 
 class QtField : public Field {
 public:
+
+    typedef enum {
+        MetaProperty,
+        DynamicProperty,
+        ChildObject
+    } QtFieldType;
+
     QtField(const QMetaProperty &p)
-        : property(p)
+        : m_type(MetaProperty), m_property(p)
+        {}
+
+    QtField(const QByteArray &b)
+        : m_type(DynamicProperty), m_dynamicProperty(b)
+        {}
+
+    QtField(QObject *child)
+        : m_type(ChildObject), m_childObject(child)
         {}
 
     virtual JSValue* valueFromInstance(ExecState*, const Instance*) const;
     virtual void setValueToInstance(ExecState*, const Instance*, JSValue*) const;
     virtual const char* name() const;
-
+    QtFieldType fieldType() const {return m_type;}
 private:
-    QMetaProperty property;
+    QtFieldType m_type;
+    QByteArray m_dynamicProperty;
+    QMetaProperty m_property;
+    QPointer<QObject> m_childObject;
 };
 
 
@@ -48,21 +70,144 @@ class QtMethod : public Method
 {
 public:
     QtMethod(const QMetaObject *mo, int i, const QByteArray &ident, int numParameters)
-        : metaObject(mo),
-          index(i),
-          identifier(ident),
-          nParams(numParameters)
+        : m_metaObject(mo),
+          m_index(i),
+          m_identifier(ident),
+          m_nParams(numParameters)
         { }
 
-    virtual const char* name() const { return identifier.constData(); }
-    virtual int numParameters() const { return nParams; }
+    virtual const char* name() const { return m_identifier.constData(); }
+    virtual int numParameters() const { return m_nParams; }
 
 private:
     friend class QtInstance;
-    const QMetaObject *metaObject;
-    int index;
-    QByteArray identifier;
-    int nParams;
+    const QMetaObject *m_metaObject;
+    int m_index;
+    QByteArray m_identifier;
+    int m_nParams;
+};
+
+
+template <typename T> class QtArray : public Array
+{
+public:
+    QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject>);
+    virtual ~QtArray();
+
+    RootObject* rootObject() const;
+
+    virtual void setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const;
+    virtual JSValue *valueAt(ExecState *exec, unsigned int index) const;
+    virtual unsigned int getLength() const {return m_length;}
+
+private:
+    mutable QList<T> m_list; // setValueAt is const!
+    unsigned int m_length;
+    QMetaType::Type m_type;
+};
+
+// Based on RuntimeMethod
+
+// Extra data classes (to avoid the CELL_SIZE limit on JS objects)
+
+class QtRuntimeMethodData {
+    public:
+        virtual ~QtRuntimeMethodData();
+        RefPtr<QtInstance> m_instance;
+};
+
+class QtRuntimeConnectionMethod;
+class QtRuntimeMetaMethodData : public QtRuntimeMethodData {
+    public:
+        ~QtRuntimeMetaMethodData();
+        QByteArray m_signature;
+        int m_index;
+        QtRuntimeConnectionMethod *m_connect;
+        QtRuntimeConnectionMethod *m_disconnect;
+};
+
+class QtRuntimeConnectionMethodData : public QtRuntimeMethodData {
+    public:
+        ~QtRuntimeConnectionMethodData();
+        QByteArray m_signature;
+        int m_index;
+        bool m_isConnect;
+};
+
+// Common base class (doesn't really do anything interesting)
+class QtRuntimeMethod : public InternalFunctionImp
+{
+public:
+    virtual ~QtRuntimeMethod();
+
+    virtual CodeType codeType() const;
+    virtual Completion execute(ExecState *exec);
+
+protected:
+    QtRuntimeMethodData *d_func() const {return d_ptr;}
+    QtRuntimeMethod(QtRuntimeMethodData *dd, ExecState *exec, const Identifier &n, PassRefPtr<QtInstance> inst);
+    QtRuntimeMethodData *d_ptr;
+};
+
+class QtRuntimeMetaMethod : public QtRuntimeMethod
+{
+public:
+    QtRuntimeMetaMethod(ExecState *exec, const Identifier &n, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature );
+
+    virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
+    virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args);
+
+    virtual void mark();
+
+protected:
+    QtRuntimeMetaMethodData* d_func() const {return reinterpret_cast<QtRuntimeMetaMethodData*>(d_ptr);}
+
+private:
+    static JSValue *lengthGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
+    static JSValue *connectGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
+    static JSValue *disconnectGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
+};
+
+class QtConnectionObject;
+class QtRuntimeConnectionMethod : public QtRuntimeMethod
+{
+public:
+    QtRuntimeConnectionMethod(ExecState *exec, const Identifier &n, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature );
+
+    virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
+    virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args);
+
+protected:
+    QtRuntimeConnectionMethodData* d_func() const {return reinterpret_cast<QtRuntimeConnectionMethodData*>(d_ptr);}
+
+private:
+    static JSValue *lengthGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
+    static QMultiMap<QObject *, QtConnectionObject *> connections;
+    friend class QtConnectionObject;
+};
+
+class QtConnectionObject: public QObject
+{
+public:
+    QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject);
+    ~QtConnectionObject();
+
+    static const QMetaObject staticMetaObject;
+    virtual const QMetaObject *metaObject() const;
+    virtual void *qt_metacast(const char *);
+    virtual int qt_metacall(QMetaObject::Call, int, void **argv);
+
+    bool match(QObject *sender, int signalIndex, JSObject* thisObject, JSObject *funcObject);
+
+    // actual slot:
+    void execute(void **argv);
+
+private:
+    RefPtr<QtInstance> m_instance;
+    int m_signalIndex;
+    QObject* m_originalObject; // only used as a key, not dereferenced
+    ProtectedPtr<JSObject> m_thisObject;
+    ProtectedPtr<JSObject> m_funcObject;
 };
 
 } // namespace Bindings
index 26d9e7687c105bdf94a7c896eee8ec289285460f..12db05d7c5a9b5924a6f129254f977987d018ae2 100644 (file)
@@ -83,7 +83,7 @@ void Instance::setValueOfField(ExecState *exec, const Field *aField, JSValue *aV
 Instance* Instance::createBindingForLanguageInstance(BindingLanguage language, void* nativeInstance, PassRefPtr<RootObject> rootObject)
 {
     Instance *newInstance = 0;
-    
+
     switch (language) {
 #if HAVE(JNI)
         case Instance::JavaLanguage: {
@@ -129,7 +129,13 @@ JSObject* Instance::createRuntimeObject(Instance* instance)
     if (instance->getBindingLanguage() == QtLanguage)
         return QtInstance::getRuntimeObject(static_cast<QtInstance*>(instance));
 #endif
+    return reallyCreateRuntimeObject(instance);
+}
+
+JSObject* Instance::reallyCreateRuntimeObject(Instance* instance)
+{
     JSLock lock;
+
     return new RuntimeObjectImp(instance);
 }
 
@@ -147,8 +153,8 @@ Instance* Instance::getInstance(JSObject* object, BindingLanguage language)
     return instance;
 }
 
-RootObject* Instance::rootObject() const
-{
+RootObject* Instance::rootObject() const 
+{ 
     return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0;
 }
 
index dbb324c64e44ceaca5f848fbd1cf0a95f9238021..c024ba876f4c6a36ae8a0e0f2b5f8fefc4fb19f7 100644 (file)
@@ -144,6 +144,8 @@ public:
     virtual BindingLanguage getBindingLanguage() const = 0;
 
 protected:
+    static JSObject* reallyCreateRuntimeObject(Instance*);
+
     RefPtr<RootObject> _rootObject;
     unsigned _refCount;
 };
index b804ed14799b7c0682e11e46164e5aaa20098399..fd7c819644042214fc723eccafbeec3d15e4952a 100644 (file)
@@ -1,3 +1,26 @@
+2008-01-23  Michael Goddard <michael.goddard@trolltech.com>
+
+        Reviewed by Lars Knoll <lars@trolltech.com>.
+
+        Reworked the JavaScriptCore Qt bindings:
+        
+        * Update JS DRT controller for Qt JS binding changes.
+        There were two functions that needed some changes
+        so that the layout tests would work, so this makes
+        a few tests pass again.
+        
+        * Bump the timeout for layout tests up to 11s.
+        At least some tests have an internal timeout of
+        10 seconds, so make the waitUntilDone approach
+        wait at least 11s.  fast/dom/open-and-close-by-DOM.html
+        is one of these - now the failure message is more
+        accurate.
+        
+
+        * DumpRenderTree/qt/jsobjects.cpp:
+        (LayoutTestController::waitUntilDone):
+        * DumpRenderTree/qt/jsobjects.h:
+
 2008-01-22  Anders Carlsson  <andersca@apple.com>
 
         Reviewed by Darin and Adam.
index f25a0e8217e7ccaedb1673ca2b0faf9b88443d8f..78a93feec8b3363f30d0d0b15c79818b8b68079f 100644 (file)
@@ -78,7 +78,7 @@ void LayoutTestController::waitUntilDone()
 {
     //qDebug() << ">>>>waitForDone";
     m_waitForDone = true;
-    m_timeoutTimer = startTimer(5000);
+    m_timeoutTimer = startTimer(11000);
 }
 
 void LayoutTestController::notifyDone()
index 0f095659c32071402a0ed26d00f0bb39f031fcca..511e857ad8624cc260978ec5762cd16ecb026e78 100644 (file)
@@ -31,6 +31,7 @@
 #include <qobject.h>
 #include <qdebug.h>
 #include <qpoint.h>
+#include <qstringlist.h>
 
 class QWebFrame;
 namespace WebCore {
@@ -69,7 +70,7 @@ public slots:
     void dumpEditingCallbacks();
     void queueReload();
     void provisionalLoad();
-    void setCloseRemainingWindowsWhenComplete(bool) {}
+    void setCloseRemainingWindowsWhenComplete(bool=false) {}
     int windowCount();
     void display() {}
     void clearBackForwardList();
@@ -104,7 +105,7 @@ public slots:
     void mouseUp();
     void mouseMoveTo(int x, int y);
     void leapForward(int ms);
-    void keyDown(const QString &string, const QStringList &modifiers);
+    void keyDown(const QString &string, const QStringList &modifiers=QStringList());
     void clearKillRing() {}
 
 private: