@@ -241,59 +241,90 @@ function deserializeError(error) {
241241
242242const globalVariable = globalThis ;
243243
244+ const SLOT_BITS = 22 ;
245+ const SLOT_MASK = ( 1 << SLOT_BITS ) - 1 ;
246+ const GEN_MASK = ( 1 << ( 32 - SLOT_BITS ) ) - 1 ;
244247class JSObjectSpace {
245248 constructor ( ) {
249+ this . _slotByValue = new Map ( ) ;
246250 this . _values = [ ] ;
251+ this . _stateBySlot = [ ] ;
252+ this . _freeSlotStack = [ ] ;
247253 this . _values [ 0 ] = undefined ;
248254 this . _values [ 1 ] = globalVariable ;
249- this . _valueRefMap = new Map ( ) ;
250- this . _valueRefMap . set ( globalVariable , 1 ) ;
251- this . _refCounts = [ ] ;
252- this . _refCounts [ 0 ] = 0 ;
253- this . _refCounts [ 1 ] = 1 ;
254- this . _freeSlotStack = [ ] ;
255+ this . _slotByValue . set ( globalVariable , 1 ) ;
256+ this . _stateBySlot [ 1 ] = 1 ; // gen=0, rc=1
255257 }
256258 retain ( value ) {
257- const id = this . _valueRefMap . get ( value ) ;
258- if ( id !== undefined ) {
259- this . _refCounts [ id ] ++ ;
260- return id ;
261- }
262- const newId = this . _freeSlotStack . length > 0
263- ? this . _freeSlotStack . pop ( )
264- : this . _values . length ;
265- this . _values [ newId ] = value ;
266- this . _refCounts [ newId ] = 1 ;
267- this . _valueRefMap . set ( value , newId ) ;
268- return newId ;
269- }
270- retainByRef ( ref ) {
271- if ( this . _refCounts [ ref ] === 0 ) {
272- throw new ReferenceError ( "Attempted to retain invalid reference " + ref ) ;
273- }
274- this . _refCounts [ ref ] ++ ;
275- return ref ;
276- }
277- release ( ref ) {
278- if ( -- this . _refCounts [ ref ] !== 0 )
279- return ;
280- const value = this . _values [ ref ] ;
281- this . _valueRefMap . delete ( value ) ;
282- if ( ref === this . _values . length - 1 ) {
283- this . _values . length = ref ;
284- this . _refCounts . length = ref ;
259+ const slot = this . _slotByValue . get ( value ) ;
260+ if ( slot !== undefined ) {
261+ const state = this . _stateBySlot [ slot ] ;
262+ const nextState = ( state + 1 ) >>> 0 ;
263+ if ( ( nextState & SLOT_MASK ) === 0 ) {
264+ throw new RangeError ( `Reference count overflow at slot ${ slot } ` ) ;
265+ }
266+ this . _stateBySlot [ slot ] = nextState ;
267+ return ( ( nextState & ~ SLOT_MASK ) | slot ) >>> 0 ;
285268 }
286- else {
287- this . _values [ ref ] = undefined ;
288- this . _freeSlotStack . push ( ref ) ;
269+ let newSlot ;
270+ let state ;
271+ if ( this . _freeSlotStack . length > 0 ) {
272+ newSlot = this . _freeSlotStack . pop ( ) ;
273+ const gen = this . _stateBySlot [ newSlot ] >>> SLOT_BITS ;
274+ state = ( ( gen << SLOT_BITS ) | 1 ) >>> 0 ;
289275 }
290- }
291- getObject ( ref ) {
292- const value = this . _values [ ref ] ;
293- if ( value === undefined ) {
294- throw new ReferenceError ( "Attempted to read invalid reference " + ref ) ;
276+ else {
277+ newSlot = this . _values . length ;
278+ if ( newSlot > SLOT_MASK ) {
279+ throw new RangeError ( `Reference slot overflow: ${ newSlot } exceeds ${ SLOT_MASK } ` ) ;
280+ }
281+ state = 1 ;
282+ }
283+ this . _stateBySlot [ newSlot ] = state ;
284+ this . _values [ newSlot ] = value ;
285+ this . _slotByValue . set ( value , newSlot ) ;
286+ return ( ( state & ~ SLOT_MASK ) | newSlot ) >>> 0 ;
287+ }
288+ retainByRef ( reference ) {
289+ const state = this . _getValidatedSlotState ( reference ) ;
290+ const slot = reference & SLOT_MASK ;
291+ const nextState = ( state + 1 ) >>> 0 ;
292+ if ( ( nextState & SLOT_MASK ) === 0 ) {
293+ throw new RangeError ( `Reference count overflow at slot ${ slot } ` ) ;
294+ }
295+ this . _stateBySlot [ slot ] = nextState ;
296+ return reference ;
297+ }
298+ release ( reference ) {
299+ const state = this . _getValidatedSlotState ( reference ) ;
300+ const slot = reference & SLOT_MASK ;
301+ if ( ( state & SLOT_MASK ) > 1 ) {
302+ this . _stateBySlot [ slot ] = ( state - 1 ) >>> 0 ;
303+ return ;
295304 }
296- return value ;
305+ this . _slotByValue . delete ( this . _values [ slot ] ) ;
306+ this . _values [ slot ] = undefined ;
307+ const nextGen = ( ( state >>> SLOT_BITS ) + 1 ) & GEN_MASK ;
308+ this . _stateBySlot [ slot ] = ( nextGen << SLOT_BITS ) >>> 0 ;
309+ this . _freeSlotStack . push ( slot ) ;
310+ }
311+ getObject ( reference ) {
312+ this . _getValidatedSlotState ( reference ) ;
313+ return this . _values [ reference & SLOT_MASK ] ;
314+ }
315+ // Returns the packed state for the slot, after validating the reference.
316+ _getValidatedSlotState ( reference ) {
317+ const slot = reference & SLOT_MASK ;
318+ if ( slot === 0 )
319+ throw new ReferenceError ( "Attempted to use invalid reference " + reference ) ;
320+ const state = this . _stateBySlot [ slot ] ;
321+ if ( state === undefined || ( state & SLOT_MASK ) === 0 ) {
322+ throw new ReferenceError ( "Attempted to use invalid reference " + reference ) ;
323+ }
324+ if ( ( state >>> SLOT_BITS ) !== ( reference >>> SLOT_BITS ) ) {
325+ throw new ReferenceError ( "Attempted to use stale reference " + reference ) ;
326+ }
327+ return state ;
297328 }
298329}
299330
0 commit comments