Remove WeakMap entries whose key is only reachable through the entry value (#10932)

This commit is contained in:
Arnaud Le Blanc 2023-07-16 13:39:08 +02:00 committed by GitHub
parent d0731934b7
commit cbf67e4fee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 981 additions and 8 deletions

View File

@ -0,0 +1,41 @@
--TEST--
Self-referencing map entry GC - 001
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
$map = new WeakMap();
$obj = new Value('a');
$map[$obj] = $obj;
gc_collect_cycles();
var_dump($map);
$obj = null;
gc_collect_cycles();
var_dump($map);
--EXPECTF--
object(WeakMap)#%d (1) {
[0]=>
array(2) {
["key"]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
["value"]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
}
}
object(WeakMap)#%d (0) {
}

View File

@ -0,0 +1,44 @@
--TEST--
Self-referencing map entry GC - 002
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
$map = new WeakMap();
$obj = new Value('a');
$map[$obj] = [$obj];
gc_collect_cycles();
var_dump($map);
$obj = null;
gc_collect_cycles();
var_dump($map);
--EXPECTF--
object(WeakMap)#%d (1) {
[0]=>
array(2) {
["key"]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
["value"]=>
array(1) {
[0]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
}
}
}
object(WeakMap)#1 (0) {
}

View File

@ -0,0 +1,50 @@
--TEST--
Self-referencing map entry GC - 003
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
$map = new WeakMap();
$obj = new Value('a');
$map[$obj] = [$obj, $map];
$ref = WeakReference::create($map);
gc_collect_cycles();
var_dump($ref->get());
gc_collect_cycles();
// $obj is first in the root buffer
$obj = null;
$map = null;
gc_collect_cycles();
var_dump($ref->get());
--EXPECTF--
object(WeakMap)#%d (1) {
[0]=>
array(2) {
["key"]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
["value"]=>
array(2) {
[0]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
[1]=>
*RECURSION*
}
}
}
NULL

View File

@ -0,0 +1,50 @@
--TEST--
Self-referencing map entry GC - 004
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
$map = new WeakMap();
$obj = new Value('a');
$map[$obj] = [$map, $obj];
$ref = WeakReference::create($map);
gc_collect_cycles();
var_dump($ref->get());
gc_collect_cycles();
// $map is first in the root buffer
$map = null;
$obj = null;
gc_collect_cycles();
var_dump($ref->get());
--EXPECTF--
object(WeakMap)#%d (1) {
[0]=>
array(2) {
["key"]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
["value"]=>
array(2) {
[0]=>
*RECURSION*
[1]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
}
}
}
NULL

View File

@ -0,0 +1,49 @@
--TEST--
Self-referencing map entry GC - 005
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
$map = new WeakMap();
$obj = new Value('a');
$value = [$obj];
$map[$obj] = $value;
$obj = null;
gc_collect_cycles();
var_dump($map);
gc_collect_cycles();
$value = null;
gc_collect_cycles();
var_dump($map);
--EXPECTF--
object(WeakMap)#%d (1) {
[0]=>
array(2) {
["key"]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
["value"]=>
array(1) {
[0]=>
object(Value)#%d (1) {
["value"]=>
string(1) "a"
}
}
}
}
object(WeakMap)#1 (0) {
}

View File

@ -0,0 +1,41 @@
--TEST--
Self-referencing map entry GC - 006
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
$map = new WeakMap();
$obj = new Value('a');
$map[$obj] = $obj;
gc_collect_cycles();
$obj2 = $obj;
$obj = null;
$map2 = $map;
$map = null;
gc_collect_cycles();
var_dump($map2);
--EXPECT--
object(WeakMap)#1 (1) {
[0]=>
array(2) {
["key"]=>
object(Value)#2 (1) {
["value"]=>
string(1) "a"
}
["value"]=>
object(Value)#2 (1) {
["value"]=>
string(1) "a"
}
}
}

View File

