2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "qt_instance.h"
24 #include "JSDOMBinding.h"
25 #include "JSGlobalObject.h"
28 #include "qt_runtime.h"
29 #include "PropertyNameArray.h"
30 #include "runtime_object.h"
31 #include "ObjectPrototype.h"
34 #include <qmetaobject.h>
36 #include <qmetatype.h>
43 typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
44 static QObjectInstanceMap cachedInstances;
46 // Derived RuntimeObject
47 class QtRuntimeObjectImp : public RuntimeObjectImp {
49 QtRuntimeObjectImp(ExecState*, PassRefPtr<Instance>);
51 static const ClassInfo s_info;
53 virtual void markChildren(MarkStack& markStack)
55 RuntimeObjectImp::markChildren(markStack);
56 QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
58 instance->markAggregate(markStack);
61 static PassRefPtr<Structure> createStructure(JSValue prototype)
63 return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags));
67 static const unsigned StructureFlags = RuntimeObjectImp::StructureFlags | OverridesMarkChildren;
70 virtual const ClassInfo* classInfo() const { return &s_info; }
73 const ClassInfo QtRuntimeObjectImp::s_info = { "QtRuntimeObject", &RuntimeObjectImp::s_info, 0, 0 };
75 QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance)
76 : RuntimeObjectImp(exec, WebCore::deprecatedGetDOMStructure<QtRuntimeObjectImp>(exec), instance)
81 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
82 : Instance(rootObject)
87 , m_ownership(ownership)
91 QtInstance::~QtInstance()
93 JSLock lock(SilenceAssertionsOnly);
95 cachedInstances.remove(m_hashkey);
97 // clean up (unprotect from gc) the JSValues we've created
100 qDeleteAll(m_fields);
104 switch (m_ownership) {
105 case QScriptEngine::QtOwnership:
107 case QScriptEngine::AutoOwnership:
108 if (m_object->parent())
111 case QScriptEngine::ScriptOwnership:
118 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
120 JSLock lock(SilenceAssertionsOnly);
122 foreach(QtInstance* instance, cachedInstances.values(o))
123 if (instance->rootObject() == rootObject) {
124 // The garbage collector removes instances, but it may happen that the wrapped
125 // QObject dies before the gc kicks in. To handle that case we have to do an additional
126 // check if to see if the instance's wrapped object is still alive. If it isn't, then
127 // we have to create a new wrapper.
128 if (!instance->getObject())
129 cachedInstances.remove(instance->hashKey());
134 RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership);
135 cachedInstances.insert(o, ret.get());
137 return ret.release();
140 bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
142 return object->JSObject::getOwnPropertySlot(exec, propertyName, slot);
145 void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
147 object->JSObject::put(exec, propertyName, value, slot);
150 void QtInstance::removeCachedMethod(JSObject* method)
152 if (m_defaultMethod == method)
155 for(QHash<QByteArray, JSObject*>::Iterator it = m_methods.begin(),
156 end = m_methods.end(); it != end; ++it)
157 if (it.value() == method) {
163 QtInstance* QtInstance::getInstance(JSObject* object)
167 if (!object->inherits(&QtRuntimeObjectImp::s_info))
169 return static_cast<QtInstance*>(static_cast<RuntimeObjectImp*>(object)->getInternalInstance());
172 Class* QtInstance::getClass() const
175 m_class = QtClass::classForObject(m_object);
179 RuntimeObjectImp* QtInstance::newRuntimeObject(ExecState* exec)
181 JSLock lock(SilenceAssertionsOnly);
182 return new (exec) QtRuntimeObjectImp(exec, this);
185 void QtInstance::markAggregate(MarkStack& markStack)
188 markStack.append(m_defaultMethod);
189 foreach(JSObject* val, m_methods.values()) {
191 markStack.append(val);
195 void QtInstance::begin()
200 void QtInstance::end()
205 void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
207 // This is the enumerable properties, so put:
209 // dynamic properties
211 QObject* obj = getObject();
213 const QMetaObject* meta = obj->metaObject();
216 for (i=0; i < meta->propertyCount(); i++) {
217 QMetaProperty prop = meta->property(i);
218 if (prop.isScriptable()) {
219 array.add(Identifier(exec, prop.name()));
223 QList<QByteArray> dynProps = obj->dynamicPropertyNames();
224 foreach(QByteArray ba, dynProps) {
225 array.add(Identifier(exec, ba.constData()));
228 for (i=0; i < meta->methodCount(); i++) {
229 QMetaMethod method = meta->method(i);
230 if (method.access() != QMetaMethod::Private) {
231 array.add(Identifier(exec, method.signature()));
237 JSValue QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&)
239 // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
240 return jsUndefined();
244 JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
246 if (hint == PreferString)
247 return stringValue(exec);
248 if (hint == PreferNumber)
249 return numberValue(exec);
250 return valueOf(exec);
253 JSValue QtInstance::stringValue(ExecState* exec) const
255 // Hmm.. see if there is a toString defined
257 bool useDefault = true;
259 QObject* obj = getObject();
260 if (m_class && obj) {
261 // Cheat and don't use the full name resolution
262 int index = obj->metaObject()->indexOfMethod("toString()");
264 QMetaMethod m = obj->metaObject()->method(index);
265 // Check to see how much we can call it
266 if (m.access() != QMetaMethod::Private
267 && m.methodType() != QMetaMethod::Signal
268 && m.parameterTypes().count() == 0) {
269 const char* retsig = m.typeName();
270 if (retsig && *retsig) {
271 QVariant ret(QMetaType::type(retsig), (void*)0);
273 qargs[0] = ret.data();
275 if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
276 if (ret.isValid() && ret.canConvert(QVariant::String)) {
277 buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
287 const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
288 QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
289 QString str = QString::fromUtf8("%0(name = \"%1\")")
290 .arg(QLatin1String(meta->className())).arg(name);
292 buf = str.toLatin1();
294 return jsString(exec, buf.constData());
297 JSValue QtInstance::numberValue(ExecState* exec) const
299 return jsNumber(exec, 0);
302 JSValue QtInstance::booleanValue() const
305 return jsBoolean(true);
308 JSValue QtInstance::valueOf(ExecState* exec) const
310 return stringValue(exec);
314 JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
315 QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
317 const char* QtField::name() const
319 if (m_type == MetaProperty)
320 return m_property.name();
321 else if (m_type == ChildObject && m_childObject)
322 return m_childObject->objectName().toLatin1();
323 else if (m_type == DynamicProperty)
324 return m_dynamicProperty.constData();
325 return ""; // deleted child object
328 JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
330 const QtInstance* instance = static_cast<const QtInstance*>(inst);
331 QObject* obj = instance->getObject();
335 if (m_type == MetaProperty) {
336 if (m_property.isReadable())
337 val = m_property.read(obj);
339 return jsUndefined();
340 } else if (m_type == ChildObject)
341 val = QVariant::fromValue((QObject*) m_childObject);
342 else if (m_type == DynamicProperty)
343 val = obj->property(m_dynamicProperty);
345 return convertQVariantToValue(exec, inst->rootObject(), val);
347 QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
348 return throwError(exec, GeneralError, msg.toLatin1().constData());
352 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const
354 if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
357 const QtInstance* instance = static_cast<const QtInstance*>(inst);
358 QObject* obj = instance->getObject();
360 QMetaType::Type argtype = QMetaType::Void;
361 if (m_type == MetaProperty)
362 argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
364 // dynamic properties just get any QVariant
365 QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
366 if (m_type == MetaProperty) {
367 if (m_property.isWritable())
368 m_property.write(obj, val);
369 } else if (m_type == DynamicProperty)
370 obj->setProperty(m_dynamicProperty.constData(), val);
372 QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
373 throwError(exec, GeneralError, msg.toLatin1().constData());