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