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