Skip to content

Commit 012bb85

Browse files
committed
BridgeJS: Simplify pointer identity wrapping and class cache generation
Replace per-class FinalizationRegistry instances with a single shared registry at module level. Move identity cache from deinit function property to per-class static __identityCache field. Cleaner codegen, fewer allocations, easier to inspect in DevTools.
1 parent 583d16b commit 012bb85

25 files changed

Lines changed: 271 additions & 820 deletions

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -85,39 +85,13 @@ public struct BridgeJSLink {
8585
return;
8686
}
8787
state.hasReleased = true;
88+
state.identityMap?.delete(state.pointer);
8889
state.deinit(state.pointer);
8990
});
9091
9192
/// Represents a Swift heap object like a class instance or an actor instance.
9293
class SwiftHeapObject {
93-
static identityCacheByDeinit = new WeakMap();
94-
static finalizerByDeinit = new WeakMap();
95-
96-
static __getFinalizer(deinit) {
97-
let finalizer = SwiftHeapObject.finalizerByDeinit.get(deinit);
98-
if (finalizer) {
99-
return finalizer;
100-
}
101-
102-
const created = new FinalizationRegistry((state) => {
103-
104-
"""
105-
if enableLifetimeTracking {
106-
output += " TRACKING.finalization(state);\n"
107-
}
108-
output += """
109-
if (state.hasReleased) {
110-
return;
111-
}
112-
state.hasReleased = true;
113-
state.identityMap?.delete(state.pointer);
114-
state.deinit(state.pointer);
115-
});
116-
SwiftHeapObject.finalizerByDeinit.set(deinit, created);
117-
return created;
118-
}
119-
120-
static __wrap(pointer, deinit, prototype) {
94+
static __wrap(pointer, deinit, prototype, identityCache) {
12195
const makeFresh = (identityMap, finalizer) => {
12296
const obj = Object.create(prototype);
12397
const state = { pointer, deinit, hasReleased: false, identityMap, finalizer };
@@ -142,22 +116,15 @@ public struct BridgeJSLink {
142116
return makeFresh(null, swiftHeapObjectFinalizationRegistry);
143117
}
144118
145-
let identityMap = SwiftHeapObject.identityCacheByDeinit.get(deinit);
146-
if (!identityMap) {
147-
identityMap = new Map();
148-
SwiftHeapObject.identityCacheByDeinit.set(deinit, identityMap);
149-
}
150-
151-
const cached = identityMap.get(pointer)?.deref();
119+
const cached = identityCache.get(pointer)?.deref();
152120
if (cached && !cached.__swiftHeapObjectState.hasReleased) {
153121
return cached;
154122
}
155123
if (!cached) {
156-
identityMap.delete(pointer);
124+
identityCache.delete(pointer);
157125
}
158126
159-
const finalizer = SwiftHeapObject.__getFinalizer(deinit);
160-
return makeFresh(identityMap, finalizer);
127+
return makeFresh(identityCache, swiftHeapObjectFinalizationRegistry);
161128
}
162129
163130
release() {
@@ -2028,10 +1995,12 @@ extension BridgeJSLink {
20281995

20291996
// Always add __construct and constructor methods for all classes
20301997
jsPrinter.indent {
1998+
jsPrinter.write("static __identityCache = new Map();")
1999+
jsPrinter.nextLine()
20312000
jsPrinter.write("static __construct(ptr) {")
20322001
jsPrinter.indent {
20332002
jsPrinter.write(
2034-
"return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_\(klass.abiName)_deinit, \(klass.name).prototype);"
2003+
"return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_\(klass.name)_deinit, \(klass.name).prototype, \(klass.name).__identityCache);"
20352004
)
20362005
}
20372006
jsPrinter.write("}")

Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,18 @@ import Testing
132132
)
133133
)
134134
#expect(outputJs.contains("if (!shouldUseIdentityMap) {"))
135+
#expect(outputJs.contains("state.identityMap?.delete(state.pointer);"))
136+
#expect(!outputJs.contains("static finalizerByDeinit"))
137+
#expect(!outputJs.contains("static __getFinalizer"))
138+
#expect(!outputJs.contains("static identityCache = new Map();"))
139+
#expect(!outputJs.contains("identityCacheByDeinit"))
140+
#expect(!outputJs.contains("identityCache ??"))
141+
#expect(outputJs.contains("static __wrap(pointer, deinit, prototype, identityCache)"))
142+
#expect(outputJs.contains("static __identityCache = new Map();"))
143+
#expect(
144+
outputJs.contains(
145+
"return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Greeter_deinit, Greeter.prototype, Greeter.__identityCache);"
146+
)
147+
)
135148
}
136149
}

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayTypes.js

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -350,33 +350,13 @@ export async function createInstantiator(options, swift) {
350350
return;
351351
}
352352
state.hasReleased = true;
353+
state.identityMap?.delete(state.pointer);
353354
state.deinit(state.pointer);
354355
});
355356

