[Qt] Port convertQVariantToValue to use the JSC C API
authorhausmann@webkit.org <hausmann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 12:26:18 +0000 (12:26 +0000)
committerhausmann@webkit.org <hausmann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 12:26:18 +0000 (12:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=93889

Reviewed by Kenneth Rohde Christiansen.

Based on patch by Noam Rosenthal.

This patch is another step towards reducing the use of internal JSC API
in the Qt bridge. Most of the conversion from QVariant to JS values is
straight-forward. The biggest behavioural change is that QVariant lists
are converted on-the-spot instead of lazily. Bug #94691 tracks fixing
that.

* bridge/qt/qt_instance.cpp:
(Bindings):
(JSC::Bindings::QtField::valueFromInstance):
* bridge/qt/qt_runtime.cpp:
(JSC::Bindings::convertQVariantToValue):
(JSC::Bindings::QtRuntimeMethod::call):
(JSC::Bindings::QtConnectionObject::execute):
(JSC::Bindings::::valueAt):
* bridge/qt/qt_runtime.h:
(Bindings):

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

Source/WebCore/ChangeLog
Source/WebCore/bridge/qt/qt_instance.cpp
Source/WebCore/bridge/qt/qt_runtime.cpp
Source/WebCore/bridge/qt/qt_runtime.h

index 41c5c94..9d4be18 100644 (file)
@@ -1,5 +1,31 @@
 2012-08-22  Simon Hausmann  <simon.hausmann@nokia.com>
 
+        [Qt] Port convertQVariantToValue to use the JSC C API
+        https://bugs.webkit.org/show_bug.cgi?id=93889
+
+        Reviewed by Kenneth Rohde Christiansen.
+
+        Based on patch by Noam Rosenthal.
+
+        This patch is another step towards reducing the use of internal JSC API
+        in the Qt bridge. Most of the conversion from QVariant to JS values is
+        straight-forward. The biggest behavioural change is that QVariant lists
+        are converted on-the-spot instead of lazily. Bug #94691 tracks fixing
+        that.
+
+        * bridge/qt/qt_instance.cpp:
+        (Bindings):
+        (JSC::Bindings::QtField::valueFromInstance):
+        * bridge/qt/qt_runtime.cpp:
+        (JSC::Bindings::convertQVariantToValue):
+        (JSC::Bindings::QtRuntimeMethod::call):
+        (JSC::Bindings::QtConnectionObject::execute):
+        (JSC::Bindings::::valueAt):
+        * bridge/qt/qt_runtime.h:
+        (Bindings):
+
+2012-08-22  Simon Hausmann  <simon.hausmann@nokia.com>
+
         [Qt] REGRESSION(r125428): fast/profiler/nested-start-and-stop-profiler.html fails
         https://bugs.webkit.org/show_bug.cgi?id=93897
 
index 513d711..0412532 100644 (file)
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "qt_instance.h"
 
+#include "APICast.h"
 #include "Error.h"
 #include "JSDOMBinding.h"
 #include "JSDOMWindowBase.h"
@@ -303,7 +304,6 @@ JSValue QtInstance::valueOf(ExecState* exec) const
 }
 
 // In qt_runtime.cpp
-JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
 QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
 
 QByteArray QtField::name() const
@@ -337,7 +337,11 @@ JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
         else if (m_type == DynamicProperty)
             val = obj->property(m_dynamicProperty);
 #endif
-        return convertQVariantToValue(exec, inst->rootObject(), val);
+        JSValueRef exception = 0;
+        JSValueRef jsValue = convertQVariantToValue(toRef(exec), inst->rootObject(), val, &exception);
+        if (exception)
+            return throwError(exec, toJS(exec, exception));
+        return toJS(exec, jsValue);
     }
     QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
     return throwError(exec, createError(exec, msg.toLatin1().constData()));
index 5124ec4..6a41e2c 100644 (file)
@@ -775,7 +775,7 @@ QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type
     return convertValueToQVariant(exec, value, hint, distance, &visitedObjects, recursionLimit);
 }
 
-JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant)
+JSValueRef convertQVariantToValue(JSContextRef context, PassRefPtr<RootObject> root, const QVariant& variant, JSValueRef *exception)
 {
     // Variants with QObject * can be isNull but not a null pointer
     // An empty QString variant is also null
@@ -786,13 +786,11 @@ JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, con
         !QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject) &&
         type != QMetaType::VoidStar &&
         type != QMetaType::QString) {
-        return jsNull();
+        return JSValueMakeNull(context);
     }
 
-    JSLockHolder lock(exec);
-
     if (type == QMetaType::Bool)
-        return jsBoolean(variant.toBool());
+        return JSValueMakeBoolean(context, variant.toBool());
 
     if (type == QMetaType::Int ||
         type == QMetaType::UInt ||
@@ -804,7 +802,7 @@ JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, con
         type == QMetaType::UShort ||
         type == QMetaType::Float ||
         type == QMetaType::Double)
