Showing the changes to libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp (1 of 2 files). Show all
Unified · Split
| @@ -134,6 +134,23 @@ V8ScriptValue ScriptObjectV8Proxy::newQObject(ScriptEngineV8* engine, QObject* o · 133 unmodified lines | ||
| 134 | 134 | QPointer<ScriptEngineV8> enginePtr = engine; |
| 135 | 135 | object->connect(object, &QObject::destroyed, engine, [enginePtr, object]() { |
| 136 | 136 | 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 | + | |
| 137 | 154 | QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect); |
| 138 | 155 | ScriptEngineV8::ObjectWrapperMap::iterator lookup = enginePtr->_qobjectWrapperMap.find(object); |
| 139 | 156 | if (lookup != enginePtr->_qobjectWrapperMap.end()) { |
| @@ -195,7 +212,13 @@ ScriptObjectV8Proxy* ScriptObjectV8Proxy::unwrapProxy(v8::Isolate* isolate, v8:: · 55 unmodified lines | ||
| 195 | 212 | qCDebug(scriptengine_v8) << "Cannot unwrap proxy - internal fields don't point to object proxy"; |
| 196 | 213 | return nullptr; |
| 197 | 214 | } |
| 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; | |
| 199 | 222 | } |
| 200 | 223 | |
| 201 | 224 | QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) { |
| @@ -204,6 +227,12 @@ QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) { · 2 unmodified lines | ||
| 204 | 227 | } |
| 205 | 228 | |
| 206 | 229 | ScriptObjectV8Proxy::~ScriptObjectV8Proxy() { |
| 230 | + for (auto signal : _signalInstances) { | |
| 231 | + if (signal) { | |
| 232 | + signal->disconnectAllScriptSignalProxies(); | |
| 233 | + } | |
| 234 | + } | |
| 235 | + _wasDestroyed = true; | |
| 207 | 236 | if (_ownsObject) { |
| 208 | 237 | auto isolate = _engine->getIsolate(); |
| 209 | 238 | v8::Locker locker(isolate); |
| @@ -1177,6 +1206,10 @@ ScriptSignalV8Proxy::ScriptSignalV8Proxy(ScriptEngineV8* engine, QObject* object · 967 unmodified lines | ||
| 1177 | 1206 | } |
| 1178 | 1207 | |
| 1179 | 1208 | ScriptSignalV8Proxy::~ScriptSignalV8Proxy() { |
| 1209 | + if (!_cleanup) { | |
| 1210 | + disconnectAllScriptSignalProxies(); | |
| 1211 | + } | |
| 1212 | + | |
| 1180 | 1213 | auto isolate = _engine->getIsolate(); |
| 1181 | 1214 | v8::Locker locker(isolate); |
| 1182 | 1215 | v8::Isolate::Scope isolateScope(isolate); |
| @@ -1408,18 +1441,23 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { · 225 unmodified lines | ||
| 1408 | 1441 | Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)); |
| 1409 | 1442 | // For debugging |
| 1410 | 1443 | ScriptSignalV8Proxy* entryProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject()); |
| 1411 | − Q_ASSERT(thisProxy); | |
| 1444 | + Q_ASSERT(entryProxy); | |
| 1412 | 1445 | qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::connect: entry proxy: " << entryProxy->fullName(); |
| 1413 | 1446 | } |
| 1414 | 1447 | if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) { |
| 1415 | 1448 | Q_ASSERT(false); |
| 1416 | 1449 | } |
| 1450 | + if (entry->StrictEquals(v8ThisObject.get())) { | |
| 1451 | + foundIt = true; | |
| 1452 | + } | |
| 1417 | 1453 | } |
| 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 | + } | |
| 1423 | 1461 | } |
| 1424 | 1462 | } else { |
| 1425 | 1463 | v8::Local<v8::Array> newArray = v8::Array::New(isolate, 1); |
| @@ -1449,9 +1487,11 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { · 23 unmodified lines | ||
| 1449 | 1487 | void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { |
| 1450 | 1488 | QObject* qobject = _object; |
| 1451 | 1489 | 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 | + } | |
| 1455 | 1495 | } |
| 1456 | 1496 | v8::Locker locker(isolate); |
| 1457 | 1497 | v8::Isolate::Scope isolateScope(isolate); |
| @@ -1502,15 +1542,11 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 44 unmodified lines | ||
| 1502 | 1542 | v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked(); |
| 1503 | 1543 | v8::Local<v8::Value> destData; |
| 1504 | 1544 | |
| 1505 | − //auto destFunctionContext = destFunction->CreationContext(); | |
| 1506 | 1545 | 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); | |
| 1514 | 1550 | if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) { |
| 1515 | 1551 | Q_ASSERT(false); |
| 1516 | 1552 | } |
| @@ -1518,24 +1554,18 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 1 unmodified line | ||
| 1518 | 1554 | v8::Local<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData); |
| 1519 | 1555 | int length = destArray->Length(); |
| 1520 | 1556 | v8::Local<v8::Array> newArray = v8::Array::New(isolate, length - 1); |
| 1521 | − bool foundIt = false; | |
| 1557 | + int findCounter = 0; | |
| 1522 | 1558 | int newIndex = 0; |
| 1523 | 1559 | for (int idx = 0; idx < length; ++idx) { |
| 1524 | 1560 | v8::Local<v8::Value> entry = destArray->Get(destFunctionContext, idx).ToLocalChecked(); |
| 1525 | 1561 | // For debugging: |
| 1526 | 1562 | { |
| 1527 | − //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: entry details: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, entry)) | |
| 1528 | − // << " Array: " << _engine->scriptValueDebugDetailsV8(V8ScriptValue(_engine, destArray)); | |
| 1529 | 1563 | Q_ASSERT(entry->IsObject()); |
| 1530 | 1564 | V8ScriptValue v8EntryObject(_engine, entry); |
| 1531 | 1565 | 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(); | |
| 1536 | 1566 | } |
| 1537 | 1567 | if (entry->StrictEquals(v8ThisObject.get())) { |
| 1538 | − foundIt = true; | |
| 1568 | + findCounter++; | |
| 1539 | 1569 | } else { |
| 1540 | 1570 | if (!newArray->Set(destFunctionContext, newIndex, entry).FromMaybe(false)) { |
| 1541 | 1571 | Q_ASSERT(false); |
| @@ -1543,7 +1573,7 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 1 unmodified line | ||
| 1543 | 1573 | newIndex++; |
| 1544 | 1574 | } |
| 1545 | 1575 | } |
| 1546 | − Q_ASSERT(foundIt); | |
| 1576 | + Q_ASSERT(findCounter == 1); | |
| 1547 | 1577 | if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) { |
| 1548 | 1578 | Q_ASSERT(false); |
| 1549 | 1579 | } |
| @@ -1554,8 +1584,30 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 4 unmodified lines | ||
| 1554 | 1584 | // inform Qt that we're no longer connected to this signal |
| 1555 | 1585 | if (_connections.empty()) { |
| 1556 | 1586 | 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 | + } | |
| 1559 | 1593 | _isConnected = false; |
| 1560 | 1594 | } |
| 1561 | 1595 | } |
| 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 | +} | |