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
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    const UString& ustring = identifier.ustring();
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 = new (exec) QtRuntimeMetaMethod(exec, identifier, 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        int iter = 0;
103        const char* signature = m.signature();
104        while (signature[iter] && signature[iter] != '(')
105            ++iter;
106
107        if (normal == QByteArray::fromRawData(signature, iter)) {
108            QtRuntimeMetaMethod* val = new (exec) QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false);
109            qtinst->m_methods.insert(name, WriteBarrier<JSObject>(exec->globalData(), qtinst->createRuntimeObject(exec), val));
110            return val;
111        }
112    }
113
114    return jsUndefined();
115}
116
117// This functionality is handled by the fallback case above...
118MethodList QtClass::methodsNamed(const Identifier&, 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
126Field* QtClass::fieldNamed(const Identifier& identifier, Instance* instance) const
127{
128    // Check static properties first
129    QtInstance* qtinst = static_cast<QtInstance*>(instance);
130
131    QObject* obj = qtinst->getObject();
132    const UString& ustring = identifier.ustring();
133    const QString name(reinterpret_cast<const QChar*>(ustring.characters()), ustring.length());
134    const QByteArray ascii = name.toAscii();
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
226