#include "config.h"
#include "qt_instance.h"
#include "ArgList.h"
#include "JSDOMBinding.h"
#include "JSGlobalObject.h"
#include "JSLock.h"
#include "qt_class.h"
#include "qt_runtime.h"
#include "PropertyNameArray.h"
#include "runtime_object.h"
#include "ObjectPrototype.h"
#include "Error.h"
#include <qmetaobject.h>
#include <qdebug.h>
#include <qmetatype.h>
#include <qhash.h>
namespace JSC {
namespace Bindings {
typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
static QObjectInstanceMap cachedInstances;
typedef QHash<QtInstance*, JSObject*> InstanceJSObjectMap;
static InstanceJSObjectMap cachedObjects;
class QtRuntimeObjectImp : public RuntimeObjectImp {
public:
QtRuntimeObjectImp(ExecState*, PassRefPtr<Instance>);
~QtRuntimeObjectImp();
virtual void invalidate();
static const ClassInfo s_info;
virtual void mark()
{
QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
if (instance)
instance->mark();
RuntimeObjectImp::mark();
}
protected:
void removeFromCache();
private:
virtual const ClassInfo* classInfo() const { return &s_info; }
};
const ClassInfo QtRuntimeObjectImp::s_info = { "QtRuntimeObject", &RuntimeObjectImp::s_info, 0, 0 };
QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance)
: RuntimeObjectImp(exec, WebCore::getDOMStructure<QtRuntimeObjectImp>(exec), instance)
{
}
QtRuntimeObjectImp::~QtRuntimeObjectImp()
{
removeFromCache();
}
void QtRuntimeObjectImp::invalidate()
{
removeFromCache();
RuntimeObjectImp::invalidate();
}
void QtRuntimeObjectImp::removeFromCache()
{
JSLock lock(false);
QtInstance* key = cachedObjects.key(this);
if (key)
cachedObjects.remove(key);
}
QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
: Instance(rootObject)
, m_class(0)
, m_object(o)
, m_hashkey(o)
, m_defaultMethod(0)
{
}
QtInstance::~QtInstance()
{
JSLock lock(false);
cachedObjects.remove(this);
cachedInstances.remove(m_hashkey);
m_methods.clear();
foreach(QtField* f, m_fields.values()) {
delete f;
}
m_fields.clear();
}
PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
{
JSLock lock(false);
foreach(QtInstance* instance, cachedInstances.values(o)) {
if (instance->rootObject() == rootObject)
return instance;
}
RefPtr<QtInstance> ret = QtInstance::create(o, rootObject);
cachedInstances.insert(o, ret.get());
return ret.release();
}
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, JSValuePtr value, PutPropertySlot& slot)
{
object->JSObject::put(exec, propertyName, value, slot);
}
QtInstance* QtInstance::getInstance(JSObject* object)
{
if (!object)
return 0;
if (!object->inherits(&QtRuntimeObjectImp::s_info))
return 0;
return static_cast<QtInstance*>(static_cast<RuntimeObjectImp*>(object)->getInternalInstance());
}
Class* QtInstance::getClass() const
{
if (!m_class)
m_class = QtClass::classForObject(m_object);
return m_class;
}
RuntimeObjectImp* QtInstance::createRuntimeObject(ExecState* exec)
{
JSLock lock(false);
RuntimeObjectImp* ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(this));
if (!ret) {
ret = new (exec) QtRuntimeObjectImp(exec, this);
cachedObjects.insert(this, ret);
ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(this));
}
return ret;
}
void QtInstance::mark()
{
if (m_defaultMethod)
m_defaultMethod->mark();
foreach(JSObject* val, m_methods.values()) {
if (val && !val->marked())
val->mark();
}
foreach(JSValuePtr val, m_children.values()) {
if (val && !val.marked())
val.mark();
}
}
void QtInstance::begin()
{
}
void QtInstance::end()
{
}
void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
{
QObject* obj = getObject();
if (obj) {
const QMetaObject* meta = obj->metaObject();
int i;
for (i=0; i < meta->propertyCount(); i++) {
QMetaProperty prop = meta->property(i);
if (prop.isScriptable()) {
array.add(Identifier(exec, prop.name()));
}
}
QList<QByteArray> dynProps = obj->dynamicPropertyNames();
foreach(QByteArray ba, dynProps) {
array.add(Identifier(exec, ba.constData()));
}
for (i=0; i < meta->methodCount(); i++) {
QMetaMethod method = meta->method(i);
if (method.access() != QMetaMethod::Private) {
array.add(Identifier(exec, method.signature()));
}
}
}
}
JSValuePtr QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&)
{
return jsUndefined();
}
JSValuePtr QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
{
if (hint == PreferString)
return stringValue(exec);
if (hint == PreferNumber)
return numberValue(exec);
return valueOf(exec);
}
JSValuePtr QtInstance::stringValue(ExecState* exec) const
{
QByteArray buf;
bool useDefault = true;
getClass();
QObject* obj = getObject();
if (m_class && obj) {
int index = obj->metaObject()->indexOfMethod("toString()");
if (index >= 0) {
QMetaMethod m = obj->metaObject()->method(index);
if (m.access() != QMetaMethod::Private
&& m.methodType() != QMetaMethod::Signal
&& m.parameterTypes().count() == 0) {
const char* retsig = m.typeName();
if (retsig && *retsig) {
QVariant ret(QMetaType::type(retsig), (void*)0);
void * qargs[1];
qargs[0] = ret.data();
if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
if (ret.isValid() && ret.canConvert(QVariant::String)) {
buf = ret.toString().toLatin1().constData(); 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());
}
JSValuePtr QtInstance::numberValue(ExecState* exec) const
{
return jsNumber(exec, 0);
}
JSValuePtr QtInstance::booleanValue() const
{
return jsBoolean(true);
}
JSValuePtr QtInstance::valueOf(ExecState* exec) const
{
return stringValue(exec);
}
JSValuePtr convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
QVariant convertValueToQVariant(ExecState*, JSValuePtr, QMetaType::Type hint, int *distance);
const char* QtField::name() const
{
if (m_type == MetaProperty)
return m_property.name();
else if (m_type == ChildObject && m_childObject)
return m_childObject->objectName().toLatin1();
else if (m_type == DynamicProperty)
return m_dynamicProperty.constData();
return ""; }
JSValuePtr QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
{
const QtInstance* instance = static_cast<const QtInstance*>(inst);
QObject* obj = instance->getObject();
if (obj) {
QVariant val;
if (m_type == MetaProperty) {
if (m_property.isReadable())
val = m_property.read(obj);
else
return jsUndefined();
} else if (m_type == ChildObject)
val = QVariant::fromValue((QObject*) m_childObject);
else if (m_type == DynamicProperty)
val = obj->property(m_dynamicProperty);
JSValuePtr ret = convertQVariantToValue(exec, inst->rootObject(), val);
if (m_type == ChildObject)
instance->m_children.insert(ret);
return ret;
} else {
QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
return throwError(exec, GeneralError, msg.toLatin1().constData());
}
}
void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValuePtr aValue) const
{
if (m_type == ChildObject) 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());
QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
if (m_type == MetaProperty) {
if (m_property.isWritable())
m_property.write(obj, val);
} else if (m_type == DynamicProperty)
obj->setProperty(m_dynamicProperty.constData(), val);
} else {
QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
throwError(exec, GeneralError, msg.toLatin1().constData());
}
}
}
}