KJsEmbed

qobject_binding.cpp
1 /* This file is part of the KDE libraries
2  Copyright (C) 2005, 2006 Ian Reinhart Geiser <[email protected]>
3  Copyright (C) 2005, 2006 Matt Broadstone <[email protected]>
4  Copyright (C) 2005, 2006 Richard J. Moore <[email protected]>
5  Copyright (C) 2005, 2006 Erik L. Bunce <[email protected]>
6  Copyright (C) 2007, 2008 Sebastian Sauer <[email protected]>
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22 */
23 #include "qobject_binding.h"
24 
25 #include <QObject>
26 #include <QArgument>
27 #include <QMetaEnum>
28 #include <QMetaType>
29 #include <QVariant>
30 #include <QVector>
31 #include <QUrl>
32 #include <QWidget>
33 
34 #include "slotproxy.h"
35 #include "eventproxy.h"
36 #include "jseventmapper.h"
37 #include "pointer.h"
38 #include "variant_binding.h"
39 
40 #include <kjs/array_instance.h>
41 #include <kjs/function_object.h>
42 
43 //#define CREATEQOBJ_DIAG
44 
45 using namespace KJSEmbed;
46 
47 QByteArray createSignal(const QByteArray &sig)
48 {
49  return '2' + sig;
50 }
51 
52 QByteArray createSlot(const QByteArray &slt)
53 {
54  return '1' + slt;
55 }
56 
57 bool validSlot(const QMetaMethod &method, QObjectBinding::AccessFlags accessflags)
58 {
59  switch (method.access()) {
60  case QMetaMethod::Private: {
61  if (!(accessflags & QObjectBinding::PrivateSlots)) {
62  return false;
63  }
64  } break;
66  if (!(accessflags & QObjectBinding::ProtectedSlots)) {
67  return false;
68  }
69  } break;
70  case QMetaMethod::Public: {
71  if (!(accessflags & QObjectBinding::PublicSlots)) {
72  return false;
73  }
74  } break;
75  }
76  if (method.attributes() & QMetaMethod::Scriptable) {
77  if (!(accessflags & QObjectBinding::ScriptableSlots)) {
78  return false;
79  }
80  } else {
81  if (!(accessflags & QObjectBinding::NonScriptableSlots)) {
82  return false;
83  }
84  }
85  return true;
86 }
87 
88 bool validSignal(const QMetaMethod &method, QObjectBinding::AccessFlags accessflags)
89 {
90  switch (method.access()) {
91  case QMetaMethod::Private: {
92  if (!(accessflags & QObjectBinding::PrivateSignals)) {
93  return false;
94  }
95  } break;
97  if (!(accessflags & QObjectBinding::ProtectedSignals)) {
98  return false;
99  }
100  } break;
101  case QMetaMethod::Public: {
102  if (!(accessflags & QObjectBinding::PublicSignals)) {
103  return false;
104  }
105  } break;
106  }
107  if (method.attributes() & QMetaMethod::Scriptable) {
108  if (!(accessflags & QObjectBinding::ScriptableSignals)) {
109  return false;
110  }
111  } else {
112  if (!(accessflags & QObjectBinding::NonScriptableSignals)) {
113  return false;
114  }
115  }
116  return true;
117 }
118 
119 bool validProperty(const QMetaProperty &property, QObjectBinding::AccessFlags accessflags)
120 {
121  if (property.isScriptable()) {
122  if (!(accessflags & QObjectBinding::ScriptableProperties)) {
123  return false;
124  }
125  } else {
126  if (!(accessflags & QObjectBinding::NonScriptableProperties)) {
127  return false;
128  }
129  }
130  return true;
131 }
132 
133 KJS::JSValue *callConnect(KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args)
134 {
135  KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, self);
136  if (!imp) { // No implementation, so we need to use the first argument as we are a global static invocation.
137  imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[0]);
138  }
139  if (!imp) {
140  return KJS::throwError(exec, KJS::GeneralError, i18n("Wrong object type."));
141  }
142  //return KJSEmbed::throwError(exec, i18n("Wrong object type."));
143 
144  if (args.size() > 2) {
145  KJSEmbed::QObjectBinding *senderImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[0]);
146  if (!senderImp) {
147  return KJS::throwError(exec, KJS::GeneralError, i18n("First argument must be a QObject."));
148  //return KJSEmbed::throwError(exec, i18n("First argument must be a QObject"));
149  }
150  QObject *receiver = nullptr;
151  QObject *sender = senderImp->object<QObject>();
152  QByteArray signal = createSignal(args[1]->toString(exec).ascii());
153  QByteArray slot;
154  KJSEmbed::QObjectBinding *receiverImp = nullptr;
155  if (args.size() >= 4) {
156  slot = createSlot(args[3]->toString(exec).ascii());
157  receiverImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, args[2]);
158  if (!receiverImp) {
159  receiver = new SlotProxy(args[2]->toObject(exec), exec->dynamicInterpreter(), sender, args[3]->toString(exec).ascii());
160  } else {
161  receiver = receiverImp->object<QObject>();
162  }
163  } else {
164  receiverImp = imp;
165  receiver = imp->object<QObject>();
166  slot = createSlot(args[2]->toString(exec).ascii());
167  }
168 
169  const QMetaObject *senderMetaObject = sender->metaObject();
170  QMetaMethod senderMetaMethod = senderMetaObject->method(senderMetaObject->indexOfSignal(signal.constData()));
171 
172  const QMetaObject *receiverMetaObject = receiver->metaObject();
173  QMetaMethod receiverMetaMethod = receiverMetaObject->method(receiverMetaObject->indexOfSlot(slot.constData()));
174 
175  if (validSignal(senderMetaMethod, senderImp->access()) && (!receiverImp || validSlot(receiverMetaMethod, receiverImp->access()))) {
176  return KJS::jsBoolean(QObject::connect(sender, signal.constData(), receiver, slot.constData()));
177  }
178 
179  return KJS::jsBoolean(false);
180  }
181  return KJS::throwError(exec, KJS::GeneralError, i18n("Incorrect number of arguments."));
182  //return KJSEmbed::throwError(exec, i18n("Incorrect number of arguments."));
183 }
184 
185 QByteArray extractMemberName(const QMetaMethod &member)
186 {
187  QString sig = member.methodSignature();
188  return sig.left(sig.indexOf('(')).toLatin1();
189 }
190 
191 void QObjectBinding::publishQObject(KJS::ExecState *exec, KJS::JSObject *target, QObject *object)
192 {
193  KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, target);
194  Q_ASSERT(imp);
195 
196  // Add the children the QObject has.
197  if (imp->access() & QObjectBinding::ChildObjects) {
198  //TODO uh, this one is dirty cause it may eat a lot of time to publish things that may not
199  //got accessed anyway. Better solution would be to provide access to them on demand only. That
200  //would also allow to manipulate the QObject-tree at runtime what is currently not possible.
201  QObjectList children = object->children();
202  QObjectList::Iterator child = children.begin();
203  for (; child != children.end(); ++child) {
204  QString objectName = (*child)->objectName();
205  if (!objectName.isEmpty()) {
206  KJS::JSObject *childObject = KJSEmbed::createQObject(exec, *child);
207  KJSEmbed::QObjectBinding *childImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, childObject);
208  if (childImp) {
209  childImp->setAccess(imp->access()); // inherit access from parent
210  target->put(exec, KJS::Identifier(toUString(objectName)), childObject);
211  }
212  }
213  }
214  }
215 
216  // Add slots of the current object.
217  const QMetaObject *metaObject = object->metaObject();
218  int methods = metaObject->methodCount();
219  for (int idx = 0; idx < methods; ++idx) {
220  QMetaMethod member = metaObject->method(idx);
221  if (validSlot(member, imp->access())) {
222  target->put(exec, KJS::Identifier(extractMemberName(member).constData()),
223  new SlotBinding(exec, member), KJS::DontDelete | KJS::ReadOnly | KJS::Function);
224  }
225  }
226 
227  // Add enums as read only uints.
228  int enums = metaObject->enumeratorCount();
229  for (int idx = 0; idx < enums; ++idx) {
230  QMetaEnum enumerator = metaObject->enumerator(idx);
231  int keys = enumerator.keyCount();
232  for (int key = 0; key < keys; ++key) {
233  target->put(exec, KJS::Identifier(enumerator.key(key)),
234  KJS::jsNumber(enumerator.value(key)), KJS::DontDelete | KJS::ReadOnly);
235  }
236  }
237 }
238 
239 QObjectBinding::QObjectBinding(KJS::ExecState *exec, QObject *object)
240  : ObjectBinding(exec, object->metaObject()->className(), object)
241  , m_evproxy(nullptr)
242  , m_access(AllSlots | AllSignals | AllProperties | AllObjects)
243 {
244  if (object->parent() != nullptr) {
245  setOwnership(ObjectBinding::QObjOwned);
246  } else {
247  setOwnership(ObjectBinding::JSOwned);
248  }
249 
250  m_cleanupHandler = new QObjectCleanupHandler();
251  watchObject(object);
252 
253  StaticBinding::publish(exec, this, QObjectFactory::methods());
254  QObjectBinding::publishQObject(exec, this, object);
255 
256  // Make "connect" a global static method.
257  exec->dynamicInterpreter()->globalObject()->put(exec, "connect", new StaticBinding(exec, &QObjectFactory::methods()[0]));
258 }
259 
260 QObjectBinding::~QObjectBinding()
261 {
262  if (m_cleanupHandler->isEmpty()) {
263  setOwnership(ObjectBinding::QObjOwned);
264  } else if (object<QObject>()->parent() != nullptr) {
265  setOwnership(ObjectBinding::QObjOwned);
266  m_cleanupHandler->remove(object<QObject>());
267  } else if (ownership() != ObjectBinding::JSOwned) {
268  m_cleanupHandler->remove(object<QObject>());
269  } else {
270  m_cleanupHandler->remove(object<QObject>());
271  }
272 
273  delete m_cleanupHandler;
274 }
275 
276 void QObjectBinding::watchObject(QObject *object)
277 {
278  m_cleanupHandler->add(object);
279 }
280 
281 bool QObjectBinding::getOwnPropertySlot(KJS::ExecState *exec, const KJS::Identifier &propertyName, KJS::PropertySlot &slot)
282 {
283  // qDebug() << "getOwnPropertySlot called";
284  QObject *obj = object<QObject>();
285  const QMetaObject *meta = obj->metaObject();
286  int propIndex = meta->indexOfProperty(propertyName.ascii());
287  if (propIndex != -1) {
288  if (! validProperty(meta->property(propIndex), m_access)) {
289  return false;
290  }
291  // qDebug() << "getOwnPropertySlot found the property " << propertyName.ascii();
292  slot.setCustom(this, propertyGetter);
293  return true;
294  }
295  return ObjectBinding::getOwnPropertySlot(exec, propertyName, slot);
296 }
297 
298 KJS::JSValue *QObjectBinding::propertyGetter(KJS::ExecState *exec, KJS::JSObject *,
299  const KJS::Identifier &propertyName, const KJS::PropertySlot &slot)
300 {
301  // qDebug() << "Getter was called";
302  QObjectBinding *self = static_cast<QObjectBinding *>(slot.slotBase());
303  QObject *obj = self->object<QObject>();
304 
305  QVariant val = obj->property(propertyName.ascii());
306  if (val.isValid()) {
307  return convertToValue(exec, val);
308  }
309  qDebug() << QString("propertyGetter called but no property, name was '%1'").arg(propertyName.ascii());
310  return nullptr; // ERROR
311 }
312 
313 QObjectBinding::AccessFlags QObjectBinding::access() const
314 {
315  return m_access;
316 }
317 
318 void QObjectBinding::setAccess(QObjectBinding::AccessFlags access)
319 {
320  m_access = access;
321 }
322 
323 void QObjectBinding::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, KJS::JSValue *value, int attr)
324 {
325  QObject *obj = object<QObject>();
326  if (obj && !m_cleanupHandler->isEmpty()) {
327  // Properties
328  const QMetaObject *meta = obj->metaObject();
329 
330  if (int propIndex = meta->indexOfProperty(propertyName.ascii()) != -1) {
331  QMetaProperty prop = meta->property(propIndex);
332  if (! validProperty(prop, m_access)) {
333  return;
334  }
335 
336  bool propSet = false;
337  QVariant val = convertToVariant(exec, value);
338  if (prop.isEnumType()) {
339  propSet = obj->setProperty(propertyName.ascii(), val.toUInt());
340  } else if (val.isValid() /*&& meta->property(propIndex).isWritable() <- wtf?*/) {
341  propSet = obj->setProperty(propertyName.ascii(), val);
342  }
343  /*
344  if( !propSet )
345  {
346  KJSEmbed::throwError(exec,
347  i18n("Setting property %1 failed: property invalid, read-only or does not exist").arg(propertyName.ascii()));
348  }
349  */
350 
351  }
352 
353  if (JSEventMapper::mapper()->isEventHandler(propertyName)) {
354  if (!m_evproxy) {
355  m_evproxy = new KJSEmbed::EventProxy(this, exec->dynamicInterpreter());
356  }
357  if (value) {
358  m_evproxy->addFilter(JSEventMapper::mapper()->findEventType(propertyName));
359  } else {
360  m_evproxy->removeFilter(JSEventMapper::mapper()->findEventType(propertyName));
361  }
362  }
363  }
364  //qDebug() << "Forward put";
365  // Set a property value
366  ObjectBinding::put(exec, propertyName, value, attr);
367 }
368 
369 bool QObjectBinding::canPut(KJS::ExecState *exec, const KJS::Identifier &propertyName) const
370 {
371  QObject *obj = object<QObject>();
372  if (obj && !m_cleanupHandler->isEmpty()) {
373  // Properties
374  const QMetaObject *meta = obj->metaObject();
375  if (int propIndex = meta->indexOfProperty(propertyName.ascii()) != -1) {
376  QMetaProperty prop = meta->property(propIndex);
377  return validProperty(prop, m_access) && prop.isWritable();
378  }
379  }
380  return ObjectBinding::canPut(exec, propertyName);
381 }
382 
383 KJS::UString QObjectBinding::className() const
384 {
385  return toUString(typeName());
386 }
387 
388 KJS::UString QObjectBinding::toString(KJS::ExecState *exec) const
389 {
390  Q_UNUSED(exec);
391  QString s("%1 (%2)");
392  s = s.arg(object<QObject>()->objectName());
393  s = s.arg(typeName());
394  return toUString(s);
395 }
396 
397 PointerBase *getArg(KJS::ExecState *exec, const QList<QByteArray> &types, const KJS::List &args, int idx, QString &errorText)
398 {
399  //qDebug("Index %d, args size %d, types size %d", idx, args.size(), types.size() );
400 
401  if (types.size() == 0 && idx == 0) {
402  return new NullPtr();
403  }
404  if (args.size() <= idx) {
405  return new NullPtr();
406  }
407 
408  if (types.size() <= idx) {
409  const QString firstPart = i18np("The slot asked for %1 argument", "The slot asked for %1 arguments", idx);
410  const QString secondPart = i18np("but there is only %1 available", "but there are only %1 available", types.size());
411  errorText = i18nc("%1 is 'the slot asked for foo arguments', %2 is 'but there are only bar available'", "%1, %2.");
412 
413  return nullptr;
414  }
415 
416  QVariant::Type varianttype = QVariant::nameToType(types[idx].constData());
417  //qDebug( QString("type=%1 argtype=%2 variantType=%3 (%4)").arg(types[idx].constData()).arg(args[idx]->type()).arg(varianttype).arg(QVariant::typeToName(varianttype)).toLatin1() );
418  switch (varianttype) {
419  case QVariant::Int:
420  if (args[idx]->type() == KJS::NumberType) {
421  return new Value<int>(int(args[idx]->toInteger(exec)));
422  }
423  break;
424  case QVariant::UInt:
425  if (args[idx]->type() == KJS::NumberType) {
426  return new Value<uint>(uint(args[idx]->toInteger(exec)));
427  }
428  break;
429  case QVariant::LongLong:
430  if (args[idx]->type() == KJS::NumberType) {
431  return new Value<qlonglong>(qlonglong(args[idx]->toInteger(exec)));
432  }
433  break;
434  case QVariant::ULongLong:
435  if (args[idx]->type() == KJS::NumberType) {
436  return new Value<qulonglong>(qulonglong(args[idx]->toInteger(exec)));
437  }
438  break;
439  case QVariant::Double:
440  if (args[idx]->type() == KJS::NumberType) {
441  return new Value<double>(args[idx]->toNumber(exec));
442  }
443  //if ( types[idx] == "float" ) return new Value<float>( args[idx]->toNumber(exec) );
444  //if ( types[idx] == "qreal" ) return new Value<qreal>( args[idx]->toNumber(exec) );
445  break;
446  case QVariant::Bool:
447  if (args[idx]->type() == KJS::BooleanType) {
448  return new Value<bool>(args[idx]->toBoolean(exec));
449  }
450  break;
451  case QVariant::ByteArray:
452  if (args[idx]->type() == KJS::StringType) {
453  return new Value<QByteArray>(toQString(args[idx]->toString(exec)).toUtf8());
454  }
455  break;
456  case QVariant::String:
457  if (args[idx]->type() == KJS::StringType) {
458  return new Value<QString>(toQString(args[idx]->toString(exec)));
459  }
460  break;
462  if (args[idx]->type() == KJS::ObjectType) {
463  return new Value<QStringList>(convertArrayToStringList(exec, args[idx]));
464  }
465  break;
466  case QVariant::Size:
467  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
468  return new Value<QSize>(valImp->variant().value<QSize>());
469  }
470  break;
471  case QVariant::SizeF:
472  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
473  return new Value<QSizeF>(valImp->variant().value<QSizeF>());
474  }
475  break;
476  case QVariant::Point:
477  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
478  return new Value<QPoint>(valImp->variant().value<QPoint>());
479  }
480  break;
481  case QVariant::PointF:
482  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
483  return new Value<QPointF>(valImp->variant().value<QPointF>());
484  }
485  break;
486  case QVariant::Rect:
487  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
488  return new Value<QRect>(valImp->variant().value<QRect>());
489  }
490  break;
491  case QVariant::RectF:
492  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
493  return new Value<QRectF>(valImp->variant().value<QRectF>());
494  }
495  break;
496  case QVariant::Color:
497  if (args[idx]->type() == KJS::StringType) {
498  return new Value<QColor>(QColor(toQString(args[idx]->toString(exec))));
499  }
500  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
501  return new Value<QColor>(valImp->variant().value<QColor>());
502  }
503  break;
504  case QVariant::Url:
505  if (args[idx]->type() == KJS::StringType) {
506  return new Value<QUrl>(QUrl(toQString(args[idx]->toString(exec))));
507  }
508  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
509  return new Value<QUrl>(valImp->variant().value<QUrl>());
510  }
511  break;
512  case QVariant::List:
513  if (args[idx]->type() == KJS::ObjectType) {
514  return new Value<QVariantList>(convertArrayToList(exec, args[idx]));
515  }
516  break;
517  case QVariant::Map:
518  if (args[idx]->type() == KJS::ObjectType) {
519  return new Value<QVariantMap>(convertArrayToMap(exec, args[idx]));
520  }
521  break;
522  case QVariant::UserType: // fall through
523  default:
524  if (args[idx]->type() == KJS::NullType) {
525  return new NullPtr();
526  }
527  if (args[idx]->type() == KJS::StringType) {
528  if (strcmp(types[idx].constData(), "KUrl") == 0) { //downcast to QUrl
529  return new Value<QUrl>(QUrl(toQString(args[idx]->toString(exec))));
530  }
531  }
532  if (args[idx]->type() == KJS::ObjectType) {
533  if (QObjectBinding *objImp = KJSEmbed::extractBindingImp<QObjectBinding>(exec, args[idx])) {
534  //qDebug("\tQObjectBinding");
535  if (QObject *qObj = objImp->qobject<QObject>()) {
536  return new Value<void *>(qObj);
537  }
538  } else if (ObjectBinding *objImp = KJSEmbed::extractBindingImp<ObjectBinding>(exec, args[idx])) {
539  //qDebug("\tObjectBinding");
540  return new Value<void *>(objImp->voidStar());
541  }
542  if (VariantBinding *valImp = KJSEmbed::extractBindingImp<VariantBinding>(exec, args[idx])) {
543  //qDebug() << "\tVariantBinding typeName=" << valImp->variant().typeName() << "type=" << valImp->variant().type() << "userType=" << valImp->variant().userType() << " variant=" << valImp->variant();
544  QVariant var = valImp->variant();
545 
546  // if the variant is the appropriate type, return its data
547  if ((var.type() == varianttype) ||
548  ((var.type() == QVariant::UserType) &&
549  (types[idx].constData() == var.typeName()))) {
550  return new Value<void *>(valImp->variant().data());
551  } else if ((var.type() != QVariant::UserType) &&
552  var.canConvert(varianttype)) {
553  // is convertible type, so convert it, and return if successful
554  if (var.convert(varianttype)) {
555  return new Value<void *>(valImp->variant().data());
556  }
557  } else if ((var.type() == QVariant::UserType) &&
558  var.canConvert<QObject *>()) {
559  QObject *qObj = var.value<QObject *>();
560  if (!qObj) {
561  qObj = reinterpret_cast<QObject *>(var.value<QWidget *>());
562  }
563  if (qObj) {
564  QByteArray typeName = types[idx].constData();
565  typeName.replace("*", ""); //krazy:exclude=doublequote_chars
566  if (qObj->inherits(typeName.constData())) {
567  return new Value<void *>(qObj);
568  }
569  }
570  }
571  }
572  }
573 
574  QVariant v = KJSEmbed::extractVariant(exec, args[idx]);
575  if (! v.isNull()) {
576  return new Value<QVariant>(v);
577  }
578 
579  break;
580  }
581 
582  qDebug("Cast failure %s value Type %d", types[idx].constData(), args[idx]->type());
583  // construct a meaningful exception message
584  QString jsType;
585  KJS::JSObject *jsObj = args[idx]->getObject();
586  if (jsObj) {
587  const KJS::ClassInfo *ci = jsObj->classInfo();
588  if (ci && ci->className) {
589  jsType = ci->className;
590  }
591  if (jsType.isEmpty()) {
592  jsType = toQString(jsObj->className());
593  }
594  }
595 
596  if (jsType.isEmpty()) {
597  switch (args[idx]->type()) {
598  case KJS::UnspecifiedType:
599  jsType = "jsUnspecified";
600  break;
601  case KJS::NumberType:
602  jsType = "jsNumber";
603  break;
604  case KJS::BooleanType:
605  jsType = "jsBoolean";
606  break;
607  case KJS::UndefinedType:
608  jsType = "jsUndefined";
609  break;
610  case KJS::NullType:
611  jsType = "jsNull";
612  break;
613  case KJS::StringType:
614  jsType = "jsString";
615  break;
616  case KJS::ObjectType:
617  jsType = "jsObject";
618  break;
619  case KJS::GetterSetterType:
620  jsType = "jsGetterSetter";
621  break;
622  default:
623  jsType = QString::number(args[idx]->type());
624  break;
625  }
626  }
627 
628  errorText = i18n("Failure to cast to %1 value from Type %2 (%3)",
629  types[idx].constData(), jsType, toQString(args[idx]->toString(exec)));
630 
631  return nullptr;
632 }
633 
634 KJS::JSValue *SlotBinding::callAsFunction(KJS::ExecState *exec, KJS::JSObject *self, const KJS::List &args)
635 {
636  QObjectBinding *imp = extractBindingImp<QObjectBinding>(exec, self);
637  if (imp == nullptr) {
638  return KJS::jsNull();
639  }
640 
641  PointerBase *qtArgs[10];
642  void *param[11];
643 
644  QObject *object = imp->object<QObject>();
645  int count = object->metaObject()->methodCount();
646  QMetaMethod metaMember;
647  int offset = 0;
648  bool success = false;
649  for (; offset < count; ++offset) {
650  metaMember = object->metaObject()->method(offset);
651  if (extractMemberName(metaMember) == m_memberName) {
652  if (metaMember.parameterTypes().size() == args.size() && validSlot(metaMember, imp->access())) {
653  success = true;
654  break;
655  }
656  }
657  }
658 
659  if (!success) {
660  return KJS::throwError(exec, KJS::GeneralError, i18n("No such method '%1'.", m_memberName.constData()));
661  //return KJSEmbed::throwError(exec, i18n("Call to '%1' failed.").arg(m_memberName.constData()));
662  }
663 
664  QList<QByteArray> types = metaMember.parameterTypes();
665 
666  QVariant::Type returnTypeId = QVariant::nameToType(metaMember.typeName());
667  int tp = QMetaType::type(metaMember.typeName());
668  PointerBase *qtRet = new Value<void *>(nullptr);
669 
670  bool returnIsMetaType = (
671  returnTypeId == QVariant::UserType ||
672  returnTypeId == QVariant::Size || returnTypeId == QVariant::SizeF ||
673  returnTypeId == QVariant::Point || returnTypeId == QVariant::PointF ||
674  returnTypeId == QVariant::Rect || returnTypeId == QVariant::RectF ||
675  returnTypeId == QVariant::Color
676  );
677  QVariant returnValue = returnIsMetaType ? QVariant(tp, (void *)nullptr) : QVariant(returnTypeId);
678  QGenericReturnArgument returnArgument(metaMember.typeName(), &returnValue);
679  param[0] = returnIsMetaType ? qtRet->voidStar() : returnArgument.data();
680 
681  QString errorText;
682  for (int idx = 0; idx < 10; ++idx) {
683  qtArgs[idx] = getArg(exec, types, args, idx, errorText);
684  if (!qtArgs[idx]) {
685  for (int i = 0; i < idx; ++i) {
686  delete qtArgs[i];
687  }
688  delete qtRet;
689  return KJS::throwError(exec, KJS::GeneralError, i18n("Call to method '%1' failed, unable to get argument %2: %3", m_memberName.constData(), idx, errorText));
690  }
691  param[idx + 1] = qtArgs[idx]->voidStar();
692  }
693 
694  success = object->qt_metacall(QMetaObject::InvokeMetaMethod, offset, param) < 0;
695 
696  KJS::JSValue *jsReturnValue = nullptr;
697  if (success) {
698  switch (returnTypeId) {
699  case QVariant::Invalid: // fall through
700  case QVariant::UserType: {
702  const QVariant v(tp, param[0]);
703  QObject *obj = v.value< QObject * >();
704  if (obj) {
705  jsReturnValue = KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::CPPOwned);
706  }
707  }
708  } break;
709  default:
710  if (returnIsMetaType) {
711  returnValue = QVariant(tp, param[0]);
712  }
713  break;
714  }
715  if (! jsReturnValue) {
716  jsReturnValue = KJSEmbed::convertToValue(exec, returnValue);
717  }
718  }
719 
720  for (int idx = 0; idx < 10; ++idx) {
721  delete qtArgs[idx];
722  }
723  delete qtRet;
724 
725  if (!success) {
726  return KJS::throwError(exec, KJS::GeneralError, i18n("Call to '%1' failed.", m_memberName.constData()));
727  }
728 
729  return jsReturnValue;
730 }
731 
732 SlotBinding::SlotBinding(KJS::ExecState *exec, const QMetaMethod &member)
733  : KJS::InternalFunctionImp(static_cast<KJS::FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype()),
734  KJS::Identifier(toUString(extractMemberName(member))))
735 {
736  m_memberName = extractMemberName(member);
737  int count = member.parameterNames().count();
738  putDirect(exec->propertyNames().length, count, LengthFlags);
739 }
740 
741 KJS::JSObject *KJSEmbed::createQObject(KJS::ExecState *exec, QObject *value, KJSEmbed::ObjectBinding::Ownership owner)
742 {
743  if (nullptr == value) {
744  return new KJS::JSObject();
745  }
746 
747  const QMetaObject *meta = value->metaObject();
748  KJS::JSObject *parent = exec->dynamicInterpreter()->globalObject();
749  KJS::JSObject *returnValue;
750  int pos;
751  QString clazz;
752  do {
753  clazz = meta->className();
754 
755 #ifdef CREATEQOBJ_DIAG
756  qDebug() << "clazz=" << clazz;
757 #endif
758  // strip off namespace since they aren't included
759  if ((pos = clazz.lastIndexOf("::")) != -1) {
760  clazz.remove(0, pos + 2);
761  }
762 #ifdef CREATEQOBJ_DIAG
763  qDebug() << "cleaned clazz=" << clazz;
764 #endif
765  if (parent->hasProperty(exec, KJS::Identifier(toUString(clazz)))) {
766 #ifdef CREATEQOBJ_DIAG
767  qDebug() << "createQObject(): clazz=" << clazz << " value=" << value;
768 #endif
769  Pointer<QObject> pov(value);
770  returnValue = StaticConstructor::bind(exec, clazz, pov);
771  if (returnValue) {
772  return returnValue;
773  }
774 
775 #ifdef CREATEQOBJ_DIAG
776  qDebug("\tresort to construct() method.");
777 #endif
778  returnValue = StaticConstructor::construct(exec, parent, toUString(clazz));
779  if (returnValue) {
780  // If it is a value type setValue
781  KJSEmbed::QObjectBinding *imp = extractBindingImp<QObjectBinding>(exec, returnValue);
782  if (imp) {
783  imp->setObject(value);
784  imp->watchObject(value);
785  imp->setOwnership(owner);
786  KJSEmbed::QObjectBinding::publishQObject(exec, returnValue, value);
787  } else {
788  KJS::throwError(exec, KJS::TypeError, i18n("%1 is not an Object type", clazz));
789  return new KJS::JSObject();
790  }
791  } else {
792  KJS::throwError(exec, KJS::TypeError, i18n("Could not construct value"));
793  return new KJS::JSObject();
794  }
795  return returnValue;
796  } else {
797 #ifdef CREATEQOBJ_DIAG
798  qDebug("%s not a bound type, move up the chain", meta->className());
799 #endif
800  meta = meta->superClass();
801  }
802 
803  } while (meta);
804 
805  KJSEmbed::QObjectBinding *imp = new KJSEmbed::QObjectBinding(exec, value);
806  imp->setOwnership(owner);
807 
808  return imp;
809 }
810 
811 START_QOBJECT_METHOD(callParent, QObject)
812 //TODO it would be better, if each QObjectBinding remembers it's parent rather then
813 //creating a new instance each time. That wouldn't only be more logical, but also
814 //does prevent losing of additional information like e.g. the access-level.
815 if (imp->access() & QObjectBinding::GetParentObject)
816 {
817  QObject *parent = imp->object<QObject>()->parent();
818  KJS::JSObject *parentObject = KJSEmbed::createQObject(exec, parent);
819  KJSEmbed::QObjectBinding *parentImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, parentObject);
820  if (parentImp) {
821  parentImp->setAccess(imp->access()); // inherit access from child since we don't know the access-level of the parent here :-(
822  }
823  result = parentObject;
824 }
825 END_QOBJECT_METHOD
826 START_QOBJECT_METHOD(callIsWidgetType, QObject)
827 result = KJS::jsBoolean(object->isWidgetType());
828 END_QOBJECT_METHOD
829 START_QOBJECT_METHOD(callInherits, QObject)
830 QByteArray className = KJSEmbed::extractQString(exec, args, 0).toLatin1();
831 result = KJS::jsBoolean(object->inherits(className.constData()));
832 END_QOBJECT_METHOD
833 START_QOBJECT_METHOD(callSetParent, QObject)
834 if (imp->access() & QObjectBinding::SetParentObject)
835 {
836  QObject *parent = KJSEmbed::extractObject<QObject>(exec, args, 0, nullptr);
837  object->setParent(parent);
838 }
839 END_QOBJECT_METHOD
840 START_QOBJECT_METHOD(callFindChild, QObject)
841 if (imp->access() & QObjectBinding::ChildObjects)
842 {
843  QString childName = KJSEmbed::extractQString(exec, args, 0);
844  QObject *child = object->findChild<QObject *>(childName);
845  KJS::JSObject *childObject = KJSEmbed::createQObject(exec, child);
846  KJSEmbed::QObjectBinding *childImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, childObject);
847  if (childImp) {
848  childImp->setAccess(imp->access()); // inherit access from parent
849  }
850  result = childObject;
851 }
852 END_QOBJECT_METHOD
853 
854 START_METHOD_LUT(QObjectFactory)
855 {"connect", 4, KJS::DontDelete | KJS::ReadOnly, &callConnect },
856 {"parent", 0, KJS::DontDelete | KJS::ReadOnly, &callParent },
857 {"inherits", 1, KJS::DontDelete | KJS::ReadOnly, &callInherits },
858 {"isWidgetType", 0, KJS::DontDelete | KJS::ReadOnly, &callIsWidgetType },
859 {"setParent", 1, KJS::DontDelete | KJS::ReadOnly, &callSetParent },
860 {"findChild", 1, KJS::DontDelete | KJS::ReadOnly, &callFindChild }
861 END_METHOD_LUT
862 
863 NO_ENUMS(QObjectFactory)
864 NO_STATICS(QObjectFactory)
865 
bool isNull() const const
int methodCount() const const
int indexOfSlot(const char *slot) const const
int value(int index) const const
A binding method that is used in VariantBinding and ObjectBinding.
const QChar * constData() const const
bool isScriptable(const QObject *object) const const
QString number(int n, int base)
JSGlobalObject * globalObject() const
int type(const char *typeName)
Type type(const QSqlDatabase &db)
int count(const T &value) const const
T value() const const
Implement QString-KJS::UString conversion methods.
bool isWritable() const const
QVariant::Type nameToType(const char *name)
bool inherits(const char *className) const const
QByteArray toLatin1() const const
int size() const
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
const char * className
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
const char * ascii() const
QStringList types(Mode mode=Writing)
QByteArray methodSignature() const const
int size() const const
QString typeName(const QJsonObject &obj)
const char * typeName() const const
QString i18n(const char *text, const TYPE &arg...)
static JSEventMapper * mapper()
Return the global event mapper.
QVariant::Type type() const const
bool isEmpty() const const
QMetaMethod method(int index) const const
const QMetaObject * superClass() const const
KJS::JSObject * construct(KJS::ExecState *exec, const KJS::List &args) override
Calls the callback that will in turn create a new instance of this object with the arguments passed i...
QList< QByteArray > parameterNames() const const
int indexOfSignal(const char *signal) const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
static void publish(KJS::ExecState *exec, KJS::JSObject *object, const Method *methods)
Publishes an array of Methods to an object.
bool convert(int targetTypeId)
uint toUInt(bool *ok, int base) const const
virtual const QMetaObject * metaObject() const const
QString & replace(int position, int n, QChar after)
QString & remove(int position, int n)
QVariant based binding.
T findChild(const QString &name, Qt::FindChildOptions options) const const
QMetaType::TypeFlags typeFlags(int type)
bool canConvert(int targetTypeId) const const
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
const char * className() const const
static ScriptableExtension * childObject(QObject *obj)
int enumeratorCount() const const
bool setProperty(const char *name, const QVariant &value)
int keyCount() const const
QMetaProperty property(int index) const const
Filters events for a QObject and forwards them to a JS handler.
Definition: eventproxy.h:43
const char * constData() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString left(int n) const const
QMetaMethod::Access access() const const
QMetaEnum enumerator(int index) const const
const char * key(int index) const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
virtual QVariant callAsFunction(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args)
Interpreter * dynamicInterpreter() const
int indexOfProperty(const char *name) const const
virtual bool put(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName, const QVariant &value)
const char * typeName() const const
QList< QByteArray > parameterTypes() const const
bool isEnumType() const const
int access(const QString &path, int mode)
char * toString(const EngineQuery &query)
QVariant property(const char *name) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Dec 10 2023 03:59:19 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.