summaryrefslogtreecommitdiffstats
path: root/WebCore/bridge/qt
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
commit8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch)
tree11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebCore/bridge/qt
parent648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff)
downloadexternal_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/bridge/qt')
-rw-r--r--WebCore/bridge/qt/qt_class.cpp217
-rw-r--r--WebCore/bridge/qt/qt_class.h60
-rw-r--r--WebCore/bridge/qt/qt_instance.cpp357
-rw-r--r--WebCore/bridge/qt/qt_instance.h88
-rw-r--r--WebCore/bridge/qt/qt_runtime.cpp1721
-rw-r--r--WebCore/bridge/qt/qt_runtime.h231
6 files changed, 2674 insertions, 0 deletions
diff --git a/WebCore/bridge/qt/qt_class.cpp b/WebCore/bridge/qt/qt_class.cpp
new file mode 100644
index 0000000..3fe3f89
--- /dev/null
+++ b/WebCore/bridge/qt/qt_class.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+#include "identifier.h"
+
+#include "qt_class.h"
+#include "qt_instance.h"
+#include "qt_runtime.h"
+
+#include <qmetaobject.h>
+#include <qdebug.h>
+
+namespace JSC {
+namespace Bindings {
+
+QtClass::QtClass(const QMetaObject* mo)
+ : m_metaObject(mo)
+{
+}
+
+QtClass::~QtClass()
+{
+}
+
+typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject;
+static ClassesByMetaObject* classesByMetaObject = 0;
+
+QtClass* QtClass::classForObject(QObject* o)
+{
+ if (!classesByMetaObject)
+ classesByMetaObject = new ClassesByMetaObject;
+
+ const QMetaObject* mo = o->metaObject();
+ QtClass* aClass = classesByMetaObject->get(mo);
+ if (!aClass) {
+ aClass = new QtClass(mo);
+ classesByMetaObject->set(mo, aClass);
+ }
+
+ return aClass;
+}
+
+const char* QtClass::name() const
+{
+ return m_metaObject->className();
+}
+
+// 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 object and return the same object for the same
+// identifier.
+JSValue* QtClass::fallbackObject(ExecState* exec, Instance* inst, const Identifier& identifier)
+{
+ QtInstance* qtinst = static_cast<QtInstance*>(inst);
+
+ QByteArray name(identifier.ascii());
+
+ // First see if we have a cache hit
+ JSObject* 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) {
+ QtRuntimeMetaMethod* val = new (exec) QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false);
+ qtinst->m_methods.insert(name, val);
+ return val;
+ }
+ }
+
+ // 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 (normal == signature) {
+ QtRuntimeMetaMethod* val = new (exec) QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false);
+ qtinst->m_methods.insert(name, val);
+ return val;
+ }
+ }
+
+ return jsUndefined();
+}
+
+// This functionality is handled by the fallback case above...
+MethodList QtClass::methodsNamed(const Identifier&, Instance*) const
+{
+ return MethodList();
+}
+
+// ### 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
+{
+ // 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;
+ }
+ }
+
+ 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;
+ }
+}
+
+}
+}
+
diff --git a/WebCore/bridge/qt/qt_class.h b/WebCore/bridge/qt/qt_class.h
new file mode 100644
index 0000000..449648d
--- /dev/null
+++ b/WebCore/bridge/qt/qt_class.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef BINDINGS_QT_CLASS_H_
+#define BINDINGS_QT_CLASS_H_
+
+#include "runtime.h"
+
+#include "qglobal.h"
+
+QT_BEGIN_NAMESPACE
+class QObject;
+class QMetaObject;
+QT_END_NAMESPACE
+
+namespace JSC {
+namespace Bindings {
+
+
+class QtClass : public Class {
+protected:
+ QtClass(const QMetaObject*);
+
+public:
+ static QtClass* classForObject(QObject*);
+ virtual ~QtClass();
+
+ 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* m_metaObject;
+};
+
+} // namespace Bindings
+} // namespace JSC
+
+#endif
diff --git a/WebCore/bridge/qt/qt_instance.cpp b/WebCore/bridge/qt/qt_instance.cpp
new file mode 100644
index 0000000..c15dbe3
--- /dev/null
+++ b/WebCore/bridge/qt/qt_instance.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+#include "qt_instance.h"
+
+#include "ArgList.h"
+#include "JSDOMBinding.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "qt_class.h"
+#include "qt_runtime.h"
+#include "PropertyNameArray.h"
+#include "runtime_object.h"
+#include "ObjectPrototype.h"
+#include "Error.h"
+
+#include <qmetaobject.h>
+#include <qdebug.h>
+#include <qmetatype.h>
+#include <qhash.h>
+
+namespace JSC {
+namespace Bindings {
+
+// Cache QtInstances
+typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
+static QObjectInstanceMap cachedInstances;
+
+// Cache JSObjects
+typedef QHash<QtInstance*, JSObject*> InstanceJSObjectMap;
+static InstanceJSObjectMap cachedObjects;
+
+// Derived RuntimeObject
+class QtRuntimeObjectImp : public RuntimeObjectImp {
+ public:
+ QtRuntimeObjectImp(ExecState*, PassRefPtr<Instance>);
+ ~QtRuntimeObjectImp();
+ virtual void invalidate();
+
+ virtual void mark() {
+ QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
+ if (instance)
+ instance->mark();
+ RuntimeObjectImp::mark();
+ }
+
+ protected:
+ void removeFromCache();
+};
+
+QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance)
+ : RuntimeObjectImp(exec, WebCore::getDOMStructure<QtRuntimeObjectImp>(exec), instance)
+{
+}
+
+QtRuntimeObjectImp::~QtRuntimeObjectImp()
+{
+ removeFromCache();
+}
+
+void QtRuntimeObjectImp::invalidate()
+{
+ removeFromCache();
+ RuntimeObjectImp::invalidate();
+}
+
+void QtRuntimeObjectImp::removeFromCache()
+{
+ JSLock lock(false);
+ QtInstance* key = cachedObjects.key(this);
+ if (key)
+ cachedObjects.remove(key);
+}
+
+// QtInstance
+QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
+ : Instance(rootObject)
+ , m_class(0)
+ , m_object(o)
+ , m_hashkey(o)
+ , m_defaultMethod(0)
+{
+}
+
+QtInstance::~QtInstance()
+{
+ JSLock lock(false);
+
+ cachedObjects.remove(this);
+ cachedInstances.remove(m_hashkey);
+
+ // clean up (unprotect from gc) the JSValues we've created
+ m_methods.clear();
+
+ foreach(QtField* f, m_fields.values()) {
+ delete f;
+ }
+ m_fields.clear();
+}
+
+PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
+{
+ JSLock lock(false);
+
+ foreach(QtInstance* instance, cachedInstances.values(o)) {
+ if (instance->rootObject() == rootObject)
+ return instance;
+ }
+
+ RefPtr<QtInstance> ret = QtInstance::create(o, rootObject);
+ cachedInstances.insert(o, ret.get());
+
+ return ret.release();
+}
+
+RuntimeObjectImp* QtInstance::getRuntimeObject(ExecState* exec, PassRefPtr<QtInstance> instance)
+{
+ JSLock lock(false);
+ QtInstance* qtInstance = instance.get();
+ RuntimeObjectImp* ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(qtInstance));
+ if (!ret) {
+ ret = new (exec) QtRuntimeObjectImp(exec, instance);
+ cachedObjects.insert(qtInstance, ret);
+ ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(qtInstance));
+ }
+ return ret;
+}
+
+Class* QtInstance::getClass() const
+{
+ if (!m_class)
+ m_class = QtClass::classForObject(m_object);
+ return m_class;
+}
+
+void QtInstance::mark()
+{
+ if (m_defaultMethod)
+ m_defaultMethod->mark();
+ foreach(JSObject* val, m_methods.values()) {
+ if (val && !val->marked())
+ val->mark();
+ }
+ foreach(JSValue* val, m_children.values()) {
+ if (val && !val->marked())
+ val->mark();
+ }
+}
+
+void QtInstance::begin()
+{
+ // Do nothing.
+}
+
+void QtInstance::end()
+{
+ // Do nothing.
+}
+
+void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
+{
+ // 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(exec, prop.name()));
+ }
+ }
+
+ QList<QByteArray> dynProps = obj->dynamicPropertyNames();
+ foreach(QByteArray ba, dynProps) {
+ array.add(Identifier(exec, ba.constData()));
+ }
+
+ for (i=0; i < meta->methodCount(); i++) {
+ QMetaMethod method = meta->method(i);
+ if (method.access() != QMetaMethod::Private) {
+ array.add(Identifier(exec, method.signature()));
+ }
+ }
+ }
+}
+
+JSValue* QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&)
+{
+ // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
+ return jsUndefined();
+}
+
+
+JSValue* QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
+{
+ if (hint == PreferString)
+ return stringValue(exec);
+ if (hint == PreferNumber)
+ return numberValue(exec);
+ return valueOf(exec);
+}
+
+JSValue* QtInstance::stringValue(ExecState* exec) const
+{
+ // Hmm.. see if there is a toString defined
+ QByteArray buf;
+ 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(exec, buf.constData());
+}
+
+JSValue* QtInstance::numberValue(ExecState* exec) const
+{
+ return jsNumber(exec, 0);
+}
+
+JSValue* QtInstance::booleanValue() const
+{
+ // ECMA 9.2
+ return jsBoolean(true);
+}
+
+JSValue* QtInstance::valueOf(ExecState* exec) const
+{
+ return stringValue(exec);
+}
+
+// In qt_runtime.cpp
+JSValue* convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
+QVariant convertValueToQVariant(ExecState*, JSValue*, 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);
+
+ JSValue* ret = convertQVariantToValue(exec, inst->rootObject(), val);
+
+ // Need to save children so we can mark them
+ if (m_type == ChildObject)
+ instance->m_children.insert(ret);
+
+ return ret;
+ } else {
+ QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(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(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
+ throwError(exec, GeneralError, msg.toLatin1().constData());
+ }
+}
+
+
+}
+}
diff --git a/WebCore/bridge/qt/qt_instance.h b/WebCore/bridge/qt/qt_instance.h
new file mode 100644
index 0000000..d70e362
--- /dev/null
+++ b/WebCore/bridge/qt/qt_instance.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef BINDINGS_QT_INSTANCE_H_
+#define BINDINGS_QT_INSTANCE_H_
+
+#include "runtime.h"
+#include "runtime_root.h"
+#include <qpointer.h>
+#include <qhash.h>
+#include <qset.h>
+
+namespace JSC {
+
+namespace Bindings {
+
+class QtClass;
+class QtField;
+class QtRuntimeMetaMethod;
+
+class QtInstance : public Instance {
+public:
+ ~QtInstance();
+
+ virtual Class* getClass() const;
+
+ virtual void begin();
+ virtual void end();
+
+ virtual JSValue* valueOf(ExecState*) const;
+ virtual JSValue* defaultValue(ExecState*, PreferredPrimitiveType) const;
+
+ virtual void mark(); // This isn't inherited
+
+ virtual JSValue* invokeMethod(ExecState*, const MethodList&, const ArgList&);
+
+ virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+
+ virtual BindingLanguage getBindingLanguage() const { return QtLanguage; }
+
+ JSValue* stringValue(ExecState* exec) const;
+ JSValue* numberValue(ExecState* exec) const;
+ JSValue* booleanValue() const;
+
+ QObject* getObject() const { return m_object; }
+
+ static PassRefPtr<QtInstance> getQtInstance(QObject*, PassRefPtr<RootObject>);
+ static RuntimeObjectImp* getRuntimeObject(ExecState* exec, PassRefPtr<QtInstance>);
+
+private:
+ static PassRefPtr<QtInstance> create(QObject *instance, PassRefPtr<RootObject> rootObject)
+ {
+ return adoptRef(new QtInstance(instance, rootObject));
+ }
+
+ friend class QtClass;
+ friend class QtField;
+ QtInstance(QObject*, PassRefPtr<RootObject>); // Factory produced only..
+ mutable QtClass* m_class;
+ QPointer<QObject> m_object;
+ QObject* m_hashkey;
+ mutable QHash<QByteArray, JSObject*> m_methods;
+ mutable QHash<QString, QtField*> m_fields;
+ mutable QSet<JSValue*> m_children;
+ mutable QtRuntimeMetaMethod* m_defaultMethod;
+};
+
+} // namespace Bindings
+
+} // namespace JSC
+
+#endif
diff --git a/WebCore/bridge/qt/qt_runtime.cpp b/WebCore/bridge/qt/qt_runtime.cpp
new file mode 100644
index 0000000..d9a2c59
--- /dev/null
+++ b/WebCore/bridge/qt/qt_runtime.cpp
@@ -0,0 +1,1721 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+#include "qt_runtime.h"
+
+#include "DateInstance.h"
+#include "DateMath.h"
+#include "DatePrototype.h"
+#include "FunctionPrototype.h"
+#include "JSArray.h"
+#include "JSDOMBinding.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "JSObject.h"
+#include "ObjectPrototype.h"
+#include "PropertyNameArray.h"
+#include "RegExpConstructor.h"
+#include "RegExpObject.h"
+#include "qdatetime.h"
+#include "qdebug.h"
+#include "qmetaobject.h"
+#include "qmetatype.h"
+#include "qobject.h"
+#include "qstringlist.h"
+#include "qt_instance.h"
+#include "qvarlengtharray.h"
+#include <JSFunction.h>
+#include <limits.h>
+#include <runtime.h>
+#include <runtime_array.h>
+#include <runtime_object.h>
+
+// QtScript has these
+Q_DECLARE_METATYPE(QObjectList);
+Q_DECLARE_METATYPE(QList<int>);
+Q_DECLARE_METATYPE(QVariant);
+
+using namespace WebCore;
+
+namespace JSC {
+namespace Bindings {
+
+// 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 = 0,
+ Number,
+ Boolean,
+ String,
+ Date,
+ RegExp,
+ Array,
+ QObj,
+ Object,
+ Null,
+ RTArray
+} JSRealType;
+
+#if defined(QTWK_RUNTIME_CONVERSION_DEBUG) || defined(QTWK_RUNTIME_MATCH_DEBUG)
+QDebug operator<<(QDebug dbg, const JSRealType &c)
+{
+ const char *map[] = { "Variant", "Number", "Boolean", "String", "Date",
+ "RegExp", "Array", "RTObject", "Object", "Null", "RTArray"};
+
+ dbg.nospace() << "JSType(" << ((int)c) << ", " << map[c] << ")";
+
+ return dbg.space();
+}
+#endif
+
+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(&RuntimeArray::s_info)) // RuntimeArray 'inherits' from Array, but not in C++
+ return RTArray;
+ else if (object->inherits(&JSArray::info))
+ return Array;
+ else if (object->inherits(&DateInstance::info))
+ return Date;
+ else if (object->inherits(&RegExpObject::info))
+ return RegExp;
+ else if (object->inherits(&RuntimeObjectImp::s_info))
+ return QObj;
+ return Object;
+ }
+
+ return String; // I don't know.
+}
+
+QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance, HashSet<JSObject*>* visitedObjects)
+{
+ JSObject* object = 0;
+ if (value->isObject()) {
+ object = value->toObject(exec);
+ if (visitedObjects->contains(object))
+ return QVariant();
+
+ visitedObjects->add(object);
+ }
+
+ // check magic pointer values before dereferencing value
+ if (value == jsNaN(exec) || value == jsUndefined()) {
+ if (distance)
+ *distance = -1;
+ return QVariant();
+ }
+
+ JSLock lock(false);
+ JSRealType type = valueRealType(exec, value);
+ if (hint == QMetaType::Void) {
+ switch(type) {
+ 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 Object:
+ hint = QMetaType::QVariantMap;
+ break;
+ case QObj:
+ hint = QMetaType::QObjectStar;
+ break;
+ case Array:
+ case RTArray:
+ hint = QMetaType::QVariantList;
+ break;
+ }
+ }
+
+ qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint;
+
+ 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 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 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 QMetaType::QString: {
+ UString ustring = value->toString(exec);
+ ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()));
+ if (type == String)
+ dist = 0;
+ else
+ dist = 10;
+ break;
+ }
+
+ case QMetaType::QVariantMap:
+ if (type == Object || type == Array || type == RTArray) {
+ // Enumerate the contents of the object
+ PropertyNameArray properties(exec);
+ 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, visitedObjects);
+ 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 == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QVariantList result;
+ int len = rtarray->getLength();
+ int objdist = 0;
+ qConvDebug() << "converting a " << len << " length Array";
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = rtarray->getConcreteArray()->valueAt(exec, i);
+ result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects));
+ if (objdist == -1) {
+ qConvDebug() << "Failed converting element at index " << i;
+ break; // Failed converting a list entry, so fail the array
+ }
+ }
+ if (objdist != -1) {
+ dist = 5;
+ ret = QVariant(result);
+ }
+ } else if (type == Array) {
+ JSArray* array = static_cast<JSArray*>(object);
+
+ QVariantList result;
+ int len = array->length();
+ int objdist = 0;
+ qConvDebug() << "converting a " << len << " length Array";
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = array->get(exec, i);
+ result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects));
+ if (objdist == -1) {
+ qConvDebug() << "Failed converting element at index " << i;
+ break; // Failed converting a list entry, so fail the array
+ }
+ }
+ if (objdist != -1) {
+ dist = 5;
+ ret = QVariant(result);
+ }
+ } else {
+ // Make a single length array
+ int objdist;
+ qConvDebug() << "making a single length variantlist";
+ QVariant var = convertValueToQVariant(exec, value, QMetaType::Void, &objdist, visitedObjects);
+ if (objdist != -1) {
+ QVariantList result;
+ result << var;
+ ret = QVariant(result);
+ dist = 10;
+ } else {
+ qConvDebug() << "failed making single length varlist";
+ }
+ }
+ break;
+
+ case QMetaType::QStringList: {
+ if (type == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QStringList result;
+ int len = rtarray->getLength();
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = rtarray->getConcreteArray()->valueAt(exec, 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 if (type == Array) {
+ JSArray* array = static_cast<JSArray*>(object);
+
+ QStringList result;
+ int len = array->length();
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = array->get(exec, 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) {
+ DateInstance* date = static_cast<DateInstance*>(object);
+ GregorianDateTime gdt;
+ date->getUTCTime(gdt);
+ if (hint == QMetaType::QDateTime) {
+ ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
+ dist = 0;
+ } else if (hint == QMetaType::QDate) {
+ ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
+ dist = 1;
+ } else {
+ ret = QTime(gdt.hour + 1900, 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 + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
+ dist = 6;
+ } else if (hint == QMetaType::QDate) {
+ ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
+ dist = 8;
+ } else {
+ ret = QTime(gdt.hour, gdt.minute, gdt.second);
+ dist = 10;
+ }
+ } 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) {
+/*
+ RegExpObject *re = static_cast<RegExpObject*>(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(QLatin1Char('/'));
+ int lastSlash = qstring.lastIndexOf(QLatin1Char('/'));
+ if (firstSlash >=0 && lastSlash > firstSlash) {
+ QRegExp realRe;
+
+ realRe.setPattern(qstring.mid(firstSlash + 1, lastSlash - firstSlash - 1));
+
+ if (qstring.mid(lastSlash + 1).contains(QLatin1Char('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) {
+ 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) {
+ 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 == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QObjectList result;
+ int len = rtarray->getLength();
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = rtarray->getConcreteArray()->valueAt(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects);
+ 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 if (type == Array) {
+ JSObject* object = value->toObject(exec);
+ JSArray* array = static_cast<JSArray *>(object);
+ QObjectList result;
+ int len = array->length();
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = array->get(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects);
+ 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 {
+ // Make a single length array
+ QObjectList result;
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, value, QMetaType::QObjectStar, &itemdist, visitedObjects);
+ 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 == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QList<int> result;
+ int len = rtarray->getLength();
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = rtarray->getConcreteArray()->valueAt(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects);
+ 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 if (type == Array) {
+ JSArray* array = static_cast<JSArray *>(object);
+
+ QList<int> result;
+ int len = array->length();
+ for (int i = 0; i < len; ++i) {
+ JSValue* val = array->get(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects);
+ 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 {
+ // Make a single length array
+ QList<int> result;
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, value, QMetaType::Int, &itemdist, visitedObjects);
+ 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, visitedObjects);
+ dist = 10;
+ break;
+ }
+
+ dist = 10;
+ break;
+ }
+
+ if (!ret.isValid())
+ dist = -1;
+ if (distance)
+ *distance = dist;
+
+ return ret;
+}
+
+QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance)
+{
+ HashSet<JSObject*> visitedObjects;
+ return convertValueToQVariant(exec, value, hint, distance, &visitedObjects);
+}
+
+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();
+
+ qConvDebug() << "convertQVariantToValue: metatype:" << type << ", isnull: " << variant.isNull();
+ if (variant.isNull() &&
+ type != QMetaType::QObjectStar &&
+ type != QMetaType::VoidStar &&
+ type != QMetaType::QWidgetStar &&
+ type != QMetaType::QString) {
+ return jsNull();
+ }
+
+ JSLock lock(false);
+
+ if (type == QMetaType::Bool)
+ return jsBoolean(variant.toBool());
+
+ 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(exec, variant.toDouble());
+
+ if (type == QMetaType::QRegExp) {
+ QRegExp re = variant.value<QRegExp>();
+
+ if (re.isValid()) {
+ UString uflags;
+ if (re.caseSensitivity() == Qt::CaseInsensitive)
+ uflags = "i"; // ### Can't do g or m
+
+ UString pattern((UChar*)re.pattern().utf16(), re.pattern().length());
+
+ RefPtr<JSC::RegExp> regExp = JSC::RegExp::create(&exec->globalData(), pattern, uflags);
+ if (regExp->isValid())
+ return new (exec) RegExpObject(exec->lexicalGlobalObject()->regExpStructure(), regExp.release());
+ else
+ return jsNull();
+ }
+ }
+
+ if (type == QMetaType::QDateTime ||
+ type == QMetaType::QDate ||
+ type == QMetaType::QTime) {
+
+ 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>().toLocalTime();
+ date = dt.date();
+ time = dt.time();
+ }
+
+ // Dates specified this way are in local time (we convert DateTimes above)
+ GregorianDateTime dt;
+ dt.year = date.year() - 1900;
+ dt.month = date.month() - 1;
+ dt.monthDay = date.day();
+ dt.hour = time.hour();
+ dt.minute = time.minute();
+ dt.second = time.second();
+ dt.isDST = -1;
+ double ms = JSC::gregorianDateTimeToMS(dt, time.msec(), /*inputIsUTC*/ false);
+
+ DateInstance* instance = new (exec) DateInstance(exec->lexicalGlobalObject()->dateStructure());
+ instance->setInternalValue(jsNumber(exec, trunc(ms)));
+ return instance;
+ }
+
+ if (type == QMetaType::QByteArray) {
+ QByteArray ba = variant.value<QByteArray>();
+ UString ustring(ba.constData());
+ return jsString(exec, ustring);
+ }
+
+ if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
+ QObject* obj = variant.value<QObject*>();
+ return Instance::createRuntimeObject(exec, QtInstance::getQtInstance(obj, root));
+ }
+
+ if (type == QMetaType::QVariantMap) {
+ // create a new object, and stuff properties into it
+ JSObject* ret = constructEmptyObject(exec);
+ 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) {
+ PutPropertySlot slot;
+ ret->put(exec, Identifier(exec, (const UChar *)s.constData(), s.length()), val, slot);
+ // ### error case?
+ }
+ ++i;
+ }
+
+ return ret;
+ }
+
+ // List types
+ if (type == QMetaType::QVariantList) {
+ QVariantList vl = variant.toList();
+ qConvDebug() << "got a " << vl.count() << " length list:" << vl;
+ return new (exec) RuntimeArray(exec, new QtArray<QVariant>(vl, QMetaType::Void, root));
+ } else if (type == QMetaType::QStringList) {
+ QStringList sl = variant.value<QStringList>();
+ return new (exec) RuntimeArray(exec, new QtArray<QString>(sl, QMetaType::QString, root));
+ } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
+ QObjectList ol= variant.value<QObjectList>();
+ return new (exec) 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 (exec) 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();
+ UString ustring((UChar*)string.utf16(), string.length());
+ 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", 0, 0, 0 };
+
+QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState* exec, const Identifier& ident, PassRefPtr<QtInstance> inst)
+ : InternalFunction(&exec->globalData(), getDOMStructure<QtRuntimeMethod>(exec), ident)
+ , d_ptr(dd)
+{
+ QW_D(QtRuntimeMethod);
+ d->m_instance = inst;
+}
+
+QtRuntimeMethod::~QtRuntimeMethod()
+{
+ delete d_ptr;
+}
+
+// ===============
+
+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
+{
+ if (isVariant())
+ return (QMetaType::Type) QMetaType::type("QVariant");
+ return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId);
+}
+
+QByteArray QtMethodMatchType::name() const
+{
+ if (!m_name.isEmpty())
+ return m_name;
+ else if (m_kind == Variant)
+ return "QVariant";
+ return QByteArray();
+}
+
+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)
+{
+ 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,
+ bool allowPrivate,
+ const ArgList& 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 && !allowPrivate)
+ 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.at(exec, 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, bool allowPrivate)
+ : 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;
+ d->m_allowPrivate = allowPrivate;
+}
+
+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::call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args)
+{
+ QtRuntimeMetaMethodData* d = static_cast<QtRuntimeMetaMethod *>(functionObject)->d_func();
+
+ // We're limited to 10 args
+ if (args.size() > 10)
+ return jsUndefined();
+
+ // We have to pick a method that matches..
+ JSLock lock(false);
+
+ 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, d->m_allowPrivate, 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();
+}
+
+CallType QtRuntimeMetaMethod::getCallData(CallData& callData)
+{
+ callData.native.function = call;
+ return CallTypeHost;
+}
+
+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* exec, const Identifier&, const PropertySlot&)
+{
+ // QtScript always returns 0
+ return jsNumber(exec, 0);
+}
+
+JSValue* QtRuntimeMetaMethod::connectGetter(ExecState* exec, const Identifier& ident, const PropertySlot& slot)
+{
+ QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slot.slotBase()));
+ QW_DS(QtRuntimeMetaMethod, thisObj);
+
+ if (!d->m_connect)
+ d->m_connect = new (exec) QtRuntimeConnectionMethod(exec, ident, true, d->m_instance, d->m_index, d->m_signature);
+ return d->m_connect;
+}
+
+JSValue* QtRuntimeMetaMethod::disconnectGetter(ExecState* exec, const Identifier& ident, const PropertySlot& slot)
+{
+ QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slot.slotBase()));
+ QW_DS(QtRuntimeMetaMethod, thisObj);
+
+ if (!d->m_disconnect)
+ d->m_disconnect = new (exec) 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::call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args)
+{
+ QtRuntimeConnectionMethodData* d = static_cast<QtRuntimeConnectionMethod *>(functionObject)->d_func();
+
+ JSLock lock(false);
+
+ 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.at(exec, 0)->toObject(exec);
+ CallData callData;
+ if (funcObject->getCallData(callData) == CallTypeNone) {
+ 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.at(exec, 0)->isObject()) {
+ thisObject = args.at(exec, 0)->toObject(exec);
+
+ // Get the actual function to call
+ JSObject *asObj = args.at(exec, 1)->toObject(exec);
+ CallData callData;
+ if (asObj->getCallData(callData) != CallTypeNone) {
+ // Function version
+ funcObject = asObj;
+ } else {
+ // Convert it to a string
+ UString funcName = args.at(exec, 1)->toString(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->getCallData(callData) != CallTypeNone) {
+ 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(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()"))
+ .arg(QLatin1String(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(QLatin1String("QtMetaMethod.disconnect: failed to disconnect from %1::%2()"))
+ .arg(QLatin1String(sender->metaObject()->className()))
+ .arg(QLatin1String(d->m_signature));
+ return throwError(exec, GeneralError, msg.toLatin1().constData());
+ }
+ }
+ } 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 throwError(exec, TypeError, msg.toLatin1().constData());
+ }
+ } else {
+ return throwError(exec, GeneralError, "cannot call function of deleted QObject");
+ }
+
+ return jsUndefined();
+}
+
+CallType QtRuntimeConnectionMethod::getCallData(CallData& callData)
+{
+ callData.native.function = call;
+ return CallTypeHost;
+}
+
+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* exec, const Identifier&, const PropertySlot&)
+{
+ // we have one formal argument, and one optional
+ return jsNumber(exec, 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[] = {
+ "JSC::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(false);
+
+ // ### 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)
+ ArgList 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());
+ }
+ }
+ CallData callData;
+ CallType callType = m_funcObject->getCallData(callData);
+ // Stuff in the __qt_sender property, if we can
+ if (m_funcObject->inherits(&JSFunction::info)) {
+ JSFunction* fimp = static_cast<JSFunction*>(m_funcObject.get());
+
+ JSObject* qt_sender = Instance::createRuntimeObject(exec, QtInstance::getQtInstance(sender(), ro));
+ JSObject* wrapper = new (exec) JSObject(JSObject::createStructureID(jsNull()));
+ PutPropertySlot slot;
+ wrapper->put(exec, Identifier(exec, "__qt_sender__"), qt_sender, slot);
+ ScopeChain oldsc = fimp->scope();
+ ScopeChain sc = oldsc;
+ sc.push(wrapper);
+ fimp->setScope(sc);
+
+ call(exec, fimp, callType, callData, m_thisObject, l);
+ fimp->setScope(oldsc);
+ } else {
+ call(exec, m_funcObject, callType, callData, 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 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();
+}
+
+// ===============
+
+} }
diff --git a/WebCore/bridge/qt/qt_runtime.h b/WebCore/bridge/qt/qt_runtime.h
new file mode 100644
index 0000000..80248a2
--- /dev/null
+++ b/WebCore/bridge/qt/qt_runtime.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef BINDINGS_QT_RUNTIME_H_
+#define BINDINGS_QT_RUNTIME_H_
+
+#include "completion.h"
+#include "runtime.h"
+#include "runtime_method.h"
+#include "protect.h"
+
+#include <qbytearray.h>
+#include <qmetaobject.h>
+#include <qpointer.h>
+#include <qvariant.h>
+
+namespace JSC {
+namespace Bindings {
+
+class QtInstance;
+
+class QtField : public Field {
+public:
+
+ typedef enum {
+ MetaProperty,
+ DynamicProperty,
+ ChildObject
+ } QtFieldType;
+
+ QtField(const QMetaProperty &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:
+ QtFieldType m_type;
+ QByteArray m_dynamicProperty;
+ QMetaProperty m_property;
+ QPointer<QObject> m_childObject;
+};
+
+
+class QtMethod : public Method
+{
+public:
+ QtMethod(const QMetaObject *mo, int i, const QByteArray &ident, int numParameters)
+ : m_metaObject(mo),
+ m_index(i),
+ m_identifier(ident),
+ m_nParams(numParameters)
+ { }
+
+ virtual const char* name() const { return m_identifier.constData(); }
+ virtual int numParameters() const { return m_nParams; }
+
+private:
+ friend class QtInstance;
+ 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*, unsigned index, JSValue*) const;
+ virtual JSValue* valueAt(ExecState*, unsigned 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;
+ bool m_allowPrivate;
+ 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 InternalFunction {
+public:
+ virtual ~QtRuntimeMethod();
+
+ static const ClassInfo s_info;
+
+ static FunctionPrototype* createPrototype(ExecState* exec)
+ {
+ return exec->lexicalGlobalObject()->functionPrototype();
+ }
+
+ static PassRefPtr<StructureID> createStructureID(JSValue* prototype)
+ {
+ return StructureID::create(prototype, TypeInfo(ObjectType));
+ }
+
+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, bool allowPrivate);
+
+ virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
+
+ virtual void mark();
+
+protected:
+ QtRuntimeMetaMethodData* d_func() const {return reinterpret_cast<QtRuntimeMetaMethodData*>(d_ptr);}
+
+private:
+ virtual CallType getCallData(CallData&);
+ static JSValue* call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args);
+ static JSValue* lengthGetter(ExecState*, const Identifier&, const PropertySlot&);
+ static JSValue* connectGetter(ExecState*, const Identifier&, const PropertySlot&);
+ static JSValue* disconnectGetter(ExecState*, 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&);
+
+protected:
+ QtRuntimeConnectionMethodData* d_func() const {return reinterpret_cast<QtRuntimeConnectionMethodData*>(d_ptr);}
+
+private:
+ virtual CallType getCallData(CallData&);
+ static JSValue* call(ExecState* exec, JSObject* functionObject, JSValue* thisValue, const ArgList& args);
+ static JSValue* lengthGetter(ExecState*, 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;
+};
+
+QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance);
+
+} // namespace Bindings
+} // namespace JSC
+
+#endif