@@ -35,8 +35,16 @@ typedef struct _zend_weakmap_iterator {
35
35
uint32_t ht_iter ;
36
36
} zend_weakmap_iterator ;
37
37
38
- /* The EG(weakrefs) ht is a map from object address a tagged pointer, that may be one of
39
- * zend_weakref*, zend_weakmap* or HashTable*. */
38
+ /* EG(weakrefs) is a map from a key corresponding to a zend_object pointer to all the WeakReference and/or WeakMap entries relating to that pointer.
39
+ *
40
+ * 1. For a single WeakReference,
41
+ * the HashTable's corresponding value's tag is a ZEND_WEAKREF_TAG_REF and the pointer is a singleton WeakReference instance (zend_weakref *) for that zend_object pointer (from WeakReference::create()).
42
+ * 2. For a single WeakMap, the HashTable's corresponding value's tag is a ZEND_WEAKREF_TAG_MAP and the pointer is a WeakMap instance (zend_weakmap *).
43
+ * 3. For multiple values associated with the same zend_object pointer, the HashTable entry's tag is a ZEND_WEAKREF_TAG_HT with a HashTable mapping
44
+ * tagged pointers of at most 1 WeakReference and 1 or more WeakMaps to the same tagged pointer.
45
+ *
46
+ * ZEND_MM_ALIGNED_OFFSET_LOG2 is at least 2 on supported architectures (pointers to the objects in question are aligned to 4 bytes (1<<2) even on 32-bit systems),
47
+ * i.e. the least two significant bits of the pointer can be used as a tag (ZEND_WEAKREF_TAG_*). */
40
48
#define ZEND_WEAKREF_TAG_REF 0
41
49
#define ZEND_WEAKREF_TAG_MAP 1
42
50
#define ZEND_WEAKREF_TAG_HT 2
@@ -56,38 +64,40 @@ static zend_object_handlers zend_weakmap_handlers;
56
64
#define zend_weakmap_fetch (z ) zend_weakmap_from(Z_OBJ_P(z))
57
65
58
66
static inline void zend_weakref_unref_single (
59
- void * ptr , uintptr_t tag , zend_ulong obj_addr )
67
+ void * ptr , uintptr_t tag , zend_object * object )
60
68
{
61
69
if (tag == ZEND_WEAKREF_TAG_REF ) {
70
+ /* Unreferencing WeakReference (at ptr) singleton that pointed to object. */
62
71
zend_weakref * wr = ptr ;
63
72
wr -> referent = NULL ;
64
73
} else {
74
+ /* unreferencing WeakMap entry (at ptr) with a key of object. */
65
75
ZEND_ASSERT (tag == ZEND_WEAKREF_TAG_MAP );
66
- zend_hash_index_del ((HashTable * ) ptr , obj_addr );
76
+ zend_hash_index_del ((HashTable * ) ptr , zend_object_to_weakref_key ( object ) );
67
77
}
68
78
}
69
79
70
- static void zend_weakref_unref (zend_ulong obj_addr , void * tagged_ptr ) {
80
+ static void zend_weakref_unref (zend_object * object , void * tagged_ptr ) {
71
81
void * ptr = ZEND_WEAKREF_GET_PTR (tagged_ptr );
72
82
uintptr_t tag = ZEND_WEAKREF_GET_TAG (tagged_ptr );
73
83
if (tag == ZEND_WEAKREF_TAG_HT ) {
74
84
HashTable * ht = ptr ;
75
85
ZEND_HASH_MAP_FOREACH_PTR (ht , tagged_ptr ) {
76
86
zend_weakref_unref_single (
77
- ZEND_WEAKREF_GET_PTR (tagged_ptr ), ZEND_WEAKREF_GET_TAG (tagged_ptr ), obj_addr );
87
+ ZEND_WEAKREF_GET_PTR (tagged_ptr ), ZEND_WEAKREF_GET_TAG (tagged_ptr ), object );
78
88
} ZEND_HASH_FOREACH_END ();
79
89
zend_hash_destroy (ht );
80
90
FREE_HASHTABLE (ht );
81
91
} else {
82
- zend_weakref_unref_single (ptr , tag , obj_addr );
92
+ zend_weakref_unref_single (ptr , tag , object );
83
93
}
84
94
}
85
95
86
96
static void zend_weakref_register (zend_object * object , void * payload ) {
87
97
GC_ADD_FLAGS (object , IS_OBJ_WEAKLY_REFERENCED );
88
98
89
- zend_ulong obj_addr = ( zend_ulong ) object ;
90
- zval * zv = zend_hash_index_lookup (& EG (weakrefs ), obj_addr );
99
+ zend_ulong obj_key = zend_object_to_weakref_key ( object ) ;
100
+ zval * zv = zend_hash_index_lookup (& EG (weakrefs ), obj_key );
91
101
if (Z_TYPE_P (zv ) == IS_NULL ) {
92
102
ZVAL_PTR (zv , payload );
93
103
return ;
@@ -105,25 +115,28 @@ static void zend_weakref_register(zend_object *object, void *payload) {
105
115
zend_hash_init (ht , 0 , NULL , NULL , 0 );
106
116
zend_hash_index_add_new_ptr (ht , (zend_ulong ) tagged_ptr , tagged_ptr );
107
117
zend_hash_index_add_new_ptr (ht , (zend_ulong ) payload , payload );
108
- zend_hash_index_update_ptr (
109
- & EG ( weakrefs ), obj_addr , ZEND_WEAKREF_ENCODE (ht , ZEND_WEAKREF_TAG_HT ));
118
+ /* Replace the single WeakMap or WeakReference entry in EG(weakrefs) with a HashTable with 2 entries in place. */
119
+ ZVAL_PTR ( zv , ZEND_WEAKREF_ENCODE (ht , ZEND_WEAKREF_TAG_HT ));
110
120
}
111
121
112
122
static void zend_weakref_unregister (zend_object * object , void * payload , bool weakref_free ) {
113
- zend_ulong obj_addr = ( zend_ulong ) object ;
114
- void * tagged_ptr = zend_hash_index_find_ptr (& EG (weakrefs ), obj_addr );
123
+ zend_ulong obj_key = zend_object_to_weakref_key ( object ) ;
124
+ void * tagged_ptr = zend_hash_index_find_ptr (& EG (weakrefs ), obj_key );
115
125
ZEND_ASSERT (tagged_ptr && "Weakref not registered?" );
116
126
117
127
void * ptr = ZEND_WEAKREF_GET_PTR (tagged_ptr );
118
128
uintptr_t tag = ZEND_WEAKREF_GET_TAG (tagged_ptr );
119
129
if (tag != ZEND_WEAKREF_TAG_HT ) {
120
130
ZEND_ASSERT (tagged_ptr == payload );
121
- zend_hash_index_del (& EG (weakrefs ), obj_addr );
131
+ zend_hash_index_del (& EG (weakrefs ), obj_key );
122
132
GC_DEL_FLAGS (object , IS_OBJ_WEAKLY_REFERENCED );
123
133
124
134
/* Do this last, as it may destroy the object. */
125
135
if (weakref_free ) {
126
- zend_weakref_unref_single (ptr , tag , obj_addr );
136
+ zend_weakref_unref_single (ptr , tag , object );
137
+ } else {
138
+ /* The optimization of skipping unref is only used in the destructor of WeakMap */
139
+ ZEND_ASSERT (ZEND_WEAKREF_GET_TAG (payload ) == ZEND_WEAKREF_TAG_MAP );
127
140
}
128
141
return ;
129
142
}
@@ -139,26 +152,29 @@ static void zend_weakref_unregister(zend_object *object, void *payload, bool wea
139
152
GC_DEL_FLAGS (object , IS_OBJ_WEAKLY_REFERENCED );
140
153
zend_hash_destroy (ht );
141
154
FREE_HASHTABLE (ht );
142
- zend_hash_index_del (& EG (weakrefs ), obj_addr );
155
+ zend_hash_index_del (& EG (weakrefs ), obj_key );
143
156
}
144
157
145
158
/* Do this last, as it may destroy the object. */
146
159
if (weakref_free ) {
147
160
zend_weakref_unref_single (
148
- ZEND_WEAKREF_GET_PTR (payload ), ZEND_WEAKREF_GET_TAG (payload ), obj_addr );
161
+ ZEND_WEAKREF_GET_PTR (payload ), ZEND_WEAKREF_GET_TAG (payload ), object );
162
+ } else {
163
+ /* The optimization of skipping unref is only used in the destructor of WeakMap */
164
+ ZEND_ASSERT (ZEND_WEAKREF_GET_TAG (payload ) == ZEND_WEAKREF_TAG_MAP );
149
165
}
150
166
}
151
167
152
168
ZEND_API zval * zend_weakrefs_hash_add (HashTable * ht , zend_object * key , zval * pData ) {
153
- zval * zv = zend_hash_index_add (ht , ( zend_ulong ) key , pData );
169
+ zval * zv = zend_hash_index_add (ht , zend_object_to_weakref_key ( key ) , pData );
154
170
if (zv ) {
155
171
zend_weakref_register (key , ZEND_WEAKREF_ENCODE (ht , ZEND_WEAKREF_TAG_MAP ));
156
172
}
157
173
return zv ;
158
174
}
159
175
160
176
ZEND_API zend_result zend_weakrefs_hash_del (HashTable * ht , zend_object * key ) {
161
- zval * zv = zend_hash_index_find (ht , ( zend_ulong ) key );
177
+ zval * zv = zend_hash_index_find (ht , zend_object_to_weakref_key ( key ) );
162
178
if (zv ) {
163
179
zend_weakref_unregister (key , ZEND_WEAKREF_ENCODE (ht , ZEND_WEAKREF_TAG_MAP ), 1 );
164
180
return SUCCESS ;
@@ -170,17 +186,19 @@ void zend_weakrefs_init(void) {
170
186
zend_hash_init (& EG (weakrefs ), 8 , NULL , NULL , 0 );
171
187
}
172
188
189
+ /* This is called when the object is garbage collected
190
+ * to remove all WeakReference and WeakMap entries weakly referencing that object. */
173
191
void zend_weakrefs_notify (zend_object * object ) {
174
192
/* Annoyingly we can't use the HT destructor here, because we need access to the key (which
175
193
* is the object address), which is not provided to the dtor. */
176
- zend_ulong obj_addr = ( zend_ulong ) object ;
177
- void * tagged_ptr = zend_hash_index_find_ptr (& EG (weakrefs ), obj_addr );
194
+ const zend_ulong obj_key = zend_object_to_weakref_key ( object ) ;
195
+ void * tagged_ptr = zend_hash_index_find_ptr (& EG (weakrefs ), obj_key );
178
196
#if ZEND_DEBUG
179
197
ZEND_ASSERT (tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise" );
180
198
#endif
181
199
if (tagged_ptr ) {
182
- zend_weakref_unref (obj_addr , tagged_ptr );
183
- zend_hash_index_del (& EG (weakrefs ), obj_addr );
200
+ zend_weakref_unref (object , tagged_ptr );
201
+ zend_hash_index_del (& EG (weakrefs ), obj_key );
184
202
}
185
203
}
186
204
@@ -199,7 +217,7 @@ static zend_object* zend_weakref_new(zend_class_entry *ce) {
199
217
}
200
218
201
219
static zend_always_inline bool zend_weakref_find (zend_object * referent , zval * return_value ) {
202
- void * tagged_ptr = zend_hash_index_find_ptr (& EG (weakrefs ), ( zend_ulong ) referent );
220
+ void * tagged_ptr = zend_hash_index_find_ptr (& EG (weakrefs ), zend_object_to_weakref_key ( referent ) );
203
221
if (!tagged_ptr ) {
204
222
return 0 ;
205
223
}
@@ -295,13 +313,13 @@ static zend_object *zend_weakmap_create_object(zend_class_entry *ce)
295
313
static void zend_weakmap_free_obj (zend_object * object )
296
314
{
297
315
zend_weakmap * wm = zend_weakmap_from (object );
298
- zend_ulong obj_addr ;
299
- ZEND_HASH_MAP_FOREACH_NUM_KEY (& wm -> ht , obj_addr ) {
316
+ zend_ulong obj_key ;
317
+ ZEND_HASH_MAP_FOREACH_NUM_KEY (& wm -> ht , obj_key ) {
300
318
/* Optimization: Don't call zend_weakref_unref_single to free individual entries from wm->ht when unregistering (which would do a hash table lookup, call zend_hash_index_del, and skip over any bucket collisions).
301
319
* Let freeing the corresponding values for WeakMap entries be done in zend_hash_destroy, freeing objects sequentially.
302
320
* The performance difference is notable for larger WeakMaps with worse cache locality. */
303
321
zend_weakref_unregister (
304
- ( zend_object * ) obj_addr , ZEND_WEAKREF_ENCODE (& wm -> ht , ZEND_WEAKREF_TAG_MAP ), 0 );
322
+ zend_weakref_key_to_object ( obj_key ) , ZEND_WEAKREF_ENCODE (& wm -> ht , ZEND_WEAKREF_TAG_MAP ), 0 );
305
323
} ZEND_HASH_FOREACH_END ();
306
324
zend_hash_destroy (& wm -> ht );
307
325
zend_object_std_dtor (& wm -> std );
@@ -320,12 +338,12 @@ static zval *zend_weakmap_read_dimension(zend_object *object, zval *offset, int
320
338
}
321
339
322
340
zend_weakmap * wm = zend_weakmap_from (object );
323
- zend_object * obj_key = Z_OBJ_P (offset );
324
- zval * zv = zend_hash_index_find (& wm -> ht , ( zend_ulong ) obj_key );
341
+ zend_object * obj_addr = Z_OBJ_P (offset );
342
+ zval * zv = zend_hash_index_find (& wm -> ht , zend_object_to_weakref_key ( obj_addr ) );
325
343
if (zv == NULL ) {
326
344
if (type != BP_VAR_IS ) {
327
345
zend_throw_error (NULL ,
328
- "Object %s#%d not contained in WeakMap" , ZSTR_VAL (obj_key -> ce -> name ), obj_key -> handle );
346
+ "Object %s#%d not contained in WeakMap" , ZSTR_VAL (obj_addr -> ce -> name ), obj_addr -> handle );
329
347
return NULL ;
330
348
}
331
349
return NULL ;
@@ -350,10 +368,11 @@ static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval
350
368
}
351
369
352
370
zend_weakmap * wm = zend_weakmap_from (object );
353
- zend_object * obj_key = Z_OBJ_P (offset );
371
+ zend_object * obj_addr = Z_OBJ_P (offset );
372
+ zend_ulong obj_key = zend_object_to_weakref_key (obj_addr );
354
373
Z_TRY_ADDREF_P (value );
355
374
356
- zval * zv = zend_hash_index_find (& wm -> ht , ( zend_ulong ) obj_key );
375
+ zval * zv = zend_hash_index_find (& wm -> ht , obj_key );
357
376
if (zv ) {
358
377
/* Because the destructors can have side effects such as resizing or rehashing the WeakMap storage,
359
378
* free the zval only after overwriting the original value. */
@@ -364,8 +383,8 @@ static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval
364
383
return ;
365
384
}
366
385
367
- zend_weakref_register (obj_key , ZEND_WEAKREF_ENCODE (& wm -> ht , ZEND_WEAKREF_TAG_MAP ));
368
- zend_hash_index_add_new (& wm -> ht , ( zend_ulong ) obj_key , value );
386
+ zend_weakref_register (obj_addr , ZEND_WEAKREF_ENCODE (& wm -> ht , ZEND_WEAKREF_TAG_MAP ));
387
+ zend_hash_index_add_new (& wm -> ht , obj_key , value );
369
388
}
370
389
371
390
/* int return and check_empty due to Object Handler API */
@@ -377,7 +396,7 @@ static int zend_weakmap_has_dimension(zend_object *object, zval *offset, int che
377
396
}
378
397
379
398
zend_weakmap * wm = zend_weakmap_from (object );
380
- zval * zv = zend_hash_index_find (& wm -> ht , ( zend_ulong ) Z_OBJ_P (offset ));
399
+ zval * zv = zend_hash_index_find (& wm -> ht , zend_object_to_weakref_key ( Z_OBJ_P (offset ) ));
381
400
if (!zv ) {
382
401
return 0 ;
383
402
}
@@ -396,13 +415,13 @@ static void zend_weakmap_unset_dimension(zend_object *object, zval *offset)
396
415
}
397
416
398
417
zend_weakmap * wm = zend_weakmap_from (object );
399
- zend_object * obj_key = Z_OBJ_P (offset );
400
- if (!zend_hash_index_exists (& wm -> ht , ( zend_ulong ) Z_OBJ_P ( offset ))) {
418
+ zend_object * obj_addr = Z_OBJ_P (offset );
419
+ if (!zend_hash_index_exists (& wm -> ht , zend_object_to_weakref_key ( obj_addr ))) {
401
420
/* Object not in WeakMap, do nothing. */
402
421
return ;
403
422
}
404
423
405
- zend_weakref_unregister (obj_key , ZEND_WEAKREF_ENCODE (& wm -> ht , ZEND_WEAKREF_TAG_MAP ), 1 );
424
+ zend_weakref_unregister (obj_addr , ZEND_WEAKREF_ENCODE (& wm -> ht , ZEND_WEAKREF_TAG_MAP ), 1 );
406
425
}
407
426
408
427
static int zend_weakmap_count_elements (zend_object * object , zend_long * count )
@@ -423,10 +442,10 @@ static HashTable *zend_weakmap_get_properties_for(zend_object *object, zend_prop
423
442
ALLOC_HASHTABLE (ht );
424
443
zend_hash_init (ht , zend_hash_num_elements (& wm -> ht ), NULL , ZVAL_PTR_DTOR , 0 );
425
444
426
- zend_ulong obj_addr ;
445
+ zend_ulong obj_key ;
427
446
zval * val ;
428
- ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL (& wm -> ht , obj_addr , val ) {
429
- zend_object * obj = ( zend_object * ) obj_addr ;
447
+ ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL (& wm -> ht , obj_key , val ) {
448
+ zend_object * obj = zend_weakref_key_to_object ( obj_key ) ;
430
449
zval pair ;
431
450
array_init (& pair );
432
451
@@ -460,11 +479,11 @@ static zend_object *zend_weakmap_clone_obj(zend_object *old_object)
460
479
zend_weakmap * new_wm = zend_weakmap_from (new_object );
461
480
zend_hash_copy (& new_wm -> ht , & old_wm -> ht , NULL );
462
481
463
- zend_ulong obj_addr ;
482
+ zend_ulong obj_key ;
464
483
zval * val ;
465
- ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL (& new_wm -> ht , obj_addr , val ) {
484
+ ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL (& new_wm -> ht , obj_key , val ) {
466
485
zend_weakref_register (
467
- ( zend_object * ) obj_addr , ZEND_WEAKREF_ENCODE (new_wm , ZEND_WEAKREF_TAG_MAP ));
486
+ zend_weakref_key_to_object ( obj_key ) , ZEND_WEAKREF_ENCODE (new_wm , ZEND_WEAKREF_TAG_MAP ));
468
487
zval_add_ref (val );
469
488
} ZEND_HASH_FOREACH_END ();
470
489
return new_object ;
@@ -511,7 +530,7 @@ static void zend_weakmap_iterator_get_current_key(zend_object_iterator *obj_iter
511
530
ZEND_ASSERT (0 && "Must have integer key" );
512
531
}
513
532
514
- ZVAL_OBJ_COPY (key , ( zend_object * ) num_key );
533
+ ZVAL_OBJ_COPY (key , zend_weakref_key_to_object ( num_key ) );
515
534
}
516
535
517
536
static void zend_weakmap_iterator_move_forward (zend_object_iterator * obj_iter )
0 commit comments