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