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