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 ¶m, 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