[Qt] Port meta method/signal/slot handling in run-time bridge to use JSC C API
authorhausmann@webkit.org <hausmann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Aug 2012 17:53:33 +0000 (17:53 +0000)
committerhausmann@webkit.org <hausmann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Aug 2012 17:53:33 +0000 (17:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=93476

Reviewed by Kenneth Rohde Christiansen.

Source/WebCore:

Based on patch by No'am Rosenthal and lots of good suggestions by Caio Marcelo.

Ported the code that mapped invokable methods (and signals/slots) as
well as the code that provides the connect() and disconnect() functions
over to use the JSC C API. In the process one behavioural change was
implemented: Previously meta methods were actually function objects
that through Function.prototype allowed calling via
object.method.call(object). Through the use of plain JS objects that is
not possible anymore.

If we tried to continue to use function objects (JSObjectMakeFunction)
then we would loose the ability to store the private pointer. An
alternative approach would be to use a regular object and install the
Function prototype (Function.prototype), but unfortunately we cannot do
that without loosing the common prototype for signals and slots.

* bridge/qt/qt_class.cpp:
(JSC::Bindings::QtClass::fallbackObject):
* bridge/qt/qt_instance.cpp:
(JSC::Bindings::QtInstance::~QtInstance):
(JSC::Bindings::QtInstance::newRuntimeObject):
* bridge/qt/qt_instance.h:
(Bindings):
(QtInstance):
* bridge/qt/qt_runtime.cpp:
(JSC::Bindings::prototypeForSignalsAndSlots):
(JSC::Bindings::QtRuntimeMethod::QtRuntimeMethod):
(JSC::Bindings::QtRuntimeMethod::~QtRuntimeMethod):
(JSC::Bindings::QtRuntimeMethod::call):
(JSC::Bindings::QtRuntimeMethod::connect):
(JSC::Bindings::QtRuntimeMethod::disconnect):
(JSC::Bindings::QtRuntimeMethod::jsObjectRef):
(JSC::Bindings::QtRuntimeMethod::connectOrDisconnect):
(Bindings):
(JSC::Bindings::QtConnectionObject::~QtConnectionObject):
* bridge/qt/qt_runtime.h:
(JSC::Bindings::QtRuntimeMethod::name):
(QtRuntimeMethod):
(QtConnectionObject):

Source/WebKit/qt:

Changed semantics of some test expectations. Similarly to r125032 when generating
error exceptions for connect/disconnect, we cannot generate explicit type error
exceptions but only generic errors. Another change is that the meta-method wrapper
doesn't support the call() through Function.prototype anymore. See WebCore changelog
for details.

* tests/qobjectbridge/tst_qobjectbridge.cpp:
(tst_QObjectBridge::connectAndDisconnect):
(tst_QObjectBridge::objectDeleted):
(tst_QObjectBridge::introspectQtMethods):

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

Source/WebCore/ChangeLog
Source/WebCore/bridge/qt/qt_class.cpp
Source/WebCore/bridge/qt/qt_instance.cpp
Source/WebCore/bridge/qt/qt_instance.h
Source/WebCore/bridge/qt/qt_runtime.cpp
Source/WebCore/bridge/qt/qt_runtime.h
Source/WebKit/qt/ChangeLog
Source/WebKit/qt/tests/qobjectbridge/tst_qobjectbridge.cpp

index 8cf549a..740264c 100644 (file)
@@ -1,3 +1,50 @@
+2012-08-13  Simon Hausmann  <simon.hausmann@nokia.com>
+
+        [Qt] Port meta method/signal/slot handling in run-time bridge to use JSC C API
+        https://bugs.webkit.org/show_bug.cgi?id=93476
+
+        Reviewed by Kenneth Rohde Christiansen.
+
+        Based on patch by No'am Rosenthal and lots of good suggestions by Caio Marcelo.
+
+        Ported the code that mapped invokable methods (and signals/slots) as
+        well as the code that provides the connect() and disconnect() functions
+        over to use the JSC C API. In the process one behavioural change was
+        implemented: Previously meta methods were actually function objects
+        that through Function.prototype allowed calling via
+        object.method.call(object). Through the use of plain JS objects that is
+        not possible anymore.
+
+        If we tried to continue to use function objects (JSObjectMakeFunction)
+        then we would loose the ability to store the private pointer. An
+        alternative approach would be to use a regular object and install the
+        Function prototype (Function.prototype), but unfortunately we cannot do
+        that without loosing the common prototype for signals and slots.
+
+        * bridge/qt/qt_class.cpp:
+        (JSC::Bindings::QtClass::fallbackObject):
+        * bridge/qt/qt_instance.cpp:
+        (JSC::Bindings::QtInstance::~QtInstance):
+        (JSC::Bindings::QtInstance::newRuntimeObject):
+        * bridge/qt/qt_instance.h:
+        (Bindings):
+        (QtInstance):
+        * bridge/qt/qt_runtime.cpp:
+        (JSC::Bindings::prototypeForSignalsAndSlots):
+        (JSC::Bindings::QtRuntimeMethod::QtRuntimeMethod):
+        (JSC::Bindings::QtRuntimeMethod::~QtRuntimeMethod):
+        (JSC::Bindings::QtRuntimeMethod::call):
+        (JSC::Bindings::QtRuntimeMethod::connect):
+        (JSC::Bindings::QtRuntimeMethod::disconnect):
+        (JSC::Bindings::QtRuntimeMethod::jsObjectRef):
+        (JSC::Bindings::QtRuntimeMethod::connectOrDisconnect):
+        (Bindings):
+        (JSC::Bindings::QtConnectionObject::~QtConnectionObject):
+        * bridge/qt/qt_runtime.h:
+        (JSC::Bindings::QtRuntimeMethod::name):
+        (QtRuntimeMethod):
+        (QtConnectionObject):
+
 2012-08-13  Leandro Gracia Gil  <leandrogracia@chromium.org>
 
         [Chromium] Fix nits in the find-in-page match rects API
index ee22a68..6248ee3 100644 (file)
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "qt_class.h"
 
+#include "APICast.h"
 #include "Identifier.h"
 #include "qt_instance.h"
 #include "qt_runtime.h"
@@ -69,44 +70,48 @@ const char* QtClass::name() const
 JSValue QtClass::fallbackObject(ExecState* exec, Instance* inst, PropertyName identifier)
 {
     QtInstance* qtinst = static_cast<QtInstance*>(inst);
+    JSContextRef context = toRef(exec);
+    JSValueRef* exception = 0;
 
     UString ustring(identifier.publicName());
     const QByteArray name = QString(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length()).toLatin1();
 
     // First see if we have a cache hit
-    JSObject* val = qtinst->m_methods.value(name).get();
-    if (val)
-        return val;
+    if (QtRuntimeMethod* method = qtinst->m_methods.value(name))
+        return toJS(method->jsObjectRef(context, exception));
 
     // Nope, create an entry
     const QByteArray normal = QMetaObject::normalizedSignature(name.constData());
 
     // See if there is an exact match
     int index = -1;
+    QMetaMethod metaMethod;
     if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) {
-        QMetaMethod m = m_metaObject->method(index);
-        if (m.access() != QMetaMethod::Private) {
-            QtRuntimeMetaMethod* val = QtRuntimeMetaMethod::create(exec, ustring, static_cast<QtInstance*>(inst), index, normal, false);
-            qtinst->m_methods.insert(name, val);
-            return val;
-        }
+        metaMethod = m_metaObject->method(index);
+        if (metaMethod.access() == QMetaMethod::Private)
+            index = -1;
     }
 
     // Nope.. try a basename match
-    const 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;
-
-        if (normal == m.name()) {
-            QtRuntimeMetaMethod* val = QtRuntimeMetaMethod::create(exec, ustring, static_cast<QtInstance*>(inst), index, normal, false);
-            qtinst->m_methods.insert(name, val);
-            return val;
+    if (index == -1) {
+        const int count = m_metaObject->methodCount();
+        for (index = count - 1; index >= 0; --index) {
+            metaMethod = m_metaObject->method(index);
+            if (metaMethod.access() == QMetaMethod::Private)
+                continue;
+
+            if (metaMethod.name() == normal)
+                break;
         }
     }
 
-    return jsUndefined();
+    if (index == -1)
+        return jsUndefined();
+
+    int flags = metaMethod.methodType() == QMetaMethod::Signal ? QtRuntimeMethod::MethodIsSignal : 0;
+    QtRuntimeMethod* method = new QtRuntimeMethod(context, exception, static_cast<QtInstance*>(inst)->getObject(), normal, index, flags, qtinst);
+    qtinst->m_methods.insert(name, method);
+    return toJS(method->jsObjectRef(context, exception));
 }
 
 // This functionality is handled by the fallback case above...
index 7852512..513d711 100644 (file)
@@ -94,7 +94,7 @@ QtInstance::~QtInstance()
 
     cachedInstances.remove(m_hashkey);
 
-    // clean up (unprotect from gc) the JSValues we've created
+    qDeleteAll(m_methods);
     m_methods.clear();
 
     qDeleteAll(m_fields);
@@ -147,16 +147,6 @@ void QtInstance::put(JSObject* object, ExecState* exec, PropertyName propertyNam
     JSObject::put(object, exec, propertyName, value, slot);
 }
 
-void QtInstance::removeUnusedMethods()
-{
-    for (QHash<QByteArray, QtWeakObjectReference>::Iterator it = m_methods.begin(), end = m_methods.end(); it != end; ) {
-        if (!it.value().get())
-            it = m_methods.erase(it);
-        else
-            ++it;
-    }
-}
-
 QtInstance* QtInstance::getInstance(JSObject* object)
 {
     if (!object)
@@ -179,6 +169,7 @@ Class* QtInstance::getClass() const
 RuntimeObject* QtInstance::newRuntimeObject(ExecState* exec)
 {
     JSLockHolder lock(exec);
+    qDeleteAll(m_methods);
     m_methods.clear();
     return QtRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
 }
index cc42aed..cdd734a 100644 (file)
@@ -33,7 +33,7 @@ namespace Bindings {
 
 class QtClass;
 class QtField;
-class QtRuntimeMetaMethod;
+class QtRuntimeMethod;
 
 class QtInstance : public Instance {
 public:
@@ -71,44 +71,9 @@ public:
     virtual bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
     virtual void put(JSObject*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
 
-    void removeUnusedMethods();
-
     static QtInstance* getInstance(JSObject*);
 
 private:
-
-    class QtWeakObjectReference {
-    public:
-        QtWeakObjectReference(JSObject* reference)
-            : m_reference(reference)
-        {
-        }
-
-        QtWeakObjectReference(const QtWeakObjectReference& source)
-            : m_reference(source.m_reference.get())
-        {
-        }
-
-        QtWeakObjectReference()
-            : m_reference()
-        {
-        }
-
-        QtWeakObjectReference& operator=(const QtWeakObjectReference& source)
-        {
-            m_reference = PassWeak<JSObject>(source.m_reference.get());
-            return *this;
-        }
-
-        JSObject* get() const
-        {
-            return m_reference.get();
-        }
-
-    private:
-        Weak<JSObject> m_reference;
-    };
-
     static PassRefPtr<QtInstance> create(QObject *instance, PassRefPtr<RootObject> rootObject, ValueOwnership ownership)
     {
         return adoptRef(new QtInstance(instance, rootObject, ownership));
@@ -120,7 +85,7 @@ private:
     mutable QtClass* m_class;
     QPointer<QObject> m_object;
     QObject* m_hashkey;
-    mutable QHash<QByteArray, QtWeakObjectReference> m_methods;
+    mutable QHash<QByteArray, QtRuntimeMethod*> m_methods;
     mutable QHash<QString, QtField*> m_fields;
     ValueOwnership m_ownership;
 };
index 951a759..caa920c 100644 (file)
@@ -913,61 +913,6 @@ JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, con
     return jsString(exec, ustring);
 }
 
-// ===============
-
-// Qt-like macros
-#define QW_D(Class) Class##Data* d = d_func()
-#define QW_DS(Class,Instance) Class##Data* d = Instance->d_func()
-
-const ClassInfo QtRuntimeMethod::s_info = { "QtRuntimeMethod", &InternalFunction::s_info, 0, 0, CREATE_METHOD_TABLE(QtRuntimeMethod) };
-
-QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState* exec, Structure* structure, const UString& identifier)
-    : InternalFunction(exec->lexicalGlobalObject(), structure)
-    , d_ptr(dd)
-{
-}
-
-void QtRuntimeMethod::finishCreation(ExecState* exec, const UString& identifier, PassRefPtr<QtInstance> instance)
-{
-    Base::finishCreation(exec->globalData(), identifier);
-    QW_D(QtRuntimeMethod);
-    d->m_instance = instance;
-    d->m_finalizer = PassWeak<QtRuntimeMethod>(this, d);
-}
-
-QtRuntimeMethod::~QtRuntimeMethod()
-{
-    delete d_ptr;
-}
-
-void QtRuntimeMethod::destroy(JSCell* cell)
-{
-    static_cast<QtRuntimeMethod*>(cell)->QtRuntimeMethod::~QtRuntimeMethod();
-}
-
-// ===============
-
-QtRuntimeMethodData::~QtRuntimeMethodData()
-{
-}
-
-void QtRuntimeMethodData::finalize(Handle<Unknown>, void*)
-{
-    m_instance->removeUnusedMethods();
-}
-
-QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData()
-{
-
-}
-
-QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData()
-{
-
-}
-
-// ===============
-
 // Type conversion metadata (from QtScript originally)
 class QtMethodMatchType
 {
@@ -1335,364 +1280,248 @@ static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray
     return index;
 }
 
-const ClassInfo QtRuntimeMetaMethod::s_info = { "QtRuntimeMethod", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(QtRuntimeMetaMethod) };
-
-QtRuntimeMetaMethod::QtRuntimeMetaMethod(ExecState* exec, Structure* structure, const UString& identifier)
-    : QtRuntimeMethod (new QtRuntimeMetaMethodData(), exec, structure, identifier)
+static JSClassRef prototypeForSignalsAndSlots()
 {
+    static JSClassDefinition classDef = {
+        0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, QtRuntimeMethod::call, 0, 0, 0
+    };
+    static JSClassRef cls = JSClassCreate(&classDef);
+    return cls;
 }
 
-void QtRuntimeMetaMethod::finishCreation(ExecState* exec, const UString& identifier, PassRefPtr<QtInstance> instance, int index, const QByteArray& signature, bool allowPrivate)
+QtRuntimeMethod::QtRuntimeMethod(JSContextRef ctx, JSValueRef* exception, QObject* object, const QByteArray& identifier, int index, int flags, QtInstance* instance)
+    : m_object(object)
+    , m_identifier(identifier)
+    , m_index(index)
+    , m_flags(flags)
+    , m_instance(instance)
 {
-    Base::finishCreation(exec, identifier, instance);
-    QW_D(QtRuntimeMetaMethod);
-    d->m_signature = signature;
-    d->m_index = index;
-    d->m_allowPrivate = allowPrivate;
 }
 
-void QtRuntimeMetaMethod::visitChildren(JSCell* cell, SlotVisitor& visitor)
+QtRuntimeMethod::~QtRuntimeMethod()
 {
-    QtRuntimeMetaMethod* thisObject = jsCast<QtRuntimeMetaMethod*>(cell);
-    QtRuntimeMethod::visitChildren(thisObject, visitor);
-    QtRuntimeMetaMethodData* d = thisObject->d_func();
-    if (d->m_connect)
-        visitor.append(&d->m_connect);
-    if (d->m_disconnect)
-        visitor.append(&d->m_disconnect);
+    if (m_jsObject)
+        JSObjectSetPrivate(toRef(m_jsObject.get()), 0);
 }
 
-EncodedJSValue QtRuntimeMetaMethod::call(ExecState* exec)
+JSValueRef QtRuntimeMethod::call(JSContextRef context, JSObjectRef function, JSObjectRef /*thisObject*/, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
-    QtRuntimeMetaMethodData* d = static_cast<QtRuntimeMetaMethod *>(exec->callee())->d_func();
-
-    // We're limited to 10 args
-    if (exec->argumentCount() > 10)
-        return JSValue::encode(jsUndefined());
-
-    // We have to pick a method that matches..
-    JSLockHolder lock(exec);
-
-    QObject *obj = d->m_instance->getObject();
-    if (obj) {
-        const int argumentCount = static_cast<int>(exec->argumentCount());
-        Vector<JSValueRef, 10> args(argumentCount);
-        for (int i = 0; i < argumentCount; ++i)
-            args[i] = toRef(exec, exec->argument(i));
-
-        QVarLengthArray<QVariant, 10> vargs;
-        void *qargs[11];
-
-        int methodIndex;
-        JSValueRef exception = 0;
-        if ((methodIndex = findMethodIndex(toRef(exec), obj->metaObject(), d->m_signature, argumentCount, args.data(), d->m_allowPrivate, vargs, (void **)qargs, &exception)) != -1) {
-            if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0)
-                return JSValue::encode(jsUndefined());
-
-            if (vargs.size() > 0 && vargs[0].isValid())
-                return JSValue::encode(convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0]));
-        }
-
-        if (exception)
-            return throwVMError(exec, toJS(exec, exception));
-    } else {
-        return throwVMError(exec, createError(exec, "cannot call function of deleted QObject"));
+    QtRuntimeMethod* d = reinterpret_cast<QtRuntimeMethod*>(JSObjectGetPrivate(function));
+    if (!d) {
+        setException(context, exception, QStringLiteral("cannot call function of deleted runtime method"));
+        return JSValueMakeUndefined(context);
     }
+    QObject* obj = d->m_object;
 
-    // void functions return undefined
-    return JSValue::encode(jsUndefined());
-}
-
-CallType QtRuntimeMetaMethod::getCallData(JSCell*, CallData& callData)
-{
-    callData.native.function = call;
-    return CallTypeHost;
-}
-
-bool QtRuntimeMetaMethod::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
-{
-    QtRuntimeMetaMethod* thisObject = jsCast<QtRuntimeMetaMethod*>(cell);
-    if (propertyName == Identifier(exec, "connect")) {
-        slot.setCustom(thisObject, thisObject->connectGetter);
-        return true;
-    }
-    if (propertyName == Identifier(exec, "disconnect")) {
-        slot.setCustom(thisObject, thisObject->disconnectGetter);
-        return true;
-    }
-    if (propertyName == exec->propertyNames().length) {
-        slot.setCustom(thisObject, thisObject->lengthGetter);
-        return true;
+    if (!obj) {
+        setException(context, exception, QStringLiteral("cannot call function of deleted QObject"));
+        return JSValueMakeUndefined(context);
     }
 
-    return QtRuntimeMethod::getOwnPropertySlot(thisObject, exec, propertyName, slot);
-}
+    // Allow for maximum of 10 arguments and size stack arrays accordingly.
+    if (argumentCount > 10)
+        return JSValueMakeUndefined(context);
 
-bool QtRuntimeMetaMethod::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
-{
-    QtRuntimeMetaMethod* thisObject = jsCast<QtRuntimeMetaMethod*>(object);
-    if (propertyName == Identifier(exec, "connect")) {
-        PropertySlot slot;
-        slot.setCustom(thisObject, connectGetter);
-        descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
-        return true;
-    }
+    QVarLengthArray<QVariant, 10> vargs;
+    void* qargs[11];
 
-    if (propertyName == Identifier(exec, "disconnect")) {
-        PropertySlot slot;
-        slot.setCustom(thisObject, disconnectGetter);
-        descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
-        return true;
-    }
-
-    if (propertyName == exec->propertyNames().length) {
-        PropertySlot slot;
-        slot.setCustom(thisObject, lengthGetter);
-        descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
-        return true;
-    }
+    int methodIndex = findMethodIndex(context, obj->metaObject(), d->m_identifier,  argumentCount, arguments,
+                                      (d->m_flags & AllowPrivate), vargs, (void **)qargs, exception);
 
-    return QtRuntimeMethod::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
-}
+    if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0)
+        return JSValueMakeUndefined(context);
 
-void QtRuntimeMetaMethod::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
-{
-    if (mode == IncludeDontEnumProperties) {
-        propertyNames.add(Identifier(exec, "connect"));
-        propertyNames.add(Identifier(exec, "disconnect"));
-        propertyNames.add(exec->propertyNames().length);
-    }
+    if (vargs.size() > 0 && vargs[0].isValid())
+        return toRef(toJS(context), convertQVariantToValue(toJS(context), d->m_instance->rootObject(), vargs[0]));
 
-    QtRuntimeMethod::getOwnPropertyNames(object, exec, propertyNames, mode);
+    return JSValueMakeUndefined(context);
 }
 
-JSValue QtRuntimeMetaMethod::lengthGetter(ExecState*, JSValue, PropertyName)
+JSValueRef QtRuntimeMethod::connect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
-    // QtScript always returns 0
-    return jsNumber(0);
+    return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, true);
 }
 