@ -0,0 +1,33 @@
--TEST--
Self-referencing map entry GC - 007
--FILE--
<?php
class Canary extends stdClass
{
public function __construct(public string $name)
{
}
function __destruct()
{
echo $this->name."\n";
}
}
$container = new Canary('container');
$canary = new Canary('canary');
$container->canary = $canary;
$map = new \WeakMap();
$map[$canary] = $container;
echo 1;
unset($container, $canary);
gc_collect_cycles();
echo 2;
--EXPECT--
1container
canary
2

View File

@ -0,0 +1,30 @@
--TEST--
Self-referencing map entry GC - 008
--FILE--
<?php
class Canary extends stdClass
{
public function __construct(public string $name)
{
}
function __destruct()
{
echo $this->name."\n";
}
}
$canary = new Canary('canary');
$map = new \WeakMap();
$map[$canary] = $canary;
echo 1;
unset($canary);
gc_collect_cycles();
echo 2;
--EXPECT--
1canary
2

View File

@ -0,0 +1,28 @@
--TEST--
Self-referencing map entry GC - 009
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
function possibleRoot($var) {
}
$map = new WeakMap();
$obj = new stdClass();
$map[$obj] = new Value('a');
$map[$map] = $map;
$ref = WeakReference::create($map);
possibleRoot($obj);
$map = null;
gc_collect_cycles();
var_dump($ref->get());
?>
--EXPECT--
NULL

View File

@ -0,0 +1,32 @@
--TEST--
Self-referencing map entry GC - 010
--FILE--
<?php
class Value {
function __construct() {
}
}
function possibleRoot($value) {
}
$map = new WeakMap();
$obj = new stdClass();
possibleRoot($obj);
$obj2 = new Value();
$map[$obj2] = [$obj2, $map];
$obj = null;
$obj3 = new class {};
$map[$obj3] = $obj3;
unset($obj3);
gc_collect_cycles();
?>
==DONE==
--EXPECT--
==DONE==

View File

@ -0,0 +1,24 @@
--TEST--
Self-referencing map entry GC - 011
--FILE--
<?php
class K {}
class V { public $k; }
$m = new WeakMap();
$k = new K;
$v = new V;
$v->k = $k;
$m[$k] = $v;
$m2 = $m;
unset($m2, $k, $v);
gc_collect_cycles();
var_dump($m);
--EXPECT--
object(WeakMap)#1 (0) {
}

View File

@ -0,0 +1,33 @@
--TEST--
Self-referencing map entry GC - 012
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
class Value2 {
public function __construct(public readonly object $value) {
}
}
function possibleRoot($var) {
}
$map = new WeakMap();
$obj = new stdClass();
$map[$obj] = new Value('a');
$map[$map] = new Value2($map);
$ref = WeakReference::create($map);
possibleRoot($obj);
$map = null;
gc_collect_cycles();
var_dump($ref->get());
?>
--EXPECT--
NULL

View File

@ -0,0 +1,41 @@
--TEST--
Self-referencing map entry GC - 014
--FILE--
<?php
class Value {
public function __construct(public readonly string $value) {
}
}
$map = new WeakMap();
$obj = new Value('a');
$map[$obj] = $obj;
gc_collect_cycles();
$obj2 = $obj;
$obj = null;
$map2 = $map;
$map = null;
gc_collect_cycles();
var_dump($map2);
?>
--EXPECT--
object(WeakMap)#1 (1) {
[0]=>
array(2) {
["key"]=>
object(Value)#2 (1) {
["value"]=>
string(1) "a"
}
["value"]=>
object(Value)#2 (1) {
["value"]=>
string(1) "a"
}
}
}

View File

@ -0,0 +1,24 @@
--TEST--
Self-referencing map entry GC - 015
--FILE--
<?php
class Value {
public function __construct() {
}
}
$map = new WeakMap();
$obj = new Value();
$map[$obj] = [$obj, $map];
$ref = WeakReference::create($map);
$map = null;
gc_collect_cycles();
var_dump($ref->get());
?>
--EXPECT--
NULL

View File

