History

first pass creating Qt <-> QtScript interface

Heather Anderson committed 4 years ago

Commit bc6eaf21 parent bd0455463 files · +1707 −294Browse files

Showing the changes to libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp (1 of 63 files). Show all

Unified · Split

libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp+622−0
@@ -0,0 +1,622 @@
1//
2// ScriptObjectQtProxy.cpp
3// libraries/script-engine/src/qtscript
4//
5// Created by Heather Anderson on 12/5/21.
6// Copyright 2021 Vircadia contributors.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#include "ScriptObjectQtProxy.h"
13
14#include <QtCore/QList>
15#include <QtCore/QSharedPointer>
16
17#include <QtScript/QScriptContext>
18
19#include "ScriptContextQtWrapper.h"
20#include "ScriptValueQtWrapper.h"
21
22Q_DECLARE_METATYPE(QScriptContext*)
23Q_DECLARE_METATYPE(ScriptValue)
24Q_DECLARE_METATYPE(QScriptValue)
25
26Q_DECLARE_METATYPE(QSharedPointer<ScriptObjectQtProxy>)
27Q_DECLARE_METATYPE(QSharedPointer<ScriptVariantQtProxy>)
28
29// Used strictly to replace the "this" object value for property access. May expand to a full context element
30// if we find it necessary to, but hopefully not needed
31class ScriptPropertyContextQtWrapper final : public ScriptContext {
32public: // construction
33 inline ScriptPropertyContextQtWrapper(const ScriptValue& object, ScriptContext* parentContext) :
34 _parent(parentContext), _object(object) {}
35
36public: // ScriptContext implementation
37 virtual int argumentCount() const override { return _parent->argumentCount(); }
38 virtual ScriptValue argument(int index) const override { return _parent->argument(index); }
39 virtual QStringList backtrace() const override { return _parent->backtrace(); }
40 virtual ScriptValue callee() const override { return _parent->callee(); }
41 virtual ScriptEnginePointer engine() const override { return _parent->engine(); }
42 virtual ScriptFunctionContextPointer functionContext() const override { return _parent->functionContext(); }
43 virtual ScriptContextPointer parentContext() const override { return _parent->parentContext(); }
44 virtual ScriptValue thisObject() const override { return _object; }
45 virtual ScriptValue throwError(const QString& text) override { return _parent->throwError(text); }
46 virtual ScriptValue throwValue(const ScriptValue& value) override { return _parent->throwValue(value); }
47
48private: // storage
49 ScriptContext* _parent;
50 const ScriptValue& _object;
51};
52
53QScriptValue ScriptObjectQtProxy::newQObject(ScriptEngineQtScript* engine, QObject* object,
54 ScriptEngine::ValueOwnership ownership,
55 const ScriptEngine::QObjectWrapOptions& options) {
56 bool ownsObject;
57 switch (ownership) {
58 case ScriptEngine::QtOwnership:
59 ownsObject = false;
60 break;
61 case ScriptEngine::ScriptOwnership:
62 ownsObject = true;
63 break;
64 case ScriptEngine::AutoOwnership:
65 ownsObject = !object->parent();
66 break;
67 }
68 QScriptEngine* qengine = static_cast<QScriptEngine*>(engine);
69 auto proxy = QSharedPointer<ScriptObjectQtProxy>::create(engine, object, ownsObject, options);
70 return static_cast<QScriptEngine*>(engine)->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy)));
71}
72
73ScriptObjectQtProxy* ScriptObjectQtProxy::unwrapProxy(const QScriptValue& val) {
74 QScriptClass* scriptClass = val.scriptClass();
75 return scriptClass ? dynamic_cast<ScriptObjectQtProxy*>(scriptClass) : nullptr;
76}
77
78QObject* ScriptObjectQtProxy::unwrap(const QScriptValue& val) {
79 if (val.isQObject()) {
80 return val.toQObject();
81 }
82 ScriptObjectQtProxy* proxy = unwrapProxy(val);
83 return proxy ? proxy->toQtValue() : nullptr;
84}
85
86ScriptObjectQtProxy::~ScriptObjectQtProxy() {
87 if (_ownsObject) {
88 QObject* qobject = _object;
89 if(qobject) qobject->deleteLater();
90 }
91}
92
93void ScriptObjectQtProxy::investigate() {
94 QObject* qobject = _object;
95 Q_ASSERT(qobject);
96 if (!qobject) return;
97
98 const QMetaObject* metaObject = qobject->metaObject();
99 _name = QString::fromLatin1(metaObject->className());
100
101 // discover properties
102 int startIdx = _wrapOptions & ScriptEngine::ExcludeSuperClassProperties ? metaObject->propertyOffset() : 0;
103 int num = metaObject->propertyCount();
104 for (int idx = startIdx; idx < num; ++idx) {
105 QMetaProperty prop = metaObject->property(idx);
106 if (!prop.isScriptable()) continue;
107
108 // always exclude child objects (at least until we decide otherwise)
109 int metaTypeId = prop.userType();
110 if (metaTypeId != QMetaType::UnknownType) {
111 QMetaType metaType(metaTypeId);
112 if (metaType.flags() & QMetaType::PointerToQObject) {
113 continue;
114 }
115 }
116
117 PropertyDef& propDef = _props.insert(idx, PropertyDef()).value();
118 propDef.name = _engine->toStringHandle(QString::fromLatin1(prop.name()));
119 propDef.flags = QScriptValue::Undeletable | QScriptValue::PropertyGetter | QScriptValue::PropertySetter |
120 QScriptValue::QObjectMember;
121 if (prop.isConstant()) propDef.flags |= QScriptValue::ReadOnly;
122 }
123
124 // discover methods
125 startIdx = _wrapOptions & ScriptEngine::ExcludeSuperClassMethods ? metaObject->methodCount() : 0;
126 num = metaObject->methodCount();
127 QHash<QScriptString, int> methodNames;
128 for (int idx = startIdx; idx < num; ++idx) {
129 QMetaMethod method = metaObject->method(idx);
130
131 // perhaps keep this comment? Calls (like AudioScriptingInterface::playSound) seem to expect non-public methods to be script-accessible
132 /* if (method.access() != QMetaMethod::Public) continue;*/
133
134 bool isSignal = false;
135 QByteArray szName = method.name();
136
137 switch (method.methodType()) {
138 case QMetaMethod::Constructor:
139 continue;
140 case QMetaMethod::Signal:
141 isSignal = true;
142 break;
143 case QMetaMethod::Slot:
144 if (_wrapOptions & ScriptEngine::ExcludeSlots) {
145 continue;
146 }
147 if (szName == "deleteLater") {
148 continue;
149 }
150 break;
151 }
152
153 QScriptString name = _engine->toStringHandle(QString::fromLatin1(szName));
154 auto nameLookup = methodNames.find(name);
155 if (isSignal) {
156 if (nameLookup == methodNames.end()) {
157 SignalDef& signalDef = _signals.insert(idx, SignalDef()).value();
158 signalDef.name = name;
159 signalDef.signal = method;
160 methodNames.insert(name, idx);
161 } else {
162 int originalMethodId = nameLookup.value();
163 SignalDefMap::iterator signalLookup = _signals.find(originalMethodId);
164 Q_ASSERT(signalLookup != _signals.end());
165 SignalDef& signalDef = signalLookup.value();
166 Q_ASSERT(signalDef.signal.parameterCount() != method.parameterCount());
167 if (signalDef.signal.parameterCount() < method.parameterCount()) {
168 signalDef.signal = method;
169 }
170 }
171 } else {
172 if (nameLookup == methodNames.end()) {
173 MethodDef& methodDef = _methods.insert(idx, MethodDef()).value();
174 methodDef.name = name;
175 methodDef.methods.append(method);
176 methodNames.insert(name, idx);
177 } else {
178 int originalMethodId = nameLookup.value();
179 MethodDefMap::iterator methodLookup = _methods.find(originalMethodId);
180 Q_ASSERT(methodLookup != _methods.end());
181 MethodDef& methodDef = methodLookup.value();
182 methodDef.methods.append(method);
183 }
184 }
185 }
186}
187
188QScriptClass::QueryFlags ScriptObjectQtProxy::queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id) {
189 // check for properties
190 for (PropertyDefMap::const_iterator trans = _props.cbegin(); trans != _props.cend(); ++trans) {
191 const PropertyDef& propDef = trans.value();
192 if (propDef.name != name) continue;
193 *id = trans.key() | PROPERTY_TYPE;
194 return flags & (HandlesReadAccess | HandlesWriteAccess);
195 }
196
197 // check for methods
198 for (MethodDefMap::const_iterator trans = _methods.cbegin(); trans != _methods.cend(); ++trans) {
199 if (trans.value().name != name) continue;
200 *id = trans.key() | METHOD_TYPE;
201 return flags & (HandlesReadAccess | HandlesWriteAccess);
202 }
203
204 // check for signals
205 for (SignalDefMap::const_iterator trans = _signals.cbegin(); trans != _signals.cend(); ++trans) {
206 if (trans.value().name != name) continue;
207 *id = trans.key() | SIGNAL_TYPE;
208 return flags & (HandlesReadAccess | HandlesWriteAccess);
209 }
210
211 return QueryFlags();
212}
213
214QScriptValue::PropertyFlags ScriptObjectQtProxy::propertyFlags(const QScriptValue& object, const QScriptString& name, uint id) {
215 QObject* qobject = _object;
216 if (!qobject) {
217 return QScriptValue::PropertyFlags();
218 }
219
220 switch (id & TYPE_MASK) {
221 case PROPERTY_TYPE: {
222 PropertyDefMap::const_iterator lookup = _props.find(id & ~TYPE_MASK);
223 if (lookup == _props.cend()) return QScriptValue::PropertyFlags();
224 const PropertyDef& propDef = lookup.value();
225 return propDef.flags;
226 }
227 case METHOD_TYPE: {
228 MethodDefMap::const_iterator lookup = _methods.find(id & ~TYPE_MASK);
229 if (lookup == _methods.cend()) return QScriptValue::PropertyFlags();
230 return QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::QObjectMember;
231 }
232 case SIGNAL_TYPE: {
233 SignalDefMap::const_iterator lookup = _signals.find(id & ~TYPE_MASK);
234 if (lookup == _signals.cend()) return QScriptValue::PropertyFlags();
235 return QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::QObjectMember;
236 }
237 }
238 return QScriptValue::PropertyFlags();
239}
240
241QScriptValue ScriptObjectQtProxy::property(const QScriptValue& object, const QScriptString& name, uint id) {
242 QObject* qobject = _object;
243 if (!qobject) {
244 QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
245 currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
246 return QScriptValue();
247 }
248
249 const QMetaObject* metaObject = qobject->metaObject();
250
251 switch (id & TYPE_MASK) {
252 case PROPERTY_TYPE: {
253 int propId = id & ~TYPE_MASK;
254 PropertyDefMap::const_iterator lookup = _props.find(propId);
255 if (lookup == _props.cend()) return QScriptValue();
256 const PropertyDef& propDef = lookup.value();
257
258 QMetaProperty prop = metaObject->property(propId);
259 ScriptValue scriptThis = ScriptValue(new ScriptValueQtWrapper(_engine, object));
260 ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext());
261 ScriptContextGuard guard(&ourContext);
262
263 QVariant varValue = prop.read(qobject);
264 return _engine->castVariantToValue(varValue);
265 }
266 case METHOD_TYPE: {
267 int methodId = id & ~TYPE_MASK;
268 MethodDefMap::const_iterator lookup = _methods.find(methodId);
269 if (lookup == _methods.cend()) return QScriptValue();
270 return static_cast<QScriptEngine*>(_engine)->newObject(
271 new ScriptMethodQtProxy(_engine, qobject, object, name, lookup.value().methods));
272 }
273 case SIGNAL_TYPE: {
274 int signalId = id & ~TYPE_MASK;
275 SignalDefMap::const_iterator defLookup = _signals.find(signalId);
276 if (defLookup == _signals.cend()) return QScriptValue();
277
278 InstanceMap::const_iterator instLookup = _signalInstances.find(signalId);
279 if (instLookup == _signalInstances.cend() || instLookup.value().isNull()) {
280 instLookup = _signalInstances.insert(signalId,
281 new ScriptSignalQtProxy(_engine, qobject, object, name, defLookup.value().signal));
282 Q_ASSERT(instLookup != _signalInstances.cend());
283 }
284 ScriptSignalQtProxy* proxy = instLookup.value();
285
286 QScriptEngine::QObjectWrapOptions options = QScriptEngine::ExcludeSuperClassContents |
287 QScriptEngine::ExcludeDeleteLater |
288 QScriptEngine::PreferExistingWrapperObject;
289 return static_cast<QScriptEngine*>(_engine)->newQObject(proxy, QScriptEngine::ScriptOwnership, options);
290 }
291 }
292 return QScriptValue();
293}
294
295void ScriptObjectQtProxy::setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) {
296 if (!(id & PROPERTY_TYPE)) return;
297 QObject* qobject = _object;
298 if (!qobject) {
299 QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
300 currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
301 return;
302 }
303
304 int propId = id & ~TYPE_MASK;
305 PropertyDefMap::const_iterator lookup = _props.find(propId);
306 if (lookup == _props.cend()) return;
307 const PropertyDef& propDef = lookup.value();
308 if (propDef.flags & QScriptValue::ReadOnly) return;
309
310 const QMetaObject* metaObject = qobject->metaObject();
311 QMetaProperty prop = metaObject->property(propId);
312
313 ScriptValue scriptThis = ScriptValue(new ScriptValueQtWrapper(_engine, object));
314 ScriptPropertyContextQtWrapper ourContext(scriptThis, _engine->currentContext());
315 ScriptContextGuard guard(&ourContext);
316
317 int propTypeId = prop.userType();
318 QVariant varValue;
319 if(!_engine->castValueToVariant(value, varValue, propTypeId)) {
320 QByteArray propTypeName = QMetaType(propTypeId).name();
321 QByteArray valTypeName = value.toVariant().typeName();
322 QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
323 currentContext->throwError(QScriptContext::TypeError, QString("Cannot convert %1 to %2").arg(valTypeName, propTypeName));
324 return;
325 }
326 prop.write(qobject, varValue);
327}
328
329ScriptVariantQtProxy::ScriptVariantQtProxy(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue scriptProto, ScriptObjectQtProxy* proto) :
330 QScriptClass(engine), _engine(engine), _variant(variant), _scriptProto(scriptProto), _proto(proto) {
331 _name = QString::fromLatin1(variant.typeName());
332}
333
334QScriptValue ScriptVariantQtProxy::newVariant(ScriptEngineQtScript* engine, const QVariant& variant, QScriptValue proto) {
335 QScriptEngine* qengine = static_cast<QScriptEngine*>(engine);
336 ScriptObjectQtProxy* protoProxy = ScriptObjectQtProxy::unwrapProxy(proto);
337 if (!protoProxy) {
338 Q_ASSERT(protoProxy);
339 return qengine->newVariant(variant);
340 }
341 auto proxy = QSharedPointer<ScriptVariantQtProxy>::create(engine, variant, proto, protoProxy);
342 return static_cast<QScriptEngine*>(engine)->newObject(proxy.get(), qengine->newVariant(QVariant::fromValue(proxy)));
343}
344
345ScriptVariantQtProxy* ScriptVariantQtProxy::unwrapProxy(const QScriptValue& val) {
346 QScriptClass* scriptClass = val.scriptClass();
347 return scriptClass ? dynamic_cast<ScriptVariantQtProxy*>(scriptClass) : nullptr;
348}
349
350QVariant ScriptVariantQtProxy::unwrap(const QScriptValue& val) {
351 ScriptVariantQtProxy* proxy = unwrapProxy(val);
352 return proxy ? proxy->toQtValue() : QVariant();
353}
354
355bool ScriptMethodQtProxy::supportsExtension(Extension extension) const {
356 switch (extension) {
357 case Callable:
358 return true;
359 default:
360 return false;
361 }
362}
363
364QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& argument) {
365 if (extension != Callable) return QVariant();
366 QScriptContext* context = qvariant_cast<QScriptContext*>(argument);
367
368 QObject* qobject = _object;
369 if (!qobject) {
370 context->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
371 return QVariant();
372 }
373
374 int scriptNumArgs = context->argumentCount();
375 Q_ASSERT(scriptNumArgs < 10);
376 int numArgs = std::min(scriptNumArgs, 10);
377
378 const int scriptValueTypeId = qMetaTypeId<ScriptValue>();
379
380 for (auto iter = _metas.cbegin(); iter != _metas.end(); ++iter) {
381 const QMetaMethod& meta = *iter;
382 int methodNumArgs = meta.parameterCount();
383 if (methodNumArgs != numArgs) {
384 continue;
385 }
386
387 QList<ScriptValue> qScriptArgList;
388 QList<QVariant> qVarArgList;
389 QGenericArgument qGenArgs[10];
390 int arg;
391 bool conversionFailed = false;
392 for (arg = 0; arg < numArgs && !conversionFailed; ++arg) {
393 int methodArgTypeId = meta.parameterType(arg);
394 QScriptValue argVal = context->argument(arg);
395 if (methodArgTypeId == scriptValueTypeId) {
396 qScriptArgList.append(ScriptValue(new ScriptValueQtWrapper(_engine, argVal)));
397 qGenArgs[arg] = Q_ARG(ScriptValue, qScriptArgList.back());
398 } else if (methodArgTypeId == QMetaType::QVariant) {
399 qVarArgList.append(argVal.toVariant());
400 qGenArgs[arg] = Q_ARG(QVariant, qVarArgList.back());
401 } else {
402 QVariant varArgVal;
403 if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) {
404 conversionFailed = true;
405 break;
406 /*QByteArray methodTypeName = QMetaType(methodArgTypeId).name();
407 QByteArray argTypeName = argVal.toVariant().typeName();
408 context->throwError(QScriptContext::TypeError,
409 QString("Cannot convert %1 to %2").arg(argTypeName, methodTypeName));
410 return QVariant();*/
411 }
412 qVarArgList.append(varArgVal);
413 const QVariant& converted = qVarArgList.back();
414
415 // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant
416 // A const_cast is needed because calling data() would detach the QVariant.
417 qGenArgs[arg] =
418 QGenericArgument(QMetaType::typeName(converted.userType()), const_cast<void*>(converted.constData()));
419 }
420 }
421 if (conversionFailed) {
422 continue;
423 }
424
425 ScriptContextQtWrapper ourContext(_engine, context);
426 ScriptContextGuard guard(&ourContext);
427
428 int returnTypeId = meta.returnType();
429 if (returnTypeId == QMetaType::Void) {
430 bool success = meta.invoke(qobject, Qt::DirectConnection, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3],
431 qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]);
432 if (!success) {
433 context->throwError("Native call failed");
434 }
435 return QVariant();
436 } else if (returnTypeId == scriptValueTypeId) {
437 ScriptValue result;
438 bool success = meta.invoke(qobject, Qt::DirectConnection, Q_RETURN_ARG(ScriptValue, result), qGenArgs[0],
439 qGenArgs[1], qGenArgs[2], qGenArgs[3], qGenArgs[4], qGenArgs[5], qGenArgs[6],
440 qGenArgs[7], qGenArgs[8], qGenArgs[9]);
441 if (!success) {
442 context->throwError("Native call failed");
443 return QVariant();
444 }
445 QScriptValue qResult = ScriptValueQtWrapper::fullUnwrap(_engine, result);
446 return QVariant::fromValue(qResult);
447 } else {
448 // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant
449 const char* typeName = meta.typeName();
450 QVariant qRetVal(returnTypeId, static_cast<void*>(NULL));
451 QGenericReturnArgument sRetVal(typeName, const_cast<void*>(qRetVal.constData()));
452
453 bool success =
454 meta.invoke(qobject, Qt::DirectConnection, sRetVal, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3],
455 qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]);
456 if (!success) {
457 context->throwError("Native call failed");
458 return QVariant();
459 }
460 QScriptValue qResult = _engine->castVariantToValue(qRetVal);
461 return QVariant::fromValue(qResult);
462 }
463 }
464 context->throwError("Native call failed: could not locate an overload with the requested arguments");
465 return QVariant();
466}
467
468// Adapted from https://doc.qt.io/archives/qq/qq16-dynamicqobject.html, for connecting to a signal without a compile-time definition for it
469int ScriptSignalQtProxy::qt_metacall(QMetaObject::Call call, int id, void** arguments) {
470 id = ScriptSignalQtProxyBase::qt_metacall(call, id, arguments);
471 if (id != 0 || call != QMetaObject::InvokeMetaMethod) {
472 return id;
473 }
474
475 QScriptValueList args;
476 int numArgs = _meta.parameterCount();
477 for (int arg = 0; arg < numArgs; ++arg) {
478 int methodArgTypeId = _meta.parameterType(arg);
479 QVariant argValue(methodArgTypeId, arguments[arg+1]);
480 args.append(_engine->castVariantToValue(argValue));
481 }
482
483 for (ConnectionList::iterator iter = _connections.begin(); iter != _connections.end(); ++iter) {
484 Connection& conn = *iter;
485 conn.callback.call(conn.thisValue, args);
486 }
487
488 return -1;
489}
490
491int ScriptSignalQtProxy::discoverMetaCallIdx() {
492 const QMetaObject* ourMeta = metaObject();
493 return ourMeta->methodCount();
494}
495
496ScriptSignalQtProxy::ConnectionList::iterator ScriptSignalQtProxy::findConnection(QScriptValue thisObject,
497 QScriptValue callback) {
498 for (ConnectionList::iterator iter = _connections.begin(); iter != _connections.end(); ++iter) {
499 Connection& conn = *iter;
500 if (conn.callback.strictlyEquals(callback) && conn.thisValue.strictlyEquals(thisObject)) {
501 return iter;
502 }
503 }
504 return _connections.end();
505}
506
507
508void ScriptSignalQtProxy::connect(QScriptValue arg0, QScriptValue arg1) {
509 QObject* qobject = _object;
510 if (!qobject) {
511 QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
512 currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
513 return;
514 }
515
516 // untangle the arguments
517 QScriptValue callback;
518 QScriptValue callbackThis;
519 if (arg1.isFunction()) {
520 callbackThis = arg0;
521 callback = arg1;
522 } else {
523 callback = arg0;
524 }
525 if (!callback.isFunction()) {
526 QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
527 currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'connect'");
528 return;
529 }
530
531 // are we already connected?
532 ConnectionList::iterator lookup = findConnection(callbackThis, callback);
533 if (lookup != _connections.end()) {
534 return; // already exists
535 }
536
537 // add a reference to ourselves to the destination callback
538 QScriptValue destData = callback.data();
539 Q_ASSERT(!destData.isValid() || destData.isArray());
540 if (!destData.isArray()) {
541 destData = static_cast<QScriptEngine*>(_engine)->newArray();
542 }
543 {
544 QScriptValueList args;
545 args << thisObject();
546 destData.property("push").call(destData, args);
547 }
548 callback.setData(destData);
549
550 // add this to our internal list of connections
551 Connection newConn;
552 newConn.callback = callback;
553 newConn.thisValue = callbackThis;
554 _connections.append(newConn);
555
556 // inform Qt that we're connecting to this signal
557 if (!_isConnected) {
558 auto result = QMetaObject::connect(qobject, _meta.methodIndex(), this, _metaCallId);
559 Q_ASSERT(result);
560 _isConnected = true;
561 }
562}
563
564void ScriptSignalQtProxy::disconnect(QScriptValue arg0, QScriptValue arg1) {
565 QObject* qobject = _object;
566 if (!qobject) {
567 QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
568 currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");
569 return;
570 }
571
572 // untangle the arguments
573 QScriptValue callback;
574 QScriptValue callbackThis;
575 if (arg1.isFunction()) {
576 callbackThis = arg0;
577 callback = arg1;
578 } else {
579 callback = arg0;
580 }
581 if (!callback.isFunction()) {
582 QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();
583 currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'disconnect'");
584 return;
585 }
586
587 // locate this connection in our list of connections
588 ConnectionList::iterator lookup = findConnection(callbackThis, callback);
589 if (lookup == _connections.end()) {
590 return; // not here
591 }
592
593 // remove it from our internal list of connections
594 _connections.erase(lookup);
595
596 // remove a reference to ourselves from the destination callback
597 QScriptValue destData = callback.data();
598 Q_ASSERT(destData.isArray());
599 if (destData.isArray()) {
600 QScriptValue qThis = thisObject();
601 int len = destData.property("length").toInteger();
602 bool foundIt = false;
603 for (int idx = 0; idx < len && !foundIt; ++idx) {
604 QScriptValue entry = destData.property(idx);
605 if (entry.strictlyEquals(qThis)) {
606 foundIt = true;
607 QScriptValueList args;
608 args << idx << 1;
609 destData.property("splice").call(destData, args);
610 }
611 }
612 Q_ASSERT(foundIt);
613 }
614
615 // inform Qt that we're no longer connected to this signal
616 if (_connections.empty()) {
617 Q_ASSERT(_isConnected);
618 bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId);
619 Q_ASSERT(result);
620 _isConnected = false;
621 }
622}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t