]> git.saurik.com Git - apple/javascriptcore.git/blob - bindings/qt/qt_class.cpp
59730b875ff4cbf1d37b385440070cbed58af3ff
[apple/javascriptcore.git] / bindings / qt / qt_class.cpp
1 /*
2 * Copyright (C) 2006 Trolltech ASA
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 "identifier.h"
22
23 #include "qt_class.h"
24 #include "qt_instance.h"
25 #include "qt_runtime.h"
26
27 #include <qmetaobject.h>
28 #include <qdebug.h>
29
30 namespace KJS {
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 JSValue* and return the same JSValue for the same
68 // identifier...
69 //
70 // Unfortunately... we need to gcProtect our JSValues, since we don't have
71 // access to an actual JS class that can mark() our JSValues.
72 //
73 JSValue* QtClass::fallbackObject(ExecState *exec, Instance *inst, const Identifier &identifier)
74 {
75 QtInstance* qtinst = static_cast<QtInstance*>(inst);
76
77 QByteArray name(identifier.ascii());
78
79 // First see if we have a cache hit
80 JSValue* val = qtinst->m_methods.value(name);
81 if (val)
82 return val;
83
84 // Nope, create an entry
85 QByteArray normal = QMetaObject::normalizedSignature(name.constData());
86
87 // See if there is an exact match
88 int index = -1;
89 if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) {
90 QMetaMethod m = m_metaObject->method(index);
91 if (m.access() != QMetaMethod::Private) {
92 JSValue *val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false);
93 gcProtect(val);
94 qtinst->m_methods.insert(name, val);
95 return val;
96 }
97 }
98
99 // Nope.. try a basename match
100 int count = m_metaObject->methodCount();
101 for (index = count - 1; index >= 0; --index) {
102 const QMetaMethod m = m_metaObject->method(index);
103 if (m.access() == QMetaMethod::Private)
104 continue;
105
106 QByteArray signature = m.signature();
107 signature.truncate(signature.indexOf('('));
108
109 if (normal == signature) {
110 JSValue* val = new QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false);
111 gcProtect(val);
112 qtinst->m_methods.insert(name, val);
113 return val;
114 }
115 }
116
117 return jsUndefined();
118 }
119
120 // This functionality is handled by the fallback case above...
121 MethodList QtClass::methodsNamed(const Identifier&, Instance*) const
122 {
123 return MethodList();
124 }
125
126 // ### we may end up with a different search order than QtScript by not
127 // folding this code into the fallbackMethod above, but Fields propagate out
128 // of the binding code
129 Field* QtClass::fieldNamed(const Identifier& identifier, Instance* instance) const
130 {
131 // Check static properties first
132 QtInstance* qtinst = static_cast<QtInstance*>(instance);
133
134 QObject* obj = qtinst->getObject();
135 UString ustring = identifier.ustring();
136 QString objName(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()));
137 QByteArray ba = objName.toAscii();
138
139 // First check for a cached field
140 QtField* f = qtinst->m_fields.value(objName);
141
142 if (obj) {
143 if (f) {
144 // We only cache real metaproperties, but we do store the
145 // other types so we can delete them later
146 if (f->fieldType() == QtField::MetaProperty)
147 return f;
148 else if (f->fieldType() == QtField::DynamicProperty) {
149 if (obj->dynamicPropertyNames().indexOf(ba) >= 0)
150 return f;
151 else {
152 // Dynamic property that disappeared
153 qtinst->m_fields.remove(objName);
154 delete f;
155 }
156 } else {
157 QList<QObject*> children = obj->children();
158 for (int index = 0; index < children.count(); ++index) {
159 QObject *child = children.at(index);
160 if (child->objectName() == objName)
161 return f;
162 }
163
164 // Didn't find it, delete it from the cache
165 qtinst->m_fields.remove(objName);
166 delete f;
167 }
168 }
169
170 int index = m_metaObject->indexOfProperty(identifier.ascii());
171 if (index >= 0) {
172 QMetaProperty prop = m_metaObject->property(index);
173
174 if (prop.isScriptable(obj)) {
175 f = new QtField(prop);
176 qtinst->m_fields.insert(objName, f);
177 return f;
178 }
179 }
180
181 // Dynamic properties
182 index = obj->dynamicPropertyNames().indexOf(ba);
183 if (index >= 0) {
184 f = new QtField(ba);
185 qtinst->m_fields.insert(objName, f);
186 return f;
187 }
188
189 // Child objects
190
191 QList<QObject*> children = obj->children();
192 for (index = 0; index < children.count(); ++index) {
193 QObject *child = children.at(index);
194 if (child->objectName() == objName) {
195 f = new QtField(child);
196 qtinst->m_fields.insert(objName, f);
197 return f;
198 }
199 }
200
201 // Nothing named this
202 return 0;
203 } else {
204 QByteArray ba(identifier.ascii());
205 // For compatibility with qtscript, cached methods don't cause
206 // errors until they are accessed, so don't blindly create an error
207 // here.
208 if (qtinst->m_methods.contains(ba))
209 return 0;
210
211 // deleted qobject, but can't throw an error from here (no exec)
212 // create a fake QtField that will throw upon access
213 if (!f) {
214 f = new QtField(ba);
215 qtinst->m_fields.insert(objName, f);
216 }
217 return f;
218 }
219 }
220
221 }
222 }
223