-JSValue QtRuntimeMetaMethod::connectGetter(ExecState* exec, JSValue slotBase, PropertyName ident)
+JSValueRef QtRuntimeMethod::disconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
-    QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slotBase));
-    QW_DS(QtRuntimeMetaMethod, thisObj);
-
-    if (!d->m_connect)
-        d->m_connect.set(exec->globalData(), thisObj, QtRuntimeConnectionMethod::create(exec, ident.publicName(), true, d->m_instance, d->m_index, d->m_signature));
-    return d->m_connect.get();
+    return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, false);
 }
 
-JSValue QtRuntimeMetaMethod::disconnectGetter(ExecState* exec, JSValue slotBase, PropertyName ident)
+JSObjectRef QtRuntimeMethod::jsObjectRef(JSContextRef context, JSValueRef* exception)
 {
-    QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slotBase));
-    QW_DS(QtRuntimeMetaMethod, thisObj);
-
-    if (!d->m_disconnect)
-        d->m_disconnect.set(exec->globalData(), thisObj, QtRuntimeConnectionMethod::create(exec, ident.publicName(), false, d->m_instance, d->m_index, d->m_signature));
-    return d->m_disconnect.get();
-}
-
-// ===============
+    if (m_jsObject)
+        return toRef(m_jsObject.get());
 
