/* | |
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2 of the License, or (at your option) any later version. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
* | |
*/ | |
#include "config.h" | |
#include "Identifier.h" | |
#include "qt_class.h" | |
#include "qt_instance.h" | |
#include "qt_runtime.h" | |
#include <qmetaobject.h> | |
#include <qdebug.h> | |
namespace JSC { | |
namespace Bindings { | |
QtClass::QtClass(const QMetaObject* mo) | |
: m_metaObject(mo) | |
{ | |
} | |
QtClass::~QtClass() | |
{ | |
} | |
typedef HashMap<const QMetaObject*, QtClass*> ClassesByMetaObject; | |
static ClassesByMetaObject* classesByMetaObject = 0; | |
QtClass* QtClass::classForObject(QObject* o) | |
{ | |
if (!classesByMetaObject) | |
classesByMetaObject = new ClassesByMetaObject; | |
const QMetaObject* mo = o->metaObject(); | |
QtClass* aClass = classesByMetaObject->get(mo); | |
if (!aClass) { | |
aClass = new QtClass(mo); | |
classesByMetaObject->set(mo, aClass); | |
} | |
return aClass; | |
} | |
const char* QtClass::name() const | |
{ | |
return m_metaObject->className(); | |
} | |
// We use this to get at signals (so we can return a proper function object, | |
// and not get wrapped in RuntimeMethod). Also, use this for methods, | |
// so we can cache the object and return the same object for the same | |
// identifier. | |
JSValue QtClass::fallbackObject(ExecState* exec, Instance* inst, const Identifier& identifier) | |
{ | |
QtInstance* qtinst = static_cast<QtInstance*>(inst); | |
QByteArray name(identifier.ascii()); | |
// First see if we have a cache hit | |
JSObject* val = qtinst->m_methods.value(name); | |
if (val) | |
return val; | |
// Nope, create an entry | |
QByteArray normal = QMetaObject::normalizedSignature(name.constData()); | |
// See if there is an exact match | |
int index = -1; | |
if (normal.contains('(') && (index = m_metaObject->indexOfMethod(normal)) != -1) { | |
QMetaMethod m = m_metaObject->method(index); | |
if (m.access() != QMetaMethod::Private) { | |
QtRuntimeMetaMethod* val = new (exec) QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false); | |
qtinst->m_methods.insert(name, val); | |
return val; | |
} | |
} | |
// Nope.. try a basename match | |
int count = m_metaObject->methodCount(); | |
for (index = count - 1; index >= 0; --index) { | |
const QMetaMethod m = m_metaObject->method(index); | |
if (m.access() == QMetaMethod::Private) | |
continue; | |
int iter = 0; | |
const char* signature = m.signature(); | |
while (signature[iter] && signature[iter] != '(') | |
++iter; | |
if (normal == QByteArray::fromRawData(signature, iter)) { | |
QtRuntimeMetaMethod* val = new (exec) QtRuntimeMetaMethod(exec, identifier, static_cast<QtInstance*>(inst), index, normal, false); | |
qtinst->m_methods.insert(name, val); | |
return val; | |
} | |
} | |
return jsUndefined(); | |
} | |
// This functionality is handled by the fallback case above... | |
MethodList QtClass::methodsNamed(const Identifier&, Instance*) const | |
{ | |
return MethodList(); | |
} | |
// ### we may end up with a different search order than QtScript by not | |
// folding this code into the fallbackMethod above, but Fields propagate out | |
// of the binding code | |
Field* QtClass::fieldNamed(const Identifier& identifier, Instance* instance) const | |
{ | |
// Check static properties first | |
QtInstance* qtinst = static_cast<QtInstance*>(instance); | |
QObject* obj = qtinst->getObject(); | |
UString ustring = identifier.ustring(); | |
QString objName((const QChar*)ustring.rep()->characters(), ustring.size()); | |
QByteArray ba = objName.toAscii(); | |
// First check for a cached field | |
QtField* f = qtinst->m_fields.value(objName); | |
if (obj) { | |
if (f) { | |
// We only cache real metaproperties, but we do store the | |
// other types so we can delete them later | |
if (f->fieldType() == QtField::MetaProperty) | |
return f; | |
#ifndef QT_NO_PROPERTIES | |
else if (f->fieldType() == QtField::DynamicProperty) { | |
if (obj->dynamicPropertyNames().indexOf(ba) >= 0) | |
return f; | |
else { | |
// Dynamic property that disappeared | |
qtinst->m_fields.remove(objName); | |
delete f; | |
} | |
} | |
#endif | |
else { | |
QList<QObject*> children = obj->children(); | |
for (int index = 0; index < children.count(); ++index) { | |
QObject *child = children.at(index); | |
if (child->objectName() == objName) | |
return f; | |
} | |
// Didn't find it, delete it from the cache | |
qtinst->m_fields.remove(objName); | |
delete f; | |
} | |
} | |
int index = m_metaObject->indexOfProperty(identifier.ascii()); | |
if (index >= 0) { | |
QMetaProperty prop = m_metaObject->property(index); | |
if (prop.isScriptable(obj)) { | |
f = new QtField(prop); | |
qtinst->m_fields.insert(objName, f); | |
return f; | |
} | |
} | |
#ifndef QT_NO_PROPERTIES | |
// Dynamic properties | |
index = obj->dynamicPropertyNames().indexOf(ba); | |
if (index >= 0) { | |
f = new QtField(ba); | |
qtinst->m_fields.insert(objName, f); | |
return f; | |
} | |
#endif | |
// Child objects | |
QList<QObject*> children = obj->children(); | |
for (index = 0; index < children.count(); ++index) { | |
QObject *child = children.at(index); | |
if (child->objectName() == objName) { | |
f = new QtField(child); | |
qtinst->m_fields.insert(objName, f); | |
return f; | |
} | |
} | |
// Nothing named this | |
return 0; | |
} else { | |
QByteArray ba(identifier.ascii()); | |
// For compatibility with qtscript, cached methods don't cause | |
// errors until they are accessed, so don't blindly create an error | |
// here. | |
if (qtinst->m_methods.contains(ba)) | |
return 0; | |
#ifndef QT_NO_PROPERTIES | |
// deleted qobject, but can't throw an error from here (no exec) | |
// create a fake QtField that will throw upon access | |
if (!f) { | |
f = new QtField(ba); | |
qtinst->m_fields.insert(objName, f); | |
} | |
#endif | |
return f; | |
} | |
} | |
} | |
} | |