Skip to content

Commit ee32155

Browse files
committed
Combine BIND_STATIC with assignment
1 parent 899fcea commit ee32155

10 files changed

+194
-63
lines changed

Zend/Optimizer/dce.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ static inline bool may_have_side_effects(
246246
if ((opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) {
247247
return 1;
248248
}
249+
/* Modifies local variables and static variables which are observable through reflection */
250+
if (opline->extended_value & ZEND_BIND_REF) {
251+
return 1;
252+
}
249253
}
250254
return 0;
251255
case ZEND_CHECK_VAR:
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
Static variable assign triggering destructor
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function __destruct() {
8+
throw new Exception('__destruct() called');
9+
}
10+
}
11+
12+
function bar() {
13+
echo "bar() called\n";
14+
return 42;
15+
}
16+
17+
function foo(bool $throw) {
18+
if ($throw) {
19+
$a = new Foo();
20+
}
21+
static $a = bar();
22+
var_dump($a);
23+
}
24+
25+
try {
26+
foo(true);
27+
} catch (Exception $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
foo(false);
31+
32+
?>
33+
--EXPECT--
34+
bar() called
35+
__destruct() called
36+
int(42)

Zend/tests/static_variables_null.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Static variable with null value doesn't retrigger the initializer
3+
--FILE--
4+
<?php
5+
6+
function bar() {
7+
echo "bar() called\n";
8+
return null;
9+
}
10+
11+
function foo() {
12+
static $a = bar();
13+
var_dump($a);
14+
}
15+
16+
foo();
17+
foo();
18+
19+
?>
20+
--EXPECT--
21+
bar() called
22+
NULL
23+
NULL
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Static variable with recursive initializer
3+
--FILE--
4+
<?php
5+
6+
function foo($i) {
7+
static $a = $i <= 10 ? foo($i + 1) : 'Done';
8+
var_dump($a);
9+
return $i;
10+
}
11+
12+
foo(0);
13+
foo(5);
14+
15+
?>
16+
--EXPECT--
17+
string(4) "Done"
18+
string(4) "Done"
19+
string(4) "Done"
20+
string(4) "Done"
21+
string(4) "Done"
22+
string(4) "Done"
23+
string(4) "Done"
24+
string(4) "Done"
25+
string(4) "Done"
26+
string(4) "Done"
27+
string(4) "Done"
28+
string(4) "Done"
29+
string(4) "Done"

Zend/zend_compile.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4877,9 +4877,7 @@ static void zend_compile_static_var(zend_ast *ast) /* {{{ */
48774877
}
48784878

48794879
if (!value_ast) {
4880-
zval value_zv;
4881-
ZVAL_NULL(&value_zv);
4882-
zend_compile_static_var_common(var_name, &value_zv, ZEND_BIND_REF);
4880+
zend_compile_static_var_common(var_name, &EG(uninitialized_zval), ZEND_BIND_REF);
48834881
} else {
48844882
zend_op *opline;
48854883

@@ -4898,7 +4896,7 @@ static void zend_compile_static_var(zend_ast *ast) /* {{{ */
48984896
}
48994897

49004898
zval *placeholder_ptr = zend_hash_update(CG(active_op_array)->static_variables, var_name, &EG(uninitialized_zval));
4901-
Z_TYPE_EXTRA_P(placeholder_ptr) = IS_TYPE_UNINITIALIZED;
4899+
Z_TYPE_EXTRA_P(placeholder_ptr) |= IS_TYPE_UNINITIALIZED;
49024900
uint32_t placeholder_offset = (uint32_t)((char*)placeholder_ptr - (char*)CG(active_op_array)->static_variables->arData);
49034901

49044902
uint32_t static_def_jmp_opnum = get_next_op_number();
@@ -4908,15 +4906,11 @@ static void zend_compile_static_var(zend_ast *ast) /* {{{ */
49084906
znode expr;
49094907
zend_compile_expr(&expr, value_ast);
49104908

4911-
opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, NULL);
4909+
opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, &expr);
49124910
opline->op1_type = IS_CV;
49134911
opline->op1.var = lookup_cv(var_name);
49144912
opline->extended_value = placeholder_offset | ZEND_BIND_REF;
49154913

4916-
opline = zend_emit_op(NULL, ZEND_ASSIGN, NULL, &expr);
4917-
opline->op1_type = IS_CV;
4918-
opline->op1.var = lookup_cv(var_name);
4919-
49204914
uint32_t skip_bind_static_jmp_opnum = zend_emit_jump(0);
49214915

49224916
zend_update_jump_target_to_next(static_def_jmp_opnum);

Zend/zend_vm_def.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8843,7 +8843,7 @@ ZEND_VM_HANDLER(182, ZEND_BIND_LEXICAL, TMP, CV, REF)
88438843
ZEND_VM_NEXT_OPCODE();
88448844
}
88458845

8846-
ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF)
8846+
ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, ANY, REF)
88478847
{
88488848
USE_OPLINE
88498849
HashTable *ht;
@@ -8868,14 +8868,23 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF)
88688868
zend_reference *ref = (zend_reference*)emalloc(sizeof(zend_reference));
88698869
GC_SET_REFCOUNT(ref, 2);
88708870
GC_TYPE_INFO(ref) = GC_REFERENCE;
8871-
ZVAL_COPY_VALUE(&ref->val, value);
8871+
if (OP2_TYPE == IS_UNUSED) {
8872+
ZVAL_COPY_VALUE(&ref->val, value);
8873+
} else {
8874+
i_zval_ptr_dtor(value);
8875+
ZVAL_COPY(&ref->val, GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R));
8876+
FREE_OP2();
8877+
}
88728878
ref->sources.ptr = NULL;
88738879
Z_REF_P(value) = ref;
88748880
Z_TYPE_INFO_P(value) = IS_REFERENCE_EX;
88758881
ZVAL_REF(variable_ptr, ref);
88768882
} else {
88778883
Z_ADDREF_P(value);
88788884
ZVAL_REF(variable_ptr, Z_REF_P(value));
8885+
if (OP2_TYPE != IS_UNUSED) {
8886+
FREE_OP2();
8887+
}
88798888
}
88808889
} else {
88818890
i_zval_ptr_dtor(variable_ptr);
@@ -8898,7 +8907,7 @@ ZEND_VM_HANDLER(203, ZEND_JMP_STATIC_DEF, ANY, JMP_ADDR)
88988907
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
88998908

89008909
value = (zval*)((char*)ht->arData + (opline->extended_value));
8901-
if (Z_TYPE_EXTRA_P(value) == IS_TYPE_UNINITIALIZED) {
8910+
if (Z_TYPE_EXTRA_P(value) & IS_TYPE_UNINITIALIZED) {
89028911
ZEND_VM_NEXT_OPCODE();
89038912
} else {
89048913
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);

Zend/zend_vm_execute.h

Lines changed: 58 additions & 49 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_vm_handlers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@
12821282
_(2435, ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_CONST) \
12831283
_(2436, ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_CONST) \
12841284
_(2438, ZEND_BIND_LEXICAL_SPEC_TMP_CV) \
1285-
_(2439, ZEND_BIND_STATIC_SPEC_CV_UNUSED) \
1285+
_(2439, ZEND_BIND_STATIC_SPEC_CV) \
12861286
_(2440, ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED) \
12871287
_(2441, ZEND_SEND_FUNC_ARG_SPEC_VAR_CONST) \
12881288
_(2444, ZEND_SEND_FUNC_ARG_SPEC_VAR_UNUSED) \

Zend/zend_vm_opcodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ static uint32_t zend_vm_opcodes_flags[204] = {
413413
0x00067000,
414414
0x00040373,
415415
0x00100101,
416-
0x00100101,
416+
0x00100001,
417417
0x00000101,
418418
0x00001301,
419419
0x00000101,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
ReflectionMethod::getStaticVariables() should not bleed IS_TYPE_UNINITIALIZED
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
echo "test() called\n";
8+
return 42;
9+
}
10+
11+
function foo() {
12+
$methodInfo = new ReflectionFunction(__FUNCTION__);
13+
$null = $methodInfo->getStaticVariables()['a'];
14+
15+
static $a = test();
16+
var_dump($a);
17+
$a = $null;
18+
}
19+
20+
foo();
21+
foo();
22+
23+
?>
24+
--EXPECT--
25+
test() called
26+
int(42)
27+
NULL

0 commit comments

Comments
 (0)