356357
/// Represents a Swift heap object like a class instance or an actor instance.
357358
class SwiftHeapObject {
358-
static identityCacheByDeinit = new WeakMap();
359-
static finalizerByDeinit = new WeakMap();
360-
361-
static __getFinalizer(deinit) {
362-
let finalizer = SwiftHeapObject.finalizerByDeinit.get(deinit);
363-
if (finalizer) {
364-
return finalizer;
365-
}
366-
367-
const created = new FinalizationRegistry((state) => {
368-
if (state.hasReleased) {
369-
return;
370-
}
371-
state.hasReleased = true;
372-
state.identityMap?.delete(state.pointer);
373-
state.deinit(state.pointer);
374-
});
375-
SwiftHeapObject.finalizerByDeinit.set(deinit, created);
376-
return created;
377-
}
378-
379-
static __wrap(pointer, deinit, prototype) {
359+
static __wrap(pointer, deinit, prototype, identityCache) {
380360
const makeFresh = (identityMap, finalizer) => {
381361
const obj = Object.create(prototype);
382362
const state = { pointer, deinit, hasReleased: false, identityMap, finalizer };
@@ -395,22 +375,15 @@ export async function createInstantiator(options, swift) {
395375
return makeFresh(null, swiftHeapObjectFinalizationRegistry);
396376
}
397377

398-
let identityMap = SwiftHeapObject.identityCacheByDeinit.get(deinit);
399-
if (!identityMap) {
400-
identityMap = new Map();
401-
SwiftHeapObject.identityCacheByDeinit.set(deinit, identityMap);
402-
}
403-
404-
const cached = identityMap.get(pointer)?.deref();
378+
const cached = identityCache.get(pointer)?.deref();
405379
if (cached && !cached.__swiftHeapObjectState.hasReleased) {
406380
return cached;
407381
}
408382
if (!cached) {
409-
identityMap.delete(pointer);
383+
identityCache.delete(pointer);
410384
}
411385

412-
const finalizer = SwiftHeapObject.__getFinalizer(deinit);
413-
return makeFresh(identityMap, finalizer);
386+
return makeFresh(identityCache, swiftHeapObjectFinalizationRegistry);
414387
}
415388

416389
release() {
@@ -425,14 +398,18 @@ export async function createInstantiator(options, swift) {
425398
}
426399
}
427400
class Item extends SwiftHeapObject {
401+
static __identityCache = new Map();
402+
428403
static __construct(ptr) {
429-
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Item_deinit, Item.prototype);
404+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Item_deinit, Item.prototype, Item.__identityCache);
430405
}
431406

432407
}
433408
class MultiArrayContainer extends SwiftHeapObject {
409+
static __identityCache = new Map();
410+
434411
static __construct(ptr) {
435-
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MultiArrayContainer_deinit, MultiArrayContainer.prototype);
412+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MultiArrayContainer_deinit, MultiArrayContainer.prototype, MultiArrayContainer.__identityCache);
436413
}
437414

438415
constructor(nums, strs) {

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.js

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -281,33 +281,13 @@ export async function createInstantiator(options, swift) {
281281
return;
282282
}
283283
state.hasReleased = true;
284+
state.identityMap?.delete(state.pointer);
284285
state.deinit(state.pointer);
285286
});
286287

