com php-src: Squashed commit of the following:: NEWS Zend/tests/bug50383.phpt Zend/tests/bug68412.phpt Zend/zend.c Zend/zend_API.c
Zend/zend_builtin_functions.c Zen d/zend_compile.h Zend/zend_execute_API.c Zend/zen d_globals.h Zend/zend_object_handlers.c Zend/zend
_object_handlers.h Zend/zend_vm_def.h Zend/zend _vm_execute.h Zend/zend_vm_opcodes.c Zend/zend _vm_opcodes.h ext/reflection/php_reflection.c

From: Date: Fri, 10 Apr 2015 20:01:00 +0000
Subject: com php-src: Squashed commit of the following:: NEWS Zend/tests/bug50383.phpt Zend/tests/bug68412.phpt Zend/zend.c Zend/zend_API.c
Zend/zend_builtin_functions.c Zen d/zend_compile.h Zend/zend_execute_API.c Zend/zen d_globals.h Zend/zend_object_handlers.c Zend/zend
_object_handlers.h Zend/zend_vm_def.h Zend/zend _vm_execute.h Zend/zend_vm_opcodes.c Zend/zend _vm_opcodes.h ext/reflection/php_reflection.c
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-86158@lists.php.net to get a copy of this message
Commit: 5e8133f4537fa303acee0240fd245d2303eb972d Author: Dmitry Stogov <dmitry@zend.com> Fri, 10 Apr 2015 23:01:00 +0300 Parents: 8eaa09887c9b6fc13217fe32aa95fb8230453f1b Branches: master Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=5e8133f4537fa303acee0240fd245d2303eb972d Log: Squashed commit of the following: commit 2399fc84c541da9c2176c5b7f6dd039a3c84dc64 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Apr 10 12:38:08 2015 +0300 Removed useless assignment commit 796b6338174348eee0d74a67706d77b7ce1a60c3 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Apr 10 12:35:31 2015 +0300 Fixed execution with overriden zend_execute_ex() commit 4a9fb125aa999059f8bc42ebb6ee573c7866b35b Author: Dmitry Stogov <dmitry@zend..com> Date: Fri Apr 10 02:02:58 2015 +0300 Fixed executor without global registers commit d456c30e00589ccda35a4b57ae903ef2d3c07d95 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Apr 10 01:30:35 2015 +0300 Restored original behavior for tests/classes/__call_004.phpt commit 479646d37fef050536f1afb12b082618f1f1a3d0 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Apr 10 00:32:17 2015 +0300 Fixed test. We don't keep stack frame for fake function anymore. commit 9ae61e33e24b2c811d4ab1ca4ab22847c64a780e Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Apr 10 00:30:09 2015 +0300 Use ZEND_ACC_CALL_VIA_TRAMPOLINE instead of ZEND_ACC_CALL_VIA_HANDLER. Keep ZEND_ACC_CALL_VIA_HANDLER for compatibility. commit 0a8403a2a0c27aa3db271774f8559739a6b8400e Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Apr 10 00:05:43 2015 +0300 Rename PROXY_CALL into CALL_TRAMPLINE. Generalize API to allow reuse EG(trampline) for other purposes. commit 4ea0525c10554e36185a0b8b6303106813b6a1c2 Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Apr 9 23:22:25 2015 +0300 Reuse EG(proxy_call_op) for all proxy. Move proxy related functions from zend_objects_API to zend_object_handlers. commit 529bf737ca388ad56fb4ae20ccb81e6276f25ec0 Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Apr 9 21:42:23 2015 +0300 Accurate use of proxy_call commit 5d62837d5ba3855743fe1981786ebd65d9da0b63 Merge: 83e749f 690843f Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Apr 9 19:40:00 2015 +0300 Merge branch 'master' into opcodefy-call * master: Fixed GOTO executor Fixed typo Changed ArrayIterator implementation using zend_hash_iterator_... API. Allowed modification of itterated ArrayObject using the same behavior as proposed in Fix "foreach" behavior. Removed "Array was modified outside object and internal position is no longer valid" hack. commit 83e749ff3b6623e39b236a72e9b907d5b788ae5e Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Apr 9 19:39:10 2015 +0300 Improved ZEND_PROXY_CALL commit 0c829afc534e6d5ff27a0dea3a4815da303bd1ef Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Apr 9 15:14:49 2015 +0300 Reverted white-space changes commit df65144488afa3e9020d75e1ada5529b138afc5a Merge: 5fd2f97 97756d9 Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Apr 9 14:37:07 2015 +0300 Merge branch 'opcodefy-call' of github.com:laruence/php-src into opcodefy-call * 'opcodefy-call' of github.com:laruence/php-src: Ready for PR Fixed static call Improve performance by using prealloated op_arrray Respect called_scope Support internal magical __call/__callStatic opcode-fy magical __callStatic Opcode-fy magical __call commit 97756d9190e07a072a7b48135304dc25a964845f Author: Xinchen Hui <laruence@gmail.com> Date: Thu Apr 9 19:07:59 2015 +0800 Ready for PR commit 74f993084627061e783645a866390b68e2981698 Author: Xinchen Hui <laruence@gmail.com> Date: Thu Apr 9 19:03:00 2015 +0800 Fixed static call commit ec1d9eb592db0c3b7b0e3d21e7f445ed8bccfd4d Author: Xinchen Hui <laruence@gmail.com> Date: Thu Apr 9 18:23:17 2015 +0800 Improve performance by using prealloated op_arrray commit df7fbbf949c99f2c5ae3da2a1199235651c7cc82 Author: Xinchen Hui <laruence@gmail.com> Date: Thu Apr 9 15:10:02 2015 +0800 Respect called_scope commit 769d1d59fb48b6f7f93d7412eefbf26135fa3e59 Author: Xinchen Hui <laruence@gmail.com> Date: Thu Apr 9 12:19:23 2015 +0800 Support internal magical __call/__callStatic commit a980fedd5b0e0683713dd4f6eaad62adf4b4732f Author: Xinchen Hui <laruence@gmail.com> Date: Wed Apr 8 18:35:41 2015 +0800 opcode-fy magical __callStatic commit 73855f7d53baa2efc2b8a88314f51c784c81b59d Author: Xinchen Hui <laruence@gmail.com> Date: Wed Apr 8 14:21:55 2015 +0800 Opcode-fy magical __call Changed paths: M NEWS M Zend/tests/bug50383.phpt A Zend/tests/bug68412.phpt M Zend/zend.c M Zend/zend_API.c M Zend/zend_builtin_functions.c M Zend/zend_compile.h M Zend/zend_execute_API.c M Zend/zend_globals.h M Zend/zend_object_handlers.c M Zend/zend_object_handlers.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/reflection/php_reflection.c

