From 6bd8f40291d1858e29a779e4c9fa0bebd98b9a3f Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Wed, 9 Nov 2022 12:47:41 +0100 Subject: [PATCH] Move observer_declared_function_notify until after pass_two() For early observing, there already exists a op_array_ctor hook on zend_extension. However the goal of the declared_function observer is noting the time when a fully defined function starts existing in the function_tables. This also prevents the observer being called in case there were compilation errors. Ultimately, this now gives a consistent behaviour with respect to how it works when opcache is enabled: - pass_two is done, opcodes and flags are all finalized. - similarly class_linked notifications also only happen once the class is actually finalized. - any extension wanting to delay the observer call may add the ZEND_COMPILE_IGNORE_OBSERVER compiler_option, then call it itself. --- Zend/zend_compile.c | 46 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index fb1b6b5d9da..f169efcffe4 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7271,7 +7271,7 @@ static uint32_t zend_add_dynamic_func_def(zend_op_array *def) { return def_offset; } -static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */ +static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */ { zend_string *unqualified_name, *name, *lcname; zend_op *opline; @@ -7305,23 +7305,20 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as if (UNEXPECTED(zend_hash_add_ptr(CG(function_table), lcname, op_array) == NULL)) { do_bind_function_error(lcname, op_array, 1); } - zend_observer_function_declared_notify(op_array, lcname); - zend_string_release_ex(lcname, 0); - return; - } - - uint32_t func_ref = zend_add_dynamic_func_def(op_array); - if (op_array->fn_flags & ZEND_ACC_CLOSURE) { - opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL); - opline->op2.num = func_ref; } else { - opline = get_next_op(); - opline->opcode = ZEND_DECLARE_FUNCTION; - opline->op1_type = IS_CONST; - LITERAL_STR(opline->op1, zend_string_copy(lcname)); - opline->op2.num = func_ref; + uint32_t func_ref = zend_add_dynamic_func_def(op_array); + if (op_array->fn_flags & ZEND_ACC_CLOSURE) { + opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL); + opline->op2.num = func_ref; + } else { + opline = get_next_op(); + opline->opcode = ZEND_DECLARE_FUNCTION; + opline->op1_type = IS_CONST; + LITERAL_STR(opline->op1, zend_string_copy(lcname)); + opline->op2.num = func_ref; + } } - zend_string_release_ex(lcname, 0); + return lcname; } /* }}} */ @@ -7333,7 +7330,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) zend_ast *stmt_ast = decl->child[2]; zend_ast *return_type_ast = decl->child[3]; bool is_method = decl->kind == ZEND_AST_METHOD; - zend_string *method_lcname = NULL; + zend_string *lcname = NULL; zend_class_entry *orig_class_entry = CG(active_class_entry); zend_op_array *orig_op_array = CG(active_op_array); @@ -7362,9 +7359,9 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) if (is_method) { bool has_body = stmt_ast != NULL; - method_lcname = zend_begin_method_decl(op_array, decl->name, has_body); + lcname = zend_begin_method_decl(op_array, decl->name, has_body); } else { - zend_begin_func_decl(result, op_array, decl, toplevel); + lcname = zend_begin_func_decl(result, op_array, decl, toplevel); if (decl->kind == ZEND_AST_ARROW_FUNC) { find_implicit_binds(&info, params_ast, stmt_ast); compile_implicit_lexical_binds(&info, result, op_array); @@ -7407,7 +7404,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) } zend_compile_params(params_ast, return_type_ast, - is_method && zend_string_equals_literal(method_lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0); + is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0); if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { zend_mark_function_as_generator(); zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL); @@ -7436,8 +7433,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) if (is_method) { CG(zend_lineno) = decl->start_lineno; zend_check_magic_method_implementation( - CG(active_class_entry), (zend_function *) op_array, method_lcname, E_COMPILE_ERROR); - zend_string_release_ex(method_lcname, 0); + CG(active_class_entry), (zend_function *) op_array, lcname, E_COMPILE_ERROR); } /* put the implicit return on the really last line */ @@ -7452,6 +7448,12 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) /* Pop the loop variable stack separator */ zend_stack_del_top(&CG(loop_var_stack)); + if (toplevel) { + zend_observer_function_declared_notify(op_array, lcname); + } + + zend_string_release_ex(lcname, 0); + CG(active_op_array) = orig_op_array; CG(active_class_entry) = orig_class_entry; }