Skip to content

Add ZEND_API for weakmap functionality via zend_weakrefs_hash_add/del #7600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions Zend/zend_weakrefs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zend_weakref_unregister already does the hash del, so it might make sense to replace your hash_index_del here with hash_index_exists.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. And yeah will provide a small test for it.

return SUCCESS;
}
return FAILURE;
}

void zend_weakrefs_init(void) {
zend_hash_init(&EG(weakrefs), 8, NULL, NULL, 0);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions Zend/zend_weakrefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -622,11 +658,13 @@ 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_hash_destroy(&ZT_G(global_weakmap));
return SUCCESS;
}

Expand Down
4 changes: 4 additions & 0 deletions ext/zend_test/test.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
19 changes: 18 additions & 1 deletion ext/zend_test/test_arginfo.h
Original file line number Diff line number Diff line change
@@ -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()
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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);
Expand All @@ -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
};
Expand Down
36 changes: 36 additions & 0 deletions ext/zend_test/tests/zend_weakmap.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
Test internal weakmap API
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a SKIPIF (8.0) or EXTENSIONS (8.1) section.

--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());

var_dump(zend_weakmap_remove($id2));
var_dump(zend_weakmap_remove($id2));

var_dump(zend_weakmap_dump());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to unset($id1) and dump again here, to shows the actual weak part :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

facepalm :-D


?>
--EXPECTF--
bool(true)
bool(false)
bool(true)
array(2) {
[%d]=>
int(1)
[%d]=>
int(2)
}
bool(true)
bool(false)
array(1) {
[%d]=>
int(1)
}