/src/php-src/Zend/zend_weakrefs.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright © The PHP Group and Contributors. | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to the Modified BSD License that is | |
6 | | | bundled with this package in the file LICENSE, and is available | |
7 | | | through the World Wide Web at <https://www.php.net/license/>. | |
8 | | | | |
9 | | | SPDX-License-Identifier: BSD-3-Clause | |
10 | | +----------------------------------------------------------------------+ |
11 | | | Authors: krakjoe@php.net | |
12 | | +----------------------------------------------------------------------+ |
13 | | */ |
14 | | |
15 | | #include "zend.h" |
16 | | #include "zend_interfaces.h" |
17 | | #include "zend_objects_API.h" |
18 | | #include "zend_types.h" |
19 | | #include "zend_weakrefs.h" |
20 | | #include "zend_weakrefs_arginfo.h" |
21 | | |
22 | | typedef struct _zend_weakref { |
23 | | zend_object *referent; |
24 | | zend_object std; |
25 | | } zend_weakref; |
26 | | |
27 | | typedef struct _zend_weakmap { |
28 | | HashTable ht; |
29 | | zend_object std; |
30 | | } zend_weakmap; |
31 | | |
32 | | typedef struct _zend_weakmap_iterator { |
33 | | zend_object_iterator it; |
34 | | uint32_t ht_iter; |
35 | | } zend_weakmap_iterator; |
36 | | |
37 | | /* EG(weakrefs) is a map from a key corresponding to a zend_object pointer to all the WeakReference, WeakMap, and/or bare HashTable entries relating to that pointer. |
38 | | * |
39 | | * 1. For a single WeakReference, |
40 | | * 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()). |
41 | | * 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 *). |
42 | | * 3. For a single bare HashTable, the HashTable's corresponding value's tag is a ZEND_WEAKREF_TAG_BARE_HT and the pointer is a HashTable*. |
43 | | * 4. 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 WeakMap or bare HashTable 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_*). */ |
48 | 336 | #define ZEND_WEAKREF_TAG_REF 0 |
49 | 1.59k | #define ZEND_WEAKREF_TAG_MAP 1 |
50 | 2.04k | #define ZEND_WEAKREF_TAG_HT 2 |
51 | 552 | #define ZEND_WEAKREF_TAG_BARE_HT 3 |
52 | 2.13k | #define ZEND_WEAKREF_GET_TAG(p) (((uintptr_t) (p)) & 3) |
53 | 2.08k | #define ZEND_WEAKREF_GET_PTR(p) ((void *) (((uintptr_t) (p)) & ~3)) |
54 | 1.02k | #define ZEND_WEAKREF_ENCODE(p, t) ((void *) (((uintptr_t) (p)) | (t))) |
55 | | |
56 | | zend_class_entry *zend_ce_weakref; |
57 | | static zend_class_entry *zend_ce_weakmap; |
58 | | static zend_object_handlers zend_weakref_handlers; |
59 | | static zend_object_handlers zend_weakmap_handlers; |
60 | | |
61 | 536 | #define zend_weakref_from(o) (ZEND_CONTAINER_OF(o, zend_weakref, std)) |
62 | 330 | #define zend_weakref_fetch(z) zend_weakref_from(Z_OBJ_P(z)) |
63 | | |
64 | 1.96k | #define zend_weakmap_from(o) (ZEND_CONTAINER_OF(o, zend_weakmap, std)) |
65 | 651 | #define zend_weakmap_fetch(z) zend_weakmap_from(Z_OBJ_P(z)) |
66 | | |
67 | | static inline void zend_weakref_unref_single( |
68 | | void *ptr, uintptr_t tag, zend_object *object) |
69 | 315 | { |
70 | 315 | if (tag == ZEND_WEAKREF_TAG_REF) { |
71 | | /* Unreferencing WeakReference (at ptr) singleton that pointed to object. */ |
72 | 177 | zend_weakref *wr = ptr; |
73 | 177 | wr->referent = NULL; |
74 | 177 | } else { |
75 | | /* unreferencing WeakMap or bare HashTable entry (at ptr) with a key of object. */ |
76 | 138 | ZEND_ASSERT(tag == ZEND_WEAKREF_TAG_MAP || tag == ZEND_WEAKREF_TAG_BARE_HT); |
77 | 138 | zend_hash_index_del((HashTable *) ptr, zend_object_to_weakref_key(object)); |
78 | 138 | } |
79 | 315 | } |
80 | | |
81 | 243 | static void zend_weakref_unref(zend_object *object, void *tagged_ptr) { |
82 | 243 | void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr); |
83 | 243 | uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr); |
84 | 243 | if (tag == ZEND_WEAKREF_TAG_HT) { |
85 | 15 | HashTable *ht = ptr; |
86 | 108 | ZEND_HASH_MAP_FOREACH_PTR(ht, tagged_ptr) { |
87 | 108 | zend_weakref_unref_single( |
88 | 108 | ZEND_WEAKREF_GET_PTR(tagged_ptr), ZEND_WEAKREF_GET_TAG(tagged_ptr), object); |
89 | 108 | } ZEND_HASH_FOREACH_END(); |
90 | 15 | zend_hash_destroy(ht); |
91 | 15 | FREE_HASHTABLE(ht); |
92 | 228 | } else { |
93 | 228 | zend_weakref_unref_single(ptr, tag, object); |
94 | 228 | } |
95 | 243 | } |
96 | | |
97 | 642 | static void zend_weakref_register(zend_object *object, void *payload) { |
98 | 642 | GC_ADD_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED); |
99 | | |
100 | 642 | zend_ulong obj_key = zend_object_to_weakref_key(object); |
101 | 642 | zval *zv = zend_hash_index_lookup(&EG(weakrefs), obj_key); |
102 | 642 | if (Z_TYPE_P(zv) == IS_NULL) { |
103 | 591 | ZVAL_PTR(zv, payload); |
104 | 591 | return; |
105 | 591 | } |
106 | | |
107 | 51 | void *tagged_ptr = Z_PTR_P(zv); |
108 | 51 | if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_HT) { |
109 | 27 | HashTable *ht = ZEND_WEAKREF_GET_PTR(tagged_ptr); |
110 | 27 | zend_hash_index_add_new_ptr(ht, (zend_ulong)(uintptr_t) payload, payload); |
111 | 27 | return; |
112 | 27 | } |
113 | | |
114 | | /* Convert simple pointer to hashtable. */ |
115 | 24 | HashTable *ht = emalloc(sizeof(HashTable)); |
116 | 24 | zend_hash_init(ht, 0, NULL, NULL, 0); |
117 | 24 | zend_hash_index_add_new_ptr(ht, (zend_ulong)(uintptr_t) tagged_ptr, tagged_ptr); |
118 | 24 | zend_hash_index_add_new_ptr(ht, (zend_ulong)(uintptr_t) payload, payload); |
119 | | /* Replace the single WeakMap or WeakReference entry in EG(weakrefs) with a HashTable with 2 entries in place. */ |
120 | 24 | ZVAL_PTR(zv, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_HT)); |
121 | 24 | } |
122 | | |
123 | 381 | static void zend_weakref_unregister(zend_object *object, void *payload, bool weakref_free) { |
124 | 381 | zend_ulong obj_key = zend_object_to_weakref_key(object); |
125 | 381 | void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key); |
126 | 381 | ZEND_ASSERT(tagged_ptr && "Weakref not registered?"); |
127 | | |
128 | 381 | void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr); |
129 | 381 | uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr); |
130 | 381 | if (tag != ZEND_WEAKREF_TAG_HT) { |
131 | 339 | ZEND_ASSERT(tagged_ptr == payload); |
132 | 339 | zend_hash_index_del(&EG(weakrefs), obj_key); |
133 | 339 | GC_DEL_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED); |
134 | | |
135 | | /* Do this last, as it may destroy the object. */ |
136 | 339 | if (weakref_free) { |
137 | 42 | zend_weakref_unref_single(ptr, tag, object); |
138 | 297 | } else { |
139 | | /* The optimization of skipping unref is used for zend_weakrefs_hash_clean_ex() */ |
140 | 297 | ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP || ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_BARE_HT); |
141 | 297 | } |
142 | 339 | return; |
143 | 339 | } |
144 | | |
145 | 42 | HashTable *ht = ptr; |
146 | 42 | #if ZEND_DEBUG |
147 | 42 | void *old_payload = zend_hash_index_find_ptr(ht, (zend_ulong)(uintptr_t) payload); |
148 | 42 | ZEND_ASSERT(old_payload && "Weakref not registered?"); |
149 | 42 | ZEND_ASSERT(old_payload == payload); |
150 | 42 | #endif |
151 | 42 | zend_hash_index_del(ht, (zend_ulong)(uintptr_t) payload); |
152 | 42 | if (zend_hash_num_elements(ht) == 0) { |
153 | 9 | GC_DEL_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED); |
154 | 9 | zend_hash_destroy(ht); |
155 | 9 | FREE_HASHTABLE(ht); |
156 | 9 | zend_hash_index_del(&EG(weakrefs), obj_key); |
157 | 9 | } |
158 | | |
159 | | /* Do this last, as it may destroy the object. */ |
160 | 42 | if (weakref_free) { |
161 | 12 | zend_weakref_unref_single( |
162 | 12 | ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), object); |
163 | 30 | } else { |
164 | | /* The optimization of skipping unref is used for zend_weakrefs_hash_clean_ex() */ |
165 | 30 | ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP || ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_BARE_HT); |
166 | 30 | } |
167 | 42 | } |
168 | | |
169 | | /* Insert 'pData' into bare HashTable 'ht', with the given 'key'. 'key' is |
170 | | * weakly referenced. 'ht' is considered to be a bare HashTable, not a WeakMap. */ |
171 | 0 | ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData) { |
172 | 0 | zval *zv = zend_hash_index_add(ht, zend_object_to_weakref_key(key), pData); |
173 | 0 | if (zv) { |
174 | 0 | zend_weakref_register(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_BARE_HT)); |
175 | 0 | } |
176 | 0 | return zv; |
177 | 0 | } |
178 | | |
179 | 0 | ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) { |
180 | 0 | zval *zv = zend_hash_index_find(ht, zend_object_to_weakref_key(key)); |
181 | 0 | if (zv) { |
182 | 0 | zend_weakref_unregister(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_BARE_HT), true); |
183 | 0 | return SUCCESS; |
184 | 0 | } |
185 | 0 | return FAILURE; |
186 | 0 | } |
187 | | |
188 | 231 | static void zend_weakrefs_hash_clean_ex(HashTable *ht, int type) { |
189 | 231 | zend_ulong obj_key; |
190 | 1.15k | ZEND_HASH_MAP_FOREACH_NUM_KEY(ht, obj_key) { |
191 | | /* Optimization: Don't call zend_weakref_unref_single to free individual entries from ht when unregistering (which would do a hash table lookup, call zend_hash_index_del, and skip over any bucket collisions). |
192 | | * Let freeing the corresponding values for WeakMap entries be done in zend_hash_clean, freeing objects sequentially. |
193 | | * The performance difference is notable for larger WeakMaps with worse cache locality. */ |
194 | 1.15k | zend_weakref_unregister( |
195 | 1.15k | zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(ht, type), false); |
196 | 1.15k | } ZEND_HASH_FOREACH_END(); |
197 | 231 | zend_hash_clean(ht); |
198 | 231 | } |
199 | | |
200 | 0 | ZEND_API void zend_weakrefs_hash_clean(HashTable *ht) { |
201 | 0 | zend_weakrefs_hash_clean_ex(ht, ZEND_WEAKREF_TAG_BARE_HT); |
202 | 0 | } |
203 | | |
204 | 44.4k | void zend_weakrefs_init(void) { |
205 | 44.4k | zend_hash_init(&EG(weakrefs), 8, NULL, NULL, 0); |
206 | 44.4k | } |
207 | | |
208 | | /* This is called when the object is garbage collected |
209 | | * to remove all WeakReference and WeakMap entries weakly referencing that object. */ |
210 | 243 | void zend_weakrefs_notify(zend_object *object) { |
211 | | /* Annoyingly we can't use the HT destructor here, because we need access to the key (which |
212 | | * is the object address), which is not provided to the dtor. */ |
213 | 243 | const zend_ulong obj_key = zend_object_to_weakref_key(object); |
214 | 243 | void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key); |
215 | 243 | #if ZEND_DEBUG |
216 | 243 | ZEND_ASSERT(tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise"); |
217 | 243 | #endif |
218 | 243 | if (tagged_ptr) { |
219 | 243 | zend_weakref_unref(object, tagged_ptr); |
220 | 243 | zend_hash_index_del(&EG(weakrefs), obj_key); |
221 | 243 | } |
222 | 243 | } |
223 | | |
224 | 44.4k | void zend_weakrefs_shutdown(void) { |
225 | 44.4k | zend_hash_destroy(&EG(weakrefs)); |
226 | 44.4k | } |
227 | | |
228 | 182 | static zend_object* zend_weakref_new(zend_class_entry *ce) { |
229 | 182 | zend_weakref *wr = zend_object_alloc(sizeof(zend_weakref), zend_ce_weakref); |
230 | | |
231 | 182 | zend_object_std_init(&wr->std, zend_ce_weakref); |
232 | 182 | return &wr->std; |
233 | 182 | } |
234 | | |
235 | 186 | static zend_always_inline bool zend_weakref_find(zend_object *referent, zval *return_value) { |
236 | 186 | void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), zend_object_to_weakref_key(referent)); |
237 | 186 | if (!tagged_ptr) { |
238 | 171 | return false; |
239 | 171 | } |
240 | | |
241 | 15 | void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr); |
242 | 15 | uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr); |
243 | 15 | if (tag == ZEND_WEAKREF_TAG_REF) { |
244 | 3 | zend_weakref *wr; |
245 | 9 | found_weakref: |
246 | 9 | wr = ptr; |
247 | 9 | RETVAL_OBJ_COPY(&wr->std); |
248 | 9 | return true; |
249 | 3 | } |
250 | | |
251 | 12 | if (tag == ZEND_WEAKREF_TAG_HT) { |
252 | 24 | ZEND_HASH_MAP_FOREACH_PTR(ptr, tagged_ptr) { |
253 | 24 | if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_REF) { |
254 | 6 | ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr); |
255 | 6 | goto found_weakref; |
256 | 6 | } |
257 | 24 | } ZEND_HASH_FOREACH_END(); |
258 | 6 | } |
259 | | |
260 | 6 | return false; |
261 | 12 | } |
262 | | |
263 | 177 | static zend_always_inline void zend_weakref_create(zend_object *referent, zval *return_value) { |
264 | 177 | zend_weakref *wr; |
265 | | |
266 | 177 | object_init_ex(return_value, zend_ce_weakref); |
267 | | |
268 | 177 | wr = zend_weakref_fetch(return_value); |
269 | 177 | wr->referent = referent; |
270 | | |
271 | 177 | zend_weakref_register(wr->referent, ZEND_WEAKREF_ENCODE(wr, ZEND_WEAKREF_TAG_REF)); |
272 | 177 | } |
273 | | |
274 | 153 | static zend_always_inline void zend_weakref_get(zval *weakref, zval *return_value) { |
275 | 153 | zend_weakref *wr = zend_weakref_fetch(weakref); |
276 | | |
277 | 153 | if (wr->referent) { |
278 | 66 | RETVAL_OBJ_COPY(wr->referent); |
279 | 66 | } |
280 | 153 | } |
281 | | |
282 | 182 | static void zend_weakref_free(zend_object *zo) { |
283 | 182 | zend_weakref *wr = zend_weakref_from(zo); |
284 | | |
285 | 182 | if (wr->referent) { |
286 | 48 | zend_weakref_unregister(wr->referent, ZEND_WEAKREF_ENCODE(wr, ZEND_WEAKREF_TAG_REF), true); |
287 | 48 | } |
288 | | |
289 | 182 | zend_object_std_dtor(&wr->std); |
290 | 182 | } |
291 | | |
292 | | static HashTable *zend_weakref_get_debug_info(zend_object *object, int *is_temp) |
293 | 24 | { |
294 | 24 | *is_temp = 1; |
295 | | |
296 | 24 | HashTable *ht = zend_new_array(1); |
297 | | |
298 | 24 | zend_object *referent = zend_weakref_from(object)->referent; |
299 | 24 | zval value; |
300 | 24 | if (referent) { |
301 | 18 | ZVAL_OBJ_COPY(&value, referent); |
302 | 18 | } else { |
303 | 6 | ZVAL_NULL(&value); |
304 | 6 | } |
305 | | |
306 | 24 | zend_hash_update(ht, ZSTR_KNOWN(ZEND_STR_OBJECT), &value); |
307 | | |
308 | 24 | return ht; |
309 | 24 | } |
310 | | |
311 | | ZEND_COLD ZEND_METHOD(WeakReference, __construct) |
312 | 5 | { |
313 | 5 | zend_throw_error(NULL, "Direct instantiation of WeakReference is not allowed, use WeakReference::create instead"); |
314 | 5 | } |
315 | | |
316 | | ZEND_METHOD(WeakReference, create) |
317 | 192 | { |
318 | 192 | zend_object *referent; |
319 | | |
320 | 576 | ZEND_PARSE_PARAMETERS_START(1,1) |
321 | 768 | Z_PARAM_OBJ(referent) |
322 | 192 | ZEND_PARSE_PARAMETERS_END(); |
323 | | |
324 | 186 | if (zend_weakref_find(referent, return_value)) { |
325 | 9 | return; |
326 | 9 | } |
327 | | |
328 | 177 | zend_weakref_create(referent, return_value); |
329 | 177 | } |
330 | | |
331 | | ZEND_METHOD(WeakReference, get) |
332 | 153 | { |
333 | 153 | ZEND_PARSE_PARAMETERS_NONE(); |
334 | | |
335 | 153 | zend_weakref_get(ZEND_THIS, return_value); |
336 | 153 | } |
337 | | |
338 | | static zend_object *zend_weakmap_create_object(zend_class_entry *ce) |
339 | 231 | { |
340 | 231 | zend_weakmap *wm = zend_object_alloc(sizeof(zend_weakmap), ce); |
341 | 231 | zend_object_std_init(&wm->std, ce); |
342 | | |
343 | 231 | zend_hash_init(&wm->ht, 0, NULL, ZVAL_PTR_DTOR, 0); |
344 | 231 | return &wm->std; |
345 | 231 | } |
346 | | |
347 | | static void zend_weakmap_free_obj(zend_object *object) |
348 | 231 | { |
349 | 231 | zend_weakmap *wm = zend_weakmap_from(object); |
350 | 231 | zend_weakrefs_hash_clean_ex(&wm->ht, ZEND_WEAKREF_TAG_MAP); |
351 | 231 | zend_hash_destroy(&wm->ht); |
352 | 231 | zend_object_std_dtor(&wm->std); |
353 | 231 | } |
354 | | |
355 | | static zval *zend_weakmap_read_dimension(zend_object *object, zval *offset, int type, zval *rv) |
356 | 72 | { |
357 | 72 | if (offset == NULL) { |
358 | 3 | zend_throw_error(NULL, "Cannot append to WeakMap"); |
359 | 3 | return NULL; |
360 | 3 | } |
361 | | |
362 | 69 | ZVAL_DEREF(offset); |
363 | 69 | if (Z_TYPE_P(offset) != IS_OBJECT) { |
364 | 3 | zend_type_error("WeakMap key must be an object"); |
365 | 3 | return NULL; |
366 | 3 | } |
367 | | |
368 | 66 | zend_weakmap *wm = zend_weakmap_from(object); |
369 | 66 | zend_object *obj_addr = Z_OBJ_P(offset); |
370 | 66 | zval *zv = zend_hash_index_find(&wm->ht, zend_object_to_weakref_key(obj_addr)); |
371 | 66 | if (type == BP_VAR_W || type == BP_VAR_RW) { |
372 | 12 | if (zv == NULL) { |
373 | 3 | zval value; |
374 | 3 | zend_weakref_register(obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP)); |
375 | 3 | ZVAL_NULL(&value); |
376 | 3 | zv = zend_hash_index_add_new(&wm->ht, zend_object_to_weakref_key(obj_addr), &value); |
377 | 3 | } |
378 | 12 | ZVAL_MAKE_REF(zv); |
379 | 54 | } else { |
380 | 54 | if (zv == NULL) { |
381 | 6 | if (type != BP_VAR_IS) { |
382 | 6 | zend_throw_error(NULL, |
383 | 6 | "Object %s#%d not contained in WeakMap", ZSTR_VAL(obj_addr->ce->name), obj_addr->handle); |
384 | 6 | return NULL; |
385 | 6 | } |
386 | 0 | return NULL; |
387 | 6 | } |
388 | 54 | } |
389 | | |
390 | 60 | return zv; |
391 | 66 | } |
392 | | |
393 | | static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval *value) |
394 | 495 | { |
395 | 495 | if (offset == NULL) { |
396 | 3 | zend_throw_error(NULL, "Cannot append to WeakMap"); |
397 | 3 | return; |
398 | 3 | } |
399 | | |
400 | 492 | ZVAL_DEREF(offset); |
401 | 492 | if (Z_TYPE_P(offset) != IS_OBJECT) { |
402 | 12 | zend_type_error("WeakMap key must be an object"); |
403 | 12 | return; |
404 | 12 | } |
405 | | |
406 | 480 | zend_weakmap *wm = zend_weakmap_from(object); |
407 | 480 | zend_object *obj_addr = Z_OBJ_P(offset); |
408 | 480 | zend_ulong obj_key = zend_object_to_weakref_key(obj_addr); |
409 | 480 | Z_TRY_ADDREF_P(value); |
410 | | |
411 | 480 | zval *zv = zend_hash_index_find(&wm->ht, obj_key); |
412 | 480 | if (zv) { |
413 | | /* Because the destructors can have side effects such as resizing or rehashing the WeakMap storage, |
414 | | * free the zval only after overwriting the original value. */ |
415 | 30 | zval zv_orig; |
416 | 30 | ZVAL_COPY_VALUE(&zv_orig, zv); |
417 | 30 | ZVAL_COPY_VALUE(zv, value); |
418 | 30 | zval_ptr_dtor(&zv_orig); |
419 | 30 | return; |
420 | 30 | } |
421 | | |
422 | 450 | zend_weakref_register(obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP)); |
423 | 450 | zend_hash_index_add_new(&wm->ht, obj_key, value); |
424 | 450 | } |
425 | | |
426 | | // todo: make zend_weakmap_has_dimension return bool as well |
427 | | /* int return and check_empty due to Object Handler API */ |
428 | | static int zend_weakmap_has_dimension(zend_object *object, zval *offset, int check_empty) |
429 | 75 | { |
430 | 75 | ZVAL_DEREF(offset); |
431 | 75 | if (Z_TYPE_P(offset) != IS_OBJECT) { |
432 | 6 | zend_type_error("WeakMap key must be an object"); |
433 | 6 | return 0; |
434 | 6 | } |
435 | | |
436 | 69 | zend_weakmap *wm = zend_weakmap_from(object); |
437 | 69 | zval *zv = zend_hash_index_find(&wm->ht, zend_object_to_weakref_key(Z_OBJ_P(offset))); |
438 | 69 | if (!zv) { |
439 | 6 | return 0; |
440 | 6 | } |
441 | | |
442 | 63 | if (check_empty) { |
443 | 30 | return i_zend_is_true(zv); |
444 | 30 | } |
445 | 33 | return Z_TYPE_P(zv) != IS_NULL; |
446 | 63 | } |
447 | | |
448 | | static void zend_weakmap_unset_dimension(zend_object *object, zval *offset) |
449 | 12 | { |
450 | 12 | ZVAL_DEREF(offset); |
451 | 12 | if (Z_TYPE_P(offset) != IS_OBJECT) { |
452 | 3 | zend_type_error("WeakMap key must be an object"); |
453 | 3 | return; |
454 | 3 | } |
455 | | |
456 | 9 | zend_weakmap *wm = zend_weakmap_from(object); |
457 | 9 | zend_object *obj_addr = Z_OBJ_P(offset); |
458 | 9 | if (!zend_hash_index_exists(&wm->ht, zend_object_to_weakref_key(obj_addr))) { |
459 | | /* Object not in WeakMap, do nothing. */ |
460 | 3 | return; |
461 | 3 | } |
462 | | |
463 | 6 | zend_weakref_unregister(obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP), true); |
464 | 6 | } |
465 | | |
466 | | static zend_result zend_weakmap_count_elements(zend_object *object, zend_long *count) |
467 | 48 | { |
468 | 48 | zend_weakmap *wm = zend_weakmap_from(object); |
469 | 48 | *count = zend_hash_num_elements(&wm->ht); |
470 | 48 | return SUCCESS; |
471 | 48 | } |
472 | | |
473 | | static HashTable *zend_weakmap_get_properties_for(zend_object *object, zend_prop_purpose purpose) |
474 | 165 | { |
475 | 165 | if (purpose != ZEND_PROP_PURPOSE_DEBUG) { |
476 | 0 | return NULL; |
477 | 0 | } |
478 | | |
479 | 165 | zend_weakmap *wm = zend_weakmap_from(object); |
480 | 165 | HashTable *ht; |
481 | 165 | ALLOC_HASHTABLE(ht); |
482 | 165 | zend_hash_init(ht, zend_hash_num_elements(&wm->ht), NULL, ZVAL_PTR_DTOR, 0); |
483 | | |
484 | 165 | zend_ulong obj_key; |
485 | 165 | zval *val; |
486 | 678 | ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&wm->ht, obj_key, val) { |
487 | 678 | zend_object *obj = zend_weakref_key_to_object(obj_key); |
488 | 678 | zval pair; |
489 | 678 | array_init(&pair); |
490 | | |
491 | 678 | GC_ADDREF(obj); |
492 | 678 | add_assoc_object(&pair, "key", obj); |
493 | 678 | Z_TRY_ADDREF_P(val); |
494 | 678 | add_assoc_zval(&pair, "value", val); |
495 | | |
496 | 678 | zend_hash_next_index_insert_new(ht, &pair); |
497 | 678 | } ZEND_HASH_FOREACH_END(); |
498 | | |
499 | 165 | return ht; |
500 | 165 | } |
501 | | |
502 | | HashTable *zend_weakmap_get_gc(zend_object *object, zval **table, int *n) |
503 | 27 | { |
504 | 27 | zend_weakmap *wm = zend_weakmap_from(object); |
505 | 27 | zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); |
506 | 27 | zval *val; |
507 | 123 | ZEND_HASH_MAP_FOREACH_VAL(&wm->ht, val) { |
508 | 123 | zend_get_gc_buffer_add_zval(gc_buffer, val); |
509 | 123 | } ZEND_HASH_FOREACH_END(); |
510 | 27 | zend_get_gc_buffer_use(gc_buffer, table, n); |
511 | 27 | return NULL; |
512 | 27 | } |
513 | | |
514 | | HashTable *zend_weakmap_get_key_entry_gc(zend_object *object, zval **table, int *n) |
515 | 69 | { |
516 | 69 | zend_weakmap *wm = zend_weakmap_from(object); |
517 | 69 | zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); |
518 | 69 | zend_ulong h; |
519 | 69 | zval *val; |
520 | 378 | ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&wm->ht, h, val) { |
521 | 378 | zend_object *key = zend_weakref_key_to_object(h); |
522 | 378 | zend_get_gc_buffer_add_obj(gc_buffer, key); |
523 | 378 | zend_get_gc_buffer_add_ptr(gc_buffer, val); |
524 | 378 | } ZEND_HASH_FOREACH_END(); |
525 | 69 | zend_get_gc_buffer_use(gc_buffer, table, n); |
526 | 69 | return NULL; |
527 | 69 | } |
528 | | |
529 | | HashTable *zend_weakmap_get_entry_gc(zend_object *object, zval **table, int *n) |
530 | 123 | { |
531 | 123 | zend_weakmap *wm = zend_weakmap_from(object); |
532 | 123 | zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); |
533 | 123 | zval *val; |
534 | 624 | ZEND_HASH_MAP_FOREACH_VAL(&wm->ht, val) { |
535 | 624 | zend_get_gc_buffer_add_ptr(gc_buffer, val); |
536 | 624 | } ZEND_HASH_FOREACH_END(); |
537 | 123 | zend_get_gc_buffer_use(gc_buffer, table, n); |
538 | 123 | return NULL; |
539 | 123 | } |
540 | | |
541 | | HashTable *zend_weakmap_get_object_key_entry_gc(zend_object *object, zval **table, int *n) |
542 | 900 | { |
543 | 900 | zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); |
544 | 900 | const zend_ulong obj_key = zend_object_to_weakref_key(object); |
545 | 900 | void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key); |
546 | 900 | #if ZEND_DEBUG |
547 | 900 | ZEND_ASSERT(tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise"); |
548 | 900 | #endif |
549 | 900 | void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr); |
550 | 900 | uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr); |
551 | | |
552 | 900 | if (tag == ZEND_WEAKREF_TAG_HT) { |
553 | 6 | HashTable *ht = ptr; |
554 | 30 | ZEND_HASH_MAP_FOREACH_PTR(ht, tagged_ptr) { |
555 | 30 | if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_MAP) { |
556 | 6 | zend_weakmap *wm = (zend_weakmap*) ZEND_WEAKREF_GET_PTR(tagged_ptr); |
557 | 6 | zval *zv = zend_hash_index_find(&wm->ht, obj_key); |
558 | 6 | ZEND_ASSERT(zv); |
559 | 6 | zend_get_gc_buffer_add_ptr(gc_buffer, zv); |
560 | 6 | zend_get_gc_buffer_add_obj(gc_buffer, &wm->std); |
561 | 6 | } else if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_BARE_HT) { |
562 | | /* Bare HashTables are intentionally ignored, since they are |
563 | | * intended for internal usage by extensions and might not be |
564 | | * collectable. */ |
565 | 0 | } |
566 | 30 | } ZEND_HASH_FOREACH_END(); |
567 | 894 | } else if (tag == ZEND_WEAKREF_TAG_MAP) { |
568 | 660 | zend_weakmap *wm = (zend_weakmap*) ptr; |
569 | 660 | zval *zv = zend_hash_index_find(&wm->ht, obj_key); |
570 | 660 | ZEND_ASSERT(zv); |
571 | 660 | zend_get_gc_buffer_add_ptr(gc_buffer, zv); |
572 | 660 | zend_get_gc_buffer_add_obj(gc_buffer, &wm->std); |
573 | 660 | } else if (tag == ZEND_WEAKREF_TAG_BARE_HT) { |
574 | | /* Bare HashTables are intentionally ignored (see above) */ |
575 | 0 | } |
576 | | |
577 | 900 | zend_get_gc_buffer_use(gc_buffer, table, n); |
578 | | |
579 | 900 | return NULL; |
580 | 900 | } |
581 | | |
582 | | HashTable *zend_weakmap_get_object_entry_gc(zend_object *object, zval **table, int *n) |
583 | 453 | { |
584 | 453 | zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); |
585 | 453 | const zend_ulong obj_key = zend_object_to_weakref_key(object); |
586 | 453 | void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key); |
587 | 453 | #if ZEND_DEBUG |
588 | 453 | ZEND_ASSERT(tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise"); |
589 | 453 | #endif |
590 | 453 | void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr); |
591 | 453 | uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr); |
592 | | |
593 | 453 | if (tag == ZEND_WEAKREF_TAG_HT) { |
594 | 12 | HashTable *ht = ptr; |
595 | 60 | ZEND_HASH_MAP_FOREACH_PTR(ht, tagged_ptr) { |
596 | 60 | if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_MAP) { |
597 | 12 | zend_weakmap *wm = (zend_weakmap*) ZEND_WEAKREF_GET_PTR(tagged_ptr); |
598 | 12 | zval *zv = zend_hash_index_find(&wm->ht, obj_key); |
599 | 12 | ZEND_ASSERT(zv); |
600 | 12 | zend_get_gc_buffer_add_ptr(gc_buffer, zv); |
601 | 12 | } else if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_BARE_HT) { |
602 | | /* Bare HashTables are intentionally ignored |
603 | | * (see zend_weakmap_get_object_key_entry_gc) */ |
604 | 0 | } |
605 | 60 | } ZEND_HASH_FOREACH_END(); |
606 | 441 | } else if (tag == ZEND_WEAKREF_TAG_MAP) { |
607 | 132 | zend_weakmap *wm = (zend_weakmap*) ptr; |
608 | 132 | zval *zv = zend_hash_index_find(&wm->ht, obj_key); |
609 | 132 | ZEND_ASSERT(zv); |
610 | 132 | zend_get_gc_buffer_add_ptr(gc_buffer, zv); |
611 | 309 | } else if (tag == ZEND_WEAKREF_TAG_BARE_HT) { |
612 | | /* Bare HashTables are intentionally ignored |
613 | | * (see zend_weakmap_get_object_key_entry_gc) */ |
614 | 0 | } |
615 | | |
616 | 453 | zend_get_gc_buffer_use(gc_buffer, table, n); |
617 | | |
618 | 453 | return NULL; |
619 | 453 | } |
620 | | |
621 | | static zend_object *zend_weakmap_clone_obj(zend_object *old_object) |
622 | 12 | { |
623 | 12 | zend_object *new_object = zend_weakmap_create_object(zend_ce_weakmap); |
624 | 12 | zend_weakmap *old_wm = zend_weakmap_from(old_object); |
625 | 12 | zend_weakmap *new_wm = zend_weakmap_from(new_object); |
626 | 12 | zend_hash_copy(&new_wm->ht, &old_wm->ht, NULL); |
627 | | |
628 | 12 | zend_ulong obj_key; |
629 | 12 | zval *val; |
630 | 48 | ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&new_wm->ht, obj_key, val) { |
631 | 48 | zend_weakref_register( |
632 | 48 | zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(new_wm, ZEND_WEAKREF_TAG_MAP)); |
633 | 48 | zval_add_ref(val); |
634 | 48 | } ZEND_HASH_FOREACH_END(); |
635 | 12 | return new_object; |
636 | 12 | } |
637 | | |
638 | 588 | static HashPosition *zend_weakmap_iterator_get_pos_ptr(zend_weakmap_iterator *iter) { |
639 | 588 | ZEND_ASSERT(iter->ht_iter != (uint32_t) -1); |
640 | 588 | return &EG(ht_iterators)[iter->ht_iter].pos; |
641 | 588 | } |
642 | | |
643 | | static void zend_weakmap_iterator_dtor(zend_object_iterator *obj_iter) |
644 | 63 | { |
645 | 63 | zend_weakmap_iterator *iter = (zend_weakmap_iterator *) obj_iter; |
646 | 63 | zend_hash_iterator_del(iter->ht_iter); |
647 | 63 | zval_ptr_dtor(&iter->it.data); |
648 | 63 | } |
649 | | |
650 | | static zend_result zend_weakmap_iterator_valid(zend_object_iterator *obj_iter) |
651 | 174 | { |
652 | 174 | zend_weakmap_iterator *iter = (zend_weakmap_iterator *) obj_iter; |
653 | 174 | zend_weakmap *wm = zend_weakmap_fetch(&iter->it.data); |
654 | 174 | HashPosition *pos = zend_weakmap_iterator_get_pos_ptr(iter); |
655 | 174 | return zend_hash_has_more_elements_ex(&wm->ht, pos); |
656 | 174 | } |
657 | | |
658 | | static zval *zend_weakmap_iterator_get_current_data(zend_object_iterator *obj_iter) |
659 | 120 | { |
660 | 120 | zend_weakmap_iterator *iter = (zend_weakmap_iterator *) obj_iter; |
661 | 120 | zend_weakmap *wm = zend_weakmap_fetch(&iter->it.data); |
662 | 120 | HashPosition *pos = zend_weakmap_iterator_get_pos_ptr(iter); |
663 | 120 | return zend_hash_get_current_data_ex(&wm->ht, pos); |
664 | 120 | } |
665 | | |
666 | | static void zend_weakmap_iterator_get_current_key(zend_object_iterator *obj_iter, zval *key) |
667 | 120 | { |
668 | 120 | zend_weakmap_iterator *iter = (zend_weakmap_iterator *) obj_iter; |
669 | 120 | zend_weakmap *wm = zend_weakmap_fetch(&iter->it.data); |
670 | 120 | HashPosition *pos = zend_weakmap_iterator_get_pos_ptr(iter); |
671 | | |
672 | 120 | zend_string *string_key; |
673 | 120 | zend_ulong num_key; |
674 | 120 | zend_hash_key_type key_type = zend_hash_get_current_key_ex(&wm->ht, &string_key, &num_key, pos); |
675 | 120 | if (key_type == HASH_KEY_NON_EXISTENT) { |
676 | 9 | ZVAL_NULL(key); |
677 | 9 | return; |
678 | 9 | } |
679 | 111 | if (key_type != HASH_KEY_IS_LONG) { |
680 | 0 | ZEND_ASSERT(0 && "Must have integer key"); |
681 | 0 | } |
682 | | |
683 | 111 | ZVAL_OBJ_COPY(key, zend_weakref_key_to_object(num_key)); |
684 | 111 | } |
685 | | |
686 | | static void zend_weakmap_iterator_move_forward(zend_object_iterator *obj_iter) |
687 | 111 | { |
688 | 111 | zend_weakmap_iterator *iter = (zend_weakmap_iterator *) obj_iter; |
689 | 111 | zend_weakmap *wm = zend_weakmap_fetch(&iter->it.data); |
690 | 111 | HashPosition *pos = zend_weakmap_iterator_get_pos_ptr(iter); |
691 | 111 | zend_hash_move_forward_ex(&wm->ht, pos); |
692 | 111 | } |
693 | | |
694 | | static void zend_weakmap_iterator_rewind(zend_object_iterator *obj_iter) |
695 | 63 | { |
696 | 63 | zend_weakmap_iterator *iter = (zend_weakmap_iterator *) obj_iter; |
697 | 63 | zend_weakmap *wm = zend_weakmap_fetch(&iter->it.data); |
698 | 63 | HashPosition *pos = zend_weakmap_iterator_get_pos_ptr(iter); |
699 | 63 | zend_hash_internal_pointer_reset_ex(&wm->ht, pos); |
700 | 63 | } |
701 | | |
702 | | static const zend_object_iterator_funcs zend_weakmap_iterator_funcs = { |
703 | | zend_weakmap_iterator_dtor, |
704 | | zend_weakmap_iterator_valid, |
705 | | zend_weakmap_iterator_get_current_data, |
706 | | zend_weakmap_iterator_get_current_key, |
707 | | zend_weakmap_iterator_move_forward, |
708 | | zend_weakmap_iterator_rewind, |
709 | | NULL, |
710 | | NULL, /* get_gc */ |
711 | | }; |
712 | | |
713 | | /* by_ref is int due to Iterator API */ |
714 | | static zend_object_iterator *zend_weakmap_get_iterator( |
715 | | zend_class_entry *ce, zval *object, int by_ref) |
716 | 63 | { |
717 | 63 | zend_weakmap *wm = zend_weakmap_fetch(object); |
718 | 63 | zend_weakmap_iterator *iter = emalloc(sizeof(zend_weakmap_iterator)); |
719 | 63 | zend_iterator_init(&iter->it); |
720 | 63 | iter->it.funcs = &zend_weakmap_iterator_funcs; |
721 | 63 | ZVAL_COPY(&iter->it.data, object); |
722 | 63 | iter->ht_iter = zend_hash_iterator_add(&wm->ht, 0); |
723 | 63 | return &iter->it; |
724 | 63 | } |
725 | | |
726 | | ZEND_METHOD(WeakMap, offsetGet) |
727 | 6 | { |
728 | 6 | zval *key; |
729 | | |
730 | 6 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) == FAILURE) { |
731 | 0 | RETURN_THROWS(); |
732 | 0 | } |
733 | | |
734 | 6 | zval *zv = zend_weakmap_read_dimension(Z_OBJ_P(ZEND_THIS), key, BP_VAR_R, NULL); |
735 | 6 | if (!zv) { |
736 | 0 | RETURN_THROWS(); |
737 | 0 | } |
738 | | |
739 | 6 | RETURN_COPY_DEREF(zv); |
740 | 6 | } |
741 | | |
742 | | ZEND_METHOD(WeakMap, offsetSet) |
743 | 3 | { |
744 | 3 | zval *key, *value; |
745 | | |
746 | 3 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &key, &value) == FAILURE) { |
747 | 0 | RETURN_THROWS(); |
748 | 0 | } |
749 | | |
750 | 3 | zend_weakmap_write_dimension(Z_OBJ_P(ZEND_THIS), key, value); |
751 | 3 | } |
752 | | |
753 | | ZEND_METHOD(WeakMap, offsetExists) |
754 | 3 | { |
755 | 3 | zval *key; |
756 | | |
757 | 3 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) == FAILURE) { |
758 | 0 | RETURN_THROWS(); |
759 | 0 | } |
760 | | |
761 | 3 | RETURN_BOOL(zend_weakmap_has_dimension(Z_OBJ_P(ZEND_THIS), key, /* check_empty */ 0)); |
762 | 3 | } |
763 | | |
764 | | ZEND_METHOD(WeakMap, offsetUnset) |
765 | 3 | { |
766 | 3 | zval *key; |
767 | | |
768 | 3 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) == FAILURE) { |
769 | 0 | RETURN_THROWS(); |
770 | 0 | } |
771 | | |
772 | 3 | zend_weakmap_unset_dimension(Z_OBJ_P(ZEND_THIS), key); |
773 | 3 | } |
774 | | |
775 | | ZEND_METHOD(WeakMap, count) |
776 | 6 | { |
777 | 6 | ZEND_PARSE_PARAMETERS_NONE(); |
778 | | |
779 | 6 | zend_long count; |
780 | 6 | zend_weakmap_count_elements(Z_OBJ_P(ZEND_THIS), &count); |
781 | 6 | RETURN_LONG(count); |
782 | 6 | } |
783 | | |
784 | | ZEND_METHOD(WeakMap, getIterator) |
785 | 9 | { |
786 | 9 | ZEND_PARSE_PARAMETERS_NONE(); |
787 | | |
788 | 9 | zend_create_internal_iterator_zval(return_value, ZEND_THIS); |
789 | 9 | } |
790 | | |
791 | | void zend_register_weakref_ce(void) /* {{{ */ |
792 | 2 | { |
793 | 2 | zend_ce_weakref = register_class_WeakReference(); |
794 | | |
795 | 2 | zend_ce_weakref->create_object = zend_weakref_new; |
796 | 2 | zend_ce_weakref->default_object_handlers = &zend_weakref_handlers; |
797 | | |
798 | 2 | memcpy(&zend_weakref_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); |
799 | 2 | zend_weakref_handlers.offset = offsetof(zend_weakref, std); |
800 | | |
801 | 2 | zend_weakref_handlers.free_obj = zend_weakref_free; |
802 | 2 | zend_weakref_handlers.get_debug_info = zend_weakref_get_debug_info; |
803 | 2 | zend_weakref_handlers.clone_obj = NULL; |
804 | | |
805 | 2 | zend_ce_weakmap = register_class_WeakMap(zend_ce_arrayaccess, zend_ce_countable, zend_ce_aggregate); |
806 | | |
807 | 2 | zend_ce_weakmap->create_object = zend_weakmap_create_object; |
808 | 2 | zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator; |
809 | 2 | zend_ce_weakmap->default_object_handlers = &zend_weakmap_handlers; |
810 | | |
811 | 2 | memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); |
812 | | zend_weakmap_handlers.offset = offsetof(zend_weakmap, std); |
813 | 2 | zend_weakmap_handlers.free_obj = zend_weakmap_free_obj; |
814 | 2 | zend_weakmap_handlers.read_dimension = zend_weakmap_read_dimension; |
815 | 2 | zend_weakmap_handlers.write_dimension = zend_weakmap_write_dimension; |
816 | 2 | zend_weakmap_handlers.has_dimension = zend_weakmap_has_dimension; |
817 | 2 | zend_weakmap_handlers.unset_dimension = zend_weakmap_unset_dimension; |
818 | 2 | zend_weakmap_handlers.count_elements = zend_weakmap_count_elements; |
819 | 2 | zend_weakmap_handlers.get_properties_for = zend_weakmap_get_properties_for; |
820 | 2 | zend_weakmap_handlers.get_gc = zend_weakmap_get_gc; |
821 | 2 | zend_weakmap_handlers.clone_obj = zend_weakmap_clone_obj; |
822 | 2 | } |
823 | | /* }}} */ |
824 | | |