com php-src: Introduce BIND_LEXICAL: Zend/zend_closures .c Zend/zend_closures.h Zend/zend_compile.c Zen d/zend_compile.h Zend/zend_types.h
Zend/zend_vari ables.c Zend/zend_variables.h Zend/zend_vm_def. h Zend/zend_vm_execute.h Zend/zend_vm_opcodes.c Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/zend_ cfg.c ext/opcache/Optimizer/zend_dfg.c ext/opcache/Op timizer/zend_dump.c ext/opcache/Optimizer/zend_inferenc e.c
ext/opcache/Optimizer/zend_ssa.c
| From: | Nikita Popov | Date: | Tue, 29 Dec 2015 22:14:53 +0000 |
| Subject: | com php-src: Introduce BIND_LEXICAL: Zend/zend_closures .c Zend/zend_closures.h Zend/zend_compile.c Zen d/zend_compile.h Zend/zend_types.h Zend/zend_vari ables.c Zend/zend_variables.h Zend/zend_vm_def. h Zend/zend_vm_execute.h Zend/zend_vm_opcodes.c Zend/zend_vm_opcodes.h ext/opcache/Optimizer/zend_ cfg.c ext/opcache/Optimizer/zend_dfg.c ext/opcache/Op timizer/zend_dump.c ext/opcache/Optimizer/zend_inferenc e.c ext/opcache/Optimizer/zend_ssa.c |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-90477@lists.php.net to get a copy of this message | ||
Commit: 65e456f3649c649d8450325fc677442380911598
Author: Nikita Popov <nikic@php.net> Tue, 29 Dec 2015 11:16:08 +0100
Parents: 4440436821404ff3c76682726e63d1aaf381f73a
Branches: master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=65e456f3649c649d8450325fc677442380911598
Log:
Introduce BIND_LEXICAL
This opcodes inserts a local CV into the closure static variable
table. This replaces the previous mechanism of having static
variables marked as LEXICAL, which perform a symtable lookup
during copying.
This means a) functions which contain closures no longer have to
rebuild their symtable (better performance) and b) we can now track
used variables in SSA.
Changed paths:
M Zend/zend_closures.c
M Zend/zend_closures.h
M Zend/zend_compile.c
M Zend/zend_compile.h
M Zend/zend_types.h
M Zend/zend_variables.c
M Zend/zend_variables.h
M Zend/zend_vm_def.h
M Zend/zend_vm_execute.h
M Zend/zend_vm_opcodes.c
M Zend/zend_vm_opcodes.h
M ext/opcache/Optimizer/zend_cfg.c
M ext/opcache/Optimizer/zend_dfg.c
M ext/opcache/Optimizer/zend_dump.c
M ext/opcache/Optimizer/zend_inference.c
M ext/opcache/Optimizer/zend_ssa.c
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index beca1ce..4d3a97c 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 8d4edfd..c6dff9b 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 3a7e0b5..9c07afe 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 b157db5..d145ffc 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 6536061..17845e0 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 7f99d71..fa57a83 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 9748a4a..41027d1 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 263e2fa..0efbd93 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 a923dff..cfe8a62 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 2776a80..1bf19d0 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include <stdio.h> #include <zend.h> -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 21f48cd..0379391 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 9446954..9ada8df 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 7c3e80d..ea81e28 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 0b4848b..c8fb945 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 8fa9bfd..84abb27 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 505c0b9..de92630 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; }
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index beca1ce..4d3a97c 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 8d4edfd..c6dff9b 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 3a7e0b5..9c07afe 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 b157db5..d145ffc 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 6536061..17845e0 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 7f99d71..fa57a83 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 9748a4a..41027d1 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 263e2fa..0efbd93 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 a923dff..cfe8a62 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 2776a80..1bf19d0 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include <stdio.h> #include <zend.h> -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 21f48cd..0379391 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 9446954..9ada8df 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 7c3e80d..ea81e28 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 0b4848b..c8fb945 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 8fa9bfd..84abb27 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 505c0b9..de92630 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; }