diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index beca1cef0750c..4d3a97c0acecd 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -570,11 +570,8 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent closure->func.common.prototype = (zend_function*)closure; closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; if (closure->func.op_array.static_variables) { - HashTable *static_variables = closure->func.op_array.static_variables; - - ALLOC_HASHTABLE(closure->func.op_array.static_variables); - zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_apply_with_arguments(static_variables, zval_copy_static_var, 1, closure->func.op_array.static_variables); + closure->func.op_array.static_variables = + zend_array_dup(closure->func.op_array.static_variables); } if (UNEXPECTED(!closure->func.op_array.run_time_cache)) { closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size); @@ -629,6 +626,14 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas } /* }}} */ +void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */ +{ + zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); + HashTable *static_variables = closure->func.op_array.static_variables; + zend_hash_update(static_variables, var_name, var); +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index 8d4edfd37a4bd..c6dff9b4bfa69 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -25,6 +25,7 @@ BEGIN_EXTERN_C() void zend_register_closure_ce(void); +void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var); extern ZEND_API zend_class_entry *zend_ce_closure; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 3a7e0b552cdce..9c07afe694d9f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2055,7 +2055,8 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */ opline->opcode == ZEND_ROPE_END || opline->opcode == ZEND_END_SILENCE || opline->opcode == ZEND_FETCH_LIST || - opline->opcode == ZEND_VERIFY_RETURN_TYPE) { + opline->opcode == ZEND_VERIFY_RETURN_TYPE || + opline->opcode == ZEND_BIND_LEXICAL) { /* these opcodes are handled separately */ } else { zend_find_live_range(opline, opline->op1_type, opline->op1.var); @@ -4893,28 +4894,44 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ } /* }}} */ -void zend_compile_closure_uses(zend_ast *ast) /* {{{ */ +static void zend_compile_closure_binding(znode *closure, zend_ast *uses_ast) /* {{{ */ { - zend_ast_list *list = zend_ast_get_list(ast); + zend_ast_list *list = zend_ast_get_list(uses_ast); uint32_t i; for (i = 0; i < list->children; ++i) { - zend_ast *var_ast = list->child[i]; - zend_string *name = zend_ast_get_str(var_ast); - zend_bool by_ref = var_ast->attr; - zval zv; + zend_ast *var_name_ast = list->child[i]; + zend_string *var_name = zend_ast_get_str(var_name_ast); + zend_bool by_ref = var_name_ast->attr; + zend_op *opline; - if (zend_string_equals_literal(name, "this")) { + if (zend_string_equals_literal(var_name, "this")) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable"); } - if (zend_is_auto_global(name)) { + if (zend_is_auto_global(var_name)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use auto-global as lexical variable"); } - ZVAL_NULL(&zv); - Z_CONST_FLAGS(zv) = by_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR; + opline = zend_emit_op(NULL, ZEND_BIND_LEXICAL, closure, NULL); + opline->op2_type = IS_CV; + opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(var_name)); + opline->extended_value = by_ref; + } +} +/* }}} */ +void zend_compile_closure_uses(zend_ast *ast) /* {{{ */ +{ + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t i; + + for (i = 0; i < list->children; ++i) { + zend_ast *var_ast = list->child[i]; + zend_bool by_ref = var_ast->attr; + + zval zv; + ZVAL_NULL(&zv); zend_compile_static_var_common(var_ast, &zv, by_ref); } } @@ -5166,6 +5183,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ zend_begin_method_decl(op_array, decl->name, has_body); } else { zend_begin_func_decl(result, op_array, decl); + if (uses_ast) { + zend_compile_closure_binding(result, uses_ast); + } } CG(active_op_array) = op_array; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index b157db5f63dae..d145ffc0a0595 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -892,7 +892,6 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name); #define ZEND_FETCH_LOCAL 0x10000000 #define ZEND_FETCH_STATIC 0x20000000 #define ZEND_FETCH_GLOBAL_LOCK 0x40000000 -#define ZEND_FETCH_LEXICAL 0x50000000 #define ZEND_FETCH_TYPE_MASK 0x70000000 diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 65360619fdf66..17845e05380b0 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -412,8 +412,6 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { /* zval.u1.v.const_flags */ #define IS_CONSTANT_UNQUALIFIED 0x010 -#define IS_LEXICAL_VAR 0x020 -#define IS_LEXICAL_REF 0x040 #define IS_CONSTANT_CLASS 0x080 /* __CLASS__ in trait */ #define IS_CONSTANT_IN_NAMESPACE 0x100 /* used only in opline->extended_value */ diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c index 7f99d71a14b95..fa57a83e705f7 100644 --- a/Zend/zend_variables.c +++ b/Zend/zend_variables.c @@ -267,59 +267,6 @@ ZEND_API void _zval_internal_ptr_dtor_wrapper(zval *zval_ptr) } #endif -ZEND_API int zval_copy_static_var(zval *p, int num_args, va_list args, zend_hash_key *key) /* {{{ */ -{ - zend_array *symbol_table; - HashTable *target = va_arg(args, HashTable*); - zend_bool is_ref; - zval tmp; - - if (Z_CONST_FLAGS_P(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) { - is_ref = Z_CONST_FLAGS_P(p) & IS_LEXICAL_REF; - - symbol_table = zend_rebuild_symbol_table(); - p = zend_hash_find(symbol_table, key->key); - if (!p) { - p = &tmp; - ZVAL_NULL(&tmp); - if (is_ref) { - ZVAL_NEW_REF(&tmp, &tmp); - zend_hash_add_new(symbol_table, key->key, &tmp); - Z_ADDREF_P(p); - } else { - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(key->key)); - } - } else { - if (Z_TYPE_P(p) == IS_INDIRECT) { - p = Z_INDIRECT_P(p); - if (Z_TYPE_P(p) == IS_UNDEF) { - if (!is_ref) { - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(key->key)); - p = &tmp; - ZVAL_NULL(&tmp); - } else { - ZVAL_NULL(p); - } - } - } - if (is_ref) { - ZVAL_MAKE_REF(p); - Z_ADDREF_P(p); - } else if (Z_ISREF_P(p)) { - ZVAL_DUP(&tmp, Z_REFVAL_P(p)); - p = &tmp; - } else if (Z_REFCOUNTED_P(p)) { - Z_ADDREF_P(p); - } - } - } else if (Z_REFCOUNTED_P(p)) { - Z_ADDREF_P(p); - } - zend_hash_add(target, key->key, p); - return ZEND_HASH_APPLY_KEEP; -} -/* }}} */ - /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_variables.h b/Zend/zend_variables.h index 9748a4ad24115..41027d18aa672 100644 --- a/Zend/zend_variables.h +++ b/Zend/zend_variables.h @@ -106,8 +106,6 @@ static zend_always_inline void _zval_opt_copy_ctor_no_imm(zval *zvalue ZEND_FILE } } -ZEND_API int zval_copy_static_var(zval *p, int num_args, va_list args, zend_hash_key *key); - ZEND_API size_t zend_print_variable(zval *var); ZEND_API void _zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC); ZEND_API void _zval_internal_dtor_for_ptr(zval *zvalue ZEND_FILE_LINE_DC); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 263e2fa756716..0efbd93f5922b 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7997,4 +7997,35 @@ ZEND_VM_C_LABEL(call_trampoline_end): ZEND_VM_LEAVE(); } +ZEND_VM_HANDLER(182, ZEND_BIND_LEXICAL, TMP, CV, REF) +{ + USE_OPLINE + zend_free_op free_op1, free_op2; + zval *closure, *var; + zend_string *var_name; + + closure = GET_OP1_ZVAL_PTR(BP_VAR_R); + if (opline->extended_value) { + /* By-ref binding */ + var = GET_OP2_ZVAL_PTR(BP_VAR_W); + ZVAL_MAKE_REF(var); + Z_ADDREF_P(var); + } else { + var = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); + if (UNEXPECTED(Z_ISUNDEF_P(var))) { + SAVE_OPLINE(); + var = GET_OP2_UNDEF_CV(var, BP_VAR_R); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } + } + ZVAL_DEREF(var); + Z_TRY_ADDREF_P(var); + } + + var_name = CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var)); + zend_closure_bind_var(closure, var_name, var); + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_DEFINE_OP(137, ZEND_OP_DATA); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a923dffd9cf59..cfe8a620ad40f 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -14210,6 +14210,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND ZEND_VM_RETURN(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_LEXICAL_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *closure, *var; + zend_string *var_name; + + closure = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + if (opline->extended_value) { + /* By-ref binding */ + var = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op2.var); + ZVAL_MAKE_REF(var); + Z_ADDREF_P(var); + } else { + var = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); + if (UNEXPECTED(Z_ISUNDEF_P(var))) { + SAVE_OPLINE(); + var = GET_OP2_UNDEF_CV(var, BP_VAR_R); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } + } + ZVAL_DEREF(var); + Z_TRY_ADDREF_P(var); + } + + var_name = CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var)); + zend_closure_bind_var(closure, var_name, var); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -49334,6 +49365,31 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_BIND_LEXICAL_SPEC_TMP_CV_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = labels; diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 2776a80e6ff2a..1bf19d01e3ff8 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -static const char *zend_vm_opcodes_names[182] = { +static const char *zend_vm_opcodes_names[183] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -204,9 +204,10 @@ static const char *zend_vm_opcodes_names[182] = { "ZEND_UNSET_STATIC_PROP", "ZEND_ISSET_ISEMPTY_STATIC_PROP", "ZEND_FETCH_CLASS_CONSTANT", + "ZEND_BIND_LEXICAL", }; -static uint32_t zend_vm_opcodes_flags[182] = { +static uint32_t zend_vm_opcodes_flags[183] = { 0x00000000, 0x00000707, 0x00000707, @@ -389,6 +390,7 @@ static uint32_t zend_vm_opcodes_flags[182] = { 0x00007307, 0x00027307, 0x00000373, + 0x00100101, }; ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 21f48cdb2b1cd..03793910b2a87 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -254,7 +254,8 @@ END_EXTERN_C() #define ZEND_UNSET_STATIC_PROP 179 #define ZEND_ISSET_ISEMPTY_STATIC_PROP 180 #define ZEND_FETCH_CLASS_CONSTANT 181 +#define ZEND_BIND_LEXICAL 182 -#define ZEND_VM_LAST_OPCODE 181 +#define ZEND_VM_LAST_OPCODE 182 #endif diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index 9446954e518e5..9ada8df1a0667 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -372,24 +372,6 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; - case ZEND_DECLARE_LAMBDA_FUNCTION: { -//??? zend_op_array *lambda_op_array; -//??? -//??? zv = CRT_CONSTANT(opline->op1); -//??? if (ctx->main_script && -//??? (lambda_op_array = zend_hash_find_ptr(&ctx->main_script->function_table, Z_STR_P(zv))) != NULL) { -//??? if (lambda_op_array->type == ZEND_USER_FUNCTION && -//??? lambda_op_array->static_variables) { -//??? // FIXME: Really we should try to perform alias -//??? // analysis on variables used by the closure -//??? info->flags |= ZEND_FUNC_TOO_DYNAMIC; -//??? } -//??? } else { -//??? // FIXME: how to find the lambda function? - flags |= ZEND_FUNC_TOO_DYNAMIC; -//??? } - } - break; case ZEND_UNSET_VAR: if (!(opline->extended_value & ZEND_QUICK_SET)) { flags |= ZEND_FUNC_TOO_DYNAMIC; diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c index 7c3e80db1fc4a..ea81e288d938f 100644 --- a/ext/opcache/Optimizer/zend_dfg.c +++ b/ext/opcache/Optimizer/zend_dfg.c @@ -99,6 +99,7 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg case ZEND_FE_RESET_RW: case ZEND_ADD_ARRAY_ELEMENT: case ZEND_INIT_ARRAY: + case ZEND_BIND_LEXICAL: if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) { // FIXME: include into "use" to ...? DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var)); diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 0b4848baf11d4..c8fb945067ccf 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -516,9 +516,6 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * case ZEND_FETCH_GLOBAL_LOCK: fprintf(stderr, " (global+lock)"); break; - case ZEND_FETCH_LEXICAL: - fprintf(stderr, " (lexical)"); - break; } } if (ZEND_VM_EXT_ISSET & flags) { diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 8fa9bfdbb5bdd..84abb27a53053 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -3062,6 +3062,20 @@ static void zend_update_type_info(const zend_op_array *op_array, UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def); } break; + case ZEND_BIND_LEXICAL: + if (ssa_ops[i].op2_def >= 0) { + tmp = t2 | MAY_BE_RC1 | MAY_BE_RCN; + if (opline->extended_value) { + tmp |= MAY_BE_REF; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def); + if ((t2 & MAY_BE_OBJECT) && ssa_var_info[ssa_ops[i].op2_use].ce) { + UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op2_use].ce, ssa_var_info[ssa_ops[i].op2_use].is_instanceof, ssa_ops[i].op2_def); + } else { + UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op2_def); + } + } + break; case ZEND_SEND_VAR_EX: case ZEND_SEND_VAR_NO_REF: case ZEND_SEND_REF: diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index 505c0b978fa0e..de9263026b7fa 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -354,6 +354,13 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, //NEW_SSA_VAR(opline->op1.var) } break; + case ZEND_BIND_LEXICAL: + if (opline->extended_value || (build_flags & ZEND_SSA_RC_INFERENCE)) { + ssa_ops[k].op2_def = ssa_vars_count; + var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count; + ssa_vars_count++; + } + break; default: break; }