mirror of
https://github.com/php/php-src.git
synced 2024-09-21 01:47:25 +00:00
Remove WeakMap entries whose key is only reachable through the entry value (#10932)
This commit is contained in:
parent
d0731934b7
commit
cbf67e4fee
41
Zend/tests/weakrefs/gh10043-001.phpt
Normal file
41
Zend/tests/weakrefs/gh10043-001.phpt
Normal 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) {
|
||||
}
|
44
Zend/tests/weakrefs/gh10043-002.phpt
Normal file
44
Zend/tests/weakrefs/gh10043-002.phpt
Normal 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) {
|
||||
}
|
50
Zend/tests/weakrefs/gh10043-003.phpt
Normal file
50
Zend/tests/weakrefs/gh10043-003.phpt
Normal 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
|
50
Zend/tests/weakrefs/gh10043-004.phpt
Normal file
50
Zend/tests/weakrefs/gh10043-004.phpt
Normal 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
|
49
Zend/tests/weakrefs/gh10043-005.phpt
Normal file
49
Zend/tests/weakrefs/gh10043-005.phpt
Normal 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) {
|
||||
}
|
41
Zend/tests/weakrefs/gh10043-006.phpt
Normal file
41
Zend/tests/weakrefs/gh10043-006.phpt
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
33
Zend/tests/weakrefs/gh10043-007.phpt
Normal file
33
Zend/tests/weakrefs/gh10043-007.phpt
Normal 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
|
30
Zend/tests/weakrefs/gh10043-008.phpt
Normal file
30
Zend/tests/weakrefs/gh10043-008.phpt
Normal 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
|
28
Zend/tests/weakrefs/gh10043-009.phpt
Normal file
28
Zend/tests/weakrefs/gh10043-009.phpt
Normal 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
|
32
Zend/tests/weakrefs/gh10043-010.phpt
Normal file
32
Zend/tests/weakrefs/gh10043-010.phpt
Normal 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==
|
24
Zend/tests/weakrefs/gh10043-011.phpt
Normal file
24
Zend/tests/weakrefs/gh10043-011.phpt
Normal 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) {
|
||||
}
|
33
Zend/tests/weakrefs/gh10043-012.phpt
Normal file
33
Zend/tests/weakrefs/gh10043-012.phpt
Normal 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
|
41
Zend/tests/weakrefs/gh10043-014.phpt
Normal file
41
Zend/tests/weakrefs/gh10043-014.phpt
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
24
Zend/tests/weakrefs/gh10043-015.phpt
Normal file
24
Zend/tests/weakrefs/gh10043-015.phpt
Normal 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
|
45
Zend/tests/weakrefs/gh10043-016.phpt
Normal file
45
Zend/tests/weakrefs/gh10043-016.phpt
Normal 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) {
|
||||
}
|
||||
}
|
||||
}
|
310
Zend/zend_gc.c
310
Zend/zend_gc.c
@ -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;
|
||||
@ -1082,7 +1301,7 @@ handle_zvals:
|
||||
} else if (GC_TYPE(ref) == IS_ARRAY) {
|
||||
ht = (HashTable *)ref;
|
||||
ZEND_ASSERT(ht != &EG(symbol_table));
|
||||
|
||||
|
||||
handle_ht:
|
||||
n = ht->nNumUsed;
|
||||
if (HT_IS_PACKED(ht)) {
|
||||
@ -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;
|
||||
@ -1231,7 +1511,7 @@ tail_call:
|
||||
}
|
||||
}
|
||||
|
||||
handle_zvals:
|
||||
handle_zvals:
|
||||
for (; n != 0; n--) {
|
||||
if (Z_REFCOUNTED_P(zv)) {
|
||||
ref = Z_COUNTED_P(zv);
|
||||
@ -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;
|
||||
@ -1472,7 +1768,7 @@ next:
|
||||
if (ref) {
|
||||
goto tail_call;
|
||||
}
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user