-        return jsNumber(variant.toDouble());
+        return JSValueMakeNumber(context, variant.toDouble());
 
     if (type == QMetaType::QDateTime ||
         type == QMetaType::QDate ||
@@ -824,59 +822,62 @@ JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, con
         }
 
         // Dates specified this way are in local time (we convert DateTimes above)
-        GregorianDateTime dt;
-        dt.setYear(date.year());
-        dt.setMonth(date.month() - 1);
-        dt.setMonthDay(date.day());
-        dt.setHour(time.hour());
-        dt.setMinute(time.minute());
-        dt.setSecond(time.second());
-        dt.setIsDST(-1);
-        double ms = gregorianDateTimeToMS(exec, dt, time.msec(), /*inputIsUTC*/ false);
-
-        return DateInstance::create(exec, exec->lexicalGlobalObject()->dateStructure(), trunc(ms));
+        const JSValueRef arguments[] = {
+            JSValueMakeNumber(context, date.year()),
+            JSValueMakeNumber(context, date.month() - 1),
+            JSValueMakeNumber(context, date.day()),
+            JSValueMakeNumber(context, time.hour()),
+            JSValueMakeNumber(context, time.minute()),
+            JSValueMakeNumber(context, time.second()),
+            JSValueMakeNumber(context, time.msec())
+        };
+        return JSObjectMakeDate(context, 7, arguments, exception);
     }
 
     if (type == QMetaType::QByteArray) {
         QByteArray qtByteArray = variant.value<QByteArray>();
         WTF::RefPtr<WTF::Uint8ClampedArray> wtfByteArray = WTF::Uint8ClampedArray::createUninitialized(qtByteArray.length());
         memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length());
-        return toJS(exec, static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), wtfByteArray.get());
+        ExecState* exec = toJS(context);
+        return toRef(exec, toJS(exec, static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), wtfByteArray.get()));
     }
 
     if (QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject)) {
         QObject* obj = variant.value<QObject*>();
         if (!obj)
-            return jsNull();
-        return QtInstance::getQtInstance(obj, root, QtInstance::QtOwnership)->createRuntimeObject(exec);
+            return JSValueMakeNull(context);
+        ExecState* exec = toJS(context);
+        return toRef(exec, QtInstance::getQtInstance(obj, root, QtInstance::QtOwnership)->createRuntimeObject(exec));
     }
 
-    if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(variant.type())))
-        return QtPixmapInstance::createPixmapRuntimeObject(exec, root, variant);
+    if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(variant.type()))) {
+        ExecState* exec = toJS(context);
+        return toRef(exec, QtPixmapInstance::createPixmapRuntimeObject(exec, root, variant));
+    }
 
     if (customRuntimeConversions()->contains(type)) {
         if (!root->globalObject()->inherits(&JSDOMWindow::s_info))
-            return jsUndefined();
+            return JSValueMakeUndefined(context);
 
         Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document();
         if (!document)
-            return jsUndefined();
-        return customRuntimeConversions()->value(type).toJSValueFunc(exec, toJSDOMGlobalObject(document, exec), variant);
+            return JSValueMakeUndefined(context);
+        ExecState* exec = toJS(context);
+        return toRef(exec, customRuntimeConversions()->value(type).toJSValueFunc(exec, toJSDOMGlobalObject(document, exec), variant));
     }
 
     if (type == QMetaType::QVariantMap) {
         // create a new object, and stuff properties into it
-        JSObject* ret = constructEmptyObject(exec);
+        JSObjectRef ret = JSObjectMake(context, 0, 0);
         QVariantMap map = variant.value<QVariantMap>();
         QVariantMap::const_iterator i = map.constBegin();
         while (i != map.constEnd()) {
             QString s = i.key();
-            JSValue val = convertQVariantToValue(exec, root.get(), i.value());
-            if (val) {
-                PutPropertySlot slot;
-                ret->methodTable()->put(ret, exec, Identifier(&exec->globalData(), reinterpret_cast_ptr<const UChar *>(s.constData()), s.length()), val, slot);
-                // ### error case?
-            }
+            JSStringRef propertyName = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(s.constData()), s.length());
+            JSValueRef propertyValue = convertQVariantToValue(context, root.get(), i.value(), /*ignored exception*/0);
+            if (propertyValue)
+                JSObjectSetProperty(context, ret, propertyName, propertyValue, kJSPropertyAttributeNone, /*ignored exception*/0);
+            JSStringRelease(propertyName);
             ++i;
         }
 