diff --git a/NEWS b/NEWS index fbdf824..fc8cf35 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,9 @@ . Update the MIME type list from the one shipped by Apache HTTPD. (Adam) - Core: + . Improved __call() and __callStatic() magic method handling. Now they are + called in a stackless way using ZEND_CALL_TRAMPOLINE opcode, without + additional stack frame. (Laruence, Dmitry) . Fixed weird operators behavior. Division by zero now emits warning and returns +/-INF, modulo by zero and intdid() throws an exception, shifts by negative offset throw exceptions. Compile-time evaluation of division diff --git a/Zend/tests/bug50383.phpt b/Zend/tests/bug50383.phpt index 2210c4b..de81fac 100644 --- a/Zend/tests/bug50383.phpt +++ b/Zend/tests/bug50383.phpt @@ -60,19 +60,6 @@ Array [1] => Array ( [file] => %s - [line] => 13 - [function] => ThrowException - [class] => myClass - [type] => :: - [args] => Array - ( - ) - - ) - - [2] => Array - ( - [file] => %s [line] => 21 [function] => thrower [args] => Array @@ -106,19 +93,6 @@ Array [1] => Array ( [file] => %s - [line] => 17 - [function] => foo - [class] => myClass - [type] => -> - [args] => Array - ( - ) - - ) - - [2] => Array - ( - [file] => %s [line] => 28 [function] => thrower2 [args] => Array diff --git a/Zend/tests/bug68412.phpt b/Zend/tests/bug68412.phpt new file mode 100644 index 0000000..c6f413c --- /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-- +<?php +class C { + public function __call($x, $y) { + global $z; + $z->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 38b5bd6..77e0b62 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 273e9e9..5e1a818 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 @@ get_function_via_handler: 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 @@ get_function_via_handler: } 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 @@ again: 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 @@ again: 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 c7d12bf..be6b11f 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 322a7d3..57b234a 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 d37c2cd..f791692 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 afd668a..c1b1531 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 00fb174..753c2ab 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 c537a16..77ac850 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 17c2041..8859d67 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 8c23e7c..f1ad2aa 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1168,7 +1168,7 @@ 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); } @@ -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 b234c2f..2214cd9 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 84b7cd9..8b9321a 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 da208f6..47cf107 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); }
« previous php.cvs (#86158) next »