• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
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     static const ClassInfo s_info;
58 
mark()59     virtual void mark()
60     {
61         QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
62         if (instance)
63             instance->mark();
64         RuntimeObjectImp::mark();
65     }
66 
67 protected:
68     void removeFromCache();
69 
70 private:
classInfo() const71     virtual const ClassInfo* classInfo() const { return &s_info; }
72 };
73 
74 const ClassInfo QtRuntimeObjectImp::s_info = { "QtRuntimeObject", &RuntimeObjectImp::s_info, 0, 0 };
75 
QtRuntimeObjectImp(ExecState * exec,PassRefPtr<Instance> instance)76 QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance)
77     : RuntimeObjectImp(exec, WebCore::getDOMStructure<QtRuntimeObjectImp>(exec), instance)
78 {
79 }
80 
~QtRuntimeObjectImp()81 QtRuntimeObjectImp::~QtRuntimeObjectImp()
82 {
83     removeFromCache();
84 }
85 
invalidate()86 void QtRuntimeObjectImp::invalidate()
87 {
88     removeFromCache();
89     RuntimeObjectImp::invalidate();
90 }
91 
removeFromCache()92 void QtRuntimeObjectImp::removeFromCache()
93 {
94     JSLock lock(false);
95     QtInstance* key = cachedObjects.key(this);
96     if (key)
97         cachedObjects.remove(key);
98 }
99 
100 // QtInstance
QtInstance(QObject * o,PassRefPtr<RootObject> rootObject)101 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
102     : Instance(rootObject)
103     , m_class(0)
104     , m_object(o)
105     , m_hashkey(o)
106     , m_defaultMethod(0)
107 {
108 }
109 
~QtInstance()110 QtInstance::~QtInstance()
111 {
112     JSLock lock(false);
113 
114     cachedObjects.remove(this);
115     cachedInstances.remove(m_hashkey);
116 
117     // clean up (unprotect from gc) the JSValues we've created
118     m_methods.clear();
119 
120     foreach(QtField* f, m_fields.values()) {
121         delete f;
122     }
123     m_fields.clear();
124 }
125 
getQtInstance(QObject * o,PassRefPtr<RootObject> rootObject)126 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject)
127 {
128     JSLock lock(false);
129 
130     foreach(QtInstance* instance, cachedInstances.values(o)) {
131         if (instance->rootObject() == rootObject)
132             return instance;
133     }
134 
135     RefPtr<QtInstance> ret = QtInstance::create(o, rootObject);
136     cachedInstances.insert(o, ret.get());
137 
138     return ret.release();
139 }
140 
getOwnPropertySlot(JSObject * object,ExecState * exec,const Identifier & propertyName,PropertySlot & slot)141 bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
142 {
143     return object->JSObject::getOwnPropertySlot(exec, propertyName, slot);
144 }
145 
put(JSObject * object,ExecState * exec,const Identifier & propertyName,JSValuePtr value,PutPropertySlot & slot)146 void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot)
147 {
148     object->JSObject::put(exec, propertyName, value, slot);
149 }
150 
getInstance(JSObject * object)151 QtInstance* QtInstance::getInstance(JSObject* object)
152 {
153     if (!object)
154         return 0;
155     if (!object->inherits(&QtRuntimeObjectImp::s_info))
156         return 0;
157     return static_cast<QtInstance*>(static_cast<RuntimeObjectImp*>(object)->getInternalInstance());
158 }
159 
getClass() const160 Class* QtInstance::getClass() const
161 {
162     if (!m_class)
163         m_class = QtClass::classForObject(m_object);
164     return m_class;
165 }
166 
createRuntimeObject(ExecState * exec)167 RuntimeObjectImp* QtInstance::createRuntimeObject(ExecState* exec)
168 {
169     JSLock lock(false);
170     RuntimeObjectImp* ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(this));
171     if (!ret) {
172         ret = new (exec) QtRuntimeObjectImp(exec, this);
173         cachedObjects.insert(this, ret);
174         ret = static_cast<RuntimeObjectImp*>(cachedObjects.value(this));
175     }
176     return ret;
177 }
178 
mark()179 void QtInstance::mark()
180 {
181     if (m_defaultMethod)
182         m_defaultMethod->mark();
183     foreach(JSObject* val, m_methods.values()) {
184         if (val && !val->marked())
185             val->mark();
186     }
187     foreach(JSValuePtr val, m_children.values()) {
188         if (val && !val.marked())
189             val.mark();
190     }
191 }
192 
begin()193 void QtInstance::begin()
194 {
195     // Do nothing.
196 }
197 
end()198 void QtInstance::end()
199 {
200     // Do nothing.
201 }
202 
getPropertyNames(ExecState * exec,PropertyNameArray & array)203 void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
204 {
205     // This is the enumerable properties, so put:
206     // properties
207     // dynamic properties
208     // slots
209     QObject* obj = getObject();
210     if (obj) {
211         const QMetaObject* meta = obj->metaObject();
212 
213         int i;
214         for (i=0; i < meta->propertyCount(); i++) {
215             QMetaProperty prop = meta->property(i);
216             if (prop.isScriptable()) {
217                 array.add(Identifier(exec, prop.name()));
218             }
219         }
220 
221         QList<QByteArray> dynProps = obj->dynamicPropertyNames();
222         foreach(QByteArray ba, dynProps) {
223             array.add(Identifier(exec, ba.constData()));
224         }
225 
226         for (i=0; i < meta->methodCount(); i++) {
227             QMetaMethod method = meta->method(i);
228             if (method.access() != QMetaMethod::Private) {
229                 array.add(Identifier(exec, method.signature()));
230             }
231         }
232     }
233 }
234 
invokeMethod(ExecState *,const MethodList &,const ArgList &)235 JSValuePtr QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&)
236 {
237     // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
238     return jsUndefined();
239 }
240 
241 
defaultValue(ExecState * exec,PreferredPrimitiveType hint) const242 JSValuePtr QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
243 {
244     if (hint == PreferString)
245         return stringValue(exec);
246     if (hint == PreferNumber)
247         return numberValue(exec);
248     return valueOf(exec);
249 }
250 
stringValue(ExecState * exec) const251 JSValuePtr QtInstance::stringValue(ExecState* exec) const
252 {
253     // Hmm.. see if there is a toString defined
254     QByteArray buf;
255     bool useDefault = true;
256     getClass();
257     QObject* obj = getObject();
258     if (m_class && obj) {
259         // Cheat and don't use the full name resolution
260         int index = obj->metaObject()->indexOfMethod("toString()");
261         if (index >= 0) {
262             QMetaMethod m = obj->metaObject()->method(index);
263             // Check to see how much we can call it
264             if (m.access() != QMetaMethod::Private
265                 && m.methodType() != QMetaMethod::Signal
266                 && m.parameterTypes().count() == 0) {
267                 const char* retsig = m.typeName();
268                 if (retsig && *retsig) {
269                     QVariant ret(QMetaType::type(retsig), (void*)0);
270                     void * qargs[1];
271                     qargs[0] = ret.data();
272 
273                     if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
274                         if (ret.isValid() && ret.canConvert(QVariant::String)) {
275                             buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
276                             useDefault = false;
277                         }
278                     }
279                 }
280             }
281         }
282     }
283 
284     if (useDefault) {
285         const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
286         QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
287         QString str = QString::fromUtf8("%0(name = \"%1\")")
288                       .arg(QLatin1String(meta->className())).arg(name);
289 
290         buf = str.toLatin1();
291     }
292     return jsString(exec, buf.constData());
293 }
294 
numberValue(ExecState * exec) const295 JSValuePtr QtInstance::numberValue(ExecState* exec) const
296 {
297     return jsNumber(exec, 0);
298 }
299 
booleanValue() const300 JSValuePtr QtInstance::booleanValue() const
301 {
302     // ECMA 9.2
303     return jsBoolean(true);
304 }
305 
valueOf(ExecState * exec) const306 JSValuePtr QtInstance::valueOf(ExecState* exec) const
307 {
308     return stringValue(exec);
309 }
310 
311 // In qt_runtime.cpp
312 JSValuePtr convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
313 QVariant convertValueToQVariant(ExecState*, JSValuePtr, QMetaType::Type hint, int *distance);
314 
name() const315 const char* QtField::name() const
316 {
317     if (m_type == MetaProperty)
318         return m_property.name();
319     else if (m_type == ChildObject && m_childObject)
320         return m_childObject->objectName().toLatin1();
321     else if (m_type == DynamicProperty)
322         return m_dynamicProperty.constData();
323     return ""; // deleted child object
324 }
325 
valueFromInstance(ExecState * exec,const Instance * inst) const326 JSValuePtr QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
327 {
328     const QtInstance* instance = static_cast<const QtInstance*>(inst);
329     QObject* obj = instance->getObject();
330 
331     if (obj) {
332         QVariant val;
333         if (m_type == MetaProperty) {
334             if (m_property.isReadable())
335                 val = m_property.read(obj);
336             else
337                 return jsUndefined();
338         } else if (m_type == ChildObject)
339             val = QVariant::fromValue((QObject*) m_childObject);
340         else if (m_type == DynamicProperty)
341             val = obj->property(m_dynamicProperty);
342 
343         JSValuePtr ret = convertQVariantToValue(exec, inst->rootObject(), val);
344 
345         // Need to save children so we can mark them
346         if (m_type == ChildObject)
347             instance->m_children.insert(ret);
348 
349         return ret;
350     } else {
351         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
352         return throwError(exec, GeneralError, msg.toLatin1().constData());
353     }
354 }
355 
setValueToInstance(ExecState * exec,const Instance * inst,JSValuePtr aValue) const356 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValuePtr aValue) const
357 {
358     if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
359         return;
360 
361     const QtInstance* instance = static_cast<const QtInstance*>(inst);
362     QObject* obj = instance->getObject();
363     if (obj) {
364         QMetaType::Type argtype = QMetaType::Void;
365         if (m_type == MetaProperty)
366             argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
367 
368         // dynamic properties just get any QVariant
369         QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
370         if (m_type == MetaProperty) {
371             if (m_property.isWritable())
372                 m_property.write(obj, val);
373         } else if (m_type == DynamicProperty)
374             obj->setProperty(m_dynamicProperty.constData(), val);
375     } else {
376         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
377         throwError(exec, GeneralError, msg.toLatin1().constData());
378     }
379 }
380 
381 
382 }
383 }
384