History

Fixed script signal proxy crashes

ksuprynowicz committed 2 years ago

Commit 4e819081 parent 8661e8a2 files · +89 −30Browse files

Showing the changes to libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp (1 of 2 files). Show all

Unified · Split

libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp+81−29
@@ -134,6 +134,23 @@ V8ScriptValue ScriptObjectV8Proxy::newQObject(ScriptEngineV8* engine, QObject* o · 133 unmodified lines
134134 QPointer<ScriptEngineV8> enginePtr = engine;
135135 object->connect(object, &QObject::destroyed, engine, [enginePtr, object]() {
136136 if (!enginePtr) return;
137+ // Lookup needs to be done twice, because _qobjectWrapperMapProtect must not be locked when disconnecting signals
138+ QSharedPointer<ScriptObjectV8Proxy> proxy;
139+ {
140+ QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect);
141+ auto lookupV8 = enginePtr->_qobjectWrapperMapV8.find(object);
142+ if (lookupV8 != enginePtr->_qobjectWrapperMapV8.end()) {
143+ proxy = lookupV8.value();
144+ }
145+ }
146+ if (proxy) {
147+ for (auto signal : proxy->_signalInstances) {
148+ if (signal) {
149+ signal->disconnectAllScriptSignalProxies();
150+ }
151+ }
152+ }
153+
137154 QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect);
138155 ScriptEngineV8::ObjectWrapperMap::iterator lookup = enginePtr->_qobjectWrapperMap.find(object);
139156 if (lookup != enginePtr->_qobjectWrapperMap.end()) {
@@ -195,7 +212,13 @@ ScriptObjectV8Proxy* ScriptObjectV8Proxy::unwrapProxy(v8::Isolate* isolate, v8:: · 55 unmodified lines
195212 qCDebug(scriptengine_v8) << "Cannot unwrap proxy - internal fields don't point to object proxy";
196213 return nullptr;
197214 }
198− return reinterpret_cast<ScriptObjectV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1));
215+
216+ ScriptObjectV8Proxy* proxy = reinterpret_cast<ScriptObjectV8Proxy*>(v8Object->GetAlignedPointerFromInternalField(1));
217+ if (proxy) {
218+ Q_ASSERT(!proxy->_wasDestroyed);
219+ }
220+
221+ return proxy;
199222 }
200223
201224 QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) {
@@ -204,6 +227,12 @@ QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) { · 2 unmodified lines
204227 }
205228
206229 ScriptObjectV8Proxy::~ScriptObjectV8Proxy() {
230+ for (auto signal : _signalInstances) {
231+ if (signal) {
232+ signal->disconnectAllScriptSignalProxies();
233+ }
234+ }
235+ _wasDestroyed = true;
207236 if (_ownsObject) {
208237 auto isolate = _engine->getIsolate();
209238 v8::Locker locker(isolate);
@@ -1177,6 +1206,10 @@ ScriptSignalV8Proxy::ScriptSignalV8Proxy(ScriptEngineV8* engine, QObject* object · 967 unmodified lines
11771206 }
11781207
11791208 ScriptSignalV8Proxy::~ScriptSignalV8Proxy() {
1209+ if (!_cleanup) {
1210+ disconnectAllScriptSignalProxies();
1211+ }
1212+
11801213 auto isolate = _engine->getIsolate();
11811214 v8::Locker locker(isolate);
11821215 v8::Isolate::Scope isolateScope(isolate);
@@ -1408,18 +1441,23 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { · 225 unmodified lines
14081441 Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject));
14091442 // For debugging
14101443 ScriptSignalV8Proxy* entryProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject());
1411− Q_ASSERT(thisProxy);
1444+ Q_ASSERT(entryProxy);
14121445 qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::connect: entry proxy: " << entryProxy->fullName();
14131446 }
14141447 if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) {
14151448 Q_ASSERT(false);
14161449 }
1450+ if (entry->StrictEquals(v8ThisObject.get())) {
1451+ foundIt = true;
1452+ }
14171453 }
1418− if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) {
1419− Q_ASSERT(false);
1420− }
1421− if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {
1422− Q_ASSERT(false);
1454+ if (!foundIt) {
1455+ if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) {
1456+ Q_ASSERT(false);
1457+ }
1458+ if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {
1459+ Q_ASSERT(false);
1460+ }
14231461 }
14241462 } else {
14251463 v8::Local<v8::Array> newArray = v8::Array::New(isolate, 1);
@@ -1449,9 +1487,11 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { · 23 unmodified lines
14491487 void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) {
14501488 QObject* qobject = _object;
14511489 v8::Isolate *isolate = _engine->getIsolate();
1452− if (!qobject) {
1453− isolate->ThrowError("Referencing deleted native object");
1454− return;
1490+ if (!_cleanup) {
1491+ if (!qobject) {
1492+ isolate->ThrowError("Referencing deleted native object");
1493+ return;
1494+ }
14551495 }
14561496 v8::Locker locker(isolate);
14571497 v8::Isolate::Scope isolateScope(isolate);
@@ -1502,15 +1542,11 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 44 unmodified lines
15021542 v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked();
15031543 v8::Local<v8::Value> destData;
15041544
1505− //auto destFunctionContext = destFunction->CreationContext();
15061545 auto destFunctionContext = context;
1507− Q_ASSERT(thisObject().isObject());
1508− V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject());
1509− Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject));
1510− // For debugging
1511− ScriptSignalV8Proxy* thisProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)->toQObject());
1512− Q_ASSERT(thisProxy);
1513− //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: " << thisProxy->fullName() << " fullName: " << fullName();
1546+ ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents |
1547+ ScriptEngine::PreferExistingWrapperObject;
1548+ // It's not necessarily new, newQObject looks for it first in object wrapper map, and for already existing signal it should be found there
1549+ V8ScriptValue v8ThisObject = ScriptObjectV8Proxy::newQObject(_engine, this, ScriptEngine::ScriptOwnership, options);
15141550 if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) {
15151551 Q_ASSERT(false);
15161552 }
@@ -1518,24 +1554,18 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 1 unmodified line
15181554 v8::Local<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData);
15191555 int length = destArray->Length();
15201556 v8::Local<v8::Array> newArray = v8::Array::New(isolate, length - 1);
1521− bool foundIt = false;
1557+ int findCounter = 0;
15221558 int newIndex = 0;
15231559 for (int idx = 0; idx < length; ++idx) {
15241560 v8::Local<v8::Value> entry = destArray->Get(destFunctionContext, idx).ToLocalChecked();
15251561 // For debugging:
15261562 {
1527− //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: entry details: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, entry))
1528− // << " Array: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, destArray));
15291563 Q_ASSERT(entry->IsObject());
15301564 V8ScriptValue v8EntryObject(_engine, entry);
15311565 Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject));
1532− // For debugging
1533− //ScriptSignalV8Proxy* entryProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject());
1534− Q_ASSERT(thisProxy);
1535− //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: entry proxy: " << entryProxy->fullName();
15361566 }
15371567 if (entry->StrictEquals(v8ThisObject.get())) {
1538− foundIt = true;
1568+ findCounter++;
15391569 } else {
15401570 if (!newArray->Set(destFunctionContext, newIndex, entry).FromMaybe(false)) {
15411571 Q_ASSERT(false);
@@ -1543,7 +1573,7 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 1 unmodified line
15431573 newIndex++;
15441574 }
15451575 }
1546− Q_ASSERT(foundIt);
1576+ Q_ASSERT(findCounter == 1);
15471577 if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {
15481578 Q_ASSERT(false);
15491579 }
@@ -1554,8 +1584,30 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 4 unmodified lines
15541584 // inform Qt that we're no longer connected to this signal
15551585 if (_connections.empty()) {
15561586 Q_ASSERT(_isConnected);
1557− bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId);
1558− Q_ASSERT(result);
1587+ // During cleanup qobject might be null since it might have been already deleted
1588+ if (!_cleanup || (_cleanup && qobject)) {
1589+ Q_ASSERT(qobject);
1590+ bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId);
1591+ Q_ASSERT(result);
1592+ }
15591593 _isConnected = false;
15601594 }
15611595 }
1596+
1597+void ScriptSignalV8Proxy::disconnectAllScriptSignalProxies() {
1598+ _cleanup = true;
1599+ QList<Connection> connections;
1600+ withReadLock([&]{
1601+ connections = _connections;
1602+ });
1603+
1604+ for (auto &connection : connections) {
1605+ ScriptValue thisValue(new ScriptValueV8Wrapper(_engine, connection.thisValue));
1606+ ScriptValue callback(new ScriptValueV8Wrapper(_engine, connection.callback));
1607+ disconnect(thisValue, callback);
1608+ }
1609+}
1610+
1611+void ScriptSignalV8Proxy::disconnectAll() {
1612+ QObject::disconnect(this, nullptr, nullptr, nullptr);
1613+}