File indexing completed on 2024-04-14 11:10:01

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