diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 629abd43998..494c8892218 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -729,6 +729,16 @@ void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) / } /* }}} */ +void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */ +{ + zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); + HashTable *static_variables = closure->func.op_array.static_variables; + zval *var = (zval*)((char*)static_variables->arData + offset); + zval_ptr_dtor(var); + ZVAL_COPY_VALUE(var, val); +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index b55174da086..0b50ef26221 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -28,6 +28,7 @@ BEGIN_EXTERN_C() void zend_register_closure_ce(void); void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var); +void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val); extern ZEND_API zend_class_entry *zend_ce_closure; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7c63771fed9..8a5d15bfa30 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4247,7 +4247,7 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */ } /* }}} */ -static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_bool by_ref) /* {{{ */ +static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, uint32_t by_ref) /* {{{ */ { znode var_node; zend_op *opline; @@ -4274,7 +4274,7 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_ } CG(active_op_array)->static_variables = zend_array_dup(CG(active_op_array)->static_variables); } - zend_hash_update(CG(active_op_array)->static_variables, var_name, value); + value = zend_hash_update(CG(active_op_array)->static_variables, var_name, value); if (zend_string_equals_literal(var_name, "this")) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as static variable"); @@ -4283,7 +4283,7 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_ opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, &var_node); opline->op1_type = IS_CV; opline->op1.var = lookup_cv(CG(active_op_array), var_name); - opline->extended_value = by_ref; + opline->extended_value = (uint32_t)((char*)value - (char*)CG(active_op_array)->static_variables->arData) | by_ref; } /* }}} */ @@ -4299,7 +4299,7 @@ void zend_compile_static_var(zend_ast *ast) /* {{{ */ ZVAL_NULL(&value_zv); } - zend_compile_static_var_common(var_ast, &value_zv, 1); + zend_compile_static_var_common(var_ast, &value_zv, ZEND_BIND_REF); } /* }}} */ @@ -5672,16 +5672,32 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ } /* }}} */ -static void zend_compile_closure_binding(znode *closure, zend_ast *uses_ast) /* {{{ */ +static void zend_compile_closure_binding(znode *closure, zend_op_array *op_array, zend_ast *uses_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(uses_ast); uint32_t i; + if (!list->children) { + return; + } + + if (!op_array->static_variables) { + op_array->static_variables = zend_new_array(8); + } + + if (GC_REFCOUNT(op_array->static_variables) > 1) { + if (!(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) { + GC_DELREF(op_array->static_variables); + } + op_array->static_variables = zend_array_dup(op_array->static_variables); + } + for (i = 0; i < list->children; ++i) { zend_ast *var_name_ast = list->child[i]; zend_string *var_name = zval_make_interned_string(zend_ast_get_zval(var_name_ast)); - zend_bool by_ref = var_name_ast->attr; + uint32_t by_ref = var_name_ast->attr; zend_op *opline; + zval *value; if (zend_string_equals_literal(var_name, "this")) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable"); @@ -5691,10 +5707,16 @@ static void zend_compile_closure_binding(znode *closure, zend_ast *uses_ast) /* zend_error_noreturn(E_COMPILE_ERROR, "Cannot use auto-global as lexical variable"); } + value = zend_hash_add(op_array->static_variables, var_name, &EG(uninitialized_zval)); + if (!value) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use variable $%s twice", ZSTR_VAL(var_name)); + } + opline = zend_emit_op(NULL, ZEND_BIND_LEXICAL, closure, NULL); opline->op2_type = IS_CV; opline->op2.var = lookup_cv(CG(active_op_array), var_name); - opline->extended_value = by_ref; + opline->extended_value = (uint32_t)((char*)value - (char*)op_array->static_variables->arData) | by_ref; } } /* }}} */ @@ -5708,16 +5730,10 @@ void zend_compile_closure_uses(zend_ast *ast) /* {{{ */ for (i = 0; i < list->children; ++i) { zend_ast *var_ast = list->child[i]; zend_string *var_name = zend_ast_get_str(var_ast); - zend_bool by_ref = var_ast->attr; + uint32_t by_ref = var_ast->attr; zval zv; ZVAL_NULL(&zv); - if (op_array->static_variables - && zend_hash_exists(op_array->static_variables, var_name)) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use variable $%s twice", ZSTR_VAL(var_name)); - } - { int i; for (i = 0; i < op_array->last_var; i++) { @@ -5996,7 +6012,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ } else { zend_begin_func_decl(result, op_array, decl); if (uses_ast) { - zend_compile_closure_binding(result, uses_ast); + zend_compile_closure_binding(result, op_array, uses_ast); } } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index e1bb9eb3763..805369edfaa 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -970,6 +970,8 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf, #define ZEND_RETURN_VAL 0 #define ZEND_RETURN_REF 1 +#define ZEND_BIND_VAL 0 +#define ZEND_BIND_REF 1 #define ZEND_RETURNS_FUNCTION (1<<0) #define ZEND_RETURNS_VALUE (1<<1) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index e984d5254e3..c6060dee320 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -1021,7 +1021,7 @@ lexical_var_list: lexical_var: T_VARIABLE { $$ = $1; } - | '&' T_VARIABLE { $$ = $2; $$->attr = 1; } + | '&' T_VARIABLE { $$ = $2; $$->attr = ZEND_BIND_REF; } ; function_call: diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 14dc9e67636..82372fdb917 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7827,10 +7827,9 @@ 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) { + if (opline->extended_value & ZEND_BIND_REF) { /* By-ref binding */ var = GET_OP2_ZVAL_PTR(BP_VAR_W); if (Z_ISREF_P(var)) { @@ -7851,8 +7850,7 @@ ZEND_VM_HANDLER(182, ZEND_BIND_LEXICAL, TMP, CV, REF) 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_closure_bind_var_ex(closure, (opline->extended_value & ~ZEND_BIND_REF), var); ZEND_VM_NEXT_OPCODE(); } @@ -7878,9 +7876,9 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, CONST, REF) } varname = GET_OP2_ZVAL_PTR(BP_VAR_R); - value = zend_hash_find_ex(ht, Z_STR_P(varname), 1); + value = (zval*)((char*)ht->arData + (opline->extended_value & ~ZEND_BIND_REF)); - if (opline->extended_value) { + if (opline->extended_value & ZEND_BIND_REF) { if (Z_TYPE_P(value) == IS_CONSTANT_AST) { SAVE_OPLINE(); if (UNEXPECTED(zval_update_constant_ex(value, EX(func)->op_array.scope) != SUCCESS)) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 523e0985f9e..20c0e672633 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -20366,10 +20366,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_LEXICAL_SPEC_TMP_CV_HANDL USE_OPLINE zend_free_op free_op1; zval *closure, *var; - zend_string *var_name; closure = _get_zval_ptr_tmp(opline->op1.var, &free_op1 EXECUTE_DATA_CC); - if (opline->extended_value) { + if (opline->extended_value & ZEND_BIND_REF) { /* By-ref binding */ var = _get_zval_ptr_cv_BP_VAR_W(opline->op2.var EXECUTE_DATA_CC); if (Z_ISREF_P(var)) { @@ -20390,8 +20389,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_LEXICAL_SPEC_TMP_CV_HANDL 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_closure_bind_var_ex(closure, (opline->extended_value & ~ZEND_BIND_REF), var); ZEND_VM_NEXT_OPCODE(); } @@ -42028,9 +42026,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_CONST_HAND } varname = RT_CONSTANT(opline, opline->op2); - value = zend_hash_find_ex(ht, Z_STR_P(varname), 1); + value = (zval*)((char*)ht->arData + (opline->extended_value & ~ZEND_BIND_REF)); - if (opline->extended_value) { + if (opline->extended_value & ZEND_BIND_REF) { if (Z_TYPE_P(value) == IS_CONSTANT_AST) { SAVE_OPLINE(); if (UNEXPECTED(zval_update_constant_ex(value, EX(func)->op_array.scope) != SUCCESS)) { diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c index 224ea56167a..403ac3c0b34 100644 --- a/ext/opcache/Optimizer/zend_dfg.c +++ b/ext/opcache/Optimizer/zend_dfg.c @@ -164,7 +164,7 @@ op1_use: } goto op2_use; case ZEND_BIND_LEXICAL: - if ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->extended_value) { + if ((build_flags & ZEND_SSA_RC_INFERENCE) || (opline->extended_value & ZEND_BIND_REF)) { goto op2_def; } goto op2_use; diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 92a4cbeaea2..46749b64c6d 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -2824,7 +2824,7 @@ static int zend_update_type_info(const zend_op_array *op_array, break; case ZEND_BIND_STATIC: tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF - | (opline->extended_value ? MAY_BE_REF : (MAY_BE_RC1 | MAY_BE_RCN)); + | ((opline->extended_value & ZEND_BIND_REF) ? MAY_BE_REF : (MAY_BE_RC1 | MAY_BE_RCN)); UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); break; case ZEND_SEND_VAR: @@ -2839,7 +2839,7 @@ static int zend_update_type_info(const zend_op_array *op_array, break; case ZEND_BIND_LEXICAL: if (ssa_ops[i].op2_def >= 0) { - if (opline->extended_value) { + if (opline->extended_value & ZEND_BIND_REF) { tmp = t2 | MAY_BE_REF; } else { tmp = t2 & ~(MAY_BE_RC1|MAY_BE_RCN); diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index a18f4a875c0..b4f54264bab 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -747,7 +747,7 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, } break; case ZEND_BIND_LEXICAL: - if (opline->extended_value || (build_flags & ZEND_SSA_RC_INFERENCE)) { + if ((opline->extended_value & ZEND_BIND_REF) || (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++;