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