2 * Copyright (C) 2006 Trolltech ASA
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"
23 #include "JSGlobalObject.h"
26 #include "qt_runtime.h"
27 #include "PropertyNameArray.h"
28 #include "runtime_object.h"
29 #include "object_object.h"
31 #include <qmetaobject.h>
33 #include <qmetatype.h>
40 typedef QMultiHash
<void*, QtInstance
*> QObjectInstanceMap
;
41 static QObjectInstanceMap cachedInstances
;
44 typedef QHash
<QtInstance
*, JSObject
*> InstanceJSObjectMap
;
45 static InstanceJSObjectMap cachedObjects
;
47 // Derived RuntimeObject
48 class QtRuntimeObjectImp
: public RuntimeObjectImp
{
50 QtRuntimeObjectImp(Instance
*);
51 ~QtRuntimeObjectImp();
52 virtual void invalidate();
55 virtual bool implementsConstruct() const {return implementsCall();}
56 virtual JSObject
* construct(ExecState
* exec
, const List
& args
);
58 void removeFromCache();
61 QtRuntimeObjectImp::QtRuntimeObjectImp(Instance
* instance
)
62 : RuntimeObjectImp(instance
)
66 QtRuntimeObjectImp::~QtRuntimeObjectImp()
71 void QtRuntimeObjectImp::invalidate()
74 RuntimeObjectImp::invalidate();
77 void QtRuntimeObjectImp::removeFromCache()
80 QtInstance
* key
= cachedObjects
.key(this);
82 cachedObjects
.remove(key
);
85 JSObject
* QtRuntimeObjectImp::construct(ExecState
* exec
, const List
& args
)
88 JSValue
*val
= callAsFunction(exec
, this, args
);
90 if (!val
|| val
->type() == NullType
|| val
->type() == UndefinedType
)
91 return new JSObject(exec
->lexicalGlobalObject()->objectPrototype());
93 return val
->toObject(exec
);
97 QtInstance::QtInstance(QObject
* o
, PassRefPtr
<RootObject
> rootObject
)
98 : Instance(rootObject
)
103 , m_defaultMethodIndex(-2)
107 QtInstance::~QtInstance()
111 cachedObjects
.remove(this);
112 cachedInstances
.remove(m_hashkey
);
114 // clean up (unprotect from gc) the JSValues we've created
115 foreach(JSValue
* val
, m_methods
.values()) {
120 foreach(QtField
* f
, m_fields
.values()) {
126 gcUnprotect(m_defaultMethod
);
129 QtInstance
* QtInstance::getQtInstance(QObject
* o
, PassRefPtr
<RootObject
> rootObject
)
133 foreach(QtInstance
* instance
, cachedInstances
.values(o
)) {
134 if (instance
->rootObject() == rootObject
)
138 QtInstance
* ret
= new QtInstance(o
, rootObject
);
139 cachedInstances
.insert(o
, ret
);
144 JSObject
* QtInstance::getRuntimeObject(QtInstance
* instance
)
147 JSObject
* ret
= cachedObjects
.value(instance
);
149 ret
= new QtRuntimeObjectImp(instance
);
150 cachedObjects
.insert(instance
, ret
);
155 Class
* QtInstance::getClass() const
158 m_class
= QtClass::classForObject(m_object
);
162 void QtInstance::begin()
167 void QtInstance::end()
172 void QtInstance::getPropertyNames(ExecState
* , PropertyNameArray
& array
)
174 // This is the enumerable properties, so put:
176 // dynamic properties
178 QObject
* obj
= getObject();
180 const QMetaObject
* meta
= obj
->metaObject();
183 for (i
=0; i
< meta
->propertyCount(); i
++) {
184 QMetaProperty prop
= meta
->property(i
);
185 if (prop
.isScriptable()) {
186 array
.add(Identifier(prop
.name()));
190 QList
<QByteArray
> dynProps
= obj
->dynamicPropertyNames();
191 foreach(QByteArray ba
, dynProps
) {
192 array
.add(Identifier(ba
.constData()));
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()));
204 JSValue
* QtInstance::invokeMethod(ExecState
*, const MethodList
&, const List
&)
206 // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
207 return jsUndefined();
210 bool QtInstance::implementsCall() const
212 // See if we have qscript_call
213 if (m_defaultMethodIndex
== -2) {
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
);
221 QByteArray signature
= m
.signature();
222 signature
.truncate(signature
.indexOf('('));
224 if (defsig
== signature
) {
225 m_defaultMethodIndex
= index
;
231 if (m_defaultMethodIndex
== -2) // Not checked
232 m_defaultMethodIndex
= -1; // No qscript_call
235 // typeof object that implements call == function
236 return (m_defaultMethodIndex
>= 0);
239 JSValue
* QtInstance::invokeDefaultMethod(ExecState
* exec
, const List
& args
)
241 // QtScript tries to invoke a meta method qscript_call
243 return throwError(exec
, GeneralError
, "cannot call function of deleted QObject");
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
);
252 return m_defaultMethod
->callAsFunction(exec
, 0, args
); // Luckily QtRuntimeMetaMethod ignores the obj parameter
254 return throwError(exec
, TypeError
, "not a function");
257 JSValue
* QtInstance::defaultValue(JSType hint
) const
259 if (hint
== StringType
)
260 return stringValue();
261 if (hint
== NumberType
)
262 return numberValue();
263 if (hint
== BooleanType
)
264 return booleanValue();
268 JSValue
* QtInstance::stringValue() const
270 // Hmm.. see if there is a toString defined
272 bool useDefault
= true;
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()");
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);
288 qargs
[0] = ret
.data();
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?
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
);
307 buf
= str
.toLatin1();
309 return jsString(buf
.constData());
312 JSValue
* QtInstance::numberValue() const
317 JSValue
* QtInstance::booleanValue() const
320 return jsBoolean(true);
323 JSValue
* QtInstance::valueOf() const
325 return stringValue();
329 JSValue
* convertQVariantToValue(ExecState
* exec
, PassRefPtr
<RootObject
> root
, const QVariant
& variant
);
330 QVariant
convertValueToQVariant(ExecState
* exec
, JSValue
* value
, QMetaType::Type hint
, int *distance
);
332 const char* QtField::name() const
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
343 JSValue
* QtField::valueFromInstance(ExecState
* exec
, const Instance
* inst
) const
345 const QtInstance
* instance
= static_cast<const QtInstance
*>(inst
);
346 QObject
* obj
= instance
->getObject();
350 if (m_type
== MetaProperty
) {
351 if (m_property
.isReadable())
352 val
= m_property
.read(obj
);
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
);
360 return convertQVariantToValue(exec
, inst
->rootObject(), val
);
362 QString msg
= QString("cannot access member `%1' of deleted QObject").arg(name());
363 return throwError(exec
, GeneralError
, msg
.toLatin1().constData());
367 void QtField::setValueToInstance(ExecState
* exec
, const Instance
* inst
, JSValue
* aValue
) const
369 if (m_type
== ChildObject
) // QtScript doesn't allow setting to a named child
372 const QtInstance
* instance
= static_cast<const QtInstance
*>(inst
);
373 QObject
* obj
= instance
->getObject();
375 QMetaType::Type argtype
= QMetaType::Void
;
376 if (m_type
== MetaProperty
)
377 argtype
= (QMetaType::Type
) QMetaType::type(m_property
.typeName());
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
);
387 QString msg
= QString("cannot access member `%1' of deleted QObject").arg(name());
388 throwError(exec
, GeneralError
, msg
.toLatin1().constData());