Skip to content

Commit f4655ef

Browse files
BridgeJS: Add lifetime tracking mode to BridgeJSLink
1 parent 28b97a9 commit f4655ef

File tree

1 file changed

+68
-25
lines changed

1 file changed

+68
-25
lines changed

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ import BridgeJSUtilities
1010
public struct BridgeJSLink {
1111
var skeletons: [BridgeJSSkeleton] = []
1212
let sharedMemory: Bool
13+
/// Whether to track the lifetime of Swift objects.
14+
///
15+
/// This is useful for debugging memory issues.
16+
let enableLifetimeTracking: Bool = false
1317
private let namespaceBuilder = NamespaceBuilder()
1418
private let intrinsicRegistry = JSIntrinsicRegistry()
1519

1620
public init(
1721
skeletons: [BridgeJSSkeleton] = [],
18-
sharedMemory: Bool
22+
sharedMemory: Bool = false
1923
) {
2024
self.skeletons = skeletons
2125
self.sharedMemory = sharedMemory
@@ -50,37 +54,76 @@ public struct BridgeJSLink {
5054
}
5155
"""
5256

53-
let swiftHeapObjectClassJs = """
54-
const swiftHeapObjectFinalizationRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => {
55-
if (state.hasReleased) {
56-
return;
57-
}
58-
state.hasReleased = true;
59-
state.deinit(state.pointer);
60-
});
57+
let lifetimeTrackingClassJs = """
58+
const TRACKING = {
59+
wrap: (pointer, deinit, prototype, state) => {
60+
console.log(JSON.stringify({ DEBUG: true, event: "WRP", class: prototype.constructor.name, state }));
61+
},
62+
release: (obj) => {
63+
console.log(JSON.stringify({ DEBUG: true, event: "REL", class: obj.constructor.name, state: obj.__swiftHeapObjectState }));
64+
},
65+
finalization: (state) => {
66+
console.log(JSON.stringify({ DEBUG: true, event: "FIN", state }));
67+
}
68+
};
69+
"""
6170

62-
/// Represents a Swift heap object like a class instance or an actor instance.
63-
class SwiftHeapObject {
64-
static __wrap(pointer, deinit, prototype) {
65-
const obj = Object.create(prototype);
66-
const state = { pointer, deinit, hasReleased: false };
67-
obj.pointer = pointer;
68-
obj.__swiftHeapObjectState = state;
69-
swiftHeapObjectFinalizationRegistry.register(obj, state, state);
70-
return obj;
71-
}
72-
73-
release() {
74-
const state = this.__swiftHeapObjectState;
71+
var swiftHeapObjectClassJs: String {
72+
var output = ""
73+
if enableLifetimeTracking {
74+
output += lifetimeTrackingClassJs + "\n"
75+
}
76+
output += """
77+
const swiftHeapObjectFinalizationRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => {
78+
79+
"""
80+
if enableLifetimeTracking {
81+
output += " TRACKING.finalization(state);\n"
82+
}
83+
output += """
7584
if (state.hasReleased) {
7685
return;
7786
}
7887
state.hasReleased = true;
79-
swiftHeapObjectFinalizationRegistry.unregister(state);
8088
state.deinit(state.pointer);
81-
}
89+
});
90+
91+
/// Represents a Swift heap object like a class instance or an actor instance.
92+
class SwiftHeapObject {
93+
static __wrap(pointer, deinit, prototype) {
94+
const obj = Object.create(prototype);
95+
const state = { pointer, deinit, hasReleased: false };
96+
97+
"""
98+
if enableLifetimeTracking {
99+
output += " TRACKING.wrap(pointer, deinit, prototype, state);\n"
82100
}
83-
"""
101+
output += """
102+
obj.pointer = pointer;
103+
obj.__swiftHeapObjectState = state;
104+
swiftHeapObjectFinalizationRegistry.register(obj, state, state);
105+
return obj;
106+
}
107+
108+
release() {
109+
110+
"""
111+
if enableLifetimeTracking {
112+
output += " TRACKING.release(this);\n"
113+
}
114+
output += """
115+
const state = this.__swiftHeapObjectState;
116+
if (state.hasReleased) {
117+
return;
118+
}
119+
state.hasReleased = true;
120+
swiftHeapObjectFinalizationRegistry.unregister(state);
121+
state.deinit(state.pointer);
122+
}
123+
}
124+
"""
125+
return output
126+
}
84127

85128
fileprivate struct LinkData {
86129
var exportsLines: [String] = []

0 commit comments

Comments
 (0)