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_instance.h"
     22 
     23 #include "ArgList.h"
     24 #include "JSDOMBinding.h"
     25 #include "JSGlobalObject.h"
     26 #include "JSLock.h"
     27 #include "qt_class.h"
     28 #include "qt_runtime.h"
     29 #include "PropertyNameArray.h"
     30 #include "runtime_object.h"
     31 #include "ObjectPrototype.h"
     32 #include "Error.h"
     33 
     34 #include <qmetaobject.h>
     35 #include <qdebug.h>
     36 #include <qmetatype.h>
     37 #include <qhash.h>
     38 
     39 namespace JSC {
     40 namespace Bindings {
     41 
     42 // Cache QtInstances
     43 typedef QMultiHash<void*, QtInstance*> QObjectInstanceMap;
     44 static QObjectInstanceMap cachedInstances;
     45 
     46 // Derived RuntimeObject
     47 class QtRuntimeObjectImp : public RuntimeObjectImp {
     48 public:
     49     QtRuntimeObjectImp(ExecState*, PassRefPtr<Instance>);
     50 
     51     static const ClassInfo s_info;
     52 
     53     virtual void markChildren(MarkStack& markStack)
     54     {
     55         RuntimeObjectImp::markChildren(markStack);
     56         QtInstance* instance = static_cast<QtInstance*>(getInternalInstance());
     57         if (instance)
     58             instance->markAggregate(markStack);
     59     }
     60 
     61     static PassRefPtr<Structure> createStructure(JSValue prototype)
     62     {
     63         return Structure::create(prototype, TypeInfo(ObjectType,  StructureFlags), AnonymousSlotCount);
     64     }
     65 
     66 protected:
     67     static const unsigned StructureFlags = RuntimeObjectImp::StructureFlags | OverridesMarkChildren;
     68 
     69 private:
     70     virtual const ClassInfo* classInfo() const { return &s_info; }
     71 };
     72 
     73 const ClassInfo QtRuntimeObjectImp::s_info = { "QtRuntimeObject", &RuntimeObjectImp::s_info, 0, 0 };
     74 
     75 QtRuntimeObjectImp::QtRuntimeObjectImp(ExecState* exec, PassRefPtr<Instance> instance)
     76     : RuntimeObjectImp(exec, WebCore::deprecatedGetDOMStructure<QtRuntimeObjectImp>(exec), instance)
     77 {
     78 }
     79 
     80 // QtInstance
     81 QtInstance::QtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
     82     : Instance(rootObject)
     83     , m_class(0)
     84     , m_object(o)
     85     , m_hashkey(o)
     86     , m_defaultMethod(0)
     87     , m_ownership(ownership)
     88 {
     89 }
     90 
     91 QtInstance::~QtInstance()
     92 {
     93     JSLock lock(SilenceAssertionsOnly);
     94 
     95     cachedInstances.remove(m_hashkey);
     96 
     97     // clean up (unprotect from gc) the JSValues we've created
     98     m_methods.clear();
     99 
    100     qDeleteAll(m_fields);
    101     m_fields.clear();
    102 
    103     if (m_object) {
    104         switch (m_ownership) {
    105         case QScriptEngine::QtOwnership:
    106             break;
    107         case QScriptEngine::AutoOwnership:
    108             if (m_object->parent())
    109                 break;
    110             // fall through!
    111         case QScriptEngine::ScriptOwnership:
    112             delete m_object;
    113             break;
    114         }
    115     }
    116 }
    117 
    118 PassRefPtr<QtInstance> QtInstance::getQtInstance(QObject* o, PassRefPtr<RootObject> rootObject, QScriptEngine::ValueOwnership ownership)
    119 {
    120     JSLock lock(SilenceAssertionsOnly);
    121 
    122     foreach(QtInstance* instance, cachedInstances.values(o))
    123         if (instance->rootObject() == rootObject) {
    124             // The garbage collector removes instances, but it may happen that the wrapped
    125             // QObject dies before the gc kicks in. To handle that case we have to do an additional
    126             // check if to see if the instance's wrapped object is still alive. If it isn't, then
    127             // we have to create a new wrapper.
    128             if (!instance->getObject())
    129                 cachedInstances.remove(instance->hashKey());
    130             else
    131                 return instance;
    132         }
    133 
    134     RefPtr<QtInstance> ret = QtInstance::create(o, rootObject, ownership);
    135     cachedInstances.insert(o, ret.get());
    136 
    137     return ret.release();
    138 }
    139 
    140 bool QtInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
    141 {
    142     return object->JSObject::getOwnPropertySlot(exec, propertyName, slot);
    143 }
    144 
    145 void QtInstance::put(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
    146 {
    147     object->JSObject::put(exec, propertyName, value, slot);
    148 }
    149 
    150 void QtInstance::removeCachedMethod(JSObject* method)
    151 {
    152     if (m_defaultMethod == method)
    153         m_defaultMethod = 0;
    154 
    155     for(QHash<QByteArray, JSObject*>::Iterator it = m_methods.begin(),
    156         end = m_methods.end(); it != end; ++it)
    157         if (it.value() == method) {
    158             m_methods.erase(it);
    159             return;
    160         }
    161 }
    162 
    163 QtInstance* QtInstance::getInstance(JSObject* object)
    164 {
    165     if (!object)
    166         return 0;
    167     if (!object->inherits(&QtRuntimeObjectImp::s_info))
    168         return 0;
    169     return static_cast<QtInstance*>(static_cast<RuntimeObjectImp*>(object)->getInternalInstance());
    170 }
    171 
    172 Class* QtInstance::getClass() const
    173 {
    174     if (!m_class)
    175         m_class = QtClass::classForObject(m_object);
    176     return m_class;
    177 }
    178 
    179 RuntimeObjectImp* QtInstance::newRuntimeObject(ExecState* exec)
    180 {
    181     JSLock lock(SilenceAssertionsOnly);
    182     return new (exec) QtRuntimeObjectImp(exec, this);
    183 }
    184 
    185 void QtInstance::markAggregate(MarkStack& markStack)
    186 {
    187     if (m_defaultMethod)
    188         markStack.append(m_defaultMethod);
    189     foreach(JSObject* val, m_methods.values()) {
    190         if (val)
    191             markStack.append(val);
    192     }
    193 }
    194 
    195 void QtInstance::begin()
    196 {
    197     // Do nothing.
    198 }
    199 
    200 void QtInstance::end()
    201 {
    202     // Do nothing.
    203 }
    204 
    205 void QtInstance::getPropertyNames(ExecState* exec, PropertyNameArray& array)
    206 {
    207     // This is the enumerable properties, so put:
    208     // properties
    209     // dynamic properties
    210     // slots
    211     QObject* obj = getObject();
    212     if (obj) {
    213         const QMetaObject* meta = obj->metaObject();
    214 
    215         int i;
    216         for (i=0; i < meta->propertyCount(); i++) {
    217             QMetaProperty prop = meta->property(i);
    218             if (prop.isScriptable()) {
    219                 array.add(Identifier(exec, prop.name()));
    220             }
    221         }
    222 
    223         QList<QByteArray> dynProps = obj->dynamicPropertyNames();
    224         foreach(QByteArray ba, dynProps) {
    225             array.add(Identifier(exec, ba.constData()));
    226         }
    227 
    228         for (i=0; i < meta->methodCount(); i++) {
    229             QMetaMethod method = meta->method(i);
    230             if (method.access() != QMetaMethod::Private) {
    231                 array.add(Identifier(exec, method.signature()));
    232             }
    233         }
    234     }
    235 }
    236 
    237 JSValue QtInstance::invokeMethod(ExecState*, const MethodList&, const ArgList&)
    238 {
    239     // Implemented via fallbackMethod & QtRuntimeMetaMethod::callAsFunction
    240     return jsUndefined();
    241 }
    242 
    243 
    244 JSValue QtInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
    245 {
    246     if (hint == PreferString)
    247         return stringValue(exec);
    248     if (hint == PreferNumber)
    249         return numberValue(exec);
    250     return valueOf(exec);
    251 }
    252 
    253 JSValue QtInstance::stringValue(ExecState* exec) const
    254 {
    255     // Hmm.. see if there is a toString defined
    256     QByteArray buf;
    257     bool useDefault = true;
    258     getClass();
    259     QObject* obj = getObject();
    260     if (m_class && obj) {
    261         // Cheat and don't use the full name resolution
    262         int index = obj->metaObject()->indexOfMethod("toString()");
    263         if (index >= 0) {
    264             QMetaMethod m = obj->metaObject()->method(index);
    265             // Check to see how much we can call it
    266             if (m.access() != QMetaMethod::Private
    267                 && m.methodType() != QMetaMethod::Signal
    268                 && m.parameterTypes().count() == 0) {
    269                 const char* retsig = m.typeName();
    270                 if (retsig && *retsig) {
    271                     QVariant ret(QMetaType::type(retsig), (void*)0);
    272                     void * qargs[1];
    273                     qargs[0] = ret.data();
    274 
    275                     if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, index, qargs) < 0) {
    276                         if (ret.isValid() && ret.canConvert(QVariant::String)) {
    277                             buf = ret.toString().toLatin1().constData(); // ### Latin 1? Ascii?
    278                             useDefault = false;
    279                         }
    280                     }
    281                 }
    282             }
    283         }
    284     }
    285 
    286     if (useDefault) {
    287         const QMetaObject* meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
    288         QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
    289         QString str = QString::fromUtf8("%0(name = \"%1\")")
    290                       .arg(QLatin1String(meta->className())).arg(name);
    291 
    292         buf = str.toLatin1();
    293     }
    294     return jsString(exec, buf.constData());
    295 }
    296 
    297 JSValue QtInstance::numberValue(ExecState* exec) const
    298 {
    299     return jsNumber(exec, 0);
    300 }
    301 
    302 JSValue QtInstance::booleanValue() const
    303 {
    304     // ECMA 9.2
    305     return jsBoolean(true);
    306 }
    307 
    308 JSValue QtInstance::valueOf(ExecState* exec) const
    309 {
    310     return stringValue(exec);
    311 }
    312 
    313 // In qt_runtime.cpp
    314 JSValue convertQVariantToValue(ExecState*, PassRefPtr<RootObject> root, const QVariant& variant);
    315 QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type hint, int *distance);
    316 
    317 const char* QtField::name() const
    318 {
    319     if (m_type == MetaProperty)
    320         return m_property.name();
    321     else if (m_type == ChildObject && m_childObject)
    322         return m_childObject->objectName().toLatin1();
    323     else if (m_type == DynamicProperty)
    324         return m_dynamicProperty.constData();
    325     return ""; // deleted child object
    326 }
    327 
    328 JSValue QtField::valueFromInstance(ExecState* exec, const Instance* inst) const
    329 {
    330     const QtInstance* instance = static_cast<const QtInstance*>(inst);
    331     QObject* obj = instance->getObject();
    332 
    333     if (obj) {
    334         QVariant val;
    335         if (m_type == MetaProperty) {
    336             if (m_property.isReadable())
    337                 val = m_property.read(obj);
    338             else
    339                 return jsUndefined();
    340         } else if (m_type == ChildObject)
    341             val = QVariant::fromValue((QObject*) m_childObject);
    342         else if (m_type == DynamicProperty)
    343             val = obj->property(m_dynamicProperty);
    344 
    345         return convertQVariantToValue(exec, inst->rootObject(), val);
    346     } else {
    347         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
    348         return throwError(exec, GeneralError, msg.toLatin1().constData());
    349     }
    350 }
    351 
    352 void QtField::setValueToInstance(ExecState* exec, const Instance* inst, JSValue aValue) const
    353 {
    354     if (m_type == ChildObject) // QtScript doesn't allow setting to a named child
    355         return;
    356 
    357     const QtInstance* instance = static_cast<const QtInstance*>(inst);
    358     QObject* obj = instance->getObject();
    359     if (obj) {
    360         QMetaType::Type argtype = QMetaType::Void;
    361         if (m_type == MetaProperty)
    362             argtype = (QMetaType::Type) QMetaType::type(m_property.typeName());
    363 
    364         // dynamic properties just get any QVariant
    365         QVariant val = convertValueToQVariant(exec, aValue, argtype, 0);
    366         if (m_type == MetaProperty) {
    367             if (m_property.isWritable())
    368                 m_property.write(obj, val);
    369         } else if (m_type == DynamicProperty)
    370             obj->setProperty(m_dynamicProperty.constData(), val);
    371     } else {
    372         QString msg = QString(QLatin1String("cannot access member `%1' of deleted QObject")).arg(QLatin1String(name()));
    373         throwError(exec, GeneralError, msg.toLatin1().constData());
    374     }
    375 }
    376 
    377 
    378 }
    379 }
    380