From 0f9ad1e028ac16082aa498009871574a265d752b Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Fri, 20 May 2022 15:25:18 +0200 Subject: [PATCH] Add logging/debugging controls for the wrapper lifecycle --- src/index.ts | 3 ++ src/lib/core/WrapperCache.ts | 58 ++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 01ab996d1..0928e68c9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -189,3 +189,6 @@ export { Margins } from './lib/utils/Margins'; // Test: export { CacheTestQObject } from './lib/core/__test__/CacheTestQObject'; + +// Debug: +export { setLogCreateQObject, setLogDestroyQObject } from './lib/core/WrapperCache'; diff --git a/src/lib/core/WrapperCache.ts b/src/lib/core/WrapperCache.ts index 61e036cfa..abfb82218 100644 --- a/src/lib/core/WrapperCache.ts +++ b/src/lib/core/WrapperCache.ts @@ -14,14 +14,29 @@ import { NativeElement } from './Component'; * wrapper automatically and unexpectedly garbage collected. */ export class WrapperCache { + // Cache for wrapper where we always hold onto the wrapper until the C++ + // object is destroyed by Qt. We don't let the V8 GC destroy the wrapper + // first. These wrapper often have signal handlers which are kept alive + // by the wrapper themselves, and not because they are part of a + // `NFooBar` subclass. For example, the `QScreen` attaches signal + // handlers to the C++ `QScreen`. If the wrapper is GC'ed, then the + // signals handlers also stop working. private _strongCache = new Map(); + + // WeakCache is for normal wrappers around `QObject` based `NFooBar` + // subclasses. private _weakCache = new Map>(); + private _wrapperRegistry = new Map(); constructor() { addon.WrapperCache_injectCallback(this._objectDestroyedCallback.bind(this)); } + logCreateQObject = false; + logDestoryQObject = false; + + // This is only need for testing purposes _flush(): void { this._strongCache = new Map(); this._weakCache = new Map>(); @@ -32,6 +47,11 @@ export class WrapperCache { const wrapper = this._strongCache.get(objectId); wrapper.native = null; this._strongCache.delete(objectId); + + if (this.logDestoryQObject) { + console.log(`NodeGui: Destroyed C++ object with ID: ${objectId}.`); + } + return; } const wrapperRef = this._weakCache.get(objectId); @@ -40,17 +60,20 @@ export class WrapperCache { if (wrapper != null) { wrapper.native = null; this._weakCache.delete(objectId); + if (this.logDestoryQObject) { + console.log(`NodeGui: Destroyed C++ object with ID: ${objectId}.`); + } } } } get(wrapperConstructor: { new (native: any): T }, native: NativeElement): T { - const id = native.__id__(); - if (this._strongCache.has(id)) { - return this._strongCache.get(id) as T; + const objectId = native.__id__(); + if (this._strongCache.has(objectId)) { + return this._strongCache.get(objectId) as T; } const wrapper = new wrapperConstructor(native); - this._strongCache.set(id, wrapper); + this._strongCache.set(objectId, wrapper); return wrapper; } @@ -90,11 +113,34 @@ export class WrapperCache { store(wrapper: QObject): void { if (wrapper.native != null) { - const id = wrapper.native.__id__(); - this._weakCache.set(id, new WeakRef(wrapper)); + const objectId = wrapper.native.__id__(); + this._weakCache.set(objectId, new WeakRef(wrapper)); addon.WrapperCache_store(wrapper.native, wrapper.native.__external_qobject__()); + if (this.logCreateQObject) { + console.log(`NodeGui: Created C++ object with ID: ${objectId}.`); + } } } } export const wrapperCache = new WrapperCache(); + +/** + * Turn on/off logging when QObjects are created. + * + * @param on When true, logging is written to console when QObjects are + * created by NodeGui. + */ +export function setLogCreateQObject(on: boolean): void { + wrapperCache.logCreateQObject = on; +} + +/** + * Turn on/off logging when QObjects are destoryed. + * + * @param on When true, logging is written to console when QObjects are + * destroyed. + */ +export function setLogDestroyQObject(on: boolean): void { + wrapperCache.logDestoryQObject = on; +}