Skip to content

Commit 471102e

Browse files
committed
Add ZEND_API for weakmap functionality via zend_weakrefs_hash_add/del
Closes GH-7600.
1 parent decf906 commit 471102e

File tree

6 files changed

+151
-6
lines changed

6 files changed

+151
-6
lines changed

Zend/zend_weakrefs.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ static inline void zend_weakref_unref_single(
6363
wr->referent = NULL;
6464
} else {
6565
ZEND_ASSERT(tag == ZEND_WEAKREF_TAG_MAP);
66-
zend_weakmap *wm = ptr;
67-
zend_hash_index_del(&wm->ht, obj_addr);
66+
zend_hash_index_del((HashTable *) ptr, obj_addr);
6867
}
6968
}
7069

@@ -144,6 +143,23 @@ static void zend_weakref_unregister(zend_object *object, void *payload) {
144143
ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), obj_addr);
145144
}
146145

146+
ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData) {
147+
zval *zv = zend_hash_index_add(ht, (zend_ulong) key, pData);
148+
if (zv) {
149+
zend_weakref_register(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP));
150+
}
151+
return zv;
152+
}
153+
154+
ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) {
155+
zval *zv = zend_hash_index_find(ht, (zend_ulong) key);
156+
if (zv) {
157+
zend_weakref_unregister(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP));
158+
return SUCCESS;
159+
}
160+
return FAILURE;
161+
}
162+
147163
void zend_weakrefs_init(void) {
148164
zend_hash_init(&EG(weakrefs), 8, NULL, NULL, 0);
149165
}
@@ -281,7 +297,7 @@ static void zend_weakmap_free_obj(zend_object *object)
281297
zend_ulong obj_addr;
282298
ZEND_HASH_FOREACH_NUM_KEY(&wm->ht, obj_addr) {
283299
zend_weakref_unregister(
284-
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(wm, ZEND_WEAKREF_TAG_MAP));
300+
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
285301
} ZEND_HASH_FOREACH_END();
286302
zend_hash_destroy(&wm->ht);
287303
zend_object_std_dtor(&wm->std);
@@ -340,7 +356,7 @@ static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval
340356
return;
341357
}
342358

343-
zend_weakref_register(obj_key, ZEND_WEAKREF_ENCODE(wm, ZEND_WEAKREF_TAG_MAP));
359+
zend_weakref_register(obj_key, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
344360
zend_hash_index_add_new(&wm->ht, (zend_ulong) obj_key, value);
345361
}
346362

@@ -378,7 +394,7 @@ static void zend_weakmap_unset_dimension(zend_object *object, zval *offset)
378394
return;
379395
}
380396

381-
zend_weakref_unregister(obj_key, ZEND_WEAKREF_ENCODE(wm, ZEND_WEAKREF_TAG_MAP));
397+
zend_weakref_unregister(obj_key, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
382398
}
383399

384400
static int zend_weakmap_count_elements(zend_object *object, zend_long *count)

Zend/zend_weakrefs.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ void zend_weakrefs_shutdown(void);
2828

2929
ZEND_API void zend_weakrefs_notify(zend_object *object);
3030

31+
ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData);
32+
ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key);
33+
static zend_always_inline void *zend_weakrefs_hash_add_ptr(HashTable *ht, zend_object *key, void *ptr) {
34+
zval tmp, *zv;
35+
ZVAL_PTR(&tmp, ptr);
36+
if ((zv = zend_weakrefs_hash_add(ht, key, &tmp))) {
37+
return Z_PTR_P(zv);
38+
} else {
39+
return NULL;
40+
}
41+
}
42+
3143
END_EXTERN_C()
3244

3345
#endif

ext/zend_test/test.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "zend_attributes.h"
2828
#include "zend_observer.h"
2929
#include "zend_smart_str.h"
30+
#include "zend_weakrefs.h"
3031

3132
ZEND_BEGIN_MODULE_GLOBALS(zend_test)
3233
int observer_enabled;
@@ -41,6 +42,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
4142
int observer_show_opcode;
4243
int observer_nesting_depth;
4344
int replace_zend_execute_ex;
45+
HashTable global_weakmap;
4446
ZEND_END_MODULE_GLOBALS(zend_test)
4547

4648
ZEND_DECLARE_MODULE_GLOBALS(zend_test)
@@ -225,6 +227,40 @@ static ZEND_FUNCTION(zend_string_or_stdclass_or_null)
225227
}
226228
/* }}} */
227229