@ -0,0 +1,45 @@
--TEST--
Self-referencing map entry GC - 016
--FILE--
<?php
class K1 { function __construct() {} }
class K2 {}
$map = new WeakMap();
$k1 = new K1();
$map[$k1] = [$k1, $map];
$k2 = new K2();
$map[$k2] = $k2;
gc_collect_cycles();
var_dump($map);
?>
--EXPECT--
object(WeakMap)#1 (2) {
[0]=>
array(2) {
["key"]=>
object(K1)#2 (0) {
}
["value"]=>
array(2) {
[0]=>
object(K1)#2 (0) {
}
[1]=>
*RECURSION*
}
}
[1]=>
array(2) {
["key"]=>
object(K2)#3 (0) {
}
["value"]=>
object(K2)#3 (0) {
}
}
}

View File

@ -70,6 +70,11 @@
#include "zend_API.h"
#include "zend_fibers.h"
#include "zend_hrtime.h"
#include "zend_weakrefs.h"
#ifndef GC_BENCH
# define GC_BENCH 0
#endif
#ifndef ZEND_GC_DEBUG
# define ZEND_GC_DEBUG 0
@ -183,6 +188,40 @@
/* GC flags */
#define GC_HAS_DESTRUCTORS (1<<0)
/* Weak maps */
#define Z_FROM_WEAKMAP_KEY (1<<0)
#define Z_FROM_WEAKMAP (1<<1)
/* The WeakMap entry zv is reachable from roots by following the virtual
* reference from the a WeakMap key to the entry */
#define GC_FROM_WEAKMAP_KEY(zv) \
(Z_TYPE_INFO_P((zv)) & (Z_FROM_WEAKMAP_KEY << Z_TYPE_INFO_EXTRA_SHIFT))
#define GC_SET_FROM_WEAKMAP_KEY(zv) do { \
zval *_z = (zv); \
Z_TYPE_INFO_P(_z) = Z_TYPE_INFO_P(_z) | (Z_FROM_WEAKMAP_KEY << Z_TYPE_INFO_EXTRA_SHIFT); \
} while (0)
#define GC_UNSET_FROM_WEAKMAP_KEY(zv) do { \
zval *_z = (zv); \
Z_TYPE_INFO_P(_z) = Z_TYPE_INFO_P(_z) & ~(Z_FROM_WEAKMAP_KEY << Z_TYPE_INFO_EXTRA_SHIFT); \
} while (0)
/* The WeakMap entry zv is reachable from roots by following the reference from
* the WeakMap */
#define GC_FROM_WEAKMAP(zv) \
(Z_TYPE_INFO_P((zv)) & (Z_FROM_WEAKMAP << Z_TYPE_INFO_EXTRA_SHIFT))
#define GC_SET_FROM_WEAKMAP(zv) do { \
zval *_z = (zv); \
Z_TYPE_INFO_P(_z) = Z_TYPE_INFO_P(_z) | (Z_FROM_WEAKMAP << Z_TYPE_INFO_EXTRA_SHIFT); \
} while (0)
#define GC_UNSET_FROM_WEAKMAP(zv) do { \
zval *_z = (zv); \
Z_TYPE_INFO_P(_z) = Z_TYPE_INFO_P(_z) & ~(Z_FROM_WEAKMAP << Z_TYPE_INFO_EXTRA_SHIFT); \
} while (0)
/* unused buffers */
#define GC_HAS_UNUSED() \
(GC_G(unused) != GC_INVALID)
@ -672,6 +711,39 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
GC_BENCH_PEAK(root_buf_peak, root_buf_length);
}
static void ZEND_FASTCALL gc_extra_root(zend_refcounted *ref)
{
uint32_t idx;
gc_root_buffer *newRoot;
if (EXPECTED(GC_HAS_UNUSED())) {
idx = GC_FETCH_UNUSED();
} else if (EXPECTED(GC_HAS_NEXT_UNUSED_UNDER_THRESHOLD())) {
idx = GC_FETCH_NEXT_UNUSED();
} else {
gc_grow_root_buffer();
if (UNEXPECTED(!GC_HAS_NEXT_UNUSED())) {
/* TODO: can this really happen? */
return;
}
idx = GC_FETCH_NEXT_UNUSED();
}
ZEND_ASSERT(GC_TYPE(ref) == IS_ARRAY || GC_TYPE(ref) == IS_OBJECT);
ZEND_ASSERT(GC_REF_ADDRESS(ref) == 0);
newRoot = GC_IDX2PTR(idx);
newRoot->ref = ref; /* GC_ROOT tag is 0 */
idx = gc_compress(idx);
GC_REF_SET_INFO(ref, idx | GC_REF_COLOR(ref));
GC_G(num_roots)++;
GC_BENCH_INC(zval_buffered);
GC_BENCH_INC(root_buf_length);
GC_BENCH_PEAK(root_buf_peak, root_buf_length);
}
static zend_never_inline void ZEND_FASTCALL gc_remove_compressed(zend_refcounted *ref, uint32_t idx)
{
gc_root_buffer *root = gc_decompress(ref, idx);
@ -717,6 +789,82 @@ tail_call:
zval *table;
int len;
if (UNEXPECTED(GC_FLAGS(obj) & IS_OBJ_WEAKLY_REFERENCED)) {
zend_weakmap_get_object_key_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n-=2) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
zval *weakmap = zv+1;
ZEND_ASSERT(Z_REFCOUNTED_P(weakmap));
if (Z_OPT_REFCOUNTED_P(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
if (GC_REF_CHECK_COLOR(Z_COUNTED_P(weakmap), GC_GREY)) {
/* Weakmap was scanned in gc_mark_roots, we must
* ensure that it's eventually scanned in
* gc_scan_roots as well. */
if (!GC_REF_ADDRESS(Z_COUNTED_P(weakmap))) {
gc_extra_root(Z_COUNTED_P(weakmap));
}
} else if (/* GC_REF_CHECK_COLOR(Z_COUNTED_P(weakmap), GC_BLACK) && */ !GC_FROM_WEAKMAP(entry)) {
/* Both the entry weakmap and key are BLACK, so we
* can mark the entry BLACK as well.
* !GC_FROM_WEAKMAP(entry) means that the weakmap
* was already scanned black (or will not be
* scanned), so it's our responsibility to mark the
* entry */
ZEND_ASSERT(GC_REF_CHECK_COLOR(Z_COUNTED_P(weakmap), GC_BLACK));
ref = Z_COUNTED_P(entry);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
GC_REF_SET_BLACK(ref);
GC_STACK_PUSH(ref);
}
}
}
zv+=2;
}
}
if (UNEXPECTED(obj->handlers->get_gc == zend_weakmap_get_gc)) {
zend_weakmap_get_key_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n-=2) {
ZEND_ASSERT(Z_TYPE_P(zv+1) == IS_PTR);
zval *key = zv;
zval *entry = (zval*) Z_PTR_P(zv+1);
if (Z_OPT_REFCOUNTED_P(entry)) {
GC_UNSET_FROM_WEAKMAP(entry);
if (GC_REF_CHECK_COLOR(Z_COUNTED_P(key), GC_GREY)) {
/* Key was scanned in gc_mark_roots, we must
* ensure that it's eventually scanned in
* gc_scan_roots as well. */
if (!GC_REF_ADDRESS(Z_COUNTED_P(key))) {
gc_extra_root(Z_COUNTED_P(key));
}
} else if (/* GC_REF_CHECK_COLOR(Z_COUNTED_P(key), GC_BLACK) && */ !GC_FROM_WEAKMAP_KEY(entry)) {
/* Both the entry weakmap and key are BLACK, so we
* can mark the entry BLACK as well.
* !GC_FROM_WEAKMAP_KEY(entry) means that the key
* was already scanned black (or will not be
* scanned), so it's our responsibility to mark the
* entry */
ZEND_ASSERT(GC_REF_CHECK_COLOR(Z_COUNTED_P(key), GC_BLACK));
ref = Z_COUNTED_P(entry);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
GC_REF_SET_BLACK(ref);
GC_STACK_PUSH(ref);
}
}
}
zv += 2;
}
goto next;
}
ht = obj->handlers->get_gc(obj, &table, &len);
n = len;
zv = table;
@ -817,6 +965,7 @@ handle_ht:
}
}
next:
ref = GC_STACK_POP();
if (ref) {
goto tail_call;
@ -841,6 +990,57 @@ tail_call:
zval *table;
int len;
if (UNEXPECTED(GC_FLAGS(obj) & IS_OBJ_WEAKLY_REFERENCED)) {
zend_weakmap_get_object_key_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n-=2) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
zval *weakmap = zv+1;
ZEND_ASSERT(Z_REFCOUNTED_P(weakmap));
if (Z_REFCOUNTED_P(entry)) {
GC_SET_FROM_WEAKMAP_KEY(entry);
ref = Z_COUNTED_P(entry);
/* Only DELREF if the contribution from the weakmap has
* not been cancelled yet */
if (!GC_FROM_WEAKMAP(entry)) {
GC_DELREF(ref);
}
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_GREY);
GC_STACK_PUSH(ref);
}
}
zv+=2;
}
}
if (UNEXPECTED(obj->handlers->get_gc == zend_weakmap_get_gc)) {
zend_weakmap_get_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_REFCOUNTED_P(entry)) {
GC_SET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
/* Only DELREF if the contribution from the weakmap key
* has not been cancelled yet */
if (!GC_FROM_WEAKMAP_KEY(entry)) {
GC_DELREF(ref);
}
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_GREY);
GC_STACK_PUSH(ref);
}
}
zv++;
}
goto next;
}
ht = obj->handlers->get_gc(obj, &table, &len);
n = len;
zv = table;
@ -940,6 +1140,7 @@ handle_ht:
}
}
next:
ref = GC_STACK_POP();
if (ref) {
goto tail_call;
@ -1035,6 +1236,24 @@ tail_call:
zval *table;
int len;
if (UNEXPECTED(GC_FLAGS(obj) & IS_OBJ_WEAKLY_REFERENCED)) {
zend_weakmap_get_object_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_OPT_REFCOUNTED_P(entry)) {
ref = Z_COUNTED_P(entry);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
GC_STACK_PUSH(ref);
}
}
zv++;
}
}
ht = obj->handlers->get_gc(obj, &table, &len);
n = len;
zv = table;
@ -1139,17 +1358,34 @@ next:
static void gc_scan_roots(gc_stack *stack)
{
gc_root_buffer *current = GC_IDX2PTR(GC_FIRST_ROOT);
gc_root_buffer *last = GC_IDX2PTR(GC_G(first_unused));
uint32_t idx, end;
gc_root_buffer *current;
while (current != last) {
/* Root buffer might be reallocated during gc_scan,
* make sure to reload pointers. */
idx = GC_FIRST_ROOT;
end = GC_G(first_unused);
while (idx != end) {
current = GC_IDX2PTR(idx);
if (GC_IS_ROOT(current->ref)) {
if (GC_REF_CHECK_COLOR(current->ref, GC_GREY)) {
GC_REF_SET_COLOR(current->ref, GC_WHITE);
gc_scan(current->ref, stack);
}
}
current++;
idx++;
}
/* Scan extra roots added during gc_scan */
while (idx != GC_G(first_unused)) {
current = GC_IDX2PTR(idx);
if (GC_IS_ROOT(current->ref)) {
if (GC_REF_CHECK_COLOR(current->ref, GC_GREY)) {
GC_REF_SET_COLOR(current->ref, GC_WHITE);
gc_scan(current->ref, stack);
}
}
idx++;
}
}
@ -1209,6 +1445,50 @@ tail_call:
|| obj->ce->destructor != NULL)) {
*flags |= GC_HAS_DESTRUCTORS;
}
if (UNEXPECTED(GC_FLAGS(obj) & IS_OBJ_WEAKLY_REFERENCED)) {
zend_weakmap_get_object_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP_KEY(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
GC_UNSET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
GC_REF_SET_BLACK(ref);
GC_STACK_PUSH(ref);
}
}
zv++;
}
}
if (UNEXPECTED(obj->handlers->get_gc == zend_weakmap_get_gc)) {
zend_weakmap_get_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
GC_UNSET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
GC_REF_SET_BLACK(ref);
GC_STACK_PUSH(ref);
}
}
zv++;
}
goto next;
}
ht = obj->handlers->get_gc(obj, &table, &len);
n = len;
zv = table;
@ -1313,6 +1593,7 @@ handle_ht:
}
}
next:
ref = GC_STACK_POP();
if (ref) {
goto tail_call;
@ -1396,6 +1677,21 @@ tail_call:
int len;
zval *table;
if (UNEXPECTED(GC_FLAGS(obj) & IS_OBJ_WEAKLY_REFERENCED)) {
zend_weakmap_get_object_entry_gc(obj, &table, &len);
n = len;
zv = table;
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_OPT_REFCOUNTED_P(entry)) {
ref = Z_COUNTED_P(entry);
GC_STACK_PUSH(ref);
}
zv++;
}
}
ht = obj->handlers->get_gc(obj, &table, &len);
n = len;
zv = table;