-QMultiMap<QObject*, QtConnectionObject*> QtRuntimeConnectionMethod::connections;
+    static const JSClassDefinition classDefForConnect = {
+        0, 0, "connect", 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, connect, 0, 0, 0
+    };
 
-const ClassInfo QtRuntimeConnectionMethod::s_info = { "QtRuntimeMethod", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(QtRuntimeConnectionMethod) };
+    static const JSClassDefinition classDefForDisconnect = {
+        0, 0, "disconnect", 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, disconnect, 0, 0, 0
+    };
 
-QtRuntimeConnectionMethod::QtRuntimeConnectionMethod(ExecState* exec, Structure* structure, const UString& identifier)
-    : QtRuntimeMethod (new QtRuntimeConnectionMethodData(), exec, structure, identifier)
-{
+    static JSClassRef classRefConnect = JSClassCreate(&classDefForConnect);
+    static JSClassRef classRefDisconnect = JSClassCreate(&classDefForDisconnect);
+    bool isSignal = m_flags & MethodIsSignal;
+    JSObjectRef object = JSObjectMake(context, prototypeForSignalsAndSlots(), this);
+    JSObjectRef connectFunction = JSObjectMake(context, classRefConnect, this);
+    JSObjectRef disconnectFunction = JSObjectMake(context, classRefDisconnect, this);
+    JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete;
+
+    static JSStringRef connectStr = JSStringCreateWithUTF8CString("connect");
+    static JSStringRef disconnectStr = JSStringCreateWithUTF8CString("disconnect");
+    static JSStringRef lengthStr = JSStringCreateWithUTF8CString("length");
+    static JSStringRef nameStr = JSStringCreateWithUTF8CString("name");
+    JSRetainPtr<JSStringRef> actualNameStr(Adopt, JSStringCreateWithUTF8CString(m_identifier.constData()));
+
+    JSObjectSetProperty(context, connectFunction, lengthStr, JSValueMakeNumber(context, isSignal ? 1 : 0), attributes, exception);
+    JSObjectSetProperty(context, connectFunction, nameStr, JSValueMakeString(context, connectStr), attributes, exception);
+    JSObjectSetProperty(context, disconnectFunction, lengthStr, JSValueMakeNumber(context, isSignal ? 1 : 0), attributes, exception);
+    JSObjectSetProperty(context, disconnectFunction, nameStr, JSValueMakeString(context, disconnectStr), attributes, exception);
+
+    JSObjectSetProperty(context, object, connectStr, connectFunction, attributes, exception);
+    JSObjectSetProperty(context, object, disconnectStr, disconnectFunction, attributes, exception);
+    JSObjectSetProperty(context, object, lengthStr, JSValueMakeNumber(context, 0), attributes, exception);
+    JSObjectSetProperty(context, object, nameStr, JSValueMakeString(context, actualNameStr.get()), attributes, exception);
+
+    m_jsObject = PassWeak<JSObject>(toJS(object));
+    return object;
 }
 