@@ -885,31 +886,58 @@ JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, con
 
     // List types
     if (type == QMetaType::QVariantList) {
+        // ### TODO: Could use special array class that lazily converts.
+        // See https://bugs.webkit.org/show_bug.cgi?id=94691
         QVariantList vl = variant.toList();
-        qConvDebug() << "got a " << vl.count() << " length list:" << vl;
-        return RuntimeArray::create(exec, new QtArray<QVariant>(vl, QMetaType::Void, root));
+        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
+        if (exception && *exception)
+            return array;
+        for (int i = 0; i < vl.count(); ++i) {
+            JSValueRef property = convertQVariantToValue(context, root.get(), vl.at(i), /*ignored exception*/0);
+            if (property)
+                JSObjectSetPropertyAtIndex(context, array, i, property, /*ignored exception*/0);
+        }
+        return array;
     } else if (type == QMetaType::QStringList) {
         QStringList sl = variant.value<QStringList>();
-        return RuntimeArray::create(exec, new QtArray<QString>(sl, QMetaType::QString, root));
-    } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
-        QObjectList ol= variant.value<QObjectList>();
-        return RuntimeArray::create(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root));
-    } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) {
-        QList<int> il= variant.value<QList<int> >();
-        return RuntimeArray::create(exec, new QtArray<int>(il, QMetaType::Int, root));
+        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
+        for (int i = 0; i < sl.count(); ++i) {
+            const QString& s = sl.at(i);
+            JSStringRef jsString = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(s.constData()), s.length());
+            JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeString(context, jsString), /*ignored exception*/0);
+            JSStringRelease(jsString);
+        }
+        return array;
+    } else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QObjectList>())) {
+        QObjectList ol = variant.value<QObjectList>();
+        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
+        ExecState* exec = toJS(context);
+        for (int i = 0; i < ol.count(); ++i) {
+            JSValueRef jsObject = toRef(exec, QtInstance::getQtInstance(ol.at(i), root, QtInstance::QtOwnership)->createRuntimeObject(exec));
+            JSObjectSetPropertyAtIndex(context, array, i, jsObject, /*ignored exception*/0);
+        }
+        return array;
+    } else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QList<int> >())) {
+        QList<int> il = variant.value<QList<int> >();
+        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
+        for (int i = 0; i < il.count(); ++i)
+            JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeNumber(context, il.at(i)), /*ignored exception*/0);
+        return array;
     }
 
     if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) {
         QVariant real = variant.value<QVariant>();
         qConvDebug() << "real variant is:" << real;
-        return convertQVariantToValue(exec, root, real);
+        return convertQVariantToValue(context, root.get(), real, exception);
     }
 
     qConvDebug() << "fallback path for" << variant << variant.userType();
 
     QString string = variant.toString();
-    UString ustring((UChar*)string.utf16(), string.length());
-    return jsString(exec, ustring);
+    JSStringRef jsstring = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(string.constData()), string.length());
+    JSValueRef value = JSValueMakeString(context, jsstring);
+    JSStringRelease(jsstring);
+    return value;
 }
 
 // Type conversion metadata (from QtScript originally)
@@ -1330,7 +1358,7 @@ JSValueRef QtRuntimeMethod::call(JSContextRef context, JSObjectRef function, JSO
         return JSValueMakeUndefined(context);
 
     if (vargs.size() > 0 && vargs[0].isValid())
-        return toRef(toJS(context), convertQVariantToValue(toJS(context), d->m_instance->rootObject(), vargs[0]));
+        return convertQVariantToValue(context, d->m_instance->rootObject(), vargs[0], exception);
 
     return JSValueMakeUndefined(context);
 }
@@ -1648,7 +1676,7 @@ void QtConnectionObject::execute(void** argv)
 
     for (int i = 0; i < argc; i++) {
         int argType = method.parameterType(i);
-        args[i] = ::toRef(exec, convertQVariantToValue(exec, m_rootObject, QVariant(argType, argv[i+1])));
+        args[i] = convertQVariantToValue(toRef(exec), m_rootObject, QVariant(argType, argv[i+1]), ignoredException);
     }
 
     JSObjectCallAsFunction(m_context, m_receiverFunction, m_receiver, argc, args.data(), 0);
@@ -1700,7 +1728,7 @@ template <typename T> JSValue QtArray<T>::valueAt(ExecState *exec, unsigned int
 {
     if (index < m_length) {
         T val = m_list.at(index);
-        return convertQVariantToValue(exec, rootObject(), QVariant::fromValue(val));
+        return convertQVariantToValue(toRef(exec), rootObject(), QVariant::fromValue(val));
     }
 
     return jsUndefined();
index 3ad89e5..6f7543d 100644 (file)
@@ -157,7 +157,7 @@ typedef JSValue (*ConvertToJSValueFunction)(ExecState* exec, WebCore::JSDOMGloba
 void registerCustomType(int qtMetaTypeId, ConvertToVariantFunction, ConvertToJSValueFunction);
 
 QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance);
-JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant);
+JSValueRef convertQVariantToValue(JSContextRef, PassRefPtr<RootObject>, const QVariant&, JSValueRef* exception);
 
 void setException(JSContextRef, JSValueRef* exception, const QString& text);