230+
static ZEND_FUNCTION(zend_weakmap_attach)
231+
{
232+
zval *value;
233+
zend_object *obj;
234+
235+
ZEND_PARSE_PARAMETERS_START(2, 2)
236+
Z_PARAM_OBJ(obj)
237+
Z_PARAM_ZVAL(value)
238+
ZEND_PARSE_PARAMETERS_END();
239+
240+
if (zend_weakrefs_hash_add(&ZT_G(global_weakmap), obj, value)) {
241+
Z_TRY_ADDREF_P(value);
242+
RETURN_TRUE;
243+
}
244+
RETURN_FALSE;
245+
}
246+
247+
static ZEND_FUNCTION(zend_weakmap_remove)
248+
{
249+
zend_object *obj;
250+
251+
ZEND_PARSE_PARAMETERS_START(1, 1)
252+
Z_PARAM_OBJ(obj)
253+
ZEND_PARSE_PARAMETERS_END();
254+
255+
RETURN_BOOL(zend_weakrefs_hash_del(&ZT_G(global_weakmap), obj) == SUCCESS);
256+
}
257+
258+
static ZEND_FUNCTION(zend_weakmap_dump)
259+
{
260+
ZEND_PARSE_PARAMETERS_NONE();
261+
RETURN_ARR(zend_array_dup(&ZT_G(global_weakmap)));
262+
}
263+
228264
/* TESTS Z_PARAM_ITERABLE and Z_PARAM_ITERABLE_OR_NULL */
229265
static ZEND_FUNCTION(zend_iterable)
230266
{
@@ -622,11 +658,17 @@ static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execu
622658

623659
PHP_RINIT_FUNCTION(zend_test)
624660
{
661+
zend_hash_init(&ZT_G(global_weakmap), 8, NULL, ZVAL_PTR_DTOR, 0);
625662
return SUCCESS;
626663
}
627664

628665
PHP_RSHUTDOWN_FUNCTION(zend_test)
629666
{
667+
zend_ulong objptr;
668+
ZEND_HASH_FOREACH_NUM_KEY(&ZT_G(global_weakmap), objptr) {
669+
zend_weakrefs_hash_del(&ZT_G(global_weakmap), (zend_object *)(uintptr_t)objptr);
670+
} ZEND_HASH_FOREACH_END();
671+
zend_hash_destroy(&ZT_G(global_weakmap));
630672
return SUCCESS;
631673
}
632674

ext/zend_test/test.stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ function zend_string_or_stdclass_or_null($param): stdClass|string|null {}
5959

6060
function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {}
6161

62+
function zend_weakmap_attach(object $object, mixed $value): bool {}
63+
function zend_weakmap_remove(object $object): bool {}
64+
function zend_weakmap_dump(): array {}
65+
6266
}
6367

6468
namespace ZendTestNS {

ext/zend_test/test_arginfo.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 3240b7fa3461b40a211371250c4975802f44185b */
2+
* Stub hash: 49b9abbc5ce826e749861fd511e98f1add001368 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
55
ZEND_END_ARG_INFO()
@@ -51,6 +51,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_iterable, 0, 1, IS_VOID, 0)
5151
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_ITERABLE, 1, "null")
5252
ZEND_END_ARG_INFO()
5353

54+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_weakmap_attach, 0, 2, _IS_BOOL, 0)
55+
ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0)
56+
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
57+
ZEND_END_ARG_INFO()
58+
59+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_weakmap_remove, 0, 1, _IS_BOOL, 0)
60+
ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0)
61+
ZEND_END_ARG_INFO()
62+
63+
#define arginfo_zend_weakmap_dump arginfo_zend_test_array_return
64+
5465
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0)
5566
ZEND_END_ARG_INFO()
5667

@@ -83,6 +94,9 @@ static ZEND_FUNCTION(zend_string_or_object_or_null);
8394
static ZEND_FUNCTION(zend_string_or_stdclass);
8495
static ZEND_FUNCTION(zend_string_or_stdclass_or_null);
8596
static ZEND_FUNCTION(zend_iterable);
97+
static ZEND_FUNCTION(zend_weakmap_attach);
98+
static ZEND_FUNCTION(zend_weakmap_remove);
99+
static ZEND_FUNCTION(zend_weakmap_dump);
86100
static ZEND_FUNCTION(namespaced_func);
87101
static ZEND_METHOD(_ZendTestClass, is_object);
88102
static ZEND_METHOD(_ZendTestClass, __toString);
@@ -106,6 +120,9 @@ static const zend_function_entry ext_functions[] = {
106120
ZEND_FE(zend_string_or_stdclass, arginfo_zend_string_or_stdclass)
107121
ZEND_FE(zend_string_or_stdclass_or_null, arginfo_zend_string_or_stdclass_or_null)
108122
ZEND_FE(zend_iterable, arginfo_zend_iterable)
123+
ZEND_FE(zend_weakmap_attach, arginfo_zend_weakmap_attach)
124+
ZEND_FE(zend_weakmap_remove, arginfo_zend_weakmap_remove)
125+
ZEND_FE(zend_weakmap_dump, arginfo_zend_weakmap_dump)
109126
ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func)
110127
ZEND_FE_END
111128
};

ext/zend_test/tests/zend_weakmap.phpt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
Test internal weakmap API
3+
--SKIPIF--
4+
<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?>
5+
--FILE--
6+
<?php
7+
8+
$id1 = new \stdClass;
9+
$id2 = new \stdClass;
10+
11+
var_dump(zend_weakmap_attach($id1, 1));
12+
var_dump(zend_weakmap_attach($id1, 3));
13+
var_dump(zend_weakmap_attach($id2, 2));
14+
15+
var_dump(zend_weakmap_dump());
16+
17+
unset($id1);
18+
19+
var_dump(zend_weakmap_dump());
20+
21+
var_dump(zend_weakmap_remove($id2));
22+
var_dump(zend_weakmap_remove($id2));
23+
24+
var_dump(zend_weakmap_dump());
25+
26+
var_dump(zend_weakmap_attach($id2, $id2));
27+
28+
var_dump(zend_weakmap_dump());
29+
30+
?>
31+
--EXPECTF--
32+
bool(true)
33+
bool(false)
34+
bool(true)
35+
array(2) {
36+
[%d]=>
37+
int(1)
38+
[%d]=>
39+
int(2)
40+
}
41+
array(1) {
42+
[%d]=>
43+
int(2)
44+
}
45+
bool(true)
46+
bool(false)
47+
array(0) {
48+
}
49+
bool(true)
50+
array(1) {
51+
[%d]=>
52+
object(stdClass)#2 (0) {
53+
}
54+
}

0 commit comments

Comments
 (0)