-void QtRuntimeConnectionMethod::finishCreation(ExecState* exec, const UString& identifier, bool isConnect, PassRefPtr<QtInstance> instance, int index, const QByteArray& signature)
+JSValueRef QtRuntimeMethod::connectOrDisconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception, bool connect)
 {
-    Base::finishCreation(exec, identifier, instance);
-    QW_D(QtRuntimeConnectionMethod);
-
-    d->m_signature = signature;
-    d->m_index = index;
-    d->m_isConnect = isConnect;
-}
+    QtRuntimeMethod* d = static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(thisObject));
+    if (!d)
+        d = static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(function));
 
-EncodedJSValue QtRuntimeConnectionMethod::call(ExecState* exec)
-{
-    QtRuntimeConnectionMethodData* d = static_cast<QtRuntimeConnectionMethod *>(exec->callee())->d_func();
+    QString functionName = connect ? QStringLiteral("connect") : QStringLiteral("disconnect");
 
-    JSLockHolder lock(exec);
+    if (!argumentCount) {
+        QString errorStr = QStringLiteral("QtMetaMethod.%1: no arguments given").arg(connect ?  QStringLiteral("connect") : QStringLiteral("disconnect"));
+        setException(context, exception, errorStr);
+        return JSValueMakeUndefined(context);
+    }
 
-    QObject* sender = d->m_instance->getObject();
+    if ((!(d->m_flags & QtRuntimeMethod::MethodIsSignal))) {
+        setException(context, exception, QStringLiteral("QtMetaMethod.%3: %1::%2() is not a signal").arg(QString::fromUtf8(d->m_object.data()->metaObject()->className())).arg(QString::fromAscii(d->m_identifier)).arg(functionName));
+        return JSValueMakeUndefined(context);
+    }
 
-    if (sender) {
+    QObject* sender = d->m_object.data();
 
-        JSObject* thisObject = exec->lexicalGlobalObject()->methodTable()->toThisObject(exec->lexicalGlobalObject(), exec);
-        JSObject* funcObject = 0;
+    if (!sender) {
+        setException(context, exception, QStringLiteral("cannot call function of deleted QObject"));
+        return JSValueMakeUndefined(context);
+    }
 
-        // QtScript checks signalness first, arguments second
-        int signalIndex = -1;
+    int signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_identifier);
 
-        // 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);
+    JSObjectRef targetObject = 0;
+    JSObjectRef targetFunction = 0;
 
-        if (signalIndex != -1) {
-            if (exec->argumentCount() == 1) {
-                funcObject = exec->argument(0).toObject(exec);
-                CallData callData;
-                if (funcObject->methodTable()->getCallData(funcObject, callData) == CallTypeNone) {
-                    if (d->m_isConnect)
-                        return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function"));
-                    else
-                        return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function"));
-                }
-            } else if (exec->argumentCount() >= 2) {
-                if (exec->argument(0).isObject()) {
-                    thisObject = exec->argument(0).toObject(exec);
-
-                    // Get the actual function to call
-                    JSObject *asObj = exec->argument(1).toObject(exec);
-                    CallData callData;
-                    if (asObj->methodTable()->getCallData(asObj, callData) != CallTypeNone) {
-                        // Function version
-                        funcObject = asObj;
-                    } else {
-                        // Convert it to a string
-                        UString funcName = exec->argument(1).toString(exec)->value(exec);
-                        Identifier funcIdent(exec, funcName);
-
-                        // ### DropAllLocks
-                        // This is resolved at this point in QtScript
-                        JSValue val = thisObject->get(exec, funcIdent);
-                        JSObject* asFuncObj = val.toObject(exec);
-
-                        if (asFuncObj->methodTable()->getCallData(asFuncObj, callData) != CallTypeNone) {
-                            funcObject = asFuncObj;
-                        } else {
-                            if (d->m_isConnect)
-                                return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function"));
-                            else
-                                return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function"));
-                        }
-                    }
-                } else {
-                    if (d->m_isConnect)
-                        return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: thisObject is not an object"));
-                    else
-                        return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: thisObject is not an object"));
-                }
-            } else {
-                if (d->m_isConnect)
-                    return throwVMError(exec, createError(exec, "QtMetaMethod.connect: no arguments given"));
-                else
-                    return throwVMError(exec, createError(exec, "QtMetaMethod.disconnect: no arguments given"));
+    if (argumentCount == 1) {
+        if (!JSValueIsObject(context, arguments[0])) {
+            setException(context, exception, QStringLiteral("QtMetaMethod.%1: target is not a function").arg(functionName));
+            return JSValueMakeUndefined(context);
+        }
+        targetFunction = JSValueToObject(context, arguments[0], exception);
+
+        // object.signal.connect(someFunction);
+        if (JSObjectIsFunction(context, targetFunction)) {
+            if (JSValueIsObjectOfClass(context, targetFunction, prototypeForSignalsAndSlots())) {
+                // object.signal.connect(otherObject.slot);
+                if (QtRuntimeMethod* targetMethod = static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(targetFunction)))
+                    targetObject = toRef(QtInstance::getQtInstance(targetMethod->m_object.data(), d->m_instance->rootObject(), QtInstance::QtOwnership)->createRuntimeObject(toJS(context)));
             }
-
-            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 = QtConnectionObject::createWithInternalJSC(exec, d->m_instance, signalIndex, thisObject, funcObject);
-                bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
-                if (!ok) {
-                    delete conn;
-                    QString msg = QString(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()"))
-                            .arg(QLatin1String(sender->metaObject()->className()))
-                            .arg(QLatin1String(d->m_signature));
-                    return throwVMError(exec, createError(exec, 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;
-
-                JSContextRef context = ::toRef(exec);
-                JSObjectRef receiver = ::toRef(thisObject);
-                JSObjectRef receiverFunction = ::toRef(funcObject);
-
-                foreach(QtConnectionObject* conn, conns) {
-                    // Is this the right connection?
-                    if (conn->match(context, sender, signalIndex, receiver, receiverFunction)) {
-                        // 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(QLatin1String("QtMetaMethod.disconnect: failed to disconnect from %1::%2()"))
-                            .arg(QLatin1String(sender->metaObject()->className()))
-                            .arg(QLatin1String(d->m_signature));
-                    return throwVMError(exec, createError(exec, msg.toLatin1().constData()));
+        } else
+            targetFunction = 0;
+    } else {
+        // object.signal.connect(object, someFunction);
+        targetObject = JSValueToObject(context, arguments[0], exception);
+        if (JSValueIsObject(context, arguments[1])) {
+            JSObjectRef obj = JSValueToObject(context, arguments[1], exception);
+            if (JSObjectIsFunction(context, obj))
+                targetFunction = obj;
+        }
+        if (!targetFunction) {
+            // Maybe the second argument is a string
+            JSValueRef conversionException = 0;
+            JSRetainPtr<JSStringRef> functionName(Adopt, JSValueToStringCopy(context, arguments[1], &conversionException));
+            if (functionName && !conversionException) {
+                JSValueRef functionProperty = JSObjectGetProperty(context, targetObject, functionName.get(), &conversionException);
+                if (!conversionException && functionProperty && JSValueIsObject(context, functionProperty)) {
+                    targetFunction = JSValueToObject(context, functionProperty, 0);
+                    if (!JSObjectIsFunction(context, targetFunction))
+                        targetFunction = 0;
                 }
             }
-        } else {
-            QString msg = QString(QLatin1String("QtMetaMethod.%1: %2::%3() is not a signal"))
-                    .arg(QLatin1String(d->m_isConnect ? "connect": "disconnect"))
-                    .arg(QLatin1String(sender->metaObject()->className()))
-                    .arg(QLatin1String(d->m_signature));
-            return throwVMError(exec, createTypeError(exec, msg.toLatin1().constData()));
         }
-    } else {
-        return throwVMError(exec, createError(exec, "cannot call function of deleted QObject"));
     }
 
-    return JSValue::encode(jsUndefined());
-}
-
-CallType QtRuntimeConnectionMethod::getCallData(JSCell*, CallData& callData)
-{
-    callData.native.function = call;
-    return CallTypeHost;
-}
-
-bool QtRuntimeConnectionMethod::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
-{
-    QtRuntimeConnectionMethod* thisObject = jsCast<QtRuntimeConnectionMethod*>(cell);
-    if (propertyName == exec->propertyNames().length) {
-        slot.setCustom(thisObject, thisObject->lengthGetter);
-        return true;
+    // object.signal.connect(someObject);
+    if (!targetFunction) {
+        QString message = QStringLiteral("QtMetaMethod.%1: target is not a function");
+        if (connect)
+            message = message.arg(QStringLiteral("connect"));
+        else
+            message = message.arg(QStringLiteral("disconnect"));
+        setException(context, exception, message);
+        return JSValueMakeUndefined(context);
     }
 
-    return QtRuntimeMethod::getOwnPropertySlot(thisObject, exec, propertyName, slot);
-}
+    if (connect) {
+        // 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(context, QtInstance::getQtInstance(sender, d->m_instance->rootObject(), QtInstance::QtOwnership), signalIndex, targetObject, targetFunction);
+        bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
+        if (!ok) {
+            delete conn;
+            QString msg = QString(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()"))
+                    .arg(QLatin1String(sender->metaObject()->className()))
+                    .arg(QLatin1String(d->m_identifier));
+            setException(context, exception, msg);
+            return JSValueMakeUndefined(context);
+        }
 
-bool QtRuntimeConnectionMethod::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
-{
-    QtRuntimeConnectionMethod* thisObject = jsCast<QtRuntimeConnectionMethod*>(object);
-    if (propertyName == exec->propertyNames().length) {
-        PropertySlot slot;
-        slot.setCustom(thisObject, lengthGetter);
-        descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
-        return true;
+        // Store connection
+        QtConnectionObject::connections.insert(sender, conn);
+
+        return JSValueMakeUndefined(context);
     }
 
-    return QtRuntimeMethod::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
-}
+    // Now to find our previous connection object.
+    QList<QtConnectionObject*> conns = QtConnectionObject::connections.values(sender);
 
-void QtRuntimeConnectionMethod::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
-{
-    if (mode == IncludeDontEnumProperties)
-        propertyNames.add(exec->propertyNames().length);
+    foreach (QtConnectionObject* conn, conns) {
+        // Is this the right connection?
+        if (!conn->match(context, sender, signalIndex, targetObject, targetFunction))
+            continue;
 
-    QtRuntimeMethod::getOwnPropertyNames(object, exec, propertyNames, mode);
-}
+        // Yep, disconnect it
+        QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
+        delete conn; // this will also remove it from the map
+        return JSValueMakeUndefined(context);
+    }
 
-JSValue QtRuntimeConnectionMethod::lengthGetter(ExecState*, JSValue, PropertyName)
-{
-    // we have one formal argument, and one optional
-    return jsNumber(1);
+    QString msg = QStringLiteral("QtMetaMethod.disconnect: failed to disconnect from %1::%2()")
+            .arg(QLatin1String(sender->metaObject()->className()))
+            .arg(QLatin1String(d->m_identifier));
+
+    setException(context, exception, msg);
+    return JSValueMakeUndefined(context);
 }
 
 // ===============
 
+QMultiMap<QObject*, QtConnectionObject*> QtConnectionObject::connections;
+
 QtConnectionObject::QtConnectionObject(JSContextRef context, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction)
     : QObject(senderInstance->getObject())
     , m_context(JSContextGetGlobalContext(context))
@@ -1711,7 +1540,7 @@ QtConnectionObject::~QtConnectionObject()
 {
     // We can safely use m_originalSender because connection object will never outlive the sender,
     // which is its QObject parent.
-    QtRuntimeConnectionMethod::connections.remove(m_originalSender, this);
+    connections.remove(m_originalSender, this);
 
     if (m_receiver)
         JSValueUnprotect(m_context, m_receiver);
@@ -1845,11 +1674,6 @@ bool QtConnectionObject::match(JSContextRef context, QObject* sender, int signal
     return receiverMatch && JSValueIsEqual(context, receiverFunction, m_receiverFunction, ignoredException);
 }
 
-QtConnectionObject* QtConnectionObject::createWithInternalJSC(ExecState* exec, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObject* receiver, JSObject* receiverFunction)
-{
-    return new QtConnectionObject(::toRef(exec), senderInstance, signalIndex, ::toRef(receiver), ::toRef(receiverFunction));
-}
-
 // ===============
 
 template <typename T> QtArray<T>::QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject> rootObject)
index b2fd66d..763acba 100644 (file)
@@ -95,143 +95,35 @@ private:
     QMetaType::Type m_type;
 };
 
-// Based on RuntimeMethod
-
-// Extra data classes (to avoid the CELL_SIZE limit on JS objects)
-class QtRuntimeMethod;
-class QtRuntimeMethodData : public WeakHandleOwner {
-    public:
-        virtual ~QtRuntimeMethodData();
-        RefPtr<QtInstance> m_instance;
-        Weak<QtRuntimeMethod> m_finalizer;
-
-    private:
-        void finalize(Handle<Unknown>, void*);
-};
-
-class QtRuntimeConnectionMethod;
-class QtRuntimeMetaMethodData : public QtRuntimeMethodData {
-    public:
-        ~QtRuntimeMetaMethodData();
-        QByteArray m_signature;
-        bool m_allowPrivate;
-        int m_index;
-        WriteBarrier<QtRuntimeConnectionMethod> m_connect;
-        WriteBarrier<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 InternalFunction {
+class QtRuntimeMethod {
 public:
-    typedef InternalFunction Base;
+    enum MethodFlags {
+        MethodIsSignal = 1,
+        AllowPrivate = 2
+    };
 
+    QtRuntimeMethod(JSContextRef, JSValueRef* exception, QObject*, const QByteArray& identifier, int signalIndex, int flags, QtInstance*);
     ~QtRuntimeMethod();
-    static void destroy(JSCell*);
 
-    static const ClassInfo s_info;
+    static JSValueRef call(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+    static JSValueRef connect(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+    static JSValueRef disconnect(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
 
-    static FunctionPrototype* createPrototype(ExecState*, JSGlobalObject* globalObject)
-    {
-        return globalObject->functionPrototype();
-    }
-
-    static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
-    {
-        return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType,  StructureFlags), &s_info);
-    }
-
-protected:
-    void finishCreation(ExecState*, const UString&, PassRefPtr<QtInstance>);
-    static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | InternalFunction::StructureFlags | OverridesVisitChildren;
-
-    QtRuntimeMethodData *d_func() const {return d_ptr;}
-    QtRuntimeMethod(QtRuntimeMethodData *dd, ExecState *, Structure*, const UString& name);
-    QtRuntimeMethodData *d_ptr;
-};
-
-class QtRuntimeMetaMethod : public QtRuntimeMethod {
-public:
-    typedef QtRuntimeMethod Base;
+    JSObjectRef jsObjectRef(JSContextRef, JSValueRef* exception);
 
-    static QtRuntimeMetaMethod* create(ExecState* exec, const UString& name, PassRefPtr<QtInstance> instance, int index, const QByteArray& signature, bool allowPrivate)
-    {
-        Structure* domStructure = WebCore::deprecatedGetDOMStructure<QtRuntimeMetaMethod>(exec);
-        QtRuntimeMetaMethod* method = new (allocateCell<QtRuntimeMetaMethod>(*exec->heap())) QtRuntimeMetaMethod(exec, domStructure, name);
-        method->finishCreation(exec, name, instance, index, signature, allowPrivate);
-        return method;
-    }
-
-    static bool getOwnPropertySlot(JSCell*, ExecState *, PropertyName, PropertySlot&);
-    static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
-    static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
-
-    static void visitChildren(JSCell*, SlotVisitor&);
-    
-    static const ClassInfo s_info;
-
-    static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
-    {
-        return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType,  StructureFlags), &s_info);
-    }
-
-protected:
-    QtRuntimeMetaMethodData* d_func() const {return reinterpret_cast<QtRuntimeMetaMethodData*>(d_ptr);}
+    const QByteArray& name() { return m_identifier; }
 
 private:
-    QtRuntimeMetaMethod(ExecState*, Structure*, const UString&);
-    void finishCreation(ExecState*, const UString&, PassRefPtr<QtInstance>, int index, const QByteArray& signature, bool allowPrivate);
-
-    static CallType getCallData(JSCell*, CallData&);
-    static EncodedJSValue JSC_HOST_CALL call(ExecState* exec);
-    static JSValue lengthGetter(ExecState*, JSValue, PropertyName);
-    static JSValue connectGetter(ExecState*, JSValue, PropertyName);
-    static JSValue disconnectGetter(ExecState*, JSValue, PropertyName);
-};
-
-class QtConnectionObject;
-class QtRuntimeConnectionMethod : public QtRuntimeMethod {
-public:
-    typedef QtRuntimeMethod Base;
-
-    static QtRuntimeConnectionMethod* create(ExecState* exec, const UString& name, bool isConnect, PassRefPtr<QtInstance> instance, int index, const QByteArray& signature)
-    {
-        Structure* domStructure = WebCore::deprecatedGetDOMStructure<QtRuntimeConnectionMethod>(exec);
-        QtRuntimeConnectionMethod* method = new (allocateCell<QtRuntimeConnectionMethod>(*exec->heap())) QtRuntimeConnectionMethod(exec, domStructure, name);
-        method->finishCreation(exec, name, isConnect, instance, index, signature);
-        return method;
-    }
-
-    static bool getOwnPropertySlot(JSCell*, ExecState *, PropertyName, PropertySlot&);
-    static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
-    static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
-    static const ClassInfo s_info;
-
-    static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
-    {
-        return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType,  StructureFlags), &s_info);
-    }
-
-protected:
-    QtRuntimeConnectionMethodData* d_func() const {return reinterpret_cast<QtRuntimeConnectionMethodData*>(d_ptr);}
-
-private:
-    QtRuntimeConnectionMethod(ExecState*, Structure*, const UString&);
-    void finishCreation(ExecState*, const UString&, bool isConnect, PassRefPtr<QtInstance>, int index, const QByteArray& signature);
-
-    static CallType getCallData(JSCell*, CallData&);
-    static EncodedJSValue JSC_HOST_CALL call(ExecState* exec);
-    static JSValue lengthGetter(ExecState*, JSValue, PropertyName);
-    static QMultiMap<QObject *, QtConnectionObject *> connections;
-    friend class QtConnectionObject;
+    static const JSStaticFunction connectFunction;
+    static const JSStaticFunction disconnectFunction;
+
+    static JSValueRef connectOrDisconnect(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception, bool connect);
+    QPointer<QObject> m_object;
+    QByteArray m_identifier;
+    int m_index;
+    int m_flags;
+    Weak<JSObject> m_jsObject;
+    QtInstance* m_instance;
 };
 
 // A QtConnectionObject represents a connection created inside JS. It will connect its own execute() slot
@@ -247,9 +139,6 @@ public:
 
     bool match(JSContextRef, QObject* sender, int signalIndex, JSObjectRef thisObject, JSObjectRef funcObject);
 
-    // Note: for callers using JSC internals, remove once we don't need anymore.
-    static QtConnectionObject* createWithInternalJSC(ExecState*, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObject* receiver, JSObject* receiverFunction);
-
 private:
     JSGlobalContextRef m_context;
     RefPtr<QtInstance> m_senderInstance;
@@ -260,6 +149,9 @@ private:
     int m_signalIndex;
     JSObjectRef m_receiver;
     JSObjectRef m_receiverFunction;
+
+    friend class QtRuntimeMethod;
+    static QMultiMap<QObject*, QtConnectionObject*> connections;
 };
 
 
index d27a626..9b0d031 100644 (file)
@@ -1,3 +1,21 @@
+2012-08-13  Simon Hausmann  <simon.hausmann@nokia.com>
+
+        [Qt] Port meta method/signal/slot handling in run-time bridge to use JSC C API
+        https://bugs.webkit.org/show_bug.cgi?id=93476
+
+        Reviewed by Kenneth Rohde Christiansen.
+
+        Changed semantics of some test expectations. Similarly to r125032 when generating
+        error exceptions for connect/disconnect, we cannot generate explicit type error
+        exceptions but only generic errors. Another change is that the meta-method wrapper
+        doesn't support the call() through Function.prototype anymore. See WebCore changelog
+        for details.
+
+        * tests/qobjectbridge/tst_qobjectbridge.cpp:
+        (tst_QObjectBridge::connectAndDisconnect):
+        (tst_QObjectBridge::objectDeleted):
+        (tst_QObjectBridge::introspectQtMethods):
+
 2012-08-13  Kwang Yul Seo  <skyul@company100.net>
 
         [Qt] Add gprof.prf to build WebKit with gprof enabled
index 45626d9..0e64418 100644 (file)
@@ -1499,10 +1499,13 @@ void tst_QObjectBridge::connectAndDisconnect()
     QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined);
 
     // connect(obj, string)
-    QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined);
-    QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined);
-    QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined);
-    QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined);
+    {
+        QString type;
+        QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')", type), sUndefined);
+        QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')", type), sUndefined);
+        QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')", type), sUndefined);
+        QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')", type), sUndefined);
+    }
 
     // check that emitting signals from script works
 
@@ -1555,6 +1558,14 @@ void tst_QObjectBridge::connectAndDisconnect()
     QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
     QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined);
 
+    QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject, 'myOverloadedSlot(int)')"), sUndefined);
+    m_myObject->resetQtFunctionInvoked();
+    QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined);
+    QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload
+    QCOMPARE(m_myObject->qtFunctionActuals().size(), 1);
+    QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456);
+    QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject, 'myOverloadedSlot(int)')"), sUndefined);
+
     // erroneous input
     {
         // ### QtScript adds .connect to all functions, WebKit does only to signals/slots
@@ -1587,20 +1598,34 @@ void tst_QObjectBridge::connectAndDisconnect()
         QString type;
         QString ret = evalJS("myObject.myInvokable.connect(123)", type);
         QCOMPARE(type, sError);
-        QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
     }
     {
         QString type;
         QString ret = evalJS("myObject.myInvokable.connect(function() { })", type);
         QCOMPARE(type, sError);
-        QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal"));
     }
 
     {
         QString type;
         QString ret = evalJS("myObject.mySignal.connect(123)", type);
         QCOMPARE(type, sError);
-        QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function"));
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.connect: target is not a function"));
+    }
+
+    {
+        QString type;
+        QString ret = evalJS("var randomObject = new Object; myObject.mySignal.connect(myObject, randomObject)", type);
+        QCOMPARE(type, sError);
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.connect: target is not a function"));
+    }
+
+    {
+        QString type;
+        QString ret = evalJS("myObject.mySignal.connect(myObject, 'nonExistantSlot')", type);
+        QCOMPARE(type, sError);
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.connect: target is not a function"));
     }
 
     {
@@ -1616,6 +1641,13 @@ void tst_QObjectBridge::connectAndDisconnect()
         QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given"));
     }
 