View File

@ -140,6 +140,15 @@ static zend_always_inline void zend_get_gc_buffer_add_obj(
gc_buffer->cur++;
}
static zend_always_inline void zend_get_gc_buffer_add_ptr(
zend_get_gc_buffer *gc_buffer, void *ptr) {
if (UNEXPECTED(gc_buffer->cur == gc_buffer->end)) {
zend_get_gc_buffer_grow(gc_buffer);
}
ZVAL_PTR(gc_buffer->cur, ptr);
gc_buffer->cur++;
}
static zend_always_inline void zend_get_gc_buffer_use(
zend_get_gc_buffer *gc_buffer, zval **table, int *n) {
*table = gc_buffer->start;

View File

@ -666,6 +666,7 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) {
#define Z_TYPE_FLAGS_MASK 0xff00
#define Z_TYPE_FLAGS_SHIFT 8
#define Z_TYPE_INFO_EXTRA_SHIFT 16
#define GC_REFCOUNT(p) zend_gc_refcount(&(p)->gc)
#define GC_SET_REFCOUNT(p, rc) zend_gc_set_refcount(&(p)->gc, rc)

View File

@ -17,6 +17,7 @@
#include "zend.h"
#include "zend_interfaces.h"
#include "zend_objects_API.h"
#include "zend_types.h"
#include "zend_weakrefs.h"
#include "zend_weakrefs_arginfo.h"
@ -460,7 +461,7 @@ static HashTable *zend_weakmap_get_properties_for(zend_object *object, zend_prop
return ht;
}
static HashTable *zend_weakmap_get_gc(zend_object *object, zval **table, int *n)
HashTable *zend_weakmap_get_gc(zend_object *object, zval **table, int *n)
{
zend_weakmap *wm = zend_weakmap_from(object);
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
@ -472,6 +473,101 @@ static HashTable *zend_weakmap_get_gc(zend_object *object, zval **table, int *n)
return NULL;
}
HashTable *zend_weakmap_get_key_entry_gc(zend_object *object, zval **table, int *n)
{
zend_weakmap *wm = zend_weakmap_from(object);
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
zend_ulong h;
zval *val;
ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&wm->ht, h, val) {
zend_object *key = zend_weakref_key_to_object(h);
zend_get_gc_buffer_add_obj(gc_buffer, key);
zend_get_gc_buffer_add_ptr(gc_buffer, val);
} ZEND_HASH_FOREACH_END();
zend_get_gc_buffer_use(gc_buffer, table, n);
return NULL;
}
HashTable *zend_weakmap_get_entry_gc(zend_object *object, zval **table, int *n)
{
zend_weakmap *wm = zend_weakmap_from(object);
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
zval *val;
ZEND_HASH_MAP_FOREACH_VAL(&wm->ht, val) {
zend_get_gc_buffer_add_ptr(gc_buffer, val);
} ZEND_HASH_FOREACH_END();
zend_get_gc_buffer_use(gc_buffer, table, n);
return NULL;
}
HashTable *zend_weakmap_get_object_key_entry_gc(zend_object *object, zval **table, int *n)
{
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
const zend_ulong obj_key = zend_object_to_weakref_key(object);
void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key);
#if ZEND_DEBUG
ZEND_ASSERT(tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise");
#endif
void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr);
uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr);
if (tag == ZEND_WEAKREF_TAG_HT) {
HashTable *ht = ptr;
ZEND_HASH_MAP_FOREACH_PTR(ht, tagged_ptr) {
if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_MAP) {
zend_weakmap *wm = (zend_weakmap*) ZEND_WEAKREF_GET_PTR(tagged_ptr);
zval *zv = zend_hash_index_find(&wm->ht, obj_key);
ZEND_ASSERT(zv);
zend_get_gc_buffer_add_ptr(gc_buffer, zv);
zend_get_gc_buffer_add_obj(gc_buffer, &wm->std);
}
} ZEND_HASH_FOREACH_END();
} else if (tag == ZEND_WEAKREF_TAG_MAP) {
zend_weakmap *wm = (zend_weakmap*) ptr;
zval *zv = zend_hash_index_find(&wm->ht, obj_key);
ZEND_ASSERT(zv);
zend_get_gc_buffer_add_ptr(gc_buffer, zv);
zend_get_gc_buffer_add_obj(gc_buffer, &wm->std);
}
zend_get_gc_buffer_use(gc_buffer, table, n);
return NULL;
}
HashTable *zend_weakmap_get_object_entry_gc(zend_object *object, zval **table, int *n)
{
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
const zend_ulong obj_key = zend_object_to_weakref_key(object);
void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key);
#if ZEND_DEBUG
ZEND_ASSERT(tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise");
#endif
void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr);
uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr);
if (tag == ZEND_WEAKREF_TAG_HT) {
HashTable *ht = ptr;
ZEND_HASH_MAP_FOREACH_PTR(ht, tagged_ptr) {
if (ZEND_WEAKREF_GET_TAG(tagged_ptr) == ZEND_WEAKREF_TAG_MAP) {
zend_weakmap *wm = (zend_weakmap*) ZEND_WEAKREF_GET_PTR(tagged_ptr);
zval *zv = zend_hash_index_find(&wm->ht, obj_key);
ZEND_ASSERT(zv);
zend_get_gc_buffer_add_ptr(gc_buffer, zv);
}
} ZEND_HASH_FOREACH_END();
} else if (tag == ZEND_WEAKREF_TAG_MAP) {
zend_weakmap *wm = (zend_weakmap*) ptr;
zval *zv = zend_hash_index_find(&wm->ht, obj_key);
ZEND_ASSERT(zv);
zend_get_gc_buffer_add_ptr(gc_buffer, zv);
}
zend_get_gc_buffer_use(gc_buffer, table, n);
return NULL;
}
static zend_object *zend_weakmap_clone_obj(zend_object *old_object)
{
zend_object *new_object = zend_weakmap_create_object(zend_ce_weakmap);

View File

@ -62,6 +62,12 @@ static zend_always_inline zend_object *zend_weakref_key_to_object(zend_ulong key
return (zend_object *) (((uintptr_t) key) << ZEND_MM_ALIGNMENT_LOG2);
}
HashTable *zend_weakmap_get_gc(zend_object *object, zval **table, int *n);
HashTable *zend_weakmap_get_key_entry_gc(zend_object *object, zval **table, int *n);
HashTable *zend_weakmap_get_entry_gc(zend_object *object, zval **table, int *n);
HashTable *zend_weakmap_get_object_key_entry_gc(zend_object *object, zval **table, int *n);
HashTable *zend_weakmap_get_object_entry_gc(zend_object *object, zval **table, int *n);
END_EXTERN_C()
#endif