Add support for private names
[WebKit-https.git] / Source / WebCore / bridge / qt / qt_instance.cpp
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 "Error.h"
24 #include "JSDOMBinding.h"
25 #include "JSGlobalObject.h"
26 #include "JSLock.h"
27 #include "ObjectPrototype.h"
28 #include "PropertyNameArray.h"
29 #include "qt_class.h"
30 #include "qt_runtime.h"
31 #include "runtime_object.h"
32 #include "runtime/FunctionPrototype.h"
33
34 #include <qdebug.h>
35 #include <qhash.h>
36 #include <qmetaobject.h>
37 #include <qmetatype.h>
38
39 namespace JSC {
40 namespace Bindings {
41
42 // Cache QtInstances
43 typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
44 static QObjectInstanceMap cachedInstances;
45
46 // Used for implementing '__qt_sender__'.
47 Q_GLOBAL_STATIC(QtInstance::QtSenderStack, senderStack)
48
49 // Derived RuntimeObject
50 class QtRuntimeObject : public RuntimeObject {
51 public:
52     typedef RuntimeObject Base;
53
54     static QtRuntimeObject* create(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<Instance> instance)
55     {
56         Structure* domStructure = WebCore::deprecatedGetDOMStructure<QtRuntimeObject>(exec);
57         QtRuntimeObject* object = new (allocateCell<QtRuntimeObject>(*exec->heap())) QtRuntimeObject(exec, globalObject, domStructure, instance);
58         object->finishCreation(globalObject);
59         return object;
60     }
61     
62     static const ClassInfo s_info;
63
64     static void visitChildren(JSCell* cell, SlotVisitor& visitor)
65     {
66         QtRuntimeObject* thisObject = jsCast<QtRuntimeObject*>(cell);
67         RuntimeObject::visitChildren(thisObject, visitor);
68         QtInstance* instance = static_cast<QtInstance*>(thisObject->getInternalInstance());
69         if (instance)
70             instance->visitAggregate(visitor);
71     }
72
73     static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
74     {
75         return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType,  StructureFlags), &s_info);
76     }
77
78 protected:
79     static const unsigned StructureFlags = RuntimeObject::StructureFlags | OverridesVisitChildren;
80
81 private:
82     QtRuntimeObject(ExecState*, JSGlobalObject*, Structure*, PassRefPtr<Instance>);
83 };
84
85 const ClassInfo QtRuntimeObject::s_info = { "QtRuntimeObject", &RuntimeObject::s_info, 0, 0, CREATE_METHOD_TABLE(QtRuntimeObject) };
86
87 QtRuntimeObject::QtRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, PassRefPtr<Instance> instance)
88     : RuntimeObject(exec, globalObject, structure, instance)
89 {
90 }
91
92 // QtInstance
93 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
94     : Instance(rootObject)
95     , m_class(0)
96     , m_object(o)
97     , m_hashkey(o)
98     , m_ownership(ownership)
99 {
100 }
101
102 QtInstance::~QtInstance()
103 {
104     JSLock lock(SilenceAssertionsOnly);
105
106     cachedInstances.remove(m_hashkey);
107
108     // clean up (unprotect from gc) the JSValues we've created
109     m_methods.clear();
110
111     qDeleteAll(m_fields);
112     m_fields.clear();
113
114     if (m_object) {
115         switch (m_ownership) {
116         case QScriptEngine::QtOwnership:
117             break;
118         case QScriptEngine::AutoOwnership:
119             if (m_object.data()->parent())
120                 break;
121             // fall through!
122         case QScriptEngine::ScriptOwnership:
123             delete m_object.data();
124             break;
125         }
126     }
127 }
128
129 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
130 {
131     JSLock lock(SilenceAssertionsOnly);
132
133     foreach (QtInstance* instance, cachedInstances.values(o))
134         if (instance->rootObject() == rootObject) {
135             // The garbage collector removes instances, but it may happen that the wrapped
136             // QObject dies before the gc kicks in. To handle that case we have to do an additional
137             // check if to see if the instance's wrapped object is still alive. If it isn't, then
138             // we have to create a new wrapper.
139             if (!instance->getObject())
140                 cachedInstances.remove(instance->hashKey());
141             else
142                 return instance;
143         }
144
145     RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership);
146     cachedInstances.insert(o, ret.get());
147
148     return ret.release();
149 }
150
151 bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
152 {
153     return JSObject::getOwnPropertySlot(object, exec, propertyName, slot);
154 }
155
156 void QtInstance::put(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
157 {
158     JSObject::put(object, exec, propertyName, value, slot);
159 }
160
161 void QtInstance::removeCachedMethod(JSObject* method)
162 {
163     for (QHash<QByteArray, WriteBarrier<JSObject> >::Iterator it = m_methods.begin(), end = m_methods.end(); it != end; ++it) {
164         if (it.value().get() == method) {
165             m_methods.erase(it);
166             return;
167         }
168     }
169 }
170
171 QtInstance* QtInstance::getInstance(JSObject* object)
172 {
173     if (!object)
174         return 0;
175     if (!object->inherits(&QtRuntimeObject::s_info))
176         return 0;
177     return static_cast<QtInstance*>(static_cast<RuntimeObject*>(object)->getInternalInstance());
178 }
179
180 Class* QtInstance::getClass() const
181 {
182     if (!m_class) {
183         if (!m_object)
184             return 0;
185         m_class = QtClass::classForObject(m_object.data());
186     }
187     return m_class;
188 }
189
190 RuntimeObject* QtInstance::newRuntimeObject(ExecState* exec)
191 {
192     JSLock lock(SilenceAssertionsOnly);
193     m_methods.clear();
194     return QtRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
195 }
196
197 void QtInstance::visitAggregate(SlotVisitor& visitor)
198 {
199     for (QHash<QByteArray, WriteBarrier<JSObject> >::Iterator it = m_methods.begin(), end = m_methods.end(); it != end; ++it)
200         visitor.append(&it.value());
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 #ifndef QT_NO_PROPERTIES
231         QList<QByteArray> dynProps = obj->dynamicPropertyNames();
232         foreach (const QByteArray& ba, dynProps)
233             array.add(Identifier(exec, ba.constData()));
234 #endif
235
236         const int methodCount = meta->methodCount();
237         for (i = 0; i < methodCount; i++) {
238             QMetaMethod method = meta->method(i);
239             if (method.access() != QMetaMethod::Private) {
240 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
241                 QString sig = QString::fromLatin1(method.methodSignature());
242                 array.add(Identifier(exec, UString(sig.utf16(), sig.length())));
243 #else
244                 array.add(Identifier(exec, method.signature()));
245 #endif
246             }
247         }
248     }
249 }
250
251 JSValue QtInstance::getMethod(ExecState* exec, PropertyName propertyName)
252 {
253     if (!getClass())
254         return jsNull();
255     MethodList methodList = m_class->methodsNamed(propertyName, this);
256     return RuntimeMethod::create(exec, exec->lexicalGlobalObject(), WebCore::deprecatedGetDOMStructure<RuntimeMethod>(exec), propertyName.publicName(), methodList);
257 }
258
259 JSValue QtInstance::invokeMethod(ExecState*, RuntimeMethod*)
260 {
261     // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
262     return jsUndefined();
263 }
264
265 JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
266 {
267     if (hint == PreferString)
268         return stringValue(exec);
269     if (hint == PreferNumber)
270         return numberValue(exec);
271     return valueOf(exec);
272 }
273
274 JSValue QtInstance::stringValue(ExecState* exec) const
275 {
276     QObject* obj = getObject();
277     if (!obj)
278         return jsNull();
279
280     // Hmm.. see if there is a toString defined
281     QByteArray buf;
282     bool useDefault = true;
283     getClass();
284     if (m_class) {
285         // Cheat and don't use the full name resolution
286         int index = obj->metaObject()->indexOfMethod("toString()");
287         if (index >= 0) {
288             QMetaMethod m = obj->metaObject()->method(index);
289             // Check to see how much we can call it
290             if (m.access() != QMetaMethod::Private
291                 && m.methodType() != QMetaMethod::Signal
292 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
293                 && m.parameterCount() == 0
294                 && m.returnType() != QMetaType::Void) {
295                 QVariant ret(m.returnType(), (void*)0);
296                 void * qargs[1];
297                 qargs[0] = ret.data();
298
299                 if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
300                     if (ret.isValid() && ret.canConvert(QVariant::String)) {
301                         buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
302                         useDefault = false;
303 #else
304                 && m.parameterTypes().isEmpty()) {
305                 const char* retsig = m.typeName();
306                 if (retsig && *retsig) {
307                     QVariant ret(QMetaType::type(retsig), (void*)0);
308                     void * qargs[1];
309                     qargs[0] = ret.data();
310
311                     if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
312                         if (ret.isValid() && ret.canConvert(QVariant::String)) {
313                             buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
314                             useDefault = false;
315                         }
316 #endif
317                     }
318                 }
319             }
320         }
321     }
322
323     if (useDefault) {
324         const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
325         QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
326         QString str = QString::fromUtf8("%0(name = \"%1\")")
327                       .arg(QLatin1String(meta->className())).arg(name);
328
329         buf = str.toLatin1();
330     }
331     return jsString(exec, buf.constData());
332 }
333
334 JSValue QtInstance::numberValue(ExecState*) const
335 {
336     return jsNumber(0);
337 }
338
339 JSValue QtInstance::booleanValue() const
340 {
341     // ECMA 9.2
342     return jsBoolean(getObject());
343 }
344
345 JSValue QtInstance::valueOf(ExecState* exec) const
346 {
347     return stringValue(exec);
348 }
349
350 QtInstance::QtSenderStack* QtInstance::qtSenderStack()
351 {
352     return senderStack();
353 }
354
355 // In qt_runtime.cpp
356 JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
357 QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
358
359 QByteArray QtField::name() const
360 {
361     if (m_type == MetaProperty)
362         return m_property.name();
363     if (m_type == ChildObject && m_childObject)
364         return m_childObject.data()->objectName().toLatin1();
365 #ifndef QT_NO_PROPERTIES
366     if (m_type == DynamicProperty)
367         return m_dynamicProperty;
368 #endif
369     return QByteArray(); // deleted child object
370 }
371
372 JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
373 {
374     const QtInstance* instance = static_cast<const QtInstance*>(inst);
375     QObject* obj = instance->getObject();
376
377     if (obj) {
378         QVariant val;
379         if (m_type == MetaProperty) {
380             if (m_property.isReadable())
381                 val = m_property.read(obj);
382             else
383                 return jsUndefined();
384         } else if (m_type == ChildObject)
385             val = QVariant::fromValue((QObject*) m_childObject.data());
386 #ifndef QT_NO_PROPERTIES
387         else if (m_type == DynamicProperty)
388             val = obj->property(m_dynamicProperty);
389 #endif
390         return convertQVariantToValue(exec, inst->rootObject(), val);
391     }
392     QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
393     return throwError(exec, createError(exec, msg.toLatin1().constData()));
394 }
395
396 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const
397 {
398     if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
399         return;
400
401     const QtInstance* instance = static_cast<const QtInstance*>(inst);
402     QObject* obj = instance->getObject();
403     if (obj) {
404         QMetaType::Type argtype = QMetaType::Void;
405         if (m_type == MetaProperty)
406 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
407             argtype = (QMetaType::Type) m_property.userType();
408 #else
409             argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
410 #endif
411
412         // dynamic properties just get any QVariant
413         QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
414         if (m_type == MetaProperty) {
415             if (m_property.isWritable())
416                 m_property.write(obj, val);
417         }
418 #ifndef QT_NO_PROPERTIES
419         else if (m_type == DynamicProperty)
420             obj->setProperty(m_dynamicProperty.constData(), val);
421 #endif
422     } else {
423         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
424         throwError(exec, createError(exec, msg.toLatin1().constData()));
425     }
426 }
427
428
429 }
430 }