Add logging/debugging controls for the wrapper lifecycle

This commit is contained in:
Simon Edwards 2022-05-20 15:25:18 +02:00
parent fcdbfb07c3
commit 0f9ad1e028
2 changed files with 55 additions and 6 deletions

View File

@ -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';

View File

@ -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<number, QObject>();
// WeakCache is for normal wrappers around `QObject` based `NFooBar`
// subclasses.
private _weakCache = new Map<number, WeakRef<QObject>>();
private _wrapperRegistry = new Map<string, { new (native: any): QObject }>();
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<number, QObject>();
this._weakCache = new Map<number, WeakRef<QObject>>();
@ -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<T extends QObject>(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<QObject>(wrapper));
const objectId = wrapper.native.__id__();
this._weakCache.set(objectId, new WeakRef<QObject>(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;
}