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+
22+Q_DECLARE_METATYPE(QScriptContext*)
23+Q_DECLARE_METATYPE(ScriptValue)
24+Q_DECLARE_METATYPE(QScriptValue)
25+
26+Q_DECLARE_METATYPE(QSharedPointer<ScriptObjectQtProxy>)
27+Q_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
31+class ScriptPropertyContextQtWrapper final : public ScriptContext {
32+public: // construction
33+ inline ScriptPropertyContextQtWrapper(const ScriptValue& object, ScriptContext* parentContext) :
34+ _parent(parentContext), _object(object) {}
35+
36+public: // 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+
48+private: // storage
49+ ScriptContext* _parent;
50+ const ScriptValue& _object;
51+};
52+
53+QScriptValue 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+
73+ScriptObjectQtProxy* ScriptObjectQtProxy::unwrapProxy(const QScriptValue& val) {
74+ QScriptClass* scriptClass = val.scriptClass();
75+ return scriptClass ? dynamic_cast<ScriptObjectQtProxy*>(scriptClass) : nullptr;
76+}
77+
78+QObject* 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+
86+ScriptObjectQtProxy::~ScriptObjectQtProxy() {
87+ if (_ownsObject) {
88+ QObject* qobject = _object;
89+ if(qobject) qobject->deleteLater();
90+ }
91+}
92+
93+void 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+
188+QScriptClass::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+
214+QScriptValue::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+
241+QScriptValue 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+
295+void 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+
329+ScriptVariantQtProxy::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+
334+QScriptValue 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+
345+ScriptVariantQtProxy* ScriptVariantQtProxy::unwrapProxy(const QScriptValue& val) {
346+ QScriptClass* scriptClass = val.scriptClass();
347+ return scriptClass ? dynamic_cast<ScriptVariantQtProxy*>(scriptClass) : nullptr;
348+}
349+
350+QVariant ScriptVariantQtProxy::unwrap(const QScriptValue& val) {
351+ ScriptVariantQtProxy* proxy = unwrapProxy(val);
352+ return proxy ? proxy->toQtValue() : QVariant();
353+}
354+
355+bool ScriptMethodQtProxy::supportsExtension(Extension extension) const {
356+ switch (extension) {
357+ case Callable:
358+ return true;
359+ default:
360+ return false;
361+ }
362+}
363+
364+QVariant 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
469+int 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+
491+int ScriptSignalQtProxy::discoverMetaCallIdx() {
492+ const QMetaObject* ourMeta = metaObject();
493+ return ourMeta->methodCount();
494+}
495+
496+ScriptSignalQtProxy::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+
508+void 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+
564+void 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