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