287288
/// Represents a Swift heap object like a class instance or an actor instance.
288289
class SwiftHeapObject {
289-
static identityCacheByDeinit = new WeakMap();
290-
static finalizerByDeinit = new WeakMap();
291-
292-
static __getFinalizer(deinit) {
293-
let finalizer = SwiftHeapObject.finalizerByDeinit.get(deinit);
294-
if (finalizer) {
295-
return finalizer;
296-
}
297-
298-
const created = new FinalizationRegistry((state) => {
299-
if (state.hasReleased) {
300-
return;
301-
}
302-
state.hasReleased = true;
303-
state.identityMap?.delete(state.pointer);
304-
state.deinit(state.pointer);
305-
});
306-
SwiftHeapObject.finalizerByDeinit.set(deinit, created);
307-
return created;
308-
}
309-
310-
static __wrap(pointer, deinit, prototype) {
290+
static __wrap(pointer, deinit, prototype, identityCache) {
311291
const makeFresh = (identityMap, finalizer) => {
312292
const obj = Object.create(prototype);
313293
const state = { pointer, deinit, hasReleased: false, identityMap, finalizer };
@@ -326,22 +306,15 @@ export async function createInstantiator(options, swift) {
326306
return makeFresh(null, swiftHeapObjectFinalizationRegistry);
327307
}
328308

329-
let identityMap = SwiftHeapObject.identityCacheByDeinit.get(deinit);
330-
if (!identityMap) {
331-
identityMap = new Map();
332-
SwiftHeapObject.identityCacheByDeinit.set(deinit, identityMap);
333-
}
334-
335-
const cached = identityMap.get(pointer)?.deref();
309+
const cached = identityCache.get(pointer)?.deref();
336310
if (cached && !cached.__swiftHeapObjectState.hasReleased) {
337311
return cached;
338312
}
339313
if (!cached) {
340-
identityMap.delete(pointer);
314+
identityCache.delete(pointer);
341315
}
342316

343-
const finalizer = SwiftHeapObject.__getFinalizer(deinit);
344-
return makeFresh(identityMap, finalizer);
317+
return makeFresh(identityCache, swiftHeapObjectFinalizationRegistry);
345318
}
346319

347320
release() {
@@ -356,8 +329,10 @@ export async function createInstantiator(options, swift) {
356329
}
357330
}
358331
class DefaultGreeter extends SwiftHeapObject {
332+
static __identityCache = new Map();
333+
359334
static __construct(ptr) {
360-
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_DefaultGreeter_deinit, DefaultGreeter.prototype);
335+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_DefaultGreeter_deinit, DefaultGreeter.prototype, DefaultGreeter.__identityCache);
361336
}
362337

363338
constructor(name) {
@@ -379,8 +354,10 @@ export async function createInstantiator(options, swift) {
379354
}
380355
}
381356
class EmptyGreeter extends SwiftHeapObject {
357+
static __identityCache = new Map();
358+
382359
static __construct(ptr) {
383-
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_EmptyGreeter_deinit, EmptyGreeter.prototype);
360+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_EmptyGreeter_deinit, EmptyGreeter.prototype, EmptyGreeter.__identityCache);
384361
}
385362

386363
constructor() {
@@ -389,8 +366,10 @@ export async function createInstantiator(options, swift) {
389366
}
390367
}
391368
class ConstructorDefaults extends SwiftHeapObject {
369+
static __identityCache = new Map();
370+
392371
static __construct(ptr) {
393-
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ConstructorDefaults_deinit, ConstructorDefaults.prototype);
372+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ConstructorDefaults_deinit, ConstructorDefaults.prototype, ConstructorDefaults.__identityCache);
394373
}
395374

396375
constructor(name = "Default", count = 42, enabled = true, status = StatusValues.Active, tag = null) {

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DictionaryTypes.js

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -290,33 +290,13 @@ export async function createInstantiator(options, swift) {
290290
return;
291291
}
292292
state.hasReleased = true;
293+
state.identityMap?.delete(state.pointer);
293294
state.deinit(state.pointer);
294295
});
295296

296297
/// Represents a Swift heap object like a class instance or an actor instance.
297298
class SwiftHeapObject {
298-
static identityCacheByDeinit = new WeakMap();
299-
static finalizerByDeinit = new WeakMap();
300-
301-
static __getFinalizer(deinit) {
302-
let finalizer = SwiftHeapObject.finalizerByDeinit.get(deinit);
303-
if (finalizer) {
304-
return finalizer;
305-
}
306-
307-
const created = new FinalizationRegistry((state) => {
308-
if (state.hasReleased) {
309-
return;
310-
}
311-
state.hasReleased = true;
312-
state.identityMap?.delete(state.pointer);
313-
state.deinit(state.pointer);
314-
});
315-
SwiftHeapObject.finalizerByDeinit.set(deinit, created);
316-
return created;
317-
}
318-
319-
static __wrap(pointer, deinit, prototype) {
299+
static __wrap(pointer, deinit, prototype, identityCache) {
320300
const makeFresh = (identityMap, finalizer) => {
321301
const obj = Object.create(prototype);
322302
const state = { pointer, deinit, hasReleased: false, identityMap, finalizer };
@@ -335,22 +315,15 @@ export async function createInstantiator(options, swift) {
335315
return makeFresh(null, swiftHeapObjectFinalizationRegistry);
336316
}
337317

338-
let identityMap = SwiftHeapObject.identityCacheByDeinit.get(deinit);
339-
if (!identityMap) {
340-
identityMap = new Map();
341-
SwiftHeapObject.identityCacheByDeinit.set(deinit, identityMap);
342-
}
343-
344-
const cached = identityMap.get(pointer)?.deref();
318+
const cached = identityCache.get(pointer)?.deref();
345319
if (cached && !cached.__swiftHeapObjectState.hasReleased) {
346320
return cached;
347321
}
348322
if (!cached) {
349-
identityMap.delete(pointer);
323+
identityCache.delete(pointer);
350324
}
351325

352-
const finalizer = SwiftHeapObject.__getFinalizer(deinit);
353-
return makeFresh(identityMap, finalizer);
326+
return makeFresh(identityCache, swiftHeapObjectFinalizationRegistry);
354327
}
355328

356329
release() {
@@ -365,8 +338,10 @@ export async function createInstantiator(options, swift) {
365338
}
366339
}
367340
class Box extends SwiftHeapObject {
341+
static __identityCache = new Map();
342+
368343
static __construct(ptr) {
369-
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Box_deinit, Box.prototype);
344+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_Box_deinit, Box.prototype, Box.__identityCache);
370345
}
371346

372347
}

0 commit comments

Comments
 (0)