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
30namespace JSC {
31namespace Bindings {
32
33QtClass::QtClass(const QMetaObject* mo)
34    : m_metaObject(mo)
35{
36}
37
38QtClass::~QtClass()
39{
40}
41
42typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject;
43static ClassesByMetaObject* classesByMetaObject = 0;
44
45QtClass* 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
60const 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.
69JSValue 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...
115MethodList 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
123Field* 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