@@ -2,6 +2,10 @@ 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_SLOT ,
7+ type HasDestroyableMetaSlot ,
8+ } from '@glimmer/util/lib/destroyable-slot' ;
59
610const LIVE_STATE = 0 ;
711const DESTROYING_STATE = 1 ;
@@ -78,22 +82,45 @@ function remove<T extends object>(collection: OneOrMany<T>, item: T, message: st
7882 }
7983}
8084
85+ function createMeta < T extends Destroyable > ( destroyable : T ) : DestroyableMeta < T > {
86+ let meta : DestroyableMeta < Destroyable > = {
87+ parents : null ,
88+ children : null ,
89+ eagerDestructors : null ,
90+ destructors : null ,
91+ state : LIVE_STATE ,
92+ } ;
93+
94+ if ( DEBUG ) {
95+ meta . source = destroyable ;
96+ }
97+
98+ return meta as unknown as DestroyableMeta < T > ;
99+ }
100+
81101function getDestroyableMeta < T extends Destroyable > ( destroyable : T ) : DestroyableMeta < T > {
82- let meta = DESTROYABLE_META . get ( destroyable ) ;
102+ let slotted = destroyable as HasDestroyableMetaSlot ;
103+ let own = slotted [ DESTROYABLE_META_SLOT ] ;
83104
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 ;
105+ if ( own !== undefined ) return own as DestroyableMeta < T > ;
106+
107+ // `in` rather than a write, so this stays a read for everything else.
108+ if ( DESTROYABLE_META_SLOT in slotted ) {
109+ let meta = createMeta ( destroyable ) ;
110+
111+ slotted [ DESTROYABLE_META_SLOT ] = meta ;
112+
113+ if ( DEBUG && DESTROYABLE_META instanceof Map ) {
114+ DESTROYABLE_META . set ( destroyable , meta as unknown as DestroyableMeta < Destroyable > ) ;
95115 }
96116
117+ return meta ;
118+ }
119+
120+ let meta = DESTROYABLE_META . get ( destroyable ) ;
121+
122+ if ( meta === undefined ) {
123+ meta = createMeta ( destroyable ) as unknown as DestroyableMeta < Destroyable > ;
97124 DESTROYABLE_META . set ( destroyable , meta ) ;
98125 }
99126
@@ -209,20 +236,33 @@ export function destroyChildren(destroyable: Destroyable) {
209236 iterate ( children , destroy ) ;
210237}
211238
239+ /** Meta if there is any, without creating it. Mirrors `getDestroyableMeta`. */
240+ function peekDestroyableMeta ( destroyable : Destroyable ) : DestroyableMeta < Destroyable > | undefined {
241+ let slotted = destroyable as HasDestroyableMetaSlot ;
242+ let own = slotted [ DESTROYABLE_META_SLOT ] ;
243+
244+ if ( own !== undefined ) return own as DestroyableMeta < Destroyable > ;
245+
246+ // An empty slot is proof there is no meta, so the map can be skipped.
247+ if ( DESTROYABLE_META_SLOT in slotted ) return undefined ;
248+
249+ return DESTROYABLE_META . get ( destroyable ) ;
250+ }
251+
212252export function _hasDestroyableChildren ( destroyable : Destroyable ) {
213- let meta = DESTROYABLE_META . get ( destroyable ) ;
253+ let meta = peekDestroyableMeta ( destroyable ) ;
214254
215255 return meta === undefined ? false : meta . children !== null ;
216256}
217257
218258export function isDestroying ( destroyable : Destroyable ) {
219- let meta = DESTROYABLE_META . get ( destroyable ) ;
259+ let meta = peekDestroyableMeta ( destroyable ) ;
220260
221261 return meta === undefined ? false : meta . state >= DESTROYING_STATE ;
222262}
223263
224264export function isDestroyed ( destroyable : Destroyable ) {
225- let meta = DESTROYABLE_META . get ( destroyable ) ;
265+ let meta = peekDestroyableMeta ( destroyable ) ;
226266
227267 return meta === undefined ? false : meta . state >= DESTROYED_STATE ;
228268}
0 commit comments