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