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
134 QPointer<ScriptEngineV8> enginePtr = engine;134 QPointer<ScriptEngineV8> enginePtr = engine;
135 object->connect(object, &QObject::destroyed, engine, [enginePtr, object]() {135 object->connect(object, &QObject::destroyed, engine, [enginePtr, object]() {
136 if (!enginePtr) return;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 QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect);154 QMutexLocker guard(&enginePtr->_qobjectWrapperMapProtect);
138 ScriptEngineV8::ObjectWrapperMap::iterator lookup = enginePtr->_qobjectWrapperMap.find(object);155 ScriptEngineV8::ObjectWrapperMap::iterator lookup = enginePtr->_qobjectWrapperMap.find(object);
139 if (lookup != enginePtr->_qobjectWrapperMap.end()) {156 if (lookup != enginePtr->_qobjectWrapperMap.end()) {
@@ -195,7 +212,13 @@ ScriptObjectV8Proxy* ScriptObjectV8Proxy::unwrapProxy(v8::Isolate* isolate, v8:: · 55 unmodified lines
195 qCDebug(scriptengine_v8) << "Cannot unwrap proxy - internal fields don't point to object proxy";212 qCDebug(scriptengine_v8) << "Cannot unwrap proxy - internal fields don't point to object proxy";
196 return nullptr;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
201QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) {224QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) {
@@ -204,6 +227,12 @@ QObject* ScriptObjectV8Proxy::unwrap(const V8ScriptValue& val) { · 2 unmodified lines
204}227}
205 228
206ScriptObjectV8Proxy::~ScriptObjectV8Proxy() {229ScriptObjectV8Proxy::~ScriptObjectV8Proxy() {
230 for (auto signal : _signalInstances) {
231 if (signal) {
232 signal->disconnectAllScriptSignalProxies();
233 }
234 }
235 _wasDestroyed = true;
207 if (_ownsObject) {236 if (_ownsObject) {
208 auto isolate = _engine->getIsolate();237 auto isolate = _engine->getIsolate();
209 v8::Locker locker(isolate);238 v8::Locker locker(isolate);
@@ -1177,6 +1206,10 @@ ScriptSignalV8Proxy::ScriptSignalV8Proxy(ScriptEngineV8* engine, QObject* object · 967 unmodified lines
1177}1206}
1178 1207
1179ScriptSignalV8Proxy::~ScriptSignalV8Proxy() {1208ScriptSignalV8Proxy::~ScriptSignalV8Proxy() {
1209 if (!_cleanup) {
1210 disconnectAllScriptSignalProxies();
1211 }
1212
1180 auto isolate = _engine->getIsolate();1213 auto isolate = _engine->getIsolate();
1181 v8::Locker locker(isolate);1214 v8::Locker locker(isolate);
1182 v8::Isolate::Scope isolateScope(isolate);1215 v8::Isolate::Scope isolateScope(isolate);
@@ -1408,18 +1441,23 @@ void ScriptSignalV8Proxy::connect(ScriptValue arg0, ScriptValue arg1) { · 225 unmodified lines
1408 Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject));1441 Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject));
1409 // For debugging1442 // For debugging
1410 ScriptSignalV8Proxy* entryProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject());1443 ScriptSignalV8Proxy* entryProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject)->toQObject());
1411 Q_ASSERT(thisProxy);1444 Q_ASSERT(entryProxy);
1412 qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::connect: entry proxy: " << entryProxy->fullName();1445 qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::connect: entry proxy: " << entryProxy->fullName();
1413 }1446 }
1414 if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) {1447 if (!newArray->Set(destFunctionContext, idx, entry).FromMaybe(false)) {
1415 Q_ASSERT(false);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)) {1454 if (!foundIt) {
1419 Q_ASSERT(false);1455 if (!newArray->Set(destFunctionContext, length, v8ThisObject.get()).FromMaybe(false)) {
1420 }1456 Q_ASSERT(false);
1421 if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {1457 }
1422 Q_ASSERT(false);1458 if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {
1459 Q_ASSERT(false);
1460 }
1423 }1461 }
1424 } else {1462 } else {
1425 v8::Local<v8::Array> newArray = v8::Array::New(isolate, 1);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
1449void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) {1487void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) {
1450 QObject* qobject = _object;1488 QObject* qobject = _object;
1451 v8::Isolate *isolate = _engine->getIsolate();1489 v8::Isolate *isolate = _engine->getIsolate();
1452 if (!qobject) {1490 if (!_cleanup) {
1453 isolate->ThrowError("Referencing deleted native object");1491 if (!qobject) {
1454 return;1492 isolate->ThrowError("Referencing deleted native object");
1493 return;
1494 }
1455 }1495 }
1456 v8::Locker locker(isolate);1496 v8::Locker locker(isolate);
1457 v8::Isolate::Scope isolateScope(isolate);1497 v8::Isolate::Scope isolateScope(isolate);
@@ -1502,15 +1542,11 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 44 unmodified lines
1502 v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked();1542 v8::Local<v8::String> destDataName = v8::String::NewFromUtf8(isolate, "__data__").ToLocalChecked();
1503 v8::Local<v8::Value> destData;1543 v8::Local<v8::Value> destData;
1504 1544
1505 //auto destFunctionContext = destFunction->CreationContext();
1506 auto destFunctionContext = context;1545 auto destFunctionContext = context;
1507 Q_ASSERT(thisObject().isObject());1546 ScriptEngine::QObjectWrapOptions options = ScriptEngine::ExcludeSuperClassContents |
1508 V8ScriptValue v8ThisObject = ScriptValueV8Wrapper::fullUnwrap(_engine, thisObject());1547 ScriptEngine::PreferExistingWrapperObject;
1509 Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject));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
1510 // For debugging1549 V8ScriptValue v8ThisObject = ScriptObjectV8Proxy::newQObject(_engine, this, ScriptEngine::ScriptOwnership, options);
1511 ScriptSignalV8Proxy* thisProxy = dynamic_cast<ScriptSignalV8Proxy*>(ScriptObjectV8Proxy::unwrapProxy(v8ThisObject)->toQObject());
1512 Q_ASSERT(thisProxy);
1513 //qCDebug(scriptengine_v8) << "ScriptSignalV8Proxy::disconnect: " << thisProxy->fullName() << " fullName: " << fullName();
1514 if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) {1550 if (!destFunction->Get(destFunctionContext, destDataName).ToLocal(&destData)) {
1515 Q_ASSERT(false);1551 Q_ASSERT(false);
1516 }1552 }
@@ -1518,24 +1554,18 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 1 unmodified line
1518 v8::Local<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData);1554 v8::Local<v8::Array> destArray = v8::Local<v8::Array>::Cast(destData);
1519 int length = destArray->Length();1555 int length = destArray->Length();
1520 v8::Local<v8::Array> newArray = v8::Array::New(isolate, length - 1);1556 v8::Local<v8::Array> newArray = v8::Array::New(isolate, length - 1);
1521 bool foundIt = false;1557 int findCounter = 0;
1522 int newIndex = 0;1558 int newIndex = 0;
1523 for (int idx = 0; idx < length; ++idx) {1559 for (int idx = 0; idx < length; ++idx) {
1524 v8::Local<v8::Value> entry = destArray->Get(destFunctionContext, idx).ToLocalChecked();1560 v8::Local<v8::Value> entry = destArray->Get(destFunctionContext, idx).ToLocalChecked();
1525 // For debugging: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 Q_ASSERT(entry->IsObject());1563 Q_ASSERT(entry->IsObject());
1530 V8ScriptValue v8EntryObject(_engine, entry);1564 V8ScriptValue v8EntryObject(_engine, entry);
1531 Q_ASSERT(ScriptObjectV8Proxy::unwrapProxy(v8EntryObject));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 if (entry->StrictEquals(v8ThisObject.get())) {1567 if (entry->StrictEquals(v8ThisObject.get())) {
1538 foundIt = true;1568 findCounter++;
1539 } else {1569 } else {
1540 if (!newArray->Set(destFunctionContext, newIndex, entry).FromMaybe(false)) {1570 if (!newArray->Set(destFunctionContext, newIndex, entry).FromMaybe(false)) {
1541 Q_ASSERT(false);1571 Q_ASSERT(false);
@@ -1543,7 +1573,7 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 1 unmodified line
1543 newIndex++;1573 newIndex++;
1544 }1574 }
1545 }1575 }
1546 Q_ASSERT(foundIt);1576 Q_ASSERT(findCounter == 1);
1547 if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {1577 if (!destFunction->Set(destFunctionContext, destDataName, newArray).FromMaybe(false)) {
1548 Q_ASSERT(false);1578 Q_ASSERT(false);
1549 }1579 }
@@ -1554,8 +1584,30 @@ void ScriptSignalV8Proxy::disconnect(ScriptValue arg0, ScriptValue arg1) { · 4 unmodified lines
1554 // inform Qt that we're no longer connected to this signal1584 // inform Qt that we're no longer connected to this signal
1555 if (_connections.empty()) {1585 if (_connections.empty()) {
1556 Q_ASSERT(_isConnected);1586 Q_ASSERT(_isConnected);
1557 bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId);1587 // During cleanup qobject might be null since it might have been already deleted
1558 Q_ASSERT(result);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 _isConnected = false;1593 _isConnected = false;
1560 }1594 }
1561}1595}
1596
1597void 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
1611void ScriptSignalV8Proxy::disconnectAll() {
1612 QObject::disconnect(this, nullptr, nullptr, nullptr);
1613}