[Qt] Port meta method/signal/slot handling in run-time bridge to use JSC C API
[WebKit-https.git] / Source / WebCore / bridge / qt / qt_class.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_class.h"
22
23 #include "APICast.h"
24 #include "Identifier.h"
25 #include "qt_instance.h"
26 #include "qt_runtime.h"
27
28 #include <qdebug.h>
29 #include <qmetaobject.h>
30
31 namespace JSC {
32 namespace Bindings {
33
34 QtClass::QtClass(const QMetaObject* mo)
35     : m_metaObject(mo)
36 {
37 }
38
39 QtClass::~QtClass()
40 {
41 }
42
43 typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject;
44 static ClassesByMetaObject* classesByMetaObject = 0;
45
46 QtClass* QtClass::classForObject(QObject* o)
47 {
48     if (!classesByMetaObject)
49         classesByMetaObject = new ClassesByMetaObject;
50
51     const QMetaObject* mo = o->metaObject();
52     QtClass* aClass = classesByMetaObject->get(mo);
53     if (!aClass) {
54         aClass = new QtClass(mo);
55         classesByMetaObject->set(mo, aClass);
56     }
57
58     return aClass;
59 }
60
61 const char* QtClass::name() const
62 {
63     return m_metaObject->className();
64 }
65
66 // We use this to get at signals (so we can return a proper function object,
67 // and not get wrapped in RuntimeMethod). Also, use this for methods,
68 // so we can cache the object and return the same object for the same
69 // identifier.
70 JSValue QtClass::fallbackObject(ExecState* exec, Instance* inst, PropertyName identifier)
71 {
72     QtInstance* qtinst = static_cast<QtInstance*>(inst);
73     JSContextRef context = toRef(exec);
74     JSValueRef* exception = 0;
75
76     UString ustring(identifier.publicName());
77     const QByteArray name = QString(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length()).toLatin1();
78
79     // First see if we have a cache hit
80     if (QtRuntimeMethod* method = qtinst->m_methods.value(name))
81         return toJS(method->jsObjectRef(context, exception));
82
83     // Nope, create an entry
84     const QByteArray normal = QMetaObject::normalizedSignature(name.constData());
85
86     // See if there is an exact match
87     int index = -1;
88     QMetaMethod metaMethod;
89     if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) {
90         metaMethod = m_metaObject->method(index);
91         if (metaMethod.access() == QMetaMethod::Private)
92             index = -1;
93     }
94
95     // Nope.. try a basename match
96     if (index == -1) {
97         const int count = m_metaObject->methodCount();
98         for (index = count - 1; index >= 0; --index) {
99             metaMethod = m_metaObject->method(index);
100             if (metaMethod.access() == QMetaMethod::Private)
101                 continue;
102
103             if (metaMethod.name() == normal)
104                 break;
105         }
106     }
107
108     if (index == -1)
109         return jsUndefined();
110
111     int flags = metaMethod.methodType() == QMetaMethod::Signal ? QtRuntimeMethod::MethodIsSignal : 0;
112     QtRuntimeMethod* method = new QtRuntimeMethod(context, exception, static_cast<QtInstance*>(inst)->getObject(), normal, index, flags, qtinst);
113     qtinst->m_methods.insert(name, method);
114     return toJS(method->jsObjectRef(context, exception));
115 }
116
117 // This functionality is handled by the fallback case above...
118 MethodList QtClass::methodsNamed(PropertyName, Instance*) const
119 {
120     return MethodList();
121 }
122
123 // ### we may end up with a different search order than QtScript by not
124 // folding this code into the fallbackMethod above, but Fields propagate out
125 // of the binding code
126 Field* QtClass::fieldNamed(PropertyName identifier, Instance* instance) const
127 {
128     // Check static properties first
129     QtInstance* qtinst = static_cast<QtInstance*>(instance);
130
131     QObject* obj = qtinst->getObject();
132     UString ustring(identifier.publicName());
133     const QString name(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length());
134     const QByteArray ascii = name.toLatin1();
135
136     // First check for a cached field
137     QtField* f = qtinst->m_fields.value(name);
138
139     if (obj) {
140         if (f) {
141             // We only cache real metaproperties, but we do store the
142             // other types so we can delete them later
143             if (f->fieldType() == QtField::MetaProperty)
144                 return f;
145 #ifndef QT_NO_PROPERTIES
146             if (f->fieldType() == QtField::DynamicProperty) {
147                 if (obj->dynamicPropertyNames().indexOf(ascii) >= 0)
148                     return f;
149                 // Dynamic property that disappeared
150                 qtinst->m_fields.remove(name);
151                 delete f;
152             }
153 #endif
154             else {
155                 const QList<QObject*>& children = obj->children();
156                 const int count = children.size();
157                 for (int index = 0; index < count; ++index) {
158                     QObject* child = children.at(index);
159                     if (child->objectName() == name)
160                         return f;
161                 }
162
163                 // Didn't find it, delete it from the cache
164                 qtinst->m_fields.remove(name);
165                 delete f;
166             }
167         }
168
169         int index = m_metaObject->indexOfProperty(ascii);
170         if (index >= 0) {
171             const QMetaProperty prop = m_metaObject->property(index);
172
173             if (prop.isScriptable(obj)) {
174                 f = new QtField(prop);
175                 qtinst->m_fields.insert(name, f);
176                 return f;
177             }
178         }
179
180 #ifndef QT_NO_PROPERTIES
181         // Dynamic properties
182         index = obj->dynamicPropertyNames().indexOf(ascii);
183         if (index >= 0) {
184             f = new QtField(ascii);
185             qtinst->m_fields.insert(name, f);
186             return f;
187         }
188 #endif
189
190         // Child objects
191
192         const QList<QObject*>& children = obj->children();
193         const int count = children.count();
194         for (index = 0; index < count; ++index) {
195             QObject* child = children.at(index);
196             if (child->objectName() == name) {
197                 f = new QtField(child);
198                 qtinst->m_fields.insert(name, f);
199                 return f;
200             }
201         }
202
203         // Nothing named this
204         return 0;
205     }
206     // For compatibility with qtscript, cached methods don't cause
207     // errors until they are accessed, so don't blindly create an error
208     // here.
209     if (qtinst->m_methods.contains(ascii))
210         return 0;
211
212 #ifndef QT_NO_PROPERTIES
213     // deleted qobject, but can't throw an error from here (no exec)
214     // create a fake QtField that will throw upon access
215     if (!f) {
216         f = new QtField(ascii);
217         qtinst->m_fields.insert(name, f);
218     }
219 #endif
220     return f;
221 }
222
223 }
224 }
225