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