Home | History | Annotate | Download | only in qt
      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 
     30 namespace JSC {
     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 object and return the same object for the same
     68 // identifier.
     69 JSValue 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...
    118 MethodList 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
    126 Field* 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