diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 74b63951fa961..e45154157c287 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3956,19 +3956,21 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) { opline = &CG(active_op_array)->opcodes[opnum_init]; opline->extended_value = 0; + /* opcode array may be reallocated, so don't access opcode field after zend_emit_op_tmp(). */ + uint8_t opcode = opline->opcode; - if (opline->opcode == ZEND_NEW) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression"); + if (opcode == ZEND_NEW) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression"); } - if (opline->opcode == ZEND_INIT_FCALL) { + if (opcode == ZEND_INIT_FCALL) { opline->op1.num = zend_vm_calc_used_stack(0, fbc); } zend_op *callable_convert_op = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); - if (opline->opcode == ZEND_INIT_FCALL - || opline->opcode == ZEND_INIT_FCALL_BY_NAME - || opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { + if (opcode == ZEND_INIT_FCALL + || opcode == ZEND_INIT_FCALL_BY_NAME + || opcode == ZEND_INIT_NS_FCALL_BY_NAME) { callable_convert_op->extended_value = zend_alloc_cache_slot(); } else { callable_convert_op->extended_value = (uint32_t)-1; @@ -8090,6 +8092,8 @@ typedef struct { bool varvars_used; } closure_info; +static void find_implicit_binds(closure_info *info, zend_ast *params_ast, zend_ast *stmt_ast); + static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) { if (!ast) { return; @@ -8134,7 +8138,15 @@ static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) { } else if (ast->kind == ZEND_AST_ARROW_FUNC) { /* For arrow functions recursively check the expression. */ zend_ast_decl *closure_ast = (zend_ast_decl *) ast; - find_implicit_binds_recursively(info, closure_ast->child[2]); + closure_info inner_info; + find_implicit_binds(&inner_info, closure_ast->child[0], closure_ast->child[2]); + if (inner_info.varvars_used) { + info->varvars_used = true; + } + if (zend_hash_num_elements(&inner_info.uses)) { + zend_hash_copy(&info->uses, &inner_info.uses, NULL); + } + zend_hash_destroy(&inner_info.uses); } else if (!zend_ast_is_special(ast)) { uint32_t i, children = zend_ast_get_num_children(ast); for (i = 0; i < children; i++) { diff --git a/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt b/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt index 594980ec0f829..81c15d53b84cb 100644 --- a/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt +++ b/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt @@ -4,6 +4,9 @@ mysqli_fetch_all() data types variation mysqli --SKIPIF-- diff --git a/ext/opcache/tests/gh19867.phpt b/ext/opcache/tests/gh19867.phpt new file mode 100644 index 0000000000000..486a366722dad --- /dev/null +++ b/ext/opcache/tests/gh19867.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-19867: Avoid capturing nested arrow function parameters +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x20000 +--FILE-- + fn($a, $b) => $a + $b; +?> +--EXPECTF-- +$_main: + ; (lines=%d, args=0, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 FREE T0 +0002 RETURN int(1) + +{closure:%s:%d}: + ; (lines=%d, args=0, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 RETURN T0 + +{closure:%s:%d}: + ; (lines=%d, args=2, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 T2 = ADD CV0($a) CV1($b) +0003 RETURN T2 diff --git a/ext/opcache/tests/gh19984.phpt b/ext/opcache/tests/gh19984.phpt index 4584fa6494c18..cb49869f5cf05 100644 --- a/ext/opcache/tests/gh19984.phpt +++ b/ext/opcache/tests/gh19984.phpt @@ -14,6 +14,10 @@ if (!function_exists('pcntl_fork')) die('skip pcntl_fork() not available'); --EXPECTF-- Warning: Unsupported declare 'unknown' in %s on line %d diff --git a/ext/standard/tests/general_functions/proc_open_multiplex.phpt b/ext/standard/tests/general_functions/proc_open_multiplex.phpt index 63e856b29c31e..798b5e74c475e 100644 --- a/ext/standard/tests/general_functions/proc_open_multiplex.phpt +++ b/ext/standard/tests/general_functions/proc_open_multiplex.phpt @@ -1,5 +1,11 @@ --TEST-- Multiplexing of child output +--SKIPIF-- + --FILE--