diff --git a/Zend/tests/bug50383.phpt b/Zend/tests/bug50383.phpt index 2210c4bc35695..de81facd053c1 100644 --- a/Zend/tests/bug50383.phpt +++ b/Zend/tests/bug50383.phpt @@ -58,19 +58,6 @@ Array ) [1] => Array - ( - [file] => %s - [line] => 13 - [function] => ThrowException - [class] => myClass - [type] => :: - [args] => Array - ( - ) - - ) - - [2] => Array ( [file] => %s [line] => 21 @@ -104,19 +91,6 @@ Array ) [1] => Array - ( - [file] => %s - [line] => 17 - [function] => foo - [class] => myClass - [type] => -> - [args] => Array - ( - ) - - ) - - [2] => Array ( [file] => %s [line] => 28 diff --git a/Zend/tests/bug68412.phpt b/Zend/tests/bug68412.phpt new file mode 100644 index 0000000000000..c6f413cc32f4d --- /dev/null +++ b/Zend/tests/bug68412.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #68412 (Infinite recursion with __call can make the program crash/segfault) +--FILE-- +bar(); + } +} +$z = new C; +function main() { + global $z; + $z->foo(); +} +main(); +?> +--EXPECTF-- +Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %sbug68412.php on line %d diff --git a/Zend/zend.c b/Zend/zend.c index 38b5bd6bc266e..77e0b624c01c4 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -432,6 +432,17 @@ static void zend_init_exception_op(void) /* {{{ */ } /* }}} */ +static void zend_init_call_trampoline_op(void) /* {{{ */ +{ + memset(&EG(call_trampoline_op), 0, sizeof(EG(call_trampoline_op))); + EG(call_trampoline_op).opcode = ZEND_CALL_TRAMPOLINE; + EG(call_trampoline_op).op1_type = IS_UNUSED; + EG(call_trampoline_op).op2_type = IS_UNUSED; + EG(call_trampoline_op).result_type = IS_UNUSED; + ZEND_VM_SET_OPCODE_HANDLER(&EG(call_trampoline_op)); +} +/* }}} */ + #ifdef ZTS static void function_copy_ctor(zval *zv) { @@ -511,6 +522,8 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ zend_copy_constants(EG(zend_constants), GLOBAL_CONSTANTS_TABLE); zend_init_rsrc_plist(); zend_init_exception_op(); + zend_init_call_trampoline_op(); + memset(&executor_globals->trampoline, 0, sizeof(zend_op_array)); executor_globals->lambda_count = 0; ZVAL_UNDEF(&executor_globals->user_error_handler); ZVAL_UNDEF(&executor_globals->user_exception_handler); @@ -722,6 +735,7 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) / #ifndef ZTS zend_init_rsrc_plist(); zend_init_exception_op(); + zend_init_call_trampoline_op(); #endif zend_ini_startup(); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 273e9e9f6ab6c..5e1a8185fd074 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3070,16 +3070,7 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca get_function_via_handler: if (fcc->object && fcc->calling_scope == ce_org) { if (strict_class && ce_org->__call) { - fcc->function_handler = emalloc(sizeof(zend_internal_function)); - fcc->function_handler->internal_function.type = ZEND_INTERNAL_FUNCTION; - fcc->function_handler->internal_function.module = (ce_org->type == ZEND_INTERNAL_CLASS) ? ce_org->info.internal.module : NULL; - fcc->function_handler->internal_function.handler = zend_std_call_user_call; - fcc->function_handler->internal_function.arg_info = NULL; - fcc->function_handler->internal_function.num_args = 0; - fcc->function_handler->internal_function.scope = ce_org; - fcc->function_handler->internal_function.fn_flags = ZEND_ACC_CALL_VIA_HANDLER; - fcc->function_handler->internal_function.function_name = mname; - zend_string_addref(mname); + fcc->function_handler = zend_get_call_trampoline_func(ce_org, mname, 0); call_via_handler = 1; retval = 1; } else if (fcc->object->handlers->get_method) { @@ -3088,15 +3079,15 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca if (strict_class && (!fcc->function_handler->common.scope || !instanceof_function(ce_org, fcc->function_handler->common.scope))) { - if ((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0) { + if (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) { zend_string_release(fcc->function_handler->common.function_name); } - efree(fcc->function_handler); + zend_free_trampoline(fcc->function_handler); } } else { retval = 1; - call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0; + call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; } } } @@ -3108,7 +3099,7 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca } if (fcc->function_handler) { retval = 1; - call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0; + call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; if (call_via_handler && !fcc->object && EG(current_execute_data) && Z_OBJ(EG(current_execute_data)->This) && instanceof_function(Z_OBJCE(EG(current_execute_data)->This), fcc->calling_scope)) { fcc->object = Z_OBJ(EG(current_execute_data)->This); @@ -3250,14 +3241,13 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint ret = zend_is_callable_check_func(check_flags, callable, fcc, 0, error); if (fcc == &fcc_local && fcc->function_handler && - ((fcc->function_handler->type == ZEND_INTERNAL_FUNCTION && - (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) || + ((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) || fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY || fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION)) { if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) { zend_string_release(fcc->function_handler->common.function_name); } - efree(fcc->function_handler); + zend_free_trampoline(fcc->function_handler); } return ret; @@ -3338,14 +3328,13 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint ret = zend_is_callable_check_func(check_flags, method, fcc, strict_class, error); if (fcc == &fcc_local && fcc->function_handler && - ((fcc->function_handler->type == ZEND_INTERNAL_FUNCTION && - (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) || + ((fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) || fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY || fcc->function_handler->type == ZEND_OVERLOADED_FUNCTION)) { if (fcc->function_handler->type != ZEND_OVERLOADED_FUNCTION) { zend_string_release(fcc->function_handler->common.function_name); } - efree(fcc->function_handler); + zend_free_trampoline(fcc->function_handler); } return ret; @@ -3414,14 +3403,13 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam add_next_index_str(callable, zend_string_copy(fcc.function_handler->common.function_name)); } if (fcc.function_handler && - ((fcc.function_handler->type == ZEND_INTERNAL_FUNCTION && - (fcc.function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER)) || + ((fcc.function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) || fcc.function_handler->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY || fcc.function_handler->type == ZEND_OVERLOADED_FUNCTION)) { if (fcc.function_handler->type != ZEND_OVERLOADED_FUNCTION) { zend_string_release(fcc.function_handler->common.function_name); } - efree(fcc.function_handler); + zend_free_trampoline(fcc.function_handler); } return 1; } diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index c7d12bf128543..be6b11f6a84c0 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1285,16 +1285,14 @@ ZEND_FUNCTION(method_exists) && Z_OBJ_HT_P(klass)->get_method != NULL && (func = Z_OBJ_HT_P(klass)->get_method(&Z_OBJ_P(klass), method_name, NULL)) != NULL ) { - if (func->type == ZEND_INTERNAL_FUNCTION - && (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0 - ) { + if (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { /* Returns true to the fake Closure's __invoke */ RETVAL_BOOL(func->common.scope == zend_ce_closure && zend_string_equals_literal(method_name, ZEND_INVOKE_FUNC_NAME)); zend_string_release(lcname); zend_string_release(func->common.function_name); - efree(func); + zend_free_trampoline(func); return; } zend_string_release(lcname); @@ -2508,8 +2506,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int if (prev_call && prev_call->func && !ZEND_USER_CODE(prev_call->func->common.type) && - !(prev_call->func->common.type == ZEND_INTERNAL_FUNCTION && - (prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER))) { + !(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { break; } if (prev->func && ZEND_USER_CODE(prev->func->common.type)) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 322a7d32e711a..57b234a12a541 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -226,8 +226,11 @@ typedef struct _zend_try_catch_element { #define ZEND_ACC_CLOSURE 0x100000 #define ZEND_ACC_GENERATOR 0x800000 -/* function flag for internal user call handlers __call, __callstatic */ -#define ZEND_ACC_CALL_VIA_HANDLER 0x200000 +/* call through user function trampoline. e.g. __call, __callstatic */ +#define ZEND_ACC_CALL_VIA_TRAMPOLINE 0x200000 + +/* call through internal function handler. e.g. Closure::invoke() */ +#define ZEND_ACC_CALL_VIA_HANDLER ZEND_ACC_CALL_VIA_TRAMPOLINE /* disable inline caching */ #define ZEND_ACC_NEVER_CACHE 0x400000 diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index d37c2cd1d8eb9..f79169232d859 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -803,7 +803,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) / Z_ADDREF_P(arg); } else { if (Z_ISREF_P(arg) && - (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) { + !(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { /* don't separate references for __call */ arg = Z_REFVAL_P(arg); } @@ -827,6 +827,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) / } if (func->type == ZEND_USER_FUNCTION) { + int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; EG(scope) = func->common.scope; call->symbol_table = fci->symbol_table; if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) { @@ -839,8 +840,12 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) / } else { zend_generator_create_zval(call, &func->op_array, fci->retval); } + if (call_via_handler) { + /* We must re-initialize function again */ + fci_cache->initialized = 0; + } } else if (func->type == ZEND_INTERNAL_FUNCTION) { - int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0; + int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; ZVAL_NULL(fci->retval); if (func->common.scope) { EG(scope) = func->common.scope; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index afd668afb8715..c1b1531eab862 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -36,6 +36,7 @@ #include "zend_modules.h" #include "zend_float.h" #include "zend_multibyte.h" +#include "zend_multiply.h" #include "zend_arena.h" /* Define ZTS if you want a thread-safe Zend */ @@ -237,6 +238,9 @@ struct _zend_executor_globals { XPFPA_CW_DATATYPE saved_fpu_cw; #endif + zend_function trampoline; + zend_op call_trampoline_op; + void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 00fb17451ea61..753c2abc780a9 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -915,47 +915,6 @@ static void zend_std_unset_dimension(zval *object, zval *offset) /* {{{ */ } /* }}} */ -ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ -{ - zend_internal_function *func = (zend_internal_function *)EX(func); - zval method_name, method_args; - zval method_result; - zend_class_entry *ce = Z_OBJCE_P(getThis()); - - array_init_size(&method_args, ZEND_NUM_ARGS()); - - if (UNEXPECTED(zend_copy_parameters_array(ZEND_NUM_ARGS(), &method_args) == FAILURE)) { - zval_dtor(&method_args); - zend_error(E_EXCEPTION | E_ERROR, "Cannot get arguments for __call"); - RETURN_FALSE; - } - - ZVAL_STR(&method_name, func->function_name); /* no dup - it's a copy */ - - /* __call handler is called with two arguments: - method name - array of method parameters - - */ - zend_call_method_with_2_params(getThis(), ce, &ce->__call, ZEND_CALL_FUNC_NAME, &method_result, &method_name, &method_args); - - if (Z_TYPE(method_result) != IS_UNDEF) { - RETVAL_ZVAL_FAST(&method_result); - zval_ptr_dtor(&method_result); - } - - /* now destruct all auxiliaries */ - zval_ptr_dtor(&method_args); - zval_ptr_dtor(&method_name); - - /* destruct the function also, then - we have allocated it in get_method */ - efree_size(func, sizeof(zend_internal_function)); -#if ZEND_DEBUG - execute_data->func = NULL; -#endif -} -/* }}} */ - /* Ensures that we're allowed to call a private method. * Returns the function address that should be called, or NULL * if no such function exists. @@ -1034,25 +993,48 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) } /* }}} */ -static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ +ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static) /* {{{ */ { - zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); - call_user_call->type = ZEND_INTERNAL_FUNCTION; - call_user_call->module = (ce->type == ZEND_INTERNAL_CLASS) ? ce->info.internal.module : NULL; - call_user_call->handler = zend_std_call_user_call; - call_user_call->arg_info = NULL; - call_user_call->num_args = 0; - call_user_call->scope = ce; - call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; + zend_op_array *func; + zend_function *fbc = is_static ? ce->__callstatic : ce->__call; + + ZEND_ASSERT(fbc); + + if (EXPECTED(EG(trampoline).common.function_name == NULL)) { + func = &EG(trampoline).op_array; + } else { + func = ecalloc(1, sizeof(zend_op_array)); + } + + func->type = ZEND_USER_FUNCTION; + func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC; + if (is_static) { + func->fn_flags |= ZEND_ACC_STATIC; + } + func->this_var = -1; + func->opcodes = &EG(call_trampoline_op); + + func->scope = ce; + func->prototype = fbc; + func->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : STR_EMPTY_ALLOC(); + func->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0; + func->line_end = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_end : 0; + //??? keep compatibility for "\0" characters //??? see: Zend/tests/bug46238.phpt if (UNEXPECTED(strlen(method_name->val) != method_name->len)) { - call_user_call->function_name = zend_string_init(method_name->val, strlen(method_name->val), 0); + func->function_name = zend_string_init(method_name->val, strlen(method_name->val), 0); } else { - call_user_call->function_name = zend_string_copy(method_name); + func->function_name = zend_string_copy(method_name); } - return (union _zend_function *)call_user_call; + return (zend_function*)func; +} +/* }}} */ + +static zend_always_inline zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ +{ + return zend_get_call_trampoline_func(ce, method_name, 0); } /* }}} */ @@ -1141,70 +1123,12 @@ static union _zend_function *zend_std_get_method(zend_object **obj_ptr, zend_str } /* }}} */ -ZEND_API void zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ -{ - zend_internal_function *func = (zend_internal_function *)EX(func); - zval method_name, method_args; - zval method_result; - zend_class_entry *ce = EG(scope); - - array_init_size(&method_args, ZEND_NUM_ARGS()); - - if (UNEXPECTED(zend_copy_parameters_array(ZEND_NUM_ARGS(), &method_args) == FAILURE)) { - zval_dtor(&method_args); - zend_error(E_EXCEPTION | E_ERROR, "Cannot get arguments for " ZEND_CALLSTATIC_FUNC_NAME); - RETURN_FALSE; - } - - ZVAL_STR(&method_name, func->function_name); /* no dup - it's a copy */ - - /* __callStatic handler is called with two arguments: - method name - array of method parameters - */ - zend_call_method_with_2_params(NULL, ce, &ce->__callstatic, ZEND_CALLSTATIC_FUNC_NAME, &method_result, &method_name, &method_args); - - if (Z_TYPE(method_result) != IS_UNDEF) { - RETVAL_ZVAL_FAST(&method_result); - zval_ptr_dtor(&method_result); - } - - /* now destruct all auxiliaries */ - zval_ptr_dtor(&method_args); - zval_ptr_dtor(&method_name); - - /* destruct the function also, then - we have allocated it in get_method */ - efree_size(func, sizeof(zend_internal_function)); -#if ZEND_DEBUG - execute_data->func = NULL; -#endif -} -/* }}} */ - -static inline union _zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ +static zend_always_inline zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ { - zend_internal_function *callstatic_user_call = emalloc(sizeof(zend_internal_function)); - callstatic_user_call->type = ZEND_INTERNAL_FUNCTION; - callstatic_user_call->module = (ce->type == ZEND_INTERNAL_CLASS) ? ce->info.internal.module : NULL; - callstatic_user_call->handler = zend_std_callstatic_user_call; - callstatic_user_call->arg_info = NULL; - callstatic_user_call->num_args = 0; - callstatic_user_call->scope = ce; - callstatic_user_call->fn_flags = ZEND_ACC_STATIC | ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER; - //??? keep compatibility for "\0" characters - //??? see: Zend/tests/bug46238.phpt - if (UNEXPECTED(strlen(method_name->val) != method_name->len)) { - callstatic_user_call->function_name = zend_string_init(method_name->val, strlen(method_name->val), 0); - } else { - callstatic_user_call->function_name = zend_string_copy(method_name); - } - - return (zend_function *)callstatic_user_call; + return zend_get_call_trampoline_func(ce, method_name, 1); } /* }}} */ -/* This is not (yet?) in the API, but it belongs in the built-in objects callbacks */ - ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name, const zval *key) /* {{{ */ { zend_function *fbc = NULL; @@ -1239,7 +1163,15 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_st if (ce->__call && Z_OBJ(EG(current_execute_data)->This) && instanceof_function(Z_OBJCE(EG(current_execute_data)->This), ce)) { - return zend_get_user_call_function(ce, function_name); + /* Call the top-level defined __call(). + * see: tests/classes/__call_004.phpt */ + + zend_class_entry *call_ce = Z_OBJCE(EG(current_execute_data)->This); + + while (!call_ce->__call) { + call_ce = call_ce->parent; + } + return zend_get_user_call_function(call_ce, function_name); } else if (ce->__callstatic) { return zend_get_user_callstatic_function(ce, function_name); } else { diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index c537a1617303e..77ac85039f066 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -22,7 +22,6 @@ #ifndef ZEND_OBJECT_HANDLERS_H #define ZEND_OBJECT_HANDLERS_H -union _zend_function; struct _zend_property_info; #define ZEND_WRONG_PROPERTY_INFO \ @@ -179,7 +178,16 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name); -ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS); +ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static); + +#define zend_free_trampoline(func) do { \ + if ((func) == &EG(trampoline)) { \ + EG(trampoline).common.function_name = NULL; \ + } else { \ + efree(func); \ + } \ + } while (0) + END_EXTERN_C() #endif diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 17c2041b3d068..8859d674ebeb1 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2890,7 +2890,7 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, TMPVAR|UNUSED|CV, CONST|TMPVAR|CV) } if (OP2_TYPE == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -2978,7 +2978,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE } if (OP2_TYPE == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (OP1_TYPE == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -4433,7 +4433,7 @@ ZEND_VM_C_LABEL(send_array): Z_ADDREF_P(arg); } else{ if (Z_ISREF_P(arg) && - (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) { + !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { /* don't separate references for __call */ arg = Z_REFVAL_P(arg); } @@ -4494,7 +4494,7 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY) Z_ADDREF_P(arg); } else { if (Z_ISREF_P(arg) && - (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) { + !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { /* don't separate references for __call */ arg = Z_REFVAL_P(arg); } @@ -6937,9 +6937,9 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) } OBJ_RELEASE(Z_OBJ(call->This)); } - if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) { + if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { zend_string_release(call->func->common.function_name); - efree(call->func); + zend_free_trampoline(call->func); } EX(call) = call->prev_execute_data; @@ -7558,3 +7558,142 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY) +{ + zend_array *args; + zend_function *fbc = EX(func); + zend_object *object = Z_OBJ(EX(This)); + zval *ret = EX(return_value); + zend_call_kind call_kind = EX_CALL_KIND(); + zend_class_entry *scope = EX(called_scope); + uint32_t num_args = EX_NUM_ARGS(); + zend_execute_data *call; + USE_OPLINE + + args = emalloc(sizeof(zend_array)); + zend_hash_init(args, num_args, NULL, ZVAL_PTR_DTOR, 0); + if (num_args) { + zval *p = ZEND_CALL_ARG(execute_data, 1); + zval *end = p + num_args; + + zend_hash_real_init(args, 1); + ZEND_HASH_FILL_PACKED(args) { + do { + ZEND_HASH_FILL_ADD(p); + p++; + } while (p != end); + } ZEND_HASH_FILL_END(); + } + + SAVE_OPLINE(); + call = execute_data; + execute_data = EG(current_execute_data) = EX(prev_execute_data); + zend_vm_stack_free_call_frame(call); + call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, object, execute_data); + + ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); + ZVAL_ARR(ZEND_CALL_ARG(call, 2), args); + zend_free_trampoline(fbc); + fbc = call->func; + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { + + ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_GENERATOR)); + + /* This must be already set on invokation of trampoline function */ + /*EG(scope) = fbc->common.scope;*/ + call->symbol_table = NULL; + i_init_func_execute_data(call, &fbc->op_array, + ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + + if (EXPECTED(zend_execute_ex == execute_ex)) { + ZEND_VM_ENTER(); + } else { + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); + zend_execute_ex(call); + } + } else { + zval retval; + + ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION); + + EG(scope) = object ? NULL : fbc->common.scope; + EG(current_execute_data) = call; + + if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) { + uint32_t i; + uint32_t num_args = ZEND_CALL_NUM_ARGS(call); + zval *p = ZEND_CALL_ARG(call, 1); + + EG(current_execute_data) = call; + + for (i = 0; i < num_args; ++i) { + zend_verify_internal_arg_type(fbc, i + 1, p); + if (UNEXPECTED(EG(exception) != NULL)) { + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_args(call); + zend_vm_stack_free_call_frame(call); + if (ret) { + ZVAL_UNDEF(ret); + } + ZEND_VM_C_GOTO(call_trampoline_end); + } + p++; + } + } + + if (ret == NULL) { + ZVAL_NULL(&retval); + ret = &retval; + } + Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0; + + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(call, ret); + } else { + zend_execute_internal(call, ret); + } + +#if ZEND_DEBUG + ZEND_ASSERT( + !call->func || + !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || + zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var))); +#endif + + EG(current_execute_data) = call->prev_execute_data; + + zend_vm_stack_free_args(call); + zend_vm_stack_free_call_frame(call); + + if (ret == &retval) { + zval_ptr_dtor(ret); + } + } + +ZEND_VM_C_LABEL(call_trampoline_end): + execute_data = EG(current_execute_data); + + if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_kind & ZEND_CALL_TOP)) { + ZEND_VM_RETURN(); + } + + LOAD_OPLINE(); + + if (object) { + OBJ_RELEASE(object); + } + EG(scope) = EX(func)->op_array.scope; + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL); + if (RETURN_VALUE_USED(opline)) { + zval_ptr_dtor(EX_VAR(opline->result.var)); + } + HANDLE_EXCEPTION_LEAVE(); + } + + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); +} diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 8c23e7c5b14b6..f1ad2aa7d619d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1168,7 +1168,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O Z_ADDREF_P(arg); } else{ if (Z_ISREF_P(arg) && - (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) { + !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { /* don't separate references for __call */ arg = Z_REFVAL_P(arg); } @@ -1551,9 +1551,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER( } OBJ_RELEASE(Z_OBJ(call->This)); } - if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) { + if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { zend_string_release(call->func->common.function_name); - efree(call->func); + zend_free_trampoline(call->func); } EX(call) = call->prev_execute_data; @@ -1772,6 +1772,145 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_HANDLER( ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zend_array *args; + zend_function *fbc = EX(func); + zend_object *object = Z_OBJ(EX(This)); + zval *ret = EX(return_value); + zend_call_kind call_kind = EX_CALL_KIND(); + zend_class_entry *scope = EX(called_scope); + uint32_t num_args = EX_NUM_ARGS(); + zend_execute_data *call; + USE_OPLINE + + args = emalloc(sizeof(zend_array)); + zend_hash_init(args, num_args, NULL, ZVAL_PTR_DTOR, 0); + if (num_args) { + zval *p = ZEND_CALL_ARG(execute_data, 1); + zval *end = p + num_args; + + zend_hash_real_init(args, 1); + ZEND_HASH_FILL_PACKED(args) { + do { + ZEND_HASH_FILL_ADD(p); + p++; + } while (p != end); + } ZEND_HASH_FILL_END(); + } + + SAVE_OPLINE(); + call = execute_data; + execute_data = EG(current_execute_data) = EX(prev_execute_data); + zend_vm_stack_free_call_frame(call); + call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, object, execute_data); + + ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); + ZVAL_ARR(ZEND_CALL_ARG(call, 2), args); + zend_free_trampoline(fbc); + fbc = call->func; + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { + + ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_GENERATOR)); + + /* This must be already set on invokation of trampoline function */ + /*EG(scope) = fbc->common.scope;*/ + call->symbol_table = NULL; + i_init_func_execute_data(call, &fbc->op_array, + ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + + if (EXPECTED(zend_execute_ex == execute_ex)) { + ZEND_VM_ENTER(); + } else { + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); + zend_execute_ex(call); + } + } else { + zval retval; + + ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION); + + EG(scope) = object ? NULL : fbc->common.scope; + EG(current_execute_data) = call; + + if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) { + uint32_t i; + uint32_t num_args = ZEND_CALL_NUM_ARGS(call); + zval *p = ZEND_CALL_ARG(call, 1); + + EG(current_execute_data) = call; + + for (i = 0; i < num_args; ++i) { + zend_verify_internal_arg_type(fbc, i + 1, p); + if (UNEXPECTED(EG(exception) != NULL)) { + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_args(call); + zend_vm_stack_free_call_frame(call); + if (ret) { + ZVAL_UNDEF(ret); + } + goto call_trampoline_end; + } + p++; + } + } + + if (ret == NULL) { + ZVAL_NULL(&retval); + ret = &retval; + } + Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0; + + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(call, ret); + } else { + zend_execute_internal(call, ret); + } + +#if ZEND_DEBUG + ZEND_ASSERT( + !call->func || + !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || + zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var))); +#endif + + EG(current_execute_data) = call->prev_execute_data; + + zend_vm_stack_free_args(call); + zend_vm_stack_free_call_frame(call); + + if (ret == &retval) { + zval_ptr_dtor(ret); + } + } + +call_trampoline_end: + execute_data = EG(current_execute_data); + + if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_kind & ZEND_CALL_TOP)) { + ZEND_VM_RETURN(); + } + + LOAD_OPLINE(); + + if (object) { + OBJ_RELEASE(object); + } + EG(scope) = EX(func)->op_array.scope; + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL); + if (RETURN_VALUE_USED(opline)) { + zval_ptr_dtor(EX_VAR(opline->result.var)); + } + HANDLE_EXCEPTION_LEAVE(); + } + + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); +} static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -5057,7 +5196,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C } if (IS_CONST == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_CONST == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -7006,7 +7145,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C } if (IS_UNUSED == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_CONST == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -8514,7 +8653,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C } if (IS_CV == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_CONST == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -10103,7 +10242,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_CONST == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -14013,7 +14152,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEN Z_ADDREF_P(arg); } else { if (Z_ISREF_P(arg) && - (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) { + !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { /* don't separate references for __call */ arg = Z_REFVAL_P(arg); } @@ -16161,7 +16300,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V } if (IS_CONST == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_VAR == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -17756,7 +17895,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V } if (IS_UNUSED == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_VAR == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -19443,7 +19582,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V } if (IS_CV == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_VAR == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -21085,7 +21224,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { if (IS_VAR == IS_CONST) { CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc); } else { @@ -22542,7 +22681,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C } if (IS_CONST == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -24935,7 +25074,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C } if (IS_CV == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -26444,7 +26583,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -27583,7 +27722,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND Z_ADDREF_P(arg); } else { if (Z_ISREF_P(arg) && - (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) { + !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { /* don't separate references for __call */ arg = Z_REFVAL_P(arg); } @@ -30526,7 +30665,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST } if (IS_CONST == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -35485,7 +35624,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA } if (IS_CV == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -38015,7 +38154,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -40089,7 +40228,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C } if (IS_CONST == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -42096,7 +42235,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C } if (IS_CV == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -43114,7 +43253,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } @@ -47346,31 +47485,31 @@ void zend_init_opcodes_handlers(void) ZEND_FETCH_CLASS_NAME_SPEC_HANDLER, ZEND_FETCH_CLASS_NAME_SPEC_HANDLER, ZEND_FETCH_CLASS_NAME_SPEC_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_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_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, + ZEND_CALL_TRAMPOLINE_SPEC_HANDLER, ZEND_DISCARD_EXCEPTION_SPEC_HANDLER, ZEND_DISCARD_EXCEPTION_SPEC_HANDLER, ZEND_DISCARD_EXCEPTION_SPEC_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index b234c2f33e12f..2214cd9dc4478 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -180,7 +180,7 @@ const char *zend_vm_opcodes_map[171] = { "ZEND_BIND_TRAITS", "ZEND_SEPARATE", "ZEND_FETCH_CLASS_NAME", - NULL, + "ZEND_CALL_TRAMPOLINE", "ZEND_DISCARD_EXCEPTION", "ZEND_YIELD", "ZEND_GENERATOR_RETURN", diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 84b7cd98828b1..8b9321a99177c 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -188,6 +188,7 @@ END_EXTERN_C() #define ZEND_BIND_TRAITS 155 #define ZEND_SEPARATE 156 #define ZEND_FETCH_CLASS_NAME 157 +#define ZEND_CALL_TRAMPOLINE 158 #define ZEND_DISCARD_EXCEPTION 159 #define ZEND_YIELD 160 #define ZEND_GENERATOR_RETURN 161 diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index da208f6c4b5fa..47cf107b19afa 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -265,8 +265,7 @@ static void _default_lookup_entry(zval *object, char *name, int name_len, zval * static zend_function *_copy_function(zend_function *fptr) /* {{{ */ { if (fptr - && fptr->type == ZEND_INTERNAL_FUNCTION - && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0) + && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { zend_function *copy_fptr; copy_fptr = emalloc(sizeof(zend_function)); @@ -283,11 +282,10 @@ static zend_function *_copy_function(zend_function *fptr) /* {{{ */ static void _free_function(zend_function *fptr) /* {{{ */ { if (fptr - && fptr->type == ZEND_INTERNAL_FUNCTION - && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0) + && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { zend_string_release(fptr->internal_function.function_name); - efree(fptr); + zend_free_trampoline(fptr); } } /* }}} */ @@ -2238,11 +2236,11 @@ ZEND_METHOD(reflection_parameter, __construct) if (Z_TYPE_P(parameter) == IS_LONG) { position= (int)Z_LVAL_P(parameter); if (position < 0 || (uint32_t)position >= num_args) { - if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) { + if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { if (fptr->type != ZEND_OVERLOADED_FUNCTION) { zend_string_release(fptr->common.function_name); } - efree(fptr); + zend_free_trampoline(fptr); } if (is_closure) { zval_ptr_dtor(reference); @@ -2276,11 +2274,11 @@ ZEND_METHOD(reflection_parameter, __construct) } } if (position == -1) { - if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) { + if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { if (fptr->type != ZEND_OVERLOADED_FUNCTION) { zend_string_release(fptr->common.function_name); } - efree(fptr); + zend_free_trampoline(fptr); } if (is_closure) { zval_ptr_dtor(reference); @@ -2841,8 +2839,8 @@ ZEND_METHOD(reflection_method, getClosure) } /* This is an original closure object and __invoke is to be called. */ - if (Z_OBJCE_P(obj) == zend_ce_closure && mptr->type == ZEND_INTERNAL_FUNCTION && - (mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0) + if (Z_OBJCE_P(obj) == zend_ce_closure && + (mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { RETURN_ZVAL(obj, 1, 0); } else { @@ -3043,8 +3041,7 @@ ZEND_METHOD(reflection_method, invokeArgs) /* * Copy the zend_function when calling via handler (e.g. Closure::__invoke()) */ - if (mptr->type == ZEND_INTERNAL_FUNCTION && - (mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0) { + if ((mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { fcc.function_handler = _copy_function(mptr); }