Skip to content

Commit 8356da6

Browse files
committed
Remove dynamic defs from methods as well
We need to remove DECLARE_FUNCTION + dynamic_defs for functions defined in methods as well, not just for those declared in the main script.
1 parent 328a07d commit 8356da6

File tree

4 files changed

+103
-54
lines changed

4 files changed

+103
-54
lines changed

Zend/zend_hash.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,10 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht,
10411041
ZEND_HASH_FOREACH(ht, 0); \
10421042
_ptr = Z_PTR_P(_z);
10431043

1044+
#define ZEND_HASH_FOREACH_PTR_FROM(ht, _ptr, _from) \
1045+
ZEND_HASH_FOREACH_FROM(ht, 0, _from); \
1046+
_ptr = Z_PTR_P(_z);
1047+
10441048
#define ZEND_HASH_REVERSE_FOREACH_PTR(ht, _ptr) \
10451049
ZEND_HASH_REVERSE_FOREACH(ht, 0); \
10461050
_ptr = Z_PTR_P(_z);

ext/opcache/ZendAccelerator.c

Lines changed: 69 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3825,6 +3825,63 @@ static void preload_error_cb(int type, zend_string *error_filename, const uint32
38253825
}
38263826
}
38273827

3828+
/* Remove DECLARE opcodes and dynamic defs. */
3829+
static void preload_remove_declares(zend_op_array *op_array)
3830+
{
3831+
zend_op *opline = op_array->opcodes;
3832+
zend_op *end = opline + op_array->last;
3833+
uint32_t skip_dynamic_func_count = 0;
3834+
zend_string *key;
3835+
zend_op_array *func;
3836+
3837+
while (opline != end) {
3838+
switch (opline->opcode) {
3839+
case ZEND_DECLARE_CLASS:
3840+
case ZEND_DECLARE_CLASS_DELAYED:
3841+
key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1);
3842+
if (!zend_hash_exists(CG(class_table), key)) {
3843+
MAKE_NOP(opline);
3844+
}
3845+
break;
3846+
case ZEND_DECLARE_FUNCTION:
3847+
opline->op2.num -= skip_dynamic_func_count;
3848+
key = Z_STR_P(RT_CONSTANT(opline, opline->op1));
3849+
func = zend_hash_find_ptr(EG(function_table), key);
3850+
if (func && func == op_array->dynamic_func_defs[opline->op2.num]) {
3851+
zend_op_array **dynamic_func_defs;
3852+
3853+
op_array->num_dynamic_func_defs--;
3854+
if (op_array->num_dynamic_func_defs == 0) {
3855+
dynamic_func_defs = NULL;
3856+
} else {
3857+
dynamic_func_defs = emalloc(sizeof(zend_op_array*) * op_array->num_dynamic_func_defs);
3858+
if (opline->op2.num > 0) {
3859+
memcpy(
3860+
dynamic_func_defs,
3861+
op_array->dynamic_func_defs,
3862+
sizeof(zend_op_array*) * opline->op2.num);
3863+
}
3864+
if (op_array->num_dynamic_func_defs - opline->op2.num > 0) {
3865+
memcpy(
3866+
dynamic_func_defs + opline->op2.num,
3867+
op_array->dynamic_func_defs + (opline->op2.num + 1),
3868+
sizeof(zend_op_array*) * (op_array->num_dynamic_func_defs - opline->op2.num));
3869+
}
3870+
}
3871+
efree(op_array->dynamic_func_defs);
3872+
op_array->dynamic_func_defs = dynamic_func_defs;
3873+
skip_dynamic_func_count++;
3874+
MAKE_NOP(opline);
3875+
}
3876+
break;
3877+
case ZEND_DECLARE_LAMBDA_FUNCTION:
3878+
opline->op2.num -= skip_dynamic_func_count;
3879+
break;
3880+
}
3881+
opline++;
3882+
}
3883+
}
3884+
38283885
static void preload_link(void)
38293886
{
38303887
zval *zv;
@@ -3965,9 +4022,7 @@ static void preload_link(void)
39654022
ZEND_HASH_FOREACH_STR_KEY_VAL_FROM(
39664023
EG(class_table), key, zv, EG(persistent_classes_count)) {
39674024
ce = Z_PTR_P(zv);
3968-
if (ce->type == ZEND_INTERNAL_CLASS) {
3969-
break;
3970-
}
4025+
ZEND_ASSERT(ce->type != ZEND_INTERNAL_CLASS);
39714026
if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
39724027
&& !(ce->ce_flags & ZEND_ACC_LINKED)) {
39734028
zend_string *lcname = zend_string_tolower(ce->name);
@@ -3995,59 +4050,9 @@ static void preload_link(void)
39954050

39964051
zend_hash_destroy(&errors);
39974052

3998-
/* Remove DECLARE opcodes */
39994053
ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
40004054
zend_op_array *op_array = &script->script.main_op_array;
4001-
zend_op *opline = op_array->opcodes;
4002-
zend_op *end = opline + op_array->last;
4003-
uint32_t skip_dynamic_func_count = 0;
4004-
4005-
while (opline != end) {
4006-
switch (opline->opcode) {
4007-
case ZEND_DECLARE_CLASS:
4008-
case ZEND_DECLARE_CLASS_DELAYED:
4009-
key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1);
4010-
if (!zend_hash_exists(CG(class_table), key)) {
4011-
MAKE_NOP(opline);
4012-
}
4013-
break;
4014-
case ZEND_DECLARE_FUNCTION:
4015-
opline->op2.num -= skip_dynamic_func_count;
4016-
key = Z_STR_P(RT_CONSTANT(opline, opline->op1));
4017-
zv = zend_hash_find(EG(function_table), key);
4018-
if (zv && Z_PTR_P(zv) == op_array->dynamic_func_defs[opline->op2.num]) {
4019-
zend_op_array **dynamic_func_defs;
4020-
4021-
op_array->num_dynamic_func_defs--;
4022-
if (op_array->num_dynamic_func_defs == 0) {
4023-
dynamic_func_defs = NULL;
4024-
} else {
4025-
dynamic_func_defs = emalloc(sizeof(zend_op_array*) * op_array->num_dynamic_func_defs);
4026-
if (opline->op2.num > 0) {
4027-
memcpy(
4028-
dynamic_func_defs,
4029-
op_array->dynamic_func_defs,
4030-
sizeof(zend_op_array*) * opline->op2.num);
4031-
}
4032-
if (op_array->num_dynamic_func_defs - opline->op2.num > 0) {
4033-
memcpy(
4034-
dynamic_func_defs + opline->op2.num,
4035-
op_array->dynamic_func_defs + (opline->op2.num + 1),
4036-
sizeof(zend_op_array*) * (op_array->num_dynamic_func_defs - opline->op2.num));
4037-
}
4038-
}
4039-
efree(op_array->dynamic_func_defs);
4040-
op_array->dynamic_func_defs = dynamic_func_defs;
4041-
skip_dynamic_func_count++;
4042-
MAKE_NOP(opline);
4043-
}
4044-
break;
4045-
case ZEND_DECLARE_LAMBDA_FUNCTION:
4046-
opline->op2.num -= skip_dynamic_func_count;
4047-
break;
4048-
}
4049-
opline++;
4050-
}
4055+
preload_remove_declares(op_array);
40514056

40524057
if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
40534058
script->script.first_early_binding_opline = zend_build_delayed_early_binding_list(op_array);
@@ -4056,6 +4061,16 @@ static void preload_link(void)
40564061
}
40574062
}
40584063
} ZEND_HASH_FOREACH_END();
4064+
4065+
/* Dynamic defs inside methods need to be removed as well. */
4066+
ZEND_HASH_FOREACH_PTR_FROM(EG(class_table), ce, EG(persistent_classes_count)) {
4067+
zend_op_array *op_array;
4068+
ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
4069+
if (op_array->type == ZEND_USER_FUNCTION) {
4070+
preload_remove_declares(op_array);
4071+
}
4072+
} ZEND_HASH_FOREACH_END();
4073+
} ZEND_HASH_FOREACH_END();
40594074
}
40604075

40614076
static zend_string *preload_resolve_path(zend_string *filename)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
class Test {
3+
function method() {
4+
function dynamic() {
5+
echo "dynamic";
6+
}
7+
}
8+
}
9+
10+
$test = new Test;
11+
$test->method();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Preloading dynamic def in method
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.optimization_level=-1
7+
opcache.preload={PWD}/preload_dynamic_def_in_method.inc
8+
--EXTENSIONS--
9+
opcache
10+
--SKIPIF--
11+
<?php
12+
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
13+
?>
14+
--FILE--
15+
<?php
16+
dynamic();
17+
?>
18+
--EXPECT--
19+
dynamic

0 commit comments

Comments
 (0)