Michael Goddard <michael.goddard@trolltech.com>
[WebKit-https.git] / WebCore / bridge / qt / qt_instance.cpp
1 /*
2  * Copyright (C) 2006 Trolltech ASA
3  *
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.
8  *
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.
13  *
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
17  *
18  */
19
20 #include "config.h"
21 #include "qt_instance.h"
22
23 #include "JSGlobalObject.h"
24 #include "list.h"
25 #include "qt_class.h"
26 #include "qt_runtime.h"
27 #include "PropertyNameArray.h"
28 #include "runtime_object.h"
29 #include "object_object.h"
30
31 #include <qmetaobject.h>
32 #include <qdebug.h>
33 #include <qmetatype.h>
34 #include <qhash.h>
35
36 namespace KJS {
37 namespace Bindings {
38
39 // Cache QtInstances
40 typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
41 static QObjectInstanceMap cachedInstances;
42
43 // Cache JSObjects
44 typedef QHash<QtInstance*, JSObject*> InstanceJSObjectMap;
45 static InstanceJSObjectMap cachedObjects;
46
47 // Derived RuntimeObject
48 class QtRuntimeObjectImp : public RuntimeObjectImp {
49     public:
50         QtRuntimeObjectImp(PassRefPtr<Instance>);
51         ~QtRuntimeObjectImp();
52         virtual void invalidate();
53
54         virtual void mark() {
55             QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
56             if (instance)
57                 instance->mark();
58             RuntimeObjectImp::mark();
59         }
60
61         // Additions
62         virtual bool implementsConstruct() const {return implementsCall();}
63         virtual JSObject* construct(ExecState* exec, const List& args);
64     protected:
65         void removeFromCache();
66 };
67
68 QtRuntimeObjectImp::QtRuntimeObjectImp(PassRefPtr<Instance> instance)
69     : RuntimeObjectImp(instance)
70 {
71 }
72
73 QtRuntimeObjectImp::~QtRuntimeObjectImp()
74 {
75     removeFromCache();
76 }
77
78 void QtRuntimeObjectImp::invalidate()
79 {
80     removeFromCache();
81     RuntimeObjectImp::invalidate();
82 }
83
84 void QtRuntimeObjectImp::removeFromCache()
85 {
86     JSLock lock;
87     QtInstance* key = cachedObjects.key(this);
88     if (key)
89         cachedObjects.remove(key);
90 }
91
92 JSObject* QtRuntimeObjectImp::construct(ExecState* exec, const List& args)
93 {
94     // ECMA 15.2.2.1 (?)
95     JSValue *val = callAsFunction(exec, this, args);
96
97     if (!val || val->type() == NullType || val->type() == UndefinedType)
98         return new JSObject(exec->lexicalGlobalObject()->objectPrototype());
99     else
100         return val->toObject(exec);
101 }
102
103 // QtInstance
104 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
105     : Instance(rootObject)
106     , m_class(0)
107     , m_object(o)
108     , m_hashkey(o)
109     , m_defaultMethod(0)
110     , m_defaultMethodIndex(-2)
111 {
112 }
113
114 QtInstance::~QtInstance()
115 {
116     JSLock lock;
117
118     cachedObjects.remove(this);
119     cachedInstances.remove(m_hashkey);
120
121     // clean up (unprotect from gc) the JSValues we've created
122     m_methods.clear();
123
124     foreach(QtField* f, m_fields.values()) {
125         delete f;
126     }
127     m_fields.clear();
128 }
129
130 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
131 {
132     JSLock lock;
133
134     foreach(QtInstance* instance, cachedInstances.values(o)) {
135         if (instance->rootObject() == rootObject)
136             return instance;
137     }
138
139     RefPtr<QtInstance> ret = adoptRef(new QtInstance(o, rootObject));
140     cachedInstances.insert(o, ret.get());
141
142     return ret.release();
143 }
144
145 JSObject* QtInstance::getRuntimeObject(PassRefPtr<QtInstance> instance)
146 {
147     JSLock lock;
148     JSObject* ret = cachedObjects.value(instance.get());
149     if (!ret) {
150         ret = new QtRuntimeObjectImp(instance);
151         cachedObjects.insert(instance.get(), ret);
152     }
153     return ret;
154 }
155
156 Class* QtInstance::getClass() const
157 {
158     if (!m_class)
159         m_class = QtClass::classForObject(m_object);
160     return m_class;
161 }
162
163 void QtInstance::mark()
164 {
165     if (m_defaultMethod)
166         m_defaultMethod->mark();
167     foreach(JSValue* val, m_methods.values()) {
168         if (val && !val->marked())
169             val->mark();
170     }
171     foreach(JSValue* val, m_children.values()) {
172         if (val && !val->marked())
173             val->mark();
174     }
175 }
176
177 void QtInstance::begin()
178 {
179     // Do nothing.
180 }
181
182 void QtInstance::end()
183 {
184     // Do nothing.
185 }
186
187 void QtInstance::getPropertyNames(ExecState* , PropertyNameArray& array)
188 {
189     // This is the enumerable properties, so put:
190     // properties
191     // dynamic properties
192     // slots
193     QObject* obj = getObject();
194     if (obj) {
195         const QMetaObject* meta = obj->metaObject();
196
197         int i;
198         for (i=0; i < meta->propertyCount(); i++) {
199             QMetaProperty prop = meta->property(i);
200             if (prop.isScriptable()) {
201                 array.add(Identifier(prop.name()));
202             }
203         }
204
205         QList<QByteArray> dynProps = obj->dynamicPropertyNames();
206         foreach(QByteArray ba, dynProps) {
207             array.add(Identifier(ba.constData()));
208         }
209
210         for (i=0; i < meta->methodCount(); i++) {
211             QMetaMethod method = meta->method(i);
212             if (method.access() != QMetaMethod::Private) {
213                 array.add(Identifier(method.signature()));
214             }
215         }
216     }
217 }
218
219 JSValue* QtInstance::invokeMethod(ExecState*, const MethodList&, const List&)
220 {
221     // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
222     return jsUndefined();
223 }
224
225 bool QtInstance::implementsCall() const
226 {
227     // See if we have qscript_call
228     if (m_defaultMethodIndex == -2) {
229         if (m_object) {
230             const QMetaObject* meta = m_object->metaObject();
231             int count = meta->methodCount();
232             const QByteArray defsig("qscript_call");
233             for (int index = count - 1; index >= 0; --index) {
234                 const QMetaMethod m = meta->method(index);
235
236                 QByteArray signature = m.signature();
237                 signature.truncate(signature.indexOf('('));
238
239                 if (defsig == signature) {
240                     m_defaultMethodIndex = index;
241                     break;
242                 }
243             }
244         }
245
246         if (m_defaultMethodIndex == -2) // Not checked
247             m_defaultMethodIndex = -1; // No qscript_call
248     }
249
250     // typeof object that implements call == function
251     return (m_defaultMethodIndex >= 0);
252 }
253
254 JSValue* QtInstance::invokeDefaultMethod(ExecState* exec, const List& args)
255 {
256     // QtScript tries to invoke a meta method qscript_call
257     if (!getObject())
258         return throwError(exec, GeneralError, "cannot call function of deleted QObject");
259
260     // implementsCall will update our default method cache, if possible
261     if (implementsCall()) {
262         if (!m_defaultMethod)
263             m_defaultMethod = new QtRuntimeMetaMethod(exec, Identifier("[[Call]]"),this, m_defaultMethodIndex, QByteArray("qscript_call"), true);
264
265         return m_defaultMethod->callAsFunction(exec, 0, args); // Luckily QtRuntimeMetaMethod ignores the obj parameter
266     } else
267         return throwError(exec, TypeError, "not a function");
268 }
269
270 JSValue* QtInstance::defaultValue(JSType hint) const
271 {
272     if (hint == StringType)
273         return stringValue();
274     if (hint == NumberType)
275         return numberValue();
276     if (hint == BooleanType)
277         return booleanValue();
278     return valueOf();
279 }
280
281 JSValue* QtInstance::stringValue() const
282 {
283     // Hmm.. see if there is a toString defined
284     QByteArray buf;
285     bool useDefault = true;
286     getClass();
287     QObject* obj = getObject();
288     if (m_class && obj) {
289         // Cheat and don't use the full name resolution
290         int index = obj->metaObject()->indexOfMethod("toString()");
291         if (index >= 0) {
292             QMetaMethod m = obj->metaObject()->method(index);
293             // Check to see how much we can call it
294             if (m.access() != QMetaMethod::Private
295                 && m.methodType() != QMetaMethod::Signal
296                 && m.parameterTypes().count() == 0) {
297                 const char* retsig = m.typeName();
298                 if (retsig && *retsig) {
299                     QVariant ret(QMetaType::type(retsig), (void*)0);
300                     void * qargs[1];
301                     qargs[0] = ret.data();
302
303                     if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
304                         if (ret.isValid() && ret.canConvert(QVariant::String)) {
305                             buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
306                             useDefault = false;
307                         }
308                     }
309                 }
310             }
311         }
312     }
313
314     if (useDefault) {
315         const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
316         QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
317         QString str = QString::fromUtf8("%0(name = \"%1\")")
318                       .arg(QLatin1String(meta->className())).arg(name);
319
320         buf = str.toLatin1();
321     }
322     return jsString(buf.constData());
323 }
324
325 JSValue* QtInstance::numberValue() const
326 {
327     return jsNumber(0);
328 }
329
330 JSValue* QtInstance::booleanValue() const
331 {
332     // ECMA 9.2
333     return jsBoolean(true);
334 }
335
336 JSValue* QtInstance::valueOf() const
337 {
338     return stringValue();
339 }
340
341 // In qt_runtime.cpp
342 JSValue* convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant);
343 QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance);
344
345 const char* QtField::name() const
346 {
347     if (m_type == MetaProperty)
348         return m_property.name();
349     else if (m_type == ChildObject && m_childObject)
350         return m_childObject->objectName().toLatin1();
351     else if (m_type == DynamicProperty)
352         return m_dynamicProperty.constData();
353     return ""; // deleted child object
354 }
355
356 JSValue* QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
357 {
358     const QtInstance* instance = static_cast<const QtInstance*>(inst);
359     QObject* obj = instance->getObject();
360
361     if (obj) {
362         QVariant val;
363         if (m_type == MetaProperty) {
364             if (m_property.isReadable())
365                 val = m_property.read(obj);
366             else
367                 return jsUndefined();
368         } else if (m_type == ChildObject)
369             val = QVariant::fromValue((QObject*) m_childObject);
370         else if (m_type == DynamicProperty)
371             val = obj->property(m_dynamicProperty);
372
373         JSValue* ret = convertQVariantToValue(exec, inst->rootObject(), val);
374
375         // Need to save children so we can mark them
376         if (m_type == ChildObject)
377             instance->m_children.insert(ret);
378
379         return ret;
380     } else {
381         QString msg = QString("cannot access member `%1' of deleted QObject").arg(name());
382         return throwError(exec, GeneralError, msg.toLatin1().constData());
383     }
384 }
385
386 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue* aValue) const
387 {
388     if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
389         return;
390
391     const QtInstance* instance = static_cast<const QtInstance*>(inst);
392     QObject* obj = instance->getObject();
393     if (obj) {
394         QMetaType::Type argtype = QMetaType::Void;
395         if (m_type == MetaProperty)
396             argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
397
398         // dynamic properties just get any QVariant
399         QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
400         if (m_type == MetaProperty) {
401             if (m_property.isWritable())
402                 m_property.write(obj, val);
403         } else if (m_type == DynamicProperty)
404             obj->setProperty(m_dynamicProperty.constData(), val);
405     } else {
406         QString msg = QString("cannot access member `%1' of deleted QObject").arg(name());
407         throwError(exec, GeneralError, msg.toLatin1().constData());
408     }
409 }
410
411
412 }
413 }