+    {
+        QString type;
+        QString ret = evalJS("myObject.mySignal.disconnect(myObject, 'nonExistantSlot')", type);
+        QCOMPARE(type, sError);
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: target is not a function"));
+    }
+
     /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots
     {
         QString type;
@@ -1629,27 +1661,27 @@ void tst_QObjectBridge::connectAndDisconnect()
         QString type;
         QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type);
         QCOMPARE(type, sError);
-        QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
     }
 
     {
         QString type;
         QString ret = evalJS("myObject.myInvokable.disconnect(123)", type);
         QCOMPARE(type, sError);
-        QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
     }
     {
         QString type;
         QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type);
         QCOMPARE(type, sError);
-        QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal"));
     }
 
     {
         QString type;
         QString ret = evalJS("myObject.mySignal.disconnect(123)", type);
         QCOMPARE(type, sError);
-        QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function"));
+        QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: target is not a function"));
     }
 
     {
@@ -1847,7 +1879,7 @@ void tst_QObjectBridge::objectDeleted()
     evalJS("bar.intProperty = 123;");
     QCOMPARE(qobj->intProperty(), 123);
     qobj->resetQtFunctionInvoked();
-    evalJS("bar.myInvokable.call(bar);");
+    evalJS("bar.myInvokable(bar);");
     QCOMPARE(qobj->qtFunctionInvoked(), 0);
 
     // do this, to ensure that we cache that it implements call
@@ -2145,7 +2177,7 @@ void tst_QObjectBridge::introspectQtMethods()
         QCOMPARE(evalJS("descriptor.set"), sUndefined);
         QCOMPARE(evalJS(QString::fromLatin1("descriptor.value === %0['%1']").arg(methodLookup).arg(name)), sTrue);
         QCOMPARE(evalJS(QString::fromLatin1("descriptor.enumerable")), sFalse);
-        QCOMPARE(evalJS(QString::fromLatin1("descriptor.configurable")), sFalse);
+        QCOMPARE(evalJS(QString::fromLatin1("descriptor.configurable")), sTrue);
     }
 
     QVERIFY(evalJSV("var props=[]; for (var p in myObject.deleteLater) {props.push(p);}; props.sort()").toStringList().isEmpty());