diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/bridge/qt | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/bridge/qt')
-rw-r--r-- | Source/WebCore/bridge/qt/qt_class.cpp | 225 | ||||
-rw-r--r-- | Source/WebCore/bridge/qt/qt_class.h | 59 | ||||
-rw-r--r-- | Source/WebCore/bridge/qt/qt_instance.cpp | 397 | ||||
-rw-r--r-- | Source/WebCore/bridge/qt/qt_instance.h | 96 | ||||
-rw-r--r-- | Source/WebCore/bridge/qt/qt_pixmapruntime.cpp | 369 | ||||
-rw-r--r-- | Source/WebCore/bridge/qt/qt_pixmapruntime.h | 54 | ||||
-rw-r--r-- | Source/WebCore/bridge/qt/qt_runtime.cpp | 1914 | ||||
-rw-r--r-- | Source/WebCore/bridge/qt/qt_runtime.h | 242 |
8 files changed, 3356 insertions, 0 deletions
diff --git a/Source/WebCore/bridge/qt/qt_class.cpp b/Source/WebCore/bridge/qt/qt_class.cpp new file mode 100644 index 0000000..4c29c69 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_class.cpp @@ -0,0 +1,225 @@ +/* + * 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_class.h" + +#include "Identifier.h" +#include "qt_instance.h" +#include "qt_runtime.h" + +#include <qdebug.h> +#include <qmetaobject.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); + + const UString& ustring = identifier.ustring(); + const QByteArray name = QString(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length()).toAscii(); + + // First see if we have a cache hit + JSObject* val = qtinst->m_methods.value(name); + if (val) + return val; + + // Nope, create an entry + const 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 + const int count = m_metaObject->methodCount(); + for (index = count - 1; index >= 0; --index) { + const QMetaMethod m = m_metaObject->method(index); + if (m.access() == QMetaMethod::Private) + continue; + + int iter = 0; + const char* signature = m.signature(); + while (signature[iter] && signature[iter] != '(') + ++iter; + + if (normal == QByteArray::fromRawData(signature, iter)) { + 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(); + const UString& ustring = identifier.ustring(); + const QString name(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length()); + const QByteArray ascii = name.toAscii(); + + // First check for a cached field + QtField* f = qtinst->m_fields.value(name); + + 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; +#ifndef QT_NO_PROPERTIES + if (f->fieldType() == QtField::DynamicProperty) { + if (obj->dynamicPropertyNames().indexOf(ascii) >= 0) + return f; + // Dynamic property that disappeared + qtinst->m_fields.remove(name); + delete f; + } +#endif + else { + const QList<QObject*>& children = obj->children(); + const int count = children.size(); + for (int index = 0; index < count; ++index) { + QObject* child = children.at(index); + if (child->objectName() == name) + return f; + } + + // Didn't find it, delete it from the cache + qtinst->m_fields.remove(name); + delete f; + } + } + + int index = m_metaObject->indexOfProperty(ascii); + if (index >= 0) { + const QMetaProperty prop = m_metaObject->property(index); + + if (prop.isScriptable(obj)) { + f = new QtField(prop); + qtinst->m_fields.insert(name, f); + return f; + } + } + +#ifndef QT_NO_PROPERTIES + // Dynamic properties + index = obj->dynamicPropertyNames().indexOf(ascii); + if (index >= 0) { + f = new QtField(ascii); + qtinst->m_fields.insert(name, f); + return f; + } +#endif + + // Child objects + + const QList<QObject*>& children = obj->children(); + const int count = children.count(); + for (index = 0; index < count; ++index) { + QObject* child = children.at(index); + if (child->objectName() == name) { + f = new QtField(child); + qtinst->m_fields.insert(name, f); + return f; + } + } + + // Nothing named this + return 0; + } + // 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(ascii)) + return 0; + +#ifndef QT_NO_PROPERTIES + // 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(ascii); + qtinst->m_fields.insert(name, f); + } +#endif + return f; +} + +} +} + diff --git a/Source/WebCore/bridge/qt/qt_class.h b/Source/WebCore/bridge/qt/qt_class.h new file mode 100644 index 0000000..9d55cc5 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_class.h @@ -0,0 +1,59 @@ +/* + * 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 qt_class_h +#define qt_class_h + +#include "Bridge.h" +#include "qglobal.h" + +QT_BEGIN_NAMESPACE +class QObject; +struct 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/Source/WebCore/bridge/qt/qt_instance.cpp b/Source/WebCore/bridge/qt/qt_instance.cpp new file mode 100644 index 0000000..78263e9 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_instance.cpp @@ -0,0 +1,397 @@ +/* + * 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 "Error.h" +#include "JSDOMBinding.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "ObjectPrototype.h" +#include "PropertyNameArray.h" +#include "qt_class.h" +#include "qt_runtime.h" +#include "runtime_object.h" + +#include <qdebug.h> +#include <qhash.h> +#include <qmetaobject.h> +#include <qmetatype.h> + +namespace JSC { +namespace Bindings { + +// Cache QtInstances +typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap; +static QObjectInstanceMap cachedInstances; + +// Derived RuntimeObject +class QtRuntimeObject : public RuntimeObject { +public: + QtRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>); + + static const ClassInfo s_info; + + virtual void markChildren(MarkStack& markStack) + { + RuntimeObject::markChildren(markStack); + QtInstance* instance = static_cast<QtInstance*>(getInternalInstance()); + if (instance) + instance->markAggregate(markStack); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +protected: + static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren; + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } +}; + +const ClassInfo QtRuntimeObject::s_info = { "QtRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +QtRuntimeObject::QtRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance) + : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtRuntimeObject>(exec), instance) +{ +} + +// QtInstance +QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership) + : Instance(rootObject) + , m_class(0) + , m_object(o) + , m_hashkey(o) + , m_defaultMethod(0) + , m_ownership(ownership) +{ +} + +QtInstance::~QtInstance() +{ + JSLock lock(SilenceAssertionsOnly); + + cachedInstances.remove(m_hashkey); + + // clean up (unprotect from gc) the JSValues we've created + m_methods.clear(); + + qDeleteAll(m_fields); + m_fields.clear(); + + if (m_object) { + switch (m_ownership) { + case QScriptEngine::QtOwnership: + break; + case QScriptEngine::AutoOwnership: + if (m_object->parent()) + break; + // fall through! + case QScriptEngine::ScriptOwnership: + delete m_object; + break; + } + } +} + +PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership) +{ + JSLock lock(SilenceAssertionsOnly); + + foreach (QtInstance* instance, cachedInstances.values(o)) + if (instance->rootObject() == rootObject) { + // The garbage collector removes instances, but it may happen that the wrapped + // QObject dies before the gc kicks in. To handle that case we have to do an additional + // check if to see if the instance's wrapped object is still alive. If it isn't, then + // we have to create a new wrapper. + if (!instance->getObject()) + cachedInstances.remove(instance->hashKey()); + else + return instance; + } + + RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership); + cachedInstances.insert(o, ret.get()); + + return ret.release(); +} + +bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + return object->JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + object->JSObject::put(exec, propertyName, value, slot); +} + +void QtInstance::removeCachedMethod(JSObject* method) +{ + if (m_defaultMethod == method) + m_defaultMethod = 0; + + for (QHash<QByteArray, JSObject*>::Iterator it = m_methods.begin(), + end = m_methods.end(); it != end; ++it) + if (it.value() == method) { + m_methods.erase(it); + return; + } +} + +QtInstance* QtInstance::getInstance(JSObject* object) +{ + if (!object) + return 0; + if (!object->inherits(&QtRuntimeObject::s_info)) + return 0; + return static_cast<QtInstance*>(static_cast<RuntimeObject*>(object)->getInternalInstance()); +} + +Class* QtInstance::getClass() const +{ + if (!m_class) { + if (!m_object) + return 0; + m_class = QtClass::classForObject(m_object); + } + return m_class; +} + +RuntimeObject* QtInstance::newRuntimeObject(ExecState* exec) +{ + JSLock lock(SilenceAssertionsOnly); + m_methods.clear(); + return new (exec) QtRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +void QtInstance::markAggregate(MarkStack& markStack) +{ + if (m_defaultMethod) + markStack.append(m_defaultMethod); + foreach (JSObject* val, m_methods.values()) { + if (val) + markStack.append(val); + } +} + +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())); + } + +#ifndef QT_NO_PROPERTIES + QList<QByteArray> dynProps = obj->dynamicPropertyNames(); + foreach (const QByteArray& ba, dynProps) + array.add(Identifier(exec, ba.constData())); +#endif + + const int methodCount = meta->methodCount(); + for (i = 0; i < methodCount; i++) { + QMetaMethod method = meta->method(i); + if (method.access() != QMetaMethod::Private) + array.add(Identifier(exec, method.signature())); + } + } +} + +JSValue QtInstance::getMethod(ExecState* exec, const Identifier& propertyName) +{ + if (!getClass()) + return jsNull(); + MethodList methodList = m_class->methodsNamed(propertyName, this); + return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue QtInstance::invokeMethod(ExecState*, RuntimeMethod*) +{ + // 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 +{ + QObject* obj = getObject(); + if (!obj) + return jsNull(); + + // Hmm.. see if there is a toString defined + QByteArray buf; + bool useDefault = true; + getClass(); + if (m_class) { + // 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().isEmpty()) { + const char* retsig = m.typeName(); + if (retsig && *retsig) { + QVariant ret(QMetaType::type(retsig), (void*)0); + void * qargs[1]; + qargs[0] = ret.data(); + + if (QMetaObject::metacall(obj, 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*) const +{ + return jsNumber(0); +} + +JSValue QtInstance::booleanValue() const +{ + // ECMA 9.2 + return jsBoolean(getObject()); +} + +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); + +QByteArray QtField::name() const +{ + if (m_type == MetaProperty) + return m_property.name(); + if (m_type == ChildObject && m_childObject) + return m_childObject->objectName().toLatin1(); +#ifndef QT_NO_PROPERTIES + if (m_type == DynamicProperty) + return m_dynamicProperty; +#endif + return QByteArray(); // 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); +#ifndef QT_NO_PROPERTIES + else if (m_type == DynamicProperty) + val = obj->property(m_dynamicProperty); +#endif + return convertQVariantToValue(exec, inst->rootObject(), val); + } + QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name())); + return throwError(exec, createError(exec, 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); + } +#ifndef QT_NO_PROPERTIES + else if (m_type == DynamicProperty) + obj->setProperty(m_dynamicProperty.constData(), val); +#endif + } else { + QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name())); + throwError(exec, createError(exec, msg.toLatin1().constData())); + } +} + + +} +} diff --git a/Source/WebCore/bridge/qt/qt_instance.h b/Source/WebCore/bridge/qt/qt_instance.h new file mode 100644 index 0000000..dd24a92 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_instance.h @@ -0,0 +1,96 @@ +/* + * 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 qt_instance_h +#define qt_instance_h + +#include "Bridge.h" +#include "runtime_root.h" +#include <QtScript/qscriptengine.h> +#include <qhash.h> +#include <qpointer.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 RuntimeObject* newRuntimeObject(ExecState*); + + virtual void begin(); + virtual void end(); + + virtual JSValue valueOf(ExecState*) const; + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + + void markAggregate(MarkStack&); + + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName); + virtual JSValue invokeMethod(ExecState*, RuntimeMethod*); + + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + JSValue stringValue(ExecState* exec) const; + JSValue numberValue(ExecState* exec) const; + JSValue booleanValue() const; + + QObject* getObject() const { return m_object; } + QObject* hashKey() const { return m_hashkey; } + + static PassRefPtr<QtInstance> getQtInstance(QObject*, PassRefPtr<RootObject>, QScriptEngine::ValueOwnership ownership); + + virtual bool getOwnPropertySlot(JSObject*, ExecState*, const Identifier&, PropertySlot&); + virtual void put(JSObject*, ExecState*, const Identifier&, JSValue, PutPropertySlot&); + + void removeCachedMethod(JSObject*); + + static QtInstance* getInstance(JSObject*); + +private: + static PassRefPtr<QtInstance> create(QObject *instance, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership) + { + return adoptRef(new QtInstance(instance, rootObject, ownership)); + } + + friend class QtClass; + friend class QtField; + QtInstance(QObject*, PassRefPtr<RootObject>, QScriptEngine::ValueOwnership ownership); // 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 QtRuntimeMetaMethod* m_defaultMethod; + QScriptEngine::ValueOwnership m_ownership; +}; + +} // namespace Bindings + +} // namespace JSC + +#endif diff --git a/Source/WebCore/bridge/qt/qt_pixmapruntime.cpp b/Source/WebCore/bridge/qt/qt_pixmapruntime.cpp new file mode 100644 index 0000000..1ef20c3 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_pixmapruntime.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2009 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_pixmapruntime.h" + +#include "CachedImage.h" +#include "HTMLImageElement.h" +#include "JSGlobalObject.h" +#include "JSHTMLImageElement.h" +#include "JSLock.h" +#include "ObjectPrototype.h" +#include "StillImageQt.h" +#include <QBuffer> +#include <QByteArray> +#include <QImage> +#include <QPixmap> +#include <QVariant> +#include <runtime_method.h> +#include <runtime_object.h> +#include <runtime_root.h> + +using namespace WebCore; +namespace JSC { + +namespace Bindings { + +class QtPixmapClass : public Class { +public: + QtPixmapClass(); + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; +}; + + +class QtPixmapWidthField : public Field { +public: + static const char* name() { return "width"; } + virtual JSValue valueFromInstance(ExecState*, const Instance* instance) const + { + return jsNumber(static_cast<const QtPixmapInstance*>(instance)->width()); + } + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const {} +}; + +class QtPixmapHeightField : public Field { +public: + static const char* name() { return "height"; } + virtual JSValue valueFromInstance(ExecState*, const Instance* instance) const + { + return jsNumber(static_cast<const QtPixmapInstance*>(instance)->height()); + } + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const {} +}; + +class QtPixmapRuntimeMethod : public Method { +public: + virtual int numParameters() const + { + return 0; + } + virtual JSValue invoke(ExecState* exec, QtPixmapInstance*) = 0; + +}; + +// this function receives an HTML image element as a parameter, makes it display the pixmap/image from Qt +class QtPixmapAssignToElementMethod : public QtPixmapRuntimeMethod { +public: + static const char* name() { return "assignToHTMLImageElement"; } + JSValue invoke(ExecState* exec, QtPixmapInstance* instance) + { + if (!exec->argumentCount()) + return jsUndefined(); + + JSObject* objectArg = exec->argument(0).toObject(exec); + if (!objectArg) + return jsUndefined(); + + if (!objectArg->inherits(&JSHTMLImageElement::s_info)) + return jsUndefined(); + + // we now know that we have a valid <img> element as the argument, we can attach the pixmap to it. + PassRefPtr<StillImage> stillImage = WebCore::StillImage::create(instance->toPixmap()); + HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(static_cast<JSHTMLImageElement*>(objectArg)->impl()); + imageElement->setCachedImage(new CachedImage(stillImage.get())); + JSDOMGlobalObject* global = static_cast<JSDOMGlobalObject*>(instance->rootObject()->globalObject()); + toJS(exec, global, imageElement->document()); + return jsUndefined(); + } + + virtual int numParameters() const + { + return 1; + } +}; + +// this function encodes the image to a dataUrl, to be used in background etc. Note: very slow. +class QtPixmapToDataUrlMethod : public QtPixmapRuntimeMethod { +public: + static const char* name() { return "toDataUrl"; } + JSValue invoke(ExecState* exec, QtPixmapInstance* instance) + { + QByteArray byteArray; + QBuffer buffer(&byteArray); + instance->toImage().save(&buffer, "PNG"); + const QString encodedString = QString("data:image/png;base64,") + byteArray.toBase64(); + const UString ustring((UChar*)encodedString.utf16(), encodedString.length()); + return jsString(exec, ustring); + } +}; + +class QtPixmapToStringMethod : public QtPixmapRuntimeMethod { + public: + static const char* name() { return "toString"; } + JSValue invoke(ExecState* exec, QtPixmapInstance* instance) + { + return instance->valueOf(exec); + } +}; + +struct QtPixmapMetaData { + QtPixmapToDataUrlMethod toDataUrlMethod; + QtPixmapAssignToElementMethod assignToElementMethod; + QtPixmapToStringMethod toStringMethod; + QtPixmapHeightField heightField; + QtPixmapWidthField widthField; + QtPixmapClass cls; +} qt_pixmap_metaData; + +// Derived RuntimeObject +class QtPixmapRuntimeObject : public RuntimeObject { +public: + QtPixmapRuntimeObject(ExecState*, JSGlobalObject*, PassRefPtr<Instance>); + + static const ClassInfo s_info; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +protected: + static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesMarkChildren; + +private: + virtual const ClassInfo* classInfo() const { return &s_info; } +}; + +QtPixmapRuntimeObject::QtPixmapRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance) + : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<QtPixmapRuntimeObject>(exec), instance) +{ +} + +const ClassInfo QtPixmapRuntimeObject::s_info = { "QtPixmapRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +QtPixmapClass::QtPixmapClass() +{ +} + + +Class* QtPixmapInstance::getClass() const +{ + return &qt_pixmap_metaData.cls; +} + +JSValue QtPixmapInstance::getMethod(ExecState* exec, const Identifier& propertyName) +{ + MethodList methodList = getClass()->methodsNamed(propertyName, this); + return new (exec) RuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue QtPixmapInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod) +{ + const MethodList& methods = *runtimeMethod->methods(); + + if (methods.size() == 1) { + QtPixmapRuntimeMethod* method = static_cast<QtPixmapRuntimeMethod*>(methods[0]); + return method->invoke(exec, this); + } + return jsUndefined(); +} + +MethodList QtPixmapClass::methodsNamed(const Identifier& identifier, Instance*) const +{ + MethodList methods; + if (identifier == QtPixmapToDataUrlMethod::name()) + methods.append(&qt_pixmap_metaData.toDataUrlMethod); + else if (identifier == QtPixmapAssignToElementMethod::name()) + methods.append(&qt_pixmap_metaData.assignToElementMethod); + else if (identifier == QtPixmapToStringMethod::name()) + methods.append(&qt_pixmap_metaData.toStringMethod); + return methods; +} + +Field* QtPixmapClass::fieldNamed(const Identifier& identifier, Instance*) const +{ + if (identifier == QtPixmapWidthField::name()) + return &qt_pixmap_metaData.widthField; + if (identifier == QtPixmapHeightField::name()) + return &qt_pixmap_metaData.heightField; + return 0; +} + +void QtPixmapInstance::getPropertyNames(ExecState*exec, PropertyNameArray& arr) +{ + arr.add(Identifier(exec, UString(QtPixmapToDataUrlMethod::name()))); + arr.add(Identifier(exec, UString(QtPixmapAssignToElementMethod::name()))); + arr.add(Identifier(exec, UString(QtPixmapToStringMethod::name()))); + arr.add(Identifier(exec, UString(QtPixmapWidthField::name()))); + arr.add(Identifier(exec, UString(QtPixmapHeightField::name()))); +} + +JSValue QtPixmapInstance::defaultValue(ExecState* exec, PreferredPrimitiveType ptype) const +{ + if (ptype == PreferNumber) { + return jsBoolean( + (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>()) && !(data.value<QImage>()).isNull()) + || (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>()) && !data.value<QPixmap>().isNull())); + } + + if (ptype == PreferString) + return valueOf(exec); + + return jsUndefined(); +} + +JSValue QtPixmapInstance::valueOf(ExecState* exec) const +{ + const QString stringValue = QString("[Qt Native Pixmap %1,%2]").arg(width()).arg(height()); + UString ustring((UChar*)stringValue.utf16(), stringValue.length()); + return jsString(exec, ustring); +} + +QtPixmapInstance::QtPixmapInstance(PassRefPtr<RootObject> rootObj, const QVariant& d) + :Instance(rootObj), data(d) +{ +} + +int QtPixmapInstance::width() const +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) + return data.value<QPixmap>().width(); + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) + return data.value<QImage>().width(); + return 0; +} + +int QtPixmapInstance::height() const +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) + return data.value<QPixmap>().height(); + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) + return data.value<QImage>().height(); + return 0; +} + +QPixmap QtPixmapInstance::toPixmap() +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) + return data.value<QPixmap>(); + + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) { + const QPixmap pixmap = QPixmap::fromImage(data.value<QImage>()); + data = QVariant::fromValue<QPixmap>(pixmap); + return pixmap; + } + + return QPixmap(); +} + +QImage QtPixmapInstance::toImage() +{ + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QImage>())) + return data.value<QImage>(); + + if (data.type() == static_cast<QVariant::Type>(qMetaTypeId<QPixmap>())) { + const QImage image = data.value<QPixmap>().toImage(); + data = QVariant::fromValue<QImage>(image); + return image; + } + + return QImage(); +} + +QVariant QtPixmapInstance::variantFromObject(JSObject* object, QMetaType::Type hint) +{ + if (!object) + goto returnEmptyVariant; + + if (object->inherits(&JSHTMLImageElement::s_info)) { + JSHTMLImageElement* elementJSWrapper = static_cast<JSHTMLImageElement*>(object); + HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(elementJSWrapper->impl()); + + if (!imageElement) + goto returnEmptyVariant; + + CachedImage* cachedImage = imageElement->cachedImage(); + if (!cachedImage) + goto returnEmptyVariant; + + Image* image = cachedImage->image(); + if (!image) + goto returnEmptyVariant; + + QPixmap* pixmap = image->nativeImageForCurrentFrame(); + if (!pixmap) + goto returnEmptyVariant; + + return (hint == static_cast<QMetaType::Type>(qMetaTypeId<QPixmap>())) + ? QVariant::fromValue<QPixmap>(*pixmap) + : QVariant::fromValue<QImage>(pixmap->toImage()); + } + + if (object->inherits(&QtPixmapRuntimeObject::s_info)) { + QtPixmapRuntimeObject* runtimeObject = static_cast<QtPixmapRuntimeObject*>(object); + QtPixmapInstance* instance = static_cast<QtPixmapInstance*>(runtimeObject->getInternalInstance()); + if (!instance) + goto returnEmptyVariant; + + if (hint == qMetaTypeId<QPixmap>()) + return QVariant::fromValue<QPixmap>(instance->toPixmap()); + + if (hint == qMetaTypeId<QImage>()) + return QVariant::fromValue<QImage>(instance->toImage()); + } + +returnEmptyVariant: + if (hint == qMetaTypeId<QPixmap>()) + return QVariant::fromValue<QPixmap>(QPixmap()); + if (hint == qMetaTypeId<QImage>()) + return QVariant::fromValue<QImage>(QImage()); + return QVariant(); +} + +RuntimeObject* QtPixmapInstance::newRuntimeObject(ExecState* exec) +{ + return new(exec) QtPixmapRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +JSObject* QtPixmapInstance::createPixmapRuntimeObject(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& data) +{ + JSLock lock(SilenceAssertionsOnly); + RefPtr<QtPixmapInstance> instance = adoptRef(new QtPixmapInstance(root, data)); + return instance->createRuntimeObject(exec); +} + +bool QtPixmapInstance::canHandle(QMetaType::Type hint) +{ + return hint == qMetaTypeId<QImage>() || hint == qMetaTypeId<QPixmap>(); +} + +} + +} diff --git a/Source/WebCore/bridge/qt/qt_pixmapruntime.h b/Source/WebCore/bridge/qt/qt_pixmapruntime.h new file mode 100644 index 0000000..e2ae5e9 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_pixmapruntime.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 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 qt_pixmapruntime_h +#define qt_pixmapruntime_h + +#include "Bridge.h" +#include <QVariant> + +namespace JSC { + +namespace Bindings { + +class QtPixmapInstance : public Instance { + QVariant data; +public: + QtPixmapInstance(PassRefPtr<RootObject> rootObj, const QVariant& newData); + virtual Class* getClass() const; + virtual JSValue getMethod(ExecState* exec, const Identifier& propertyName); + virtual JSValue invokeMethod(ExecState*, RuntimeMethod*); + virtual void getPropertyNames(ExecState*, PropertyNameArray&); + + virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + virtual JSValue valueOf(ExecState* exec) const; + int width() const; + int height() const; + QPixmap toPixmap(); + QImage toImage(); + RuntimeObject* newRuntimeObject(ExecState* exec); + static JSObject* createPixmapRuntimeObject(ExecState*, PassRefPtr<RootObject>, const QVariant&); + static QVariant variantFromObject(JSObject*, QMetaType::Type hint); + static bool canHandle(QMetaType::Type hint); +}; + +} + +} +#endif diff --git a/Source/WebCore/bridge/qt/qt_runtime.cpp b/Source/WebCore/bridge/qt/qt_runtime.cpp new file mode 100644 index 0000000..0fb811b --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_runtime.cpp @@ -0,0 +1,1914 @@ +/* + * 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 "BooleanObject.h" +#include "DateInstance.h" +#include "DateMath.h" +#include "DatePrototype.h" +#include "DumpRenderTreeSupportQt.h" +#include "FunctionPrototype.h" +#include "Interpreter.h" +#include "JSArray.h" +#include "JSByteArray.h" +#include "JSDocument.h" +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include <JSFunction.h> +#include "JSGlobalObject.h" +#include "JSHTMLElement.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 "qt_pixmapruntime.h" +#include "qvarlengtharray.h" +#include "qwebelement.h" +#include <limits.h> +#include <runtime/Error.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, + JSByteArray +} 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 + +// this is here as a proxy, so we'd have a class to friend in QWebElement, +// as getting/setting a WebCore in QWebElement is private +class QtWebElementRuntime { +public: + static QWebElement create(Element* element) + { + return QWebElement(element); + } + + static Element* get(const QWebElement& element) + { + return element.m_element; + } +}; + +// this is here as a proxy, so we'd have a class to friend in QDRTNode, +// as getting/setting a WebCore in QDRTNode is private. +// We only need to pass WebCore Nodes for layout tests. +class QtDRTNodeRuntime { +public: + static QDRTNode create(Node* node) + { + return QDRTNode(node); + } + + static Node* get(const QDRTNode& node) + { + return node.m_node; + } +}; + +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 (isJSByteArray(&exec->globalData(), val)) + return JSByteArray; + 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(&RuntimeObject::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, int recursionLimit) +{ + --recursionLimit; + + if (!value || !recursionLimit) + return QVariant(); + + 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() + || (value == jsUndefined() + && hint != QMetaType::QString + && hint != (QMetaType::Type) qMetaTypeId<QVariant>())) { + if (distance) + *distance = -1; + return QVariant(); + } + + JSLock lock(SilenceAssertionsOnly); + 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: + if (object->inherits(&NumberObject::info)) + hint = QMetaType::Double; + else if (object->inherits(&BooleanObject::info)) + hint = QMetaType::Bool; + else + hint = QMetaType::QVariantMap; + break; + case QObj: + hint = QMetaType::QObjectStar; + break; + case JSByteArray: + hint = QMetaType::QByteArray; + 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 + && hint != QMetaType::QString + && hint != (QMetaType::Type) qMetaTypeId<QVariant>()) { + if (distance) + *distance = -1; + return QVariant(); + } + + QVariant ret; + int dist = -1; + switch (hint) { + case QMetaType::Bool: + if (type == Object && object->inherits(&BooleanObject::info)) + ret = QVariant(asBooleanObject(value)->internalValue().toBoolean(exec)); + else + 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.length() ? *(const ushort*)str.impl()->characters() : 0)); + if (type == String) + dist = 3; + else + dist = 10; + } + break; + + case QMetaType::QString: { + if (value.isUndefinedOrNull()) { + if (distance) + *distance = 1; + return QString(); + } else { + UString ustring = value.toString(exec); + ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length())); + 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, recursionLimit); + if (objdist >= 0) { + UString ustring = (*it).ustring(); + QString id = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + 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, recursionLimit)); + 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, recursionLimit)); + 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, recursionLimit); + 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((const QChar*)ustring.impl()->characters(), ustring.length()); + + 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((const QChar*)ustring.impl()->characters(), ustring.length()); + + result.append(qstring); + } + dist = 5; + ret = QVariant(result); + } else { + // Make a single length array + UString ustring = value.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + QStringList result; + result.append(qstring); + ret = QVariant(result); + dist = 10; + } + break; + } + + case QMetaType::QByteArray: { + if (type == JSByteArray) { + WTF::ByteArray* arr = asByteArray(value)->storage(); + ret = QVariant(QByteArray(reinterpret_cast<const char*>(arr->data()), arr->length())); + dist = 0; + } else { + UString ustring = value.toString(exec); + ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length()).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; + msToGregorianDateTime(exec, date->internalNumber(), 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 = 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(exec, 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; + } +#ifndef QT_NO_DATESTRING + } else if (type == String) { + UString ustring = value.toString(exec); + QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length()); + + 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; + } + } +#endif // QT_NO_DATESTRING + } + 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((const QChar*)ustring.impl()->characters(), ustring.length()); + + // 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((const QChar*)ustring.impl()->characters(), ustring.length()); + + QRegExp re(qstring); + if (re.isValid()) { + ret = qVariantFromValue(re); + dist = 10; + } + } + break; + + case QMetaType::QObjectStar: + if (type == QObj) { + QtInstance* qtinst = QtInstance::getInstance(object); + 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 = QtInstance::getInstance(object); + 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, recursionLimit); + 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, recursionLimit); + 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, recursionLimit); + 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, recursionLimit); + 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, recursionLimit); + 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, recursionLimit); + if (itemdist >= 0) { + result.append(item.value<int>()); + dist = 10; + ret = QVariant::fromValue(result); + } + } + break; + } else if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(hint))) { + ret = QtPixmapInstance::variantFromObject(object, static_cast<QMetaType::Type>(hint)); + } else if (hint == (QMetaType::Type) qMetaTypeId<QWebElement>()) { + if (object && object->inherits(&JSHTMLElement::s_info)) + ret = QVariant::fromValue<QWebElement>(QtWebElementRuntime::create((static_cast<JSHTMLElement*>(object))->impl())); + else if (object && object->inherits(&JSDocument::s_info)) + ret = QVariant::fromValue<QWebElement>(QtWebElementRuntime::create((static_cast<JSDocument*>(object))->impl()->documentElement())); + else + ret = QVariant::fromValue<QWebElement>(QWebElement()); + } else if (hint == (QMetaType::Type) qMetaTypeId<QDRTNode>()) { + if (object && object->inherits(&JSNode::s_info)) + ret = QVariant::fromValue<QDRTNode>(QtDRTNodeRuntime::create((static_cast<JSNode*>(object))->impl())); + } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) { + if (value.isUndefinedOrNull()) { + if (distance) + *distance = 1; + return QVariant(); + } else { + if (type == Object) { + // Since we haven't really visited this object yet, we remove it + visitedObjects->remove(object); + } + + // And then recurse with the autodetect flag + ret = convertValueToQVariant(exec, value, QMetaType::Void, distance, visitedObjects, recursionLimit); + 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) +{ + const int recursionLimit = 200; + HashSet<JSObject*> visitedObjects; + return convertValueToQVariant(exec, value, hint, distance, &visitedObjects, recursionLimit); +} + +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(SilenceAssertionsOnly); + + 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(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(), 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 = gregorianDateTimeToMS(exec, dt, time.msec(), /*inputIsUTC*/ false); + + return new (exec) DateInstance(exec, trunc(ms)); + } + + if (type == QMetaType::QByteArray) { + QByteArray qtByteArray = variant.value<QByteArray>(); + WTF::RefPtr<WTF::ByteArray> wtfByteArray = WTF::ByteArray::create(qtByteArray.length()); + memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length()); + return new (exec) JSC::JSByteArray(exec, JSC::JSByteArray::createStructure(jsNull()), wtfByteArray.get()); + } + + if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) { + QObject* obj = variant.value<QObject*>(); + if (!obj) + return jsNull(); + return QtInstance::getQtInstance(obj, root, QScriptEngine::QtOwnership)->createRuntimeObject(exec); + } + + if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(variant.type()))) + return QtPixmapInstance::createPixmapRuntimeObject(exec, root, variant); + + if (type == qMetaTypeId<QWebElement>()) { + if (!root->globalObject()->inherits(&JSDOMWindow::s_info)) + return jsUndefined(); + + Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document(); + if (!document) + return jsUndefined(); + + return toJS(exec, toJSDOMGlobalObject(document, exec), QtWebElementRuntime::get(variant.value<QWebElement>())); + } + + if (type == qMetaTypeId<QDRTNode>()) { + if (!root->globalObject()->inherits(&JSDOMWindow::s_info)) + return jsUndefined(); + + Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document(); + if (!document) + return jsUndefined(); + + return toJS(exec, toJSDOMGlobalObject(document, exec), QtDRTNodeRuntime::get(variant.value<QDRTNode>())); + } + + 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.get(), i.value()); + if (val) { + PutPropertySlot slot; + ret->put(exec, Identifier(exec, reinterpret_cast_ptr<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(), exec->lexicalGlobalObject(), deprecatedGetDOMStructure<QtRuntimeMethod>(exec), ident) + , d_ptr(dd) +{ + QW_D(QtRuntimeMethod); + d->m_instance = inst; +} + +QtRuntimeMethod::~QtRuntimeMethod() +{ + QW_D(QtRuntimeMethod); + d->m_instance->removeCachedMethod(this); + 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, + 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 the native method requires more arguments than what was passed from JavaScript + if (exec->argumentCount() + 1 < static_cast<unsigned>(types.count())) { + 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 (unsigned i = 0; converted && i + 1 < static_cast<unsigned>(types.count()); ++i) { + JSValue arg = i < exec->argumentCount() ? exec->argument(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 ((exec->argumentCount() + 1 == static_cast<unsigned>(types.count())) + && (matchDistance == 0)) { + // perfect match, use this one + chosenIndex = index; + break; + } else { + QtMethodMatchData currentMatch(matchDistance, index, types, args); + if (candidates.isEmpty()) { + candidates.append(currentMatch); + } else { + QtMethodMatchData bestMatchSoFar = candidates.at(0); + if ((args.count() > bestMatchSoFar.args.count()) + || ((args.count() == bestMatchSoFar.args.count()) + && (matchDistance <= bestMatchSoFar.matchDistance))) { + candidates.prepend(currentMatch); + } else { + candidates.append(currentMatch); + } + } + } + } 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, createTypeError(exec, 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, createTypeError(exec, 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, createSyntaxError(exec, message.toLatin1().constData())); + } + } + + if (chosenIndex == -1 && candidates.count() > 0) { + QtMethodMatchData bestMatch = candidates.at(0); + if ((candidates.size() > 1) + && (bestMatch.args.count() == candidates.at(1).args.count()) + && (bestMatch.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) { + // Only candidate for overload if argument count and match distance is same as best match + if (candidates.at(i).args.count() == bestMatch.args.count() + || candidates.at(i).matchDistance == bestMatch.matchDistance) { + 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, createTypeError(exec, message.toLatin1().constData())); + } else { + chosenIndex = bestMatch.index; + args = bestMatch.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::markChildren(MarkStack& markStack) +{ + QtRuntimeMethod::markChildren(markStack); + QW_D(QtRuntimeMetaMethod); + if (d->m_connect) + markStack.append(d->m_connect); + if (d->m_disconnect) + markStack.append(d->m_disconnect); +} + +EncodedJSValue QtRuntimeMetaMethod::call(ExecState* exec) +{ + QtRuntimeMetaMethodData* d = static_cast<QtRuntimeMetaMethod *>(exec->callee())->d_func(); + + // We're limited to 10 args + if (exec->argumentCount() > 10) + return JSValue::encode(jsUndefined()); + + // We have to pick a method that matches.. + JSLock lock(SilenceAssertionsOnly); + + 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, vargs, (void **)qargs, &errorObj)) != -1) { + if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0) + return JSValue::encode(jsUndefined()); + + if (vargs[0].isValid()) + return JSValue::encode(convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0])); + } + + if (errorObj) + return JSValue::encode(errorObj); + } else { + return throwVMError(exec, createError(exec, "cannot call function of deleted QObject")); + } + + // void functions return undefined + return JSValue::encode(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); +} + +bool QtRuntimeMetaMethod::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == "connect") { + PropertySlot slot; + slot.setCustom(this, connectGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + if (propertyName == "disconnect") { + PropertySlot slot; + slot.setCustom(this, disconnectGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + if (propertyName == exec->propertyNames().length) { + PropertySlot slot; + slot.setCustom(this, lengthGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + return QtRuntimeMethod::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void QtRuntimeMetaMethod::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode == IncludeDontEnumProperties) { + propertyNames.add(Identifier(exec, "connect")); + propertyNames.add(Identifier(exec, "disconnect")); + propertyNames.add(exec->propertyNames().length); + } + + QtRuntimeMethod::getOwnPropertyNames(exec, propertyNames, mode); +} + +JSValue QtRuntimeMetaMethod::lengthGetter(ExecState*, JSValue, const Identifier&) +{ + // QtScript always returns 0 + return jsNumber(0); +} + +JSValue QtRuntimeMetaMethod::connectGetter(ExecState* exec, JSValue slotBase, const Identifier& ident) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(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, JSValue slotBase, const Identifier& ident) +{ + QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(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; +} + +EncodedJSValue QtRuntimeConnectionMethod::call(ExecState* exec) +{ + QtRuntimeConnectionMethodData* d = static_cast<QtRuntimeConnectionMethod *>(exec->callee())->d_func(); + + JSLock lock(SilenceAssertionsOnly); + + 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 (exec->argumentCount() == 1) { + funcObject = exec->argument(0).toObject(exec); + CallData callData; + if (funcObject->getCallData(callData) == CallTypeNone) { + if (d->m_isConnect) + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function")); + else + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function")); + } + } else if (exec->argumentCount() >= 2) { + if (exec->argument(0).isObject()) { + thisObject = exec->argument(0).toObject(exec); + + // Get the actual function to call + JSObject *asObj = exec->argument(1).toObject(exec); + CallData callData; + if (asObj->getCallData(callData) != CallTypeNone) { + // Function version + funcObject = asObj; + } else { + // Convert it to a string + UString funcName = exec->argument(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 throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function")); + else + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function")); + } + } + } else { + if (d->m_isConnect) + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: thisObject is not an object")); + else + return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: thisObject is not an object")); + } + } else { + if (d->m_isConnect) + return throwVMError(exec, createError(exec, "QtMetaMethod.connect: no arguments given")); + else + return throwVMError(exec, createError(exec, "QtMetaMethod.disconnect: no arguments given")); + } + + if (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 throwVMError(exec, createError(exec, msg.toLatin1().constData())); + } + else { + // Store connection + connections.insert(sender, conn); + } + } else { + // Now to find our previous connection object. Hmm. + QList<QtConnectionObject*> conns = connections.values(sender); + bool ret = false; + + 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 throwVMError(exec, createError(exec, 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 throwVMError(exec, createTypeError(exec, msg.toLatin1().constData())); + } + } else { + return throwVMError(exec, createError(exec, "cannot call function of deleted QObject")); + } + + return JSValue::encode(jsUndefined()); +} + +CallType QtRuntimeConnectionMethod::getCallData(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); +} + +bool QtRuntimeConnectionMethod::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (propertyName == exec->propertyNames().length) { + PropertySlot slot; + slot.setCustom(this, lengthGetter); + descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum); + return true; + } + + return QtRuntimeMethod::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void QtRuntimeConnectionMethod::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + if (mode == IncludeDontEnumProperties) + propertyNames.add(exec->propertyNames().length); + + QtRuntimeMethod::getOwnPropertyNames(exec, propertyNames, mode); +} + +JSValue QtRuntimeConnectionMethod::lengthGetter(ExecState*, JSValue, const Identifier&) +{ + // we have one formal argument, and one optional + return jsNumber(1); +} + +// =============== + +QtConnectionObject::QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject) + : m_instance(instance) + , m_signalIndex(signalIndex) + , m_originalObject(m_instance->getObject()) + , m_thisObject(thisObject) + , m_funcObject(funcObject) +{ + setParent(m_originalObject); + ASSERT(JSLock::currentThreadIsHoldingLock()); // so our ProtectedPtrs are safe +} + +QtConnectionObject::~QtConnectionObject() +{ + // Remove us from the map of active connections + QtRuntimeConnectionMethod::connections.remove(m_originalObject, this); +} + +static const uint qt_meta_data_QtConnectionObject[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 1, 10, // methods + 0, 0, // properties + 0, 0, // enums/sets + + // slots: signature, parameters, type, tag, flags + 28, 27, 27, 27, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_QtConnectionObject[] = { + "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(SilenceAssertionsOnly); + + // ### 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) + MarkedArgumentBuffer l; + // ### DropAllLocks? + int funcArgC = m_funcObject->get(exec, exec->propertyNames().length).toInt32(exec); + int argTotal = qMax(funcArgC, argc); + for(int i=0; i < argTotal; i++) { + if (i < argc) { + int argType = QMetaType::type(parameterTypes.at(i)); + l.append(convertQVariantToValue(exec, ro, QVariant(argType, argv[i+1]))); + } else { + l.append(jsUndefined()); + } + } + // Stuff in the __qt_sender property, if we can + ScopeChain oldsc = ScopeChain(NoScopeChain()); + JSFunction* fimp = 0; + if (m_funcObject->inherits(&JSFunction::info)) { + fimp = static_cast<JSFunction*>(m_funcObject.get()); + + JSObject* qt_sender = QtInstance::getQtInstance(sender(), ro, QScriptEngine::QtOwnership)->createRuntimeObject(exec); + JSObject* wrapper = new (exec) JSObject(JSObject::createStructure(jsNull())); + PutPropertySlot slot; + wrapper->put(exec, Identifier(exec, "__qt_sender__"), qt_sender, slot); + oldsc = fimp->scope(); + ScopeChain sc = oldsc; + sc.push(wrapper); + fimp->setScope(sc); + } + + CallData callData; + CallType callType = m_funcObject->getCallData(callData); + call(exec, m_funcObject, callType, callData, m_thisObject, l); + + if (fimp) + fimp->setScope(oldsc); + } + } + } + } 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 m_rootObject && m_rootObject->isValid() ? m_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/Source/WebCore/bridge/qt/qt_runtime.h b/Source/WebCore/bridge/qt/qt_runtime.h new file mode 100644 index 0000000..68bf865 --- /dev/null +++ b/Source/WebCore/bridge/qt/qt_runtime.h @@ -0,0 +1,242 @@ +/* + * 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 "Bridge.h" +#include "Completion.h" +#include "Protect.h" +#include "runtime_method.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, +#ifndef QT_NO_PROPERTIES + DynamicProperty, +#endif + ChildObject + } QtFieldType; + + QtField(const QMetaProperty &p) + : m_type(MetaProperty), m_property(p) + {} + +#ifndef QT_NO_PROPERTIES + QtField(const QByteArray &b) + : m_type(DynamicProperty), m_dynamicProperty(b) + {} +#endif + + QtField(QObject *child) + : m_type(ChildObject), m_childObject(child) + {} + + virtual JSValue valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const; + QByteArray 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*, JSGlobalObject* globalObject) + { + return globalObject->functionPrototype(); + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + +protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | InternalFunction::StructureFlags | OverridesMarkChildren; + + 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 bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + + virtual void markChildren(MarkStack& markStack); + +protected: + QtRuntimeMetaMethodData* d_func() const {return reinterpret_cast<QtRuntimeMetaMethodData*>(d_ptr);} + +private: + virtual CallType getCallData(CallData&); + static EncodedJSValue JSC_HOST_CALL call(ExecState* exec); + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + static JSValue connectGetter(ExecState*, JSValue, const Identifier&); + static JSValue disconnectGetter(ExecState*, JSValue, const Identifier&); +}; + +class QtConnectionObject; +class QtRuntimeConnectionMethod : public QtRuntimeMethod +{ +public: + QtRuntimeConnectionMethod(ExecState *exec, const Identifier &n, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature ); + + virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&); + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + +protected: + QtRuntimeConnectionMethodData* d_func() const {return reinterpret_cast<QtRuntimeConnectionMethodData*>(d_ptr);} + +private: + virtual CallType getCallData(CallData&); + static EncodedJSValue JSC_HOST_CALL call(ExecState* exec); + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); + 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); +JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant); + +} // namespace Bindings +} // namespace JSC + +#endif |