@@ -2,6 +2,12 @@ import { DEBUG } from '@glimmer/env';
22import type { Destroyable , Destructor } from '@glimmer/interfaces' ;
33import debugToString from '@glimmer/debug-util/lib/debug-to-string' ;
44import { scheduleDestroy , scheduleDestroyed } from '@glimmer/global-context' ;
5+ import {
6+ DESTROYABLE_META_KEY ,
7+ type HasDestroyableMetaSlot ,
8+ } from '@glimmer/util/lib/destroyable-key' ;
9+
10+ export { DESTROYABLE_META_KEY } from '@glimmer/util/lib/destroyable-key' ;
511
612const LIVE_STATE = 0 ;
713const DESTROYING_STATE = 1 ;
@@ -78,22 +84,45 @@ function remove<T extends object>(collection: OneOrMany<T>, item: T, message: st
7884 }
7985}
8086
87+ function createMeta < T extends Destroyable > ( destroyable : T ) : DestroyableMeta < T > {
88+ let meta : DestroyableMeta < Destroyable > = {
89+ parents : null ,
90+ children : null ,
91+ eagerDestructors : null ,
92+ destructors : null ,
93+ state : LIVE_STATE ,
94+ } ;
95+
96+ if ( DEBUG ) {
97+ meta . source = destroyable ;
98+ }
99+
100+ return meta as unknown as DestroyableMeta < T > ;
101+ }
102+
81103function getDestroyableMeta < T extends Destroyable > ( destroyable : T ) : DestroyableMeta < T > {
82- let meta = DESTROYABLE_META . get ( destroyable ) ;
104+ let slotted = destroyable as HasDestroyableMetaSlot ;
105+ let own = slotted [ DESTROYABLE_META_KEY ] ;
83106
84- if ( meta === undefined ) {
85- meta = {
86- parents : null ,
87- children : null ,
88- eagerDestructors : null ,
89- destructors : null ,
90- state : LIVE_STATE ,
91- } ;
92-
93- if ( DEBUG ) {
94- meta . source = destroyable ;
107+ if ( own !== undefined ) return own as DestroyableMeta < T > ;
108+
109+ // `in` rather than a write, so this stays a read for everything else.
110+ if ( DESTROYABLE_META_KEY in slotted ) {
111+ let meta = createMeta ( destroyable ) ;
112+
113+ slotted [ DESTROYABLE_META_KEY ] = meta ;
114+
115+ if ( DEBUG && DESTROYABLE_META instanceof Map ) {
116+ DESTROYABLE_META . set ( destroyable , meta as unknown as DestroyableMeta < Destroyable > ) ;
95117 }
96118
119+ return meta ;
120+ }
121+
122+ let meta = DESTROYABLE_META . get ( destroyable ) ;
123+
124+ if ( meta === undefined ) {
125+ meta = createMeta ( destroyable ) as unknown as DestroyableMeta < Destroyable > ;
97126 DESTROYABLE_META . set ( destroyable , meta ) ;
98127 }
99128
@@ -209,20 +238,33 @@ export function destroyChildren(destroyable: Destroyable) {
209238 iterate ( children , destroy ) ;
210239}
211240
241+ /** Meta if there is any, without creating it. Mirrors `getDestroyableMeta`. */
242+ function peekDestroyableMeta ( destroyable : Destroyable ) : DestroyableMeta < Destroyable > | undefined {
243+ let slotted = destroyable as HasDestroyableMetaSlot ;
244+ let own = slotted [ DESTROYABLE_META_KEY ] ;
245+
246+ if ( own !== undefined ) return own as DestroyableMeta < Destroyable > ;
247+
248+ // An empty slot is proof there is no meta, so the map can be skipped.
249+ if ( DESTROYABLE_META_KEY in slotted ) return undefined ;
250+
251+ return DESTROYABLE_META . get ( destroyable ) ;
252+ }
253+
212254export function _hasDestroyableChildren ( destroyable : Destroyable ) {
213- let meta = DESTROYABLE_META . get ( destroyable ) ;
255+ let meta = peekDestroyableMeta ( destroyable ) ;
214256
215257 return meta === undefined ? false : meta . children !== null ;
216258}
217259
218260export function isDestroying ( destroyable : Destroyable ) {
219- let meta = DESTROYABLE_META . get ( destroyable ) ;
261+ let meta = peekDestroyableMeta ( destroyable ) ;
220262
221263 return meta === undefined ? false : meta . state >= DESTROYING_STATE ;
222264}
223265
224266export function isDestroyed ( destroyable : Destroyable ) {
225- let meta = DESTROYABLE_META . get ( destroyable ) ;
267+ let meta = peekDestroyableMeta ( destroyable ) ;
226268
227269 return meta === undefined ? false : meta . state >= DESTROYED_STATE ;
228270}
0 commit comments