Skip to content

Commit 04dd5b2

Browse files
committed
Reference dynamic functions through dynamic_defs
1 parent 33b5c02 commit 04dd5b2

13 files changed

+135
-96
lines changed

Zend/zend_compile.c

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,23 +1071,22 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zen
10711071
}
10721072
}
10731073

1074-
ZEND_API int do_bind_function(zval *lcname) /* {{{ */
1074+
ZEND_API int do_bind_function(zend_function *func, zval *lcname) /* {{{ */
10751075
{
1076-
zend_function *function;
1077-
zval *rtd_key, *zv;
1078-
1079-
rtd_key = lcname + 1;
1080-
zv = zend_hash_find_ex(EG(function_table), Z_STR_P(rtd_key), 1);
1081-
if (UNEXPECTED(!zv)) {
1082-
do_bind_function_error(Z_STR_P(lcname), NULL, 0);
1076+
zend_function *added_func = zend_hash_add_ptr(EG(function_table), Z_STR_P(lcname), func);
1077+
if (UNEXPECTED(!added_func)) {
1078+
do_bind_function_error(Z_STR_P(lcname), &func->op_array, 0);
10831079
return FAILURE;
10841080
}
1085-
function = (zend_function*)Z_PTR_P(zv);
1086-
zv = zend_hash_set_bucket_key(EG(function_table), (Bucket*)zv, Z_STR_P(lcname));
1087-
if (UNEXPECTED(!zv)) {
1088-
do_bind_function_error(Z_STR_P(lcname), &function->op_array, 0);
1089-
return FAILURE;
1081+
1082+
if (func->op_array.refcount) {
1083+
++*func->op_array.refcount;
10901084
}
1085+
if (func->common.function_name) {
1086+
zend_string_addref(func->common.function_name);
1087+
}
1088+
/* We can't addref static_variables here :(
1089+
* We make sure it's NULLed when destroyed though. */
10911090
return SUCCESS;
10921091
}
10931092
/* }}} */
@@ -6174,9 +6173,18 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name,
61746173
}
61756174
/* }}} */
61766175

6176+
static uint32_t zend_add_dynamic_func_def(zend_op_array *def) {
6177+
zend_op_array *op_array = CG(active_op_array);
6178+
uint32_t def_offset = op_array->num_dynamic_func_defs++;
6179+
op_array->dynamic_func_defs = erealloc(
6180+
op_array->dynamic_func_defs, op_array->num_dynamic_func_defs * sizeof(zend_op_array *));
6181+
op_array->dynamic_func_defs[def_offset] = def;
6182+
return def_offset;
6183+
}
6184+
61776185
static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, zend_bool toplevel) /* {{{ */
61786186
{
6179-
zend_string *unqualified_name, *name, *lcname, *key;
6187+
zend_string *unqualified_name, *name, *lcname;
61806188
zend_op *opline;
61816189

61826190
unqualified_name = decl->name;
@@ -6212,24 +6220,16 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
62126220
return;
62136221
}
62146222

6215-
key = zend_build_runtime_definition_key(lcname, decl->start_lineno);
6216-
if (!zend_hash_add_ptr(CG(function_table), key, op_array)) {
6217-
zend_error_noreturn(E_ERROR,
6218-
"Runtime definition key collision for function %s. This is a bug", ZSTR_VAL(name));
6219-
}
6220-
6223+
uint32_t func_ref = zend_add_dynamic_func_def(op_array);
62216224
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
62226225
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
6223-
opline->extended_value = zend_alloc_cache_slot();
6224-
opline->op1_type = IS_CONST;
6225-
LITERAL_STR(opline->op1, key);
6226+
opline->op2.num = func_ref;
62266227
} else {
62276228
opline = get_next_op();
62286229
opline->opcode = ZEND_DECLARE_FUNCTION;
62296230
opline->op1_type = IS_CONST;
62306231
LITERAL_STR(opline->op1, zend_string_copy(lcname));
6231-
/* RTD key is placed after lcname literal in op1 */
6232-
zend_add_literal_string(&key);
6232+
opline->op2.num = func_ref;
62336233
}
62346234
zend_string_release_ex(lcname, 0);
62356235
}

Zend/zend_compile.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,13 @@ struct _zend_op_array {
429429
zend_string *doc_comment;
430430

431431
int last_literal;
432+
uint32_t num_dynamic_func_defs;
432433
zval *literals;
433434

435+
/* Functions and classes that are declared dynamically are stored here and
436+
* referenced by index from opcodes. */
437+
zend_op_array **dynamic_func_defs;
438+
434439
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
435440
};
436441

@@ -746,7 +751,7 @@ zend_bool zend_handle_encoding_declaration(zend_ast *ast);
746751
/* parser-driven code generators */
747752
void zend_do_free(znode *op1);
748753

749-
ZEND_API int do_bind_function(zval *lcname);
754+
ZEND_API int do_bind_function(zend_function *func, zval *lcname);
750755
ZEND_API int do_bind_class(zval *lcname, zend_string *lc_parent_name);
751756
ZEND_API uint32_t zend_build_delayed_early_binding_list(const zend_op_array *op_array);
752757
ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t first_early_binding_opline);

Zend/zend_opcode.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
8484
op_array->last_literal = 0;
8585
op_array->literals = NULL;
8686

87+
op_array->num_dynamic_func_defs = 0;
88+
op_array->dynamic_func_defs = NULL;
89+
8790
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
8891
op_array->cache_size = zend_op_array_extension_handles * sizeof(void*);
8992

@@ -438,6 +441,7 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
438441
if (ht && !(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
439442
if (GC_DELREF(ht) == 0) {
440443
zend_array_destroy(ht);
444+
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
441445
}
442446
}
443447
}
@@ -513,6 +517,12 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
513517
}
514518
efree(arg_info);
515519
}
520+
if (op_array->num_dynamic_func_defs) {
521+
for (i = 0; i < op_array->num_dynamic_func_defs; i++) {
522+
destroy_op_array(op_array->dynamic_func_defs[i]);
523+
}
524+
efree(op_array->dynamic_func_defs);
525+
}
516526
}
517527

518528
static void zend_update_extended_stmts(zend_op_array *op_array)

Zend/zend_vm_def.h

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7183,12 +7183,14 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
71837183
ZEND_VM_NEXT_OPCODE();
71847184
}
71857185

7186-
ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, ANY)
7186+
ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, UNUSED)
71877187
{
7188+
zend_function *func;
71887189
USE_OPLINE
71897190

71907191
SAVE_OPLINE();
7191-
do_bind_function(RT_CONSTANT(opline, opline->op1));
7192+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
7193+
do_bind_function(func, RT_CONSTANT(opline, opline->op1));
71927194
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
71937195
}
71947196

@@ -7448,23 +7450,14 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST)
74487450
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
74497451
}
74507452

7451-
ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED, CACHE_SLOT)
7453+
ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
74527454
{
74537455
USE_OPLINE
74547456
zend_function *func;
7455-
zval *zfunc;
74567457
zval *object;
74577458
zend_class_entry *called_scope;
74587459

7459-
func = CACHED_PTR(opline->extended_value);
7460-
if (UNEXPECTED(func == NULL)) {
7461-
zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
7462-
ZEND_ASSERT(zfunc != NULL);
7463-
func = Z_FUNC_P(zfunc);
7464-
ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
7465-
CACHE_PTR(opline->extended_value, func);
7466-
}
7467-
7460+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
74687461
if (Z_TYPE(EX(This)) == IS_OBJECT) {
74697462
called_scope = Z_OBJCE(EX(This));
74707463
if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||

Zend/zend_vm_execute.h

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,15 +2434,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
24342434
ZEND_VM_NEXT_OPCODE();
24352435
}
24362436

2437-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
2438-
{
2439-
USE_OPLINE
2440-
2441-
SAVE_OPLINE();
2442-
do_bind_function(RT_CONSTANT(opline, opline->op1));
2443-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
2444-
}
2445-
24462437
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TICKS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
24472438
{
24482439
USE_OPLINE
@@ -3158,6 +3149,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_VARIADIC_SPEC_UNUSED_HAND
31583149
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
31593150
}
31603151

3152+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
3153+
{
3154+
zend_function *func;
3155+
USE_OPLINE
3156+
3157+
SAVE_OPLINE();
3158+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
3159+
do_bind_function(func, RT_CONSTANT(opline, opline->op1));
3160+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
3161+
}
3162+
31613163
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
31623164
{
31633165
USE_OPLINE
@@ -9101,19 +9103,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_C
91019103
{
91029104
USE_OPLINE
91039105
zend_function *func;
9104-
zval *zfunc;
91059106
zval *object;
91069107
zend_class_entry *called_scope;
91079108

9108-
func = CACHED_PTR(opline->extended_value);
9109-
if (UNEXPECTED(func == NULL)) {
9110-
zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
9111-
ZEND_ASSERT(zfunc != NULL);
9112-
func = Z_FUNC_P(zfunc);
9113-
ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
9114-
CACHE_PTR(opline->extended_value, func);
9115-
}
9116-
9109+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
91179110
if (Z_TYPE(EX(This)) == IS_OBJECT) {
91189111
called_scope = Z_OBJCE(EX(This));
91199112
if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
@@ -50713,7 +50706,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5071350706
(void*)&&ZEND_MAKE_REF_SPEC_VAR_UNUSED_LABEL,
5071450707
(void*)&&ZEND_NULL_LABEL,
5071550708
(void*)&&ZEND_MAKE_REF_SPEC_CV_UNUSED_LABEL,
50716-
(void*)&&ZEND_DECLARE_FUNCTION_SPEC_LABEL,
50709+
(void*)&&ZEND_DECLARE_FUNCTION_SPEC_UNUSED_LABEL,
5071750710
(void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_LABEL,
5071850711
(void*)&&ZEND_DECLARE_CONST_SPEC_CONST_CONST_LABEL,
5071950712
(void*)&&ZEND_DECLARE_CLASS_SPEC_CONST_LABEL,
@@ -52111,10 +52104,6 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5211152104
VM_TRACE(ZEND_DECLARE_ANON_CLASS_SPEC)
5211252105
ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
5211352106
HYBRID_BREAK();
52114-
HYBRID_CASE(ZEND_DECLARE_FUNCTION_SPEC):
52115-
VM_TRACE(ZEND_DECLARE_FUNCTION_SPEC)
52116-
ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
52117-
HYBRID_BREAK();
5211852107
HYBRID_CASE(ZEND_TICKS_SPEC):
5211952108
VM_TRACE(ZEND_TICKS_SPEC)
5212052109
ZEND_TICKS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -52191,6 +52180,10 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5219152180
VM_TRACE(ZEND_RECV_VARIADIC_SPEC_UNUSED)
5219252181
ZEND_RECV_VARIADIC_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
5219352182
HYBRID_BREAK();
52183+
HYBRID_CASE(ZEND_DECLARE_FUNCTION_SPEC_UNUSED):
52184+
VM_TRACE(ZEND_DECLARE_FUNCTION_SPEC_UNUSED)
52185+
ZEND_DECLARE_FUNCTION_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
52186+
HYBRID_BREAK();
5219452187
HYBRID_CASE(ZEND_INIT_DYNAMIC_CALL_SPEC_CV):
5219552188
VM_TRACE(ZEND_INIT_DYNAMIC_CALL_SPEC_CV)
5219652189
ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -58257,7 +58250,7 @@ void zend_vm_init(void)
5825758250
ZEND_MAKE_REF_SPEC_VAR_UNUSED_HANDLER,
5825858251
ZEND_NULL_HANDLER,
5825958252
ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER,
58260-
ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
58253+
ZEND_DECLARE_FUNCTION_SPEC_UNUSED_HANDLER,
5826158254
ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER,
5826258255
ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER,
5826358256
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER,

Zend/zend_vm_handlers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@
11191119
_(2102, ZEND_GENERATOR_CREATE_SPEC) \
11201120
_(2105, ZEND_MAKE_REF_SPEC_VAR_UNUSED) \
11211121
_(2107, ZEND_MAKE_REF_SPEC_CV_UNUSED) \
1122-
_(2108, ZEND_DECLARE_FUNCTION_SPEC) \
1122+
_(2108, ZEND_DECLARE_FUNCTION_SPEC_UNUSED) \
11231123
_(2109, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED) \
11241124
_(2110, ZEND_DECLARE_CONST_SPEC_CONST_CONST) \
11251125
_(2111, ZEND_DECLARE_CLASS_SPEC_CONST) \

Zend/zend_vm_opcodes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,8 @@ static uint32_t zend_vm_opcodes_flags[195] = {
362362
0x00047305,
363363
0x00000000,
364364
0x00000101,
365-
0x00000000,
366-
0x00040103,
365+
0x00000100,
366+
0x00000103,
367367
0x00000303,
368368
0x00000003,
369369
0x00000303,

ext/opcache/Optimizer/compact_literals.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
238238
case ZEND_RECV_INIT:
239239
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
240240
break;
241-
case ZEND_DECLARE_FUNCTION:
242-
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
243-
break;
244241
case ZEND_DECLARE_CLASS:
245242
case ZEND_DECLARE_CLASS_DELAYED:
246243
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
@@ -773,7 +770,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
773770
bind_var_slot[opline->op2.constant] = opline->extended_value;
774771
}
775772
break;
776-
case ZEND_DECLARE_LAMBDA_FUNCTION:
777773
case ZEND_DECLARE_ANON_CLASS:
778774
case ZEND_DECLARE_CLASS_DELAYED:
779775
opline->extended_value = cache_size;

ext/opcache/Optimizer/zend_optimizer.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,16 +1333,24 @@ static zend_bool needs_live_range(zend_op_array *op_array, zend_op *def_opline)
13331333
return 1;
13341334
}
13351335

1336+
static void zend_foreach_op_array_helper(
1337+
zend_op_array *op_array, zend_op_array_func_t func, void *context) {
1338+
func(op_array, context);
1339+
for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
1340+
func(op_array->dynamic_func_defs[i], context);
1341+
}
1342+
}
1343+
13361344
void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
13371345
{
13381346
zend_class_entry *ce;
13391347
zend_string *key;
13401348
zend_op_array *op_array;
13411349

1342-
func(&script->main_op_array, context);
1350+
zend_foreach_op_array_helper(&script->main_op_array, func, context);
13431351

13441352
ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1345-
func(op_array, context);
1353+
zend_foreach_op_array_helper(op_array, func, context);
13461354
} ZEND_HASH_FOREACH_END();
13471355

13481356
ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
@@ -1353,7 +1361,7 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void
13531361
if (op_array->scope == ce
13541362
&& op_array->type == ZEND_USER_FUNCTION
13551363
&& !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
1356-
func(op_array, context);
1364+
zend_foreach_op_array_helper(op_array, func, context);
13571365
}
13581366
} ZEND_HASH_FOREACH_END();
13591367
} ZEND_HASH_FOREACH_END();

0 commit comments

Comments
 (0)