File indexing completed on 2025-02-16 13:08:58
0001 /* This file is part of the KDE libraries 0002 Copyright (C) 2005, 2006 Ian Reinhart Geiser <geiseri@kde.org> 0003 Copyright (C) 2005, 2006 Matt Broadstone <mbroadst@gmail.com> 0004 Copyright (C) 2005, 2006 Richard J. Moore <rich@kde.org> 0005 Copyright (C) 2005, 2006 Erik L. Bunce <kde@bunce.us> 0006 Copyright (C) 2007, 2008 Sebastian Sauer <mail@dipe.org> 0007 0008 This library is free software; you can redistribute it and/or 0009 modify it under the terms of the GNU Library General Public 0010 License as published by the Free Software Foundation; either 0011 version 2 of the License, or (at your option) any later version. 0012 0013 This library is distributed in the hope that it will be useful, 0014 but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 Library General Public License for more details. 0017 0018 You should have received a copy of the GNU Library General Public License 0019 along with this library; see the file COPYING.LIB. If not, write to 0020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 Boston, MA 02110-1301, USA. 0022 */ 0023 #include "qobject_binding.h" 0024 0025 #include <QObject> 0026 #include <QArgument> 0027 #include <QMetaEnum> 0028 #include <QMetaType> 0029 #include <QVariant> 0030 #include <QVector> 0031 #include <QUrl> 0032 #include <QWidget> 0033 0034 #include "slotproxy.h" 0035 #include "eventproxy.h" 0036 #include "jseventmapper.h" 0037 #include "pointer.h" 0038 #include "variant_binding.h" 0039 0040 #include <kjs/array_instance.h> 0041 #include <kjs/function_object.h> 0042 0043 //#define CREATEQOBJ_DIAG 0044 0045 using namespace KJSEmbed; 0046 0047 QByteArray createSignal(const QByteArray &sig) 0048 { 0049 return '2' + sig; 0050 } 0051 0052 QByteArray createSlot(const QByteArray &slt) 0053 { 0054 return '1' + slt; 0055 } 0056 0057 bool validSlot(const QMetaMethod &method, QObjectBinding::AccessFlags accessflags) 0058 { 0059 switch (method.access()) { 0060 case QMetaMethod::Private: { 0061 if (!(accessflags & QObjectBinding::PrivateSlots)) { 0062 return false; 0063 } 0064 } break; 0065 case QMetaMethod::Protected: { 0066 if (!(accessflags & QObjectBinding::ProtectedSlots)) { 0067 return false; 0068 } 0069 } break; 0070 case QMetaMethod::Public: { 0071 if (!(accessflags & QObjectBinding::PublicSlots)) { 0072 return false; 0073 } 0074 } break; 0075 } 0076 if (method.attributes() & QMetaMethod::Scriptable) { 0077 if (!(accessflags & QObjectBinding::ScriptableSlots)) { 0078 return false; 0079 } 0080 } else { 0081 if (!(accessflags & QObjectBinding::NonScriptableSlots)) { 0082 return false; 0083 } 0084 } 0085 return true; 0086 } 0087 0088 bool validSignal(const QMetaMethod &method, QObjectBinding::AccessFlags accessflags) 0089 { 0090 switch (method.access()) { 0091 case QMetaMethod::Private: { 0092 if (!(accessflags & QObjectBinding::PrivateSignals)) { 0093 return false; 0094 } 0095 } break; 0096 case QMetaMethod::Protected: { 0097 if (!(accessflags & QObjectBinding::ProtectedSignals)) { 0098 return false; 0099 } 0100 } break; 0101 case QMetaMethod::Public: { 0102 if (!(accessflags & QObjectBinding::PublicSignals)) { 0103 return false; 0104 } 0105 } break; 0106 } 0107 if (method.attributes() & QMetaMethod::Scriptable) { 0108 if (!(accessflags & QObjectBinding::ScriptableSignals)) { 0109 return false; 0110 } 0111 } else { 0112 if (!(accessflags & QObjectBinding::NonScriptableSignals)) { 0113 return false; 0114 } 0115 } 0116 return true; 0117 } 0118 0119 bool validProperty(const QMetaProperty &property, QObjectBinding::AccessFlags accessflags) 0120 { 0121 if (property.isScriptable()) { 0122 if (!(accessflags & QObjectBinding::ScriptableProperties)) { 0123 return false; 0124 } 0125 } else { 0126 if (!(accessflags & QObjectBinding::NonScriptableProperties)) { 0127 return false; 0128 } 0129 } 0130 return true; 0131 } 0132 0133 KJS::JSValue *callConnect(KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args) 0134 { 0135 KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, self); 0136 if (!imp) { // No implementation, so we need to use the first argument as we are a global static invocation. 0137 imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[0]); 0138 } 0139 if (!imp) { 0140 return KJS::throwError(exec, KJS::GeneralError, i18n("Wrong object type.")); 0141 } 0142 //return KJSEmbed::throwError(exec, i18n("Wrong object type.")); 0143 0144 if (args.size() > 2) { 0145 KJSEmbed::QObjectBinding *senderImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[0]); 0146 if (!senderImp) { 0147 return KJS::throwError(exec, KJS::GeneralError, i18n("First argument must be a QObject.")); 0148 //return KJSEmbed::throwError(exec, i18n("First argument must be a QObject")); 0149 } 0150 QObject *receiver = nullptr; 0151 QObject *sender = senderImp->object<QObject>(); 0152 QByteArray signal = createSignal(args[1]->toString(exec).ascii()); 0153 QByteArray slot; 0154 KJSEmbed::QObjectBinding *receiverImp = nullptr; 0155 if (args.size() >= 4) { 0156 slot = createSlot(args[3]->toString(exec).ascii()); 0157 receiverImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[2]); 0158 if (!receiverImp) { 0159 receiver = new SlotProxy(args[2]->toObject(exec), exec->dynamicInterpreter(), sender, args[3]->toString(exec).ascii()); 0160 } else { 0161 receiver = receiverImp->object<QObject>(); 0162 } 0163 } else { 0164 receiverImp = imp; 0165 receiver = imp->object<QObject>(); 0166 slot = createSlot(args[2]->toString(exec).ascii()); 0167 } 0168 0169 const QMetaObject *senderMetaObject = sender->metaObject(); 0170 QMetaMethod senderMetaMethod = senderMetaObject->method(senderMetaObject->indexOfSignal(signal.constData())); 0171 0172 const QMetaObject *receiverMetaObject = receiver->metaObject(); 0173 QMetaMethod receiverMetaMethod = receiverMetaObject->method(receiverMetaObject->indexOfSlot(slot.constData())); 0174 0175 if (validSignal(senderMetaMethod, senderImp->access()) && (!receiverImp || validSlot(receiverMetaMethod, receiverImp->access()))) { 0176 return KJS::jsBoolean(QObject::connect(sender, signal.constData(), receiver, slot.constData())); 0177 } 0178 0179 return KJS::jsBoolean(false); 0180 } 0181 return KJS::throwError(exec, KJS::GeneralError, i18n("Incorrect number of arguments.")); 0182 //return KJSEmbed::throwError(exec, i18n("Incorrect number of arguments.")); 0183 } 0184 0185 QByteArray extractMemberName(const QMetaMethod &member) 0186 { 0187 QString sig = member.methodSignature(); 0188 return sig.left(sig.indexOf('(')).toLatin1(); 0189 } 0190 0191 void QObjectBinding::publishQObject(KJS::ExecState *exec, KJS::JSObject *target, QObject *object) 0192 { 0193 KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, target); 0194 Q_ASSERT(imp); 0195 0196 // Add the children the QObject has. 0197 if (imp->access() & QObjectBinding::ChildObjects) { 0198 //TODO uh, this one is dirty cause it may eat a lot of time to publish things that may not 0199 //got accessed anyway. Better solution would be to provide access to them on demand only. That 0200 //would also allow to manipulate the QObject-tree at runtime what is currently not possible. 0201 QObjectList children = object->children(); 0202 QObjectList::Iterator child = children.begin(); 0203 for (; child != children.end(); ++child) { 0204 QString objectName = (*child)->objectName(); 0205 if (!objectName.isEmpty()) { 0206 KJS::JSObject *childObject = KJSEmbed::createQObject(exec, *child); 0207 KJSEmbed::QObjectBinding *childImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, childObject); 0208 if (childImp) { 0209 childImp->setAccess(imp->access()); // inherit access from parent 0210 target->put(exec, KJS::Identifier(toUString(objectName)), childObject); 0211 } 0212 } 0213 } 0214 } 0215 0216 // Add slots of the current object. 0217 const QMetaObject *metaObject = object->metaObject(); 0218 int methods = metaObject->methodCount(); 0219 for (int idx = 0; idx < methods; ++idx) { 0220 QMetaMethod member = metaObject->method(idx); 0221 if (validSlot(member, imp->access())) { 0222 target->put(exec, KJS::Identifier(extractMemberName(member).constData()), 0223 new SlotBinding(exec, member), KJS::DontDelete | KJS::ReadOnly | KJS::Function); 0224 } 0225 } 0226 0227 // Add enums as read only uints. 0228 int enums = metaObject->enumeratorCount(); 0229 for (int idx = 0; idx < enums; ++idx) { 0230 QMetaEnum enumerator = metaObject->enumerator(idx); 0231 int keys = enumerator.keyCount(); 0232 for (int key = 0; key < keys; ++key) { 0233 target->put(exec, KJS::Identifier(enumerator.key(key)), 0234 KJS::jsNumber(enumerator.value(key)), KJS::DontDelete | KJS::ReadOnly); 0235 } 0236 } 0237 } 0238 0239 QObjectBinding::QObjectBinding(KJS::ExecState *exec, QObject *object) 0240 : ObjectBinding(exec, object->metaObject()->className(), object) 0241 , m_evproxy(nullptr) 0242 , m_access(AllSlots | AllSignals | AllProperties | AllObjects) 0243 { 0244 if (object->parent() != nullptr) { 0245 setOwnership(ObjectBinding::QObjOwned); 0246 } else { 0247 setOwnership(ObjectBinding::JSOwned); 0248 } 0249 0250 m_cleanupHandler = new QObjectCleanupHandler(); 0251 watchObject(object); 0252 0253 StaticBinding::publish(exec, this, QObjectFactory::methods()); 0254 QObjectBinding::publishQObject(exec, this, object); 0255 0256 // Make "connect" a global static method. 0257 exec->dynamicInterpreter()->globalObject()->put(exec, "connect", new StaticBinding(exec, &QObjectFactory::methods()[0])); 0258 } 0259 0260 QObjectBinding::~QObjectBinding() 0261 { 0262 if (m_cleanupHandler->isEmpty()) { 0263 setOwnership(ObjectBinding::QObjOwned); 0264 } else if (object<QObject>()->parent() != nullptr) { 0265 setOwnership(ObjectBinding::QObjOwned); 0266 m_cleanupHandler->remove(object<QObject>()); 0267 } else if (ownership() != ObjectBinding::JSOwned) { 0268 m_cleanupHandler->remove(object<QObject>()); 0269 } else { 0270 m_cleanupHandler->remove(object<QObject>()); 0271 } 0272 0273 delete m_cleanupHandler; 0274 } 0275 0276 void QObjectBinding::watchObject(QObject *object) 0277 { 0278 m_cleanupHandler->add(object); 0279 } 0280 0281 bool QObjectBinding::getOwnPropertySlot(KJS::ExecState *exec, const KJS::Identifier &propertyName, KJS::PropertySlot &slot) 0282 { 0283 // qDebug() << "getOwnPropertySlot called"; 0284 QObject *obj = object<QObject>(); 0285 const QMetaObject *meta = obj->metaObject(); 0286 int propIndex = meta->indexOfProperty(propertyName.ascii()); 0287 if (propIndex != -1) { 0288 if (! validProperty(meta->property(propIndex), m_access)) { 0289 return false; 0290 } 0291 // qDebug() << "getOwnPropertySlot found the property " << propertyName.ascii(); 0292 slot.setCustom(this, propertyGetter); 0293 return true; 0294 } 0295 return ObjectBinding::getOwnPropertySlot(exec, propertyName, slot); 0296 } 0297 0298 KJS::JSValue *QObjectBinding::propertyGetter(KJS::ExecState *exec, KJS::JSObject *, 0299 const KJS::Identifier &propertyName, const KJS::PropertySlot &slot) 0300 { 0301 // qDebug() << "Getter was called"; 0302 QObjectBinding *self = static_cast<QObjectBinding *>(slot.slotBase()); 0303 QObject *obj = self->object<QObject>(); 0304 0305 QVariant val = obj->property(propertyName.ascii()); 0306 if (val.isValid()) { 0307 return convertToValue(exec, val); 0308 } 0309 qDebug() << QString("propertyGetter called but no property, name was '%1'").arg(propertyName.ascii()); 0310 return nullptr; // ERROR 0311 } 0312 0313 QObjectBinding::AccessFlags QObjectBinding::access() const 0314 { 0315 return m_access; 0316 } 0317 0318 void QObjectBinding::setAccess(QObjectBinding::AccessFlags access) 0319 { 0320 m_access = access; 0321 } 0322 0323 void QObjectBinding::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, KJS::JSValue *value, int attr) 0324 { 0325 QObject *obj = object<QObject>(); 0326 if (obj && !m_cleanupHandler->isEmpty()) { 0327 // Properties 0328 const QMetaObject *meta = obj->metaObject(); 0329 0330 if (int propIndex = meta->indexOfProperty(propertyName.ascii()) != -1) { 0331 QMetaProperty prop = meta->property(propIndex); 0332 if (! validProperty(prop, m_access)) { 0333 return; 0334 } 0335 0336 bool propSet = false; 0337 QVariant val = convertToVariant(exec, value); 0338 if (prop.isEnumType()) { 0339 propSet = obj->setProperty(propertyName.ascii(), val.toUInt()); 0340 } else if (val.isValid() /*&& meta->property(propIndex).isWritable() <- wtf?*/) { 0341 propSet = obj->setProperty(propertyName.ascii(), val); 0342 } 0343 /* 0344 if( !propSet ) 0345 { 0346 KJSEmbed::throwError(exec, 0347 i18n("Setting property %1 failed: property invalid, read-only or does not exist").arg(propertyName.ascii())); 0348 } 0349 */ 0350 0351 } 0352 0353 if (JSEventMapper::mapper()->isEventHandler(propertyName)) { 0354 if (!m_evproxy) { 0355 m_evproxy = new KJSEmbed::EventProxy(this, exec->dynamicInterpreter()); 0356 } 0357 if (value) { 0358 m_evproxy->addFilter(JSEventMapper::mapper()->findEventType(propertyName)); 0359 } else { 0360 m_evproxy->removeFilter(JSEventMapper::mapper()->findEventType(propertyName)); 0361 } 0362 } 0363 } 0364 //qDebug() << "Forward put"; 0365 // Set a property value 0366 ObjectBinding::put(exec, propertyName, value, attr); 0367 } 0368 0369 bool QObjectBinding::canPut(KJS::ExecState *exec, const KJS::Identifier &propertyName) const 0370 { 0371 QObject *obj = object<QObject>(); 0372 if (obj && !m_cleanupHandler->isEmpty()) { 0373 // Properties 0374 const QMetaObject *meta = obj->metaObject(); 0375 if (int propIndex = meta->indexOfProperty(propertyName.ascii()) != -1) { 0376 QMetaProperty prop = meta->property(propIndex); 0377 return validProperty(prop, m_access) && prop.isWritable(); 0378 } 0379 } 0380 return ObjectBinding::canPut(exec, propertyName); 0381 } 0382 0383 KJS::UString QObjectBinding::className() const 0384 { 0385 return toUString(typeName()); 0386 } 0387 0388 KJS::UString QObjectBinding::toString(KJS::ExecState *exec) const 0389 { 0390 Q_UNUSED(exec); 0391 QString s("%1 (%2)"); 0392 s = s.arg(object<QObject>()->objectName()); 0393 s = s.arg(typeName()); 0394 return toUString(s); 0395 } 0396 0397 PointerBase *getArg(KJS::ExecState *exec, const QList<QByteArray> &types, const KJS::List &args, int idx, QString &errorText) 0398 { 0399 //qDebug("Index %d, args size %d, types size %d", idx, args.size(), types.size() ); 0400 0401 if (types.size() == 0 && idx == 0) { 0402 return new NullPtr(); 0403 } 0404 if (args.size() <= idx) { 0405 return new NullPtr(); 0406 } 0407 0408 if (types.size() <= idx) { 0409 const QString firstPart = i18np("The slot asked for %1 argument", "The slot asked for %1 arguments", idx); 0410 const QString secondPart = i18np("but there is only %1 available", "but there are only %1 available", types.size()); 0411 errorText = i18nc("%1 is 'the slot asked for foo arguments', %2 is 'but there are only bar available'", "%1, %2."); 0412 0413 return nullptr; 0414 } 0415 0416 QVariant::Type varianttype = QVariant::nameToType(types[idx].constData()); 0417 //qDebug( QString("type=%1 argtype=%2 variantType=%3 (%4)").arg(types[idx].constData()).arg(args[idx]->type()).arg(varianttype).arg(QVariant::typeToName(varianttype)).toLatin1() ); 0418 switch (varianttype) { 0419 case QVariant::Int: 0420 if (args[idx]->type() == KJS::NumberType) { 0421 return new Value<int>(int(args[idx]->toInteger(exec))); 0422 } 0423 break; 0424 case QVariant::UInt: 0425 if (args[idx]->type() == KJS::NumberType) { 0426 return new Value<uint>(uint(args[idx]->toInteger(exec))); 0427 } 0428 break; 0429 case QVariant::LongLong: 0430 if (args[idx]->type() == KJS::NumberType) { 0431 return new Value<qlonglong>(qlonglong(args[idx]->toInteger(exec))); 0432 } 0433 break; 0434 case QVariant::ULongLong: 0435 if (args[idx]->type() == KJS::NumberType) { 0436 return new Value<qulonglong>(qulonglong(args[idx]->toInteger(exec))); 0437 } 0438 break; 0439 case QVariant::Double: 0440 if (args[idx]->type() == KJS::NumberType) { 0441 return new Value<double>(args[idx]->toNumber(exec)); 0442 } 0443 //if ( types[idx] == "float" ) return new Value<float>( args[idx]->toNumber(exec) ); 0444 //if ( types[idx] == "qreal" ) return new Value<qreal>( args[idx]->toNumber(exec) ); 0445 break; 0446 case QVariant::Bool: 0447 if (args[idx]->type() == KJS::BooleanType) { 0448 return new Value<bool>(args[idx]->toBoolean(exec)); 0449 } 0450 break; 0451 case QVariant::ByteArray: 0452 if (args[idx]->type() == KJS::StringType) { 0453 return new Value<QByteArray>(toQString(args[idx]->toString(exec)).toUtf8()); 0454 } 0455 break; 0456 case QVariant::String: 0457 if (args[idx]->type() == KJS::StringType) { 0458 return new Value<QString>(toQString(args[idx]->toString(exec))); 0459 } 0460 break; 0461 case QVariant::StringList: 0462 if (args[idx]->type() == KJS::ObjectType) { 0463 return new Value<QStringList>(convertArrayToStringList(exec, args[idx])); 0464 } 0465 break; 0466 case QVariant::Size: 0467 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0468 return new Value<QSize>(valImp->variant().value<QSize>()); 0469 } 0470 break; 0471 case QVariant::SizeF: 0472 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0473 return new Value<QSizeF>(valImp->variant().value<QSizeF>()); 0474 } 0475 break; 0476 case QVariant::Point: 0477 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0478 return new Value<QPoint>(valImp->variant().value<QPoint>()); 0479 } 0480 break; 0481 case QVariant::PointF: 0482 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0483 return new Value<QPointF>(valImp->variant().value<QPointF>()); 0484 } 0485 break; 0486 case QVariant::Rect: 0487 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0488 return new Value<QRect>(valImp->variant().value<QRect>()); 0489 } 0490 break; 0491 case QVariant::RectF: 0492 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0493 return new Value<QRectF>(valImp->variant().value<QRectF>()); 0494 } 0495 break; 0496 case QVariant::Color: 0497 if (args[idx]->type() == KJS::StringType) { 0498 return new Value<QColor>(QColor(toQString(args[idx]->toString(exec)))); 0499 } 0500 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0501 return new Value<QColor>(valImp->variant().value<QColor>()); 0502 } 0503 break; 0504 case QVariant::Url: 0505 if (args[idx]->type() == KJS::StringType) { 0506 return new Value<QUrl>(QUrl(toQString(args[idx]->toString(exec)))); 0507 } 0508 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0509 return new Value<QUrl>(valImp->variant().value<QUrl>()); 0510 } 0511 break; 0512 case QVariant::List: 0513 if (args[idx]->type() == KJS::ObjectType) { 0514 return new Value<QVariantList>(convertArrayToList(exec, args[idx])); 0515 } 0516 break; 0517 case QVariant::Map: 0518 if (args[idx]->type() == KJS::ObjectType) { 0519 return new Value<QVariantMap>(convertArrayToMap(exec, args[idx])); 0520 } 0521 break; 0522 case QVariant::UserType: // fall through 0523 default: 0524 if (args[idx]->type() == KJS::NullType) { 0525 return new NullPtr(); 0526 } 0527 if (args[idx]->type() == KJS::StringType) { 0528 if (strcmp(types[idx].constData(), "KUrl") == 0) { //downcast to QUrl 0529 return new Value<QUrl>(QUrl(toQString(args[idx]->toString(exec)))); 0530 } 0531 } 0532 if (args[idx]->type() == KJS::ObjectType) { 0533 if (QObjectBinding *objImp = KJSEmbed::extractBindingImp<QObjectBinding>(exec, args[idx])) { 0534 //qDebug("\tQObjectBinding"); 0535 if (QObject *qObj = objImp->qobject<QObject>()) { 0536 return new Value<void *>(qObj); 0537 } 0538 } else if (ObjectBinding *objImp = KJSEmbed::extractBindingImp<ObjectBinding>(exec, args[idx])) { 0539 //qDebug("\tObjectBinding"); 0540 return new Value<void *>(objImp->voidStar()); 0541 } 0542 if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) { 0543 //qDebug() << "\tVariantBinding typeName=" << valImp->variant().typeName() << "type=" << valImp->variant().type() << "userType=" << valImp->variant().userType() << " variant=" << valImp->variant(); 0544 QVariant var = valImp->variant(); 0545 0546 // if the variant is the appropriate type, return its data 0547 if ((var.type() == varianttype) || 0548 ((var.type() == QVariant::UserType) && 0549 (types[idx].constData() == var.typeName()))) { 0550 return new Value<void *>(valImp->variant().data()); 0551 } else if ((var.type() != QVariant::UserType) && 0552 var.canConvert(varianttype)) { 0553 // is convertible type, so convert it, and return if successful 0554 if (var.convert(varianttype)) { 0555 return new Value<void *>(valImp->variant().data()); 0556 } 0557 } else if ((var.type() == QVariant::UserType) && 0558 var.canConvert<QObject *>()) { 0559 QObject *qObj = var.value<QObject *>(); 0560 if (!qObj) { 0561 qObj = reinterpret_cast<QObject *>(var.value<QWidget *>()); 0562 } 0563 if (qObj) { 0564 QByteArray typeName = types[idx].constData(); 0565 typeName.replace("*", ""); //krazy:exclude=doublequote_chars 0566 if (qObj->inherits(typeName.constData())) { 0567 return new Value<void *>(qObj); 0568 } 0569 } 0570 } 0571 } 0572 } 0573 0574 QVariant v = KJSEmbed::extractVariant(exec, args[idx]); 0575 if (! v.isNull()) { 0576 return new Value<QVariant>(v); 0577 } 0578 0579 break; 0580 } 0581 0582 qDebug("Cast failure %s value Type %d", types[idx].constData(), args[idx]->type()); 0583 // construct a meaningful exception message 0584 QString jsType; 0585 KJS::JSObject *jsObj = args[idx]->getObject(); 0586 if (jsObj) { 0587 const KJS::ClassInfo *ci = jsObj->classInfo(); 0588 if (ci && ci->className) { 0589 jsType = ci->className; 0590 } 0591 if (jsType.isEmpty()) { 0592 jsType = toQString(jsObj->className()); 0593 } 0594 } 0595 0596 if (jsType.isEmpty()) { 0597 switch (args[idx]->type()) { 0598 case KJS::UnspecifiedType: 0599 jsType = "jsUnspecified"; 0600 break; 0601 case KJS::NumberType: 0602 jsType = "jsNumber"; 0603 break; 0604 case KJS::BooleanType: 0605 jsType = "jsBoolean"; 0606 break; 0607 case KJS::UndefinedType: 0608 jsType = "jsUndefined"; 0609 break; 0610 case KJS::NullType: 0611 jsType = "jsNull"; 0612 break; 0613 case KJS::StringType: 0614 jsType = "jsString"; 0615 break; 0616 case KJS::ObjectType: 0617 jsType = "jsObject"; 0618 break; 0619 case KJS::GetterSetterType: 0620 jsType = "jsGetterSetter"; 0621 break; 0622 default: 0623 jsType = QString::number(args[idx]->type()); 0624 break; 0625 } 0626 } 0627 0628 errorText = i18n("Failure to cast to %1 value from Type %2 (%3)", 0629 types[idx].constData(), jsType, toQString(args[idx]->toString(exec))); 0630 0631 return nullptr; 0632 } 0633 0634 KJS::JSValue *SlotBinding::callAsFunction(KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args) 0635 { 0636 QObjectBinding *imp = extractBindingImp<QObjectBinding>(exec, self); 0637 if (imp == nullptr) { 0638 return KJS::jsNull(); 0639 } 0640 0641 PointerBase *qtArgs[10]; 0642 void *param[11]; 0643 0644 QObject *object = imp->object<QObject>(); 0645 int count = object->metaObject()->methodCount(); 0646 QMetaMethod metaMember; 0647 int offset = 0; 0648 bool success = false; 0649 for (; offset < count; ++offset) { 0650 metaMember = object->metaObject()->method(offset); 0651 if (extractMemberName(metaMember) == m_memberName) { 0652 if (metaMember.parameterTypes().size() == args.size() && validSlot(metaMember, imp->access())) { 0653 success = true; 0654 break; 0655 } 0656 } 0657 } 0658 0659 if (!success) { 0660 return KJS::throwError(exec, KJS::GeneralError, i18n("No such method '%1'.", m_memberName.constData())); 0661 //return KJSEmbed::throwError(exec, i18n("Call to '%1' failed.").arg(m_memberName.constData())); 0662 } 0663 0664 QList<QByteArray> types = metaMember.parameterTypes(); 0665 0666 QVariant::Type returnTypeId = QVariant::nameToType(metaMember.typeName()); 0667 int tp = QMetaType::type(metaMember.typeName()); 0668 PointerBase *qtRet = new Value<void *>(nullptr); 0669 0670 bool returnIsMetaType = ( 0671 returnTypeId == QVariant::UserType || 0672 returnTypeId == QVariant::Size || returnTypeId == QVariant::SizeF || 0673 returnTypeId == QVariant::Point || returnTypeId == QVariant::PointF || 0674 returnTypeId == QVariant::Rect || returnTypeId == QVariant::RectF || 0675 returnTypeId == QVariant::Color 0676 ); 0677 QVariant returnValue = returnIsMetaType ? QVariant(tp, (void *)nullptr) : QVariant(returnTypeId); 0678 QGenericReturnArgument returnArgument(metaMember.typeName(), &returnValue); 0679 param[0] = returnIsMetaType ? qtRet->voidStar() : returnArgument.data(); 0680 0681 QString errorText; 0682 for (int idx = 0; idx < 10; ++idx) { 0683 qtArgs[idx] = getArg(exec, types, args, idx, errorText); 0684 if (!qtArgs[idx]) { 0685 for (int i = 0; i < idx; ++i) { 0686 delete qtArgs[i]; 0687 } 0688 delete qtRet; 0689 return KJS::throwError(exec, KJS::GeneralError, i18n("Call to method '%1' failed, unable to get argument %2: %3", m_memberName.constData(), idx, errorText)); 0690 } 0691 param[idx + 1] = qtArgs[idx]->voidStar(); 0692 } 0693 0694 success = object->qt_metacall(QMetaObject::InvokeMetaMethod, offset, param) < 0; 0695 0696 KJS::JSValue *jsReturnValue = nullptr; 0697 if (success) { 0698 switch (returnTypeId) { 0699 case QVariant::Invalid: // fall through 0700 case QVariant::UserType: { 0701 if (QMetaType::typeFlags(tp) & QMetaType::PointerToQObject) { 0702 const QVariant v(tp, param[0]); 0703 QObject *obj = v.value< QObject * >(); 0704 if (obj) { 0705 jsReturnValue = KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::CPPOwned); 0706 } 0707 } 0708 } break; 0709 default: 0710 if (returnIsMetaType) { 0711 returnValue = QVariant(tp, param[0]); 0712 } 0713 break; 0714 } 0715 if (! jsReturnValue) { 0716 jsReturnValue = KJSEmbed::convertToValue(exec, returnValue); 0717 } 0718 } 0719 0720 for (int idx = 0; idx < 10; ++idx) { 0721 delete qtArgs[idx]; 0722 } 0723 delete qtRet; 0724 0725 if (!success) { 0726 return KJS::throwError(exec, KJS::GeneralError, i18n("Call to '%1' failed.", m_memberName.constData())); 0727 } 0728 0729 return jsReturnValue; 0730 } 0731 0732 SlotBinding::SlotBinding(KJS::ExecState *exec, const QMetaMethod &member) 0733 : KJS::InternalFunctionImp(static_cast<KJS::FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype()), 0734 KJS::Identifier(toUString(extractMemberName(member)))) 0735 { 0736 m_memberName = extractMemberName(member); 0737 int count = member.parameterNames().count(); 0738 putDirect(exec->propertyNames().length, count, LengthFlags); 0739 } 0740 0741 KJS::JSObject *KJSEmbed::createQObject(KJS::ExecState *exec, QObject *value, KJSEmbed::ObjectBinding::Ownership owner) 0742 { 0743 if (nullptr == value) { 0744 return new KJS::JSObject(); 0745 } 0746 0747 const QMetaObject *meta = value->metaObject(); 0748 KJS::JSObject *parent = exec->dynamicInterpreter()->globalObject(); 0749 KJS::JSObject *returnValue; 0750 int pos; 0751 QString clazz; 0752 do { 0753 clazz = meta->className(); 0754 0755 #ifdef CREATEQOBJ_DIAG 0756 qDebug() << "clazz=" << clazz; 0757 #endif 0758 // strip off namespace since they aren't included 0759 if ((pos = clazz.lastIndexOf("::")) != -1) { 0760 clazz.remove(0, pos + 2); 0761 } 0762 #ifdef CREATEQOBJ_DIAG 0763 qDebug() << "cleaned clazz=" << clazz; 0764 #endif 0765 if (parent->hasProperty(exec, KJS::Identifier(toUString(clazz)))) { 0766 #ifdef CREATEQOBJ_DIAG 0767 qDebug() << "createQObject(): clazz=" << clazz << " value=" << value; 0768 #endif 0769 Pointer<QObject> pov(value); 0770 returnValue = StaticConstructor::bind(exec, clazz, pov); 0771 if (returnValue) { 0772 return returnValue; 0773 } 0774 0775 #ifdef CREATEQOBJ_DIAG 0776 qDebug("\tresort to construct() method."); 0777 #endif 0778 returnValue = StaticConstructor::construct(exec, parent, toUString(clazz)); 0779 if (returnValue) { 0780 // If it is a value type setValue 0781 KJSEmbed::QObjectBinding *imp = extractBindingImp<QObjectBinding>(exec, returnValue); 0782 if (imp) { 0783 imp->setObject(value); 0784 imp->watchObject(value); 0785 imp->setOwnership(owner); 0786 KJSEmbed::QObjectBinding::publishQObject(exec, returnValue, value); 0787 } else { 0788 KJS::throwError(exec, KJS::TypeError, i18n("%1 is not an Object type", clazz)); 0789 return new KJS::JSObject(); 0790 } 0791 } else { 0792 KJS::throwError(exec, KJS::TypeError, i18n("Could not construct value")); 0793 return new KJS::JSObject(); 0794 } 0795 return returnValue; 0796 } else { 0797 #ifdef CREATEQOBJ_DIAG 0798 qDebug("%s not a bound type, move up the chain", meta->className()); 0799 #endif 0800 meta = meta->superClass(); 0801 } 0802 0803 } while (meta); 0804 0805 KJSEmbed::QObjectBinding *imp = new KJSEmbed::QObjectBinding(exec, value); 0806 imp->setOwnership(owner); 0807 0808 return imp; 0809 } 0810 0811 START_QOBJECT_METHOD(callParent, QObject) 0812 //TODO it would be better, if each QObjectBinding remembers it's parent rather then 0813 //creating a new instance each time. That wouldn't only be more logical, but also 0814 //does prevent losing of additional information like e.g. the access-level. 0815 if (imp->access() & QObjectBinding::GetParentObject) 0816 { 0817 QObject *parent = imp->object<QObject>()->parent(); 0818 KJS::JSObject *parentObject = KJSEmbed::createQObject(exec, parent); 0819 KJSEmbed::QObjectBinding *parentImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, parentObject); 0820 if (parentImp) { 0821 parentImp->setAccess(imp->access()); // inherit access from child since we don't know the access-level of the parent here :-( 0822 } 0823 result = parentObject; 0824 } 0825 END_QOBJECT_METHOD 0826 START_QOBJECT_METHOD(callIsWidgetType, QObject) 0827 result = KJS::jsBoolean(object->isWidgetType()); 0828 END_QOBJECT_METHOD 0829 START_QOBJECT_METHOD(callInherits, QObject) 0830 QByteArray className = KJSEmbed::extractQString(exec, args, 0).toLatin1(); 0831 result = KJS::jsBoolean(object->inherits(className.constData())); 0832 END_QOBJECT_METHOD 0833 START_QOBJECT_METHOD(callSetParent, QObject) 0834 if (imp->access() & QObjectBinding::SetParentObject) 0835 { 0836 QObject *parent = KJSEmbed::extractObject<QObject>(exec, args, 0, nullptr); 0837 object->setParent(parent); 0838 } 0839 END_QOBJECT_METHOD 0840 START_QOBJECT_METHOD(callFindChild, QObject) 0841 if (imp->access() & QObjectBinding::ChildObjects) 0842 { 0843 QString childName = KJSEmbed::extractQString(exec, args, 0); 0844 QObject *child = object->findChild<QObject *>(childName); 0845 KJS::JSObject *childObject = KJSEmbed::createQObject(exec, child); 0846 KJSEmbed::QObjectBinding *childImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, childObject); 0847 if (childImp) { 0848 childImp->setAccess(imp->access()); // inherit access from parent 0849 } 0850 result = childObject; 0851 } 0852 END_QOBJECT_METHOD 0853 0854 START_METHOD_LUT(QObjectFactory) 0855 {"connect", 4, KJS::DontDelete | KJS::ReadOnly, &callConnect }, 0856 {"parent", 0, KJS::DontDelete | KJS::ReadOnly, &callParent }, 0857 {"inherits", 1, KJS::DontDelete | KJS::ReadOnly, &callInherits }, 0858 {"isWidgetType", 0, KJS::DontDelete | KJS::ReadOnly, &callIsWidgetType }, 0859 {"setParent", 1, KJS::DontDelete | KJS::ReadOnly, &callSetParent }, 0860 {"findChild", 1, KJS::DontDelete | KJS::ReadOnly, &callFindChild } 0861 END_METHOD_LUT 0862 0863 NO_ENUMS(QObjectFactory) 0864 NO_STATICS(QObjectFactory) 0865