File indexing completed on 2024-12-08 12:23:10

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 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Library General Public
0009     License as published by the Free Software Foundation; either
0010     version 2 of the License, or (at your option) any later version.
0011 
0012     This library is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015     Library General Public License for more details.
0016 
0017     You should have received a copy of the GNU Library General Public License
0018     along with this library; see the file COPYING.LIB.  If not, write to
0019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020     Boston, MA 02110-1301, USA.
0021 */
0022 #include "slotproxy.h"
0023 
0024 #include <QMetaEnum>
0025 #include <QMetaType>
0026 #include <QDebug>
0027 #include <QWidget>
0028 
0029 #include <kjs/interpreter.h>
0030 
0031 #include "variant_binding.h"
0032 #include "qobject_binding.h"
0033 
0034 //#define DEBUG_SLOTPROXY 1
0035 
0036 using namespace KJSEmbed;
0037 
0038 SlotProxy::SlotProxy(KJS::JSObject *obj, KJS::Interpreter *interpreter, QObject *parent, const QByteArray &signature)
0039     : QObject(parent), m_interpreter(interpreter), m_object(obj)
0040 {
0041     m_signature = QMetaObject::normalizedSignature(signature.constData());
0042     uint signatureSize = m_signature.size() + 1;
0043 
0044     // content:
0045     m_data[0] = 1;  // revision
0046     m_data[1] = 0;  // classname
0047     m_data[2] = 0;  // classinfo
0048     m_data[3] = 0;  // classinfo
0049     m_data[4] = 1;  // methods
0050     m_data[5] = 10; // methods
0051     m_data[6] = 0;  // properties
0052     m_data[7] = 0;  // properties
0053     m_data[8] = 0;  // enums/sets
0054     m_data[9] = 0;  // enums/sets
0055     // Q_SLOTS:
0056     m_data[10] = 10;  //signature start
0057     m_data[11] = 10 + signatureSize;  //parameters start
0058     m_data[12] = 10 + signatureSize;  //type start
0059     m_data[13] = 10 + signatureSize;  //tag start
0060     m_data[14] = 0x0a;//flags
0061     m_data[15] = 0;    // eod
0062 
0063     m_stringData = QByteArray("SlotProxy\0", 10);
0064     m_stringData += m_signature;
0065     m_stringData += QByteArray("\0\0", 2);
0066 
0067     staticMetaObject.d.superdata = &QObject::staticMetaObject;
0068     staticMetaObject.d.stringdata = m_stringData.data_ptr();
0069     staticMetaObject.d.data = m_data;
0070     staticMetaObject.d.extradata = nullptr;
0071 #ifdef DEBUG_SLOTPROXY
0072     qDebug() << "SlotProxy() obj=" << this <<  " m_signature=" << m_signature;
0073 #endif
0074 }
0075 
0076 SlotProxy::~SlotProxy()
0077 {
0078 #ifdef DEBUG_SLOTPROXY
0079     qDebug() << "??????SlotProxy::~SlotProxy() obj=" << this << " m_signature=" << m_signature;
0080 #endif
0081 }
0082 
0083 const QMetaObject *SlotProxy::metaObject() const
0084 {
0085     return &staticMetaObject;
0086 }
0087 
0088 void *SlotProxy::qt_metacast(const char *_clname)
0089 {
0090     if (!_clname) {
0091         return nullptr;
0092     }
0093     if (!strcmp(_clname, m_stringData.constData())) {
0094         return static_cast<void *>(const_cast<SlotProxy *>(this));
0095     }
0096     return QObject::qt_metacast(_clname);
0097 }
0098 
0099 KJS::JSValue *SlotProxy::callMethod(const QByteArray &methodName, void **_a)
0100 {
0101 #ifdef DEBUG_SLOTPROXY
0102     qDebug() << "SlotProxy::callMethod(" << methodName << ",_a) obj=" << this;
0103 #endif
0104     KJS::ExecState *exec = m_interpreter->globalExec();
0105     exec->clearException();
0106 
0107     // Crash
0108     // KJS::Interpreter::globalExec()->context().thisValue()
0109     KJS::List args = convertArguments(exec, _a);
0110     KJS::Identifier id = KJS::Identifier(KJS::UString(methodName.data()));
0111     KJS::JSObject *fun = m_object->get(exec, id)->toObject(exec);
0112     KJS::JSValue *retValue;
0113     if (!fun->implementsCall()) {
0114 #ifdef DEBUG_SLOTPROXY
0115         qDebug() << "SlotProxy::callMethod got bad handler";
0116 #endif
0117         QString msg = i18n("Bad slot handler: Object %1 Identifier %2 Method %3 Signature: %4.",
0118                            m_object->className().ascii(),
0119                            id.ascii(),
0120                            methodName.data(),
0121                            QString(m_signature));
0122 
0123         retValue = throwError(exec, KJS::TypeError, msg);
0124     } else {
0125         retValue = fun->call(exec, m_object, args);
0126     }
0127 
0128     if (exec->hadException()) {
0129 #ifdef DEBUG_SLOTPROXY
0130         qDebug() << "SlotProxy::callMethod had exception";
0131 #endif
0132         if (m_interpreter->shouldPrintExceptions()) {
0133             KJS::JSLock lock;
0134             KJS::JSObject *exceptObj = exec->exception()->toObject(exec);//retValue->toObject(exec);
0135             QString message = toQString(exceptObj->toString(exec));
0136             QString sourceURL = toQString(exceptObj->get(exec, "sourceURL")->toString(exec));
0137             int sourceId = exceptObj->get(exec, "sourceId")->toUInt32(exec);
0138             // would include the line number, but it's always last line of file
0139             int line = exceptObj->get(exec, "line")->toUInt32(exec);
0140             (*KJSEmbed::conerr()) << i18n("Exception calling '%1' slot from %2:%3:%4", QString(methodName), !sourceURL.isEmpty() ? sourceURL : QString::number(sourceId), line, message) << Qt::endl;
0141         }
0142 
0143         // clear it so it doesn't stop other things
0144         exec->clearException();
0145 
0146         return KJS::jsNull();
0147     } else {
0148         if (retValue->type() == 1 || retValue->type() == 0) {
0149             return KJS::jsNull();
0150         }
0151     }
0152     return retValue;
0153 }
0154 
0155 KJS::List SlotProxy::convertArguments(KJS::ExecState *exec, void **_a)
0156 {
0157     KJS::List args;
0158     int offset = metaObject()->indexOfMethod(m_signature.constData());
0159     QMetaMethod method = metaObject()->method(offset);
0160     QList<QByteArray> params = method.parameterTypes();
0161     int idx = 1;
0162 #ifdef DEBUG_SLOTPROXY
0163     qDebug() << "SlotProxy::convertArguments(): obj=" << this << " m_signature=" << m_signature << " offset=" << offset << " params=" << params;
0164 #endif
0165     foreach (const QByteArray &param, params) {
0166 #ifdef DEBUG_SLOTPROXY
0167         int type = QMetaType::type(param.constData());
0168         qDebug("\tGot a %d - %s - _a[%d] = %p", type, param.data(), idx, _a[idx]);
0169         qDebug("\t QMetaType::type()=%d", QMetaType::type(QByteArray("Pinya::") + param.constData()));
0170 #endif
0171         int tp = QVariant::nameToType(param.constData());
0172         switch (tp) {
0173         case QVariant::Int:
0174             args.append(KJS::jsNumber(*(int *)_a[idx]));
0175             break;
0176         case QVariant::UInt:
0177             args.append(KJS::jsNumber(*(uint *)_a[idx]));
0178             break;
0179         case QVariant::LongLong:
0180             args.append(KJS::jsNumber(*(qlonglong *)_a[idx]));
0181             break;
0182         case QVariant::ULongLong:
0183             args.append(KJS::jsNumber(*(qulonglong *)_a[idx]));
0184             break;
0185         case QVariant::Double:
0186             args.append(KJS::jsNumber(*(double *)_a[idx]));
0187             break;
0188         case QVariant::Bool:
0189             args.append(KJS::jsBoolean(*(bool *)_a[idx]));
0190             break;
0191         case QVariant::String:
0192             args.append(KJS::jsString((*reinterpret_cast<QString(*)>(_a[idx]))));
0193             break;
0194         case QVariant::UserType: {
0195             KJS::JSObject *returnValue;
0196             KJS::JSObject *parent = exec->dynamicInterpreter()->globalObject();
0197             QByteArray typeName = param.constData();
0198             bool isPtr = typeName.contains("*");
0199             if (isPtr) {
0200                 typeName.replace("*", "");    //krazy:exclude=doublequote_chars
0201             }
0202 #ifdef DEBUG_SLOTPROXY
0203             qDebug() << "\tQVariant::UserType: typeName=" << typeName << " param=" << param.constData() << " isPtr" << isPtr;
0204 #endif
0205             if (parent->hasProperty(exec, KJS::Identifier(toUString(typeName)))) {
0206                 QObject *qObj;
0207                 if (isPtr &&
0208                         ((qObj = *reinterpret_cast<QObject **>(_a[idx])) != nullptr)) {
0209 #ifdef DEBUG_SLOTPROXY
0210                     qDebug() << "qObj=" << qObj;
0211 #endif
0212                     Pointer<QObject> pov(*reinterpret_cast<QObject*(*)>(_a[idx]));
0213                     returnValue = StaticConstructor::bind(exec, typeName, pov);
0214                     if (returnValue) {
0215                         args.append(returnValue);
0216                         break;
0217                     } else {
0218 #ifdef DEBUG_SLOTPROXY
0219                         qDebug("\t\tNo binding retrieved");
0220 #endif
0221                         returnValue = StaticConstructor::construct(exec, parent, toUString(typeName));
0222                         if (returnValue) {
0223                             if (QObjectBinding *objImp = KJSEmbed::extractBindingImp<QObjectBinding>(exec, returnValue)) {
0224 #ifdef DEBUG_SLOTPROXY
0225                                 qDebug() << "\t\t\tFound QObjectBinding";
0226 #endif
0227                                 objImp->setOwnership(KJSEmbed::ObjectBinding::JSOwned);
0228                                 objImp->setObject(qObj);
0229                                 if (qObj->parent() != nullptr) {
0230                                     objImp->setOwnership(KJSEmbed::ObjectBinding::QObjOwned);
0231                                 } else {
0232                                     objImp->setOwnership(KJSEmbed::ObjectBinding::CPPOwned);
0233                                 }
0234                                 args.append(returnValue);
0235                                 break;
0236                             }
0237                         }
0238                     }
0239                 }
0240             } else {
0241 #ifdef DEBUG_SLOTPROXY
0242                 qDebug("\t\tNo binding registered");
0243 #endif
0244                 KJS::JSObject *returnValue = nullptr;
0245                 const int metaTypeId = QMetaType::type(param.constData());
0246                 if (QMetaType::typeFlags(metaTypeId) & QMetaType::PointerToQObject) {
0247                     QObject *obj = (*reinterpret_cast< QObject*(*)>(_a[idx]));
0248                     returnValue = KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::QObjOwned);
0249                 }
0250                 if (returnValue) {
0251                     args.append(returnValue);
0252                     break;
0253                 }
0254             }
0255         }
0256         case QVariant::StringList:
0257         case QVariant::List:
0258         case QVariant::Map:
0259         default:
0260             //qDebug("\t\tconverting to variant");
0261             QVariant variant(tp, _a[idx]);
0262             args.append(KJSEmbed::convertToValue(exec, variant));
0263             break;
0264         }
0265         ++idx;
0266     }
0267 
0268     return args;
0269 }
0270 
0271 int SlotProxy::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
0272 {
0273 #ifdef DEBUG_SLOTPROXY
0274     qDebug("SlotProxy::qt_metacall(_c=%d, _id=%d, _a=%p _a[0]=%p _a[1]=%p) obj=", _c, _id, _a, _a[0], _a[1], this);
0275 #endif
0276     _id = QObject::qt_metacall(_c, _id, _a);
0277     if (_id < 0) {
0278         return _id;
0279     }
0280     if (_c == QMetaObject::InvokeMetaMethod) {
0281         switch (_id) {
0282         case 0: {
0283             // invoke js method here
0284             QByteArray method = m_signature.left(m_signature.indexOf('('));
0285             KJS::JSValue *result = callMethod(method, _a);
0286             m_tmpResult = convertToVariant(m_interpreter->globalExec(), result);
0287 #ifdef DEBUG_SLOTPROXY
0288             qDebug() << "SlotProxy::qt_metacall result=" << m_tmpResult.toString();
0289 #endif
0290             _a[0] = &(m_tmpResult);
0291         } break;
0292         }
0293         _id -= 1;
0294     }
0295     return _id;
0296 }
0297