Add ZEND_API for weakmap functionality via zend_weakrefs_hash_add/del

Closes GH-7600.
This commit is contained in:
Bob Weinand 2021-10-21 13:34:22 +02:00
parent decf906a90
commit 471102edcd
6 changed files with 151 additions and 6 deletions

View file

@ -63,8 +63,7 @@ static inline void zend_weakref_unref_single(
wr->referent = NULL;
} else {
ZEND_ASSERT(tag == ZEND_WEAKREF_TAG_MAP);
zend_weakmap *wm = ptr;
zend_hash_index_del(&wm->ht, obj_addr);
zend_hash_index_del((HashTable *) ptr, obj_addr);
}
}
@ -144,6 +143,23 @@ static void zend_weakref_unregister(zend_object *object, void *payload) {
ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), obj_addr);
}
ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData) {
zval *zv = zend_hash_index_add(ht, (zend_ulong) key, pData);
if (zv) {
zend_weakref_register(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP));
}
return zv;
}
ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) {
zval *zv = zend_hash_index_find(ht, (zend_ulong) key);
if (zv) {
zend_weakref_unregister(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP));
return SUCCESS;
}
return FAILURE;
}
void zend_weakrefs_init(void) {
zend_hash_init(&EG(weakrefs), 8, NULL, NULL, 0);
}
@ -281,7 +297,7 @@ static void zend_weakmap_free_obj(zend_object *object)
zend_ulong obj_addr;
ZEND_HASH_FOREACH_NUM_KEY(&wm->ht, obj_addr) {
zend_weakref_unregister(
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(wm, ZEND_WEAKREF_TAG_MAP));
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&wm->ht);
zend_object_std_dtor(&wm->std);
@ -340,7 +356,7 @@ static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval
return;
}
zend_weakref_register(obj_key, ZEND_WEAKREF_ENCODE(wm, ZEND_WEAKREF_TAG_MAP));
zend_weakref_register(obj_key, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
zend_hash_index_add_new(&wm->ht, (zend_ulong) obj_key, value);
}
@ -378,7 +394,7 @@ static void zend_weakmap_unset_dimension(zend_object *object, zval *offset)
return;
}
zend_weakref_unregister(obj_key, ZEND_WEAKREF_ENCODE(wm, ZEND_WEAKREF_TAG_MAP));
zend_weakref_unregister(obj_key, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
}
static int zend_weakmap_count_elements(zend_object *object, zend_long *count)

View file

@ -28,6 +28,18 @@ void zend_weakrefs_shutdown(void);
ZEND_API void zend_weakrefs_notify(zend_object *object);
ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData);
ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key);
static zend_always_inline void *zend_weakrefs_hash_add_ptr(HashTable *ht, zend_object *key, void *ptr) {
zval tmp, *zv;
ZVAL_PTR(&tmp, ptr);
if ((zv = zend_weakrefs_hash_add(ht, key, &tmp))) {
return Z_PTR_P(zv);
} else {
return NULL;
}
}
END_EXTERN_C()
#endif

View file

@ -27,6 +27,7 @@
#include "zend_attributes.h"
#include "zend_observer.h"
#include "zend_smart_str.h"
#include "zend_weakrefs.h"
ZEND_BEGIN_MODULE_GLOBALS(zend_test)
int observer_enabled;
@ -41,6 +42,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
int observer_show_opcode;
int observer_nesting_depth;
int replace_zend_execute_ex;
HashTable global_weakmap;
ZEND_END_MODULE_GLOBALS(zend_test)
ZEND_DECLARE_MODULE_GLOBALS(zend_test)
@ -225,6 +227,40 @@ static ZEND_FUNCTION(zend_string_or_stdclass_or_null)
}
/* }}} */
static ZEND_FUNCTION(zend_weakmap_attach)
{
zval *value;
zend_object *obj;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_OBJ(obj)
Z_PARAM_ZVAL(value)
ZEND_PARSE_PARAMETERS_END();
if (zend_weakrefs_hash_add(&ZT_G(global_weakmap), obj, value)) {
Z_TRY_ADDREF_P(value);
RETURN_TRUE;
}
RETURN_FALSE;
}
static ZEND_FUNCTION(zend_weakmap_remove)
{
zend_object *obj;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJ(obj)
ZEND_PARSE_PARAMETERS_END();
RETURN_BOOL(zend_weakrefs_hash_del(&ZT_G(global_weakmap), obj) == SUCCESS);
}
static ZEND_FUNCTION(zend_weakmap_dump)
{
ZEND_PARSE_PARAMETERS_NONE();
RETURN_ARR(zend_array_dup(&ZT_G(global_weakmap)));
}
/* TESTS Z_PARAM_ITERABLE and Z_PARAM_ITERABLE_OR_NULL */
static ZEND_FUNCTION(zend_iterable)
{
@ -622,11 +658,17 @@ static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execu
PHP_RINIT_FUNCTION(zend_test)
{
zend_hash_init(&ZT_G(global_weakmap), 8, NULL, ZVAL_PTR_DTOR, 0);
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(zend_test)
{
zend_ulong objptr;
ZEND_HASH_FOREACH_NUM_KEY(&ZT_G(global_weakmap), objptr) {
zend_weakrefs_hash_del(&ZT_G(global_weakmap), (zend_object *)(uintptr_t)objptr);
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&ZT_G(global_weakmap));
return SUCCESS;
}

View file

@ -59,6 +59,10 @@ function zend_string_or_stdclass_or_null($param): stdClass|string|null {}
function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {}
function zend_weakmap_attach(object $object, mixed $value): bool {}
function zend_weakmap_remove(object $object): bool {}
function zend_weakmap_dump(): array {}
}
namespace ZendTestNS {

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 3240b7fa3461b40a211371250c4975802f44185b */
* Stub hash: 49b9abbc5ce826e749861fd511e98f1add001368 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
@ -51,6 +51,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_iterable, 0, 1, IS_VOID, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_ITERABLE, 1, "null")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_weakmap_attach, 0, 2, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0)
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_weakmap_remove, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0)
ZEND_END_ARG_INFO()
#define arginfo_zend_weakmap_dump arginfo_zend_test_array_return
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0)
ZEND_END_ARG_INFO()
@ -83,6 +94,9 @@ static ZEND_FUNCTION(zend_string_or_object_or_null);
static ZEND_FUNCTION(zend_string_or_stdclass);
static ZEND_FUNCTION(zend_string_or_stdclass_or_null);
static ZEND_FUNCTION(zend_iterable);
static ZEND_FUNCTION(zend_weakmap_attach);
static ZEND_FUNCTION(zend_weakmap_remove);
static ZEND_FUNCTION(zend_weakmap_dump);
static ZEND_FUNCTION(namespaced_func);
static ZEND_METHOD(_ZendTestClass, is_object);
static ZEND_METHOD(_ZendTestClass, __toString);
@ -106,6 +120,9 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(zend_string_or_stdclass, arginfo_zend_string_or_stdclass)
ZEND_FE(zend_string_or_stdclass_or_null, arginfo_zend_string_or_stdclass_or_null)
ZEND_FE(zend_iterable, arginfo_zend_iterable)
ZEND_FE(zend_weakmap_attach, arginfo_zend_weakmap_attach)
ZEND_FE(zend_weakmap_remove, arginfo_zend_weakmap_remove)
ZEND_FE(zend_weakmap_dump, arginfo_zend_weakmap_dump)
ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func)
ZEND_FE_END
};

View file

@ -0,0 +1,54 @@
--TEST--
Test internal weakmap API
--SKIPIF--
<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?>
--FILE--
<?php
$id1 = new \stdClass;
$id2 = new \stdClass;
var_dump(zend_weakmap_attach($id1, 1));
var_dump(zend_weakmap_attach($id1, 3));
var_dump(zend_weakmap_attach($id2, 2));
var_dump(zend_weakmap_dump());
unset($id1);
var_dump(zend_weakmap_dump());
var_dump(zend_weakmap_remove($id2));
var_dump(zend_weakmap_remove($id2));
var_dump(zend_weakmap_dump());
var_dump(zend_weakmap_attach($id2, $id2));
var_dump(zend_weakmap_dump());
?>
--EXPECTF--
bool(true)
bool(false)
bool(true)
array(2) {
[%d]=>
int(1)
[%d]=>
int(2)
}
array(1) {
[%d]=>
int(2)
}
bool(true)
bool(false)
array(0) {
}
bool(true)
array(1) {
[%d]=>
object(stdClass)#2 (0) {
}
}