From 73855f7d53baa2efc2b8a88314f51c784c81b59d Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Wed, 8 Apr 2015 14:21:55 +0800 Subject: [PATCH 1/7] Opcode-fy magical __call --- Zend/tests/bug68412.phpt | 19 +++++++ Zend/zend_builtin_functions.c | 4 +- Zend/zend_execute_API.c | 5 ++ Zend/zend_object_handlers.c | 53 +++++++++++++------ Zend/zend_vm_def.h | 44 +++++++++++++++- Zend/zend_vm_execute.h | 98 ++++++++++++++++++++++++----------- Zend/zend_vm_opcodes.c | 2 +- Zend/zend_vm_opcodes.h | 1 + 8 files changed, 176 insertions(+), 50 deletions(-) create mode 100644 Zend/tests/bug68412.phpt 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_builtin_functions.c b/Zend/zend_builtin_functions.c index ffb78c343e39f..4cec5e6c01ea8 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1285,9 +1285,7 @@ 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_HANDLER) != 0) { /* 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)); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index d37c2cd1d8eb9..f58235d31f7ce 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -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_HANDLER) != 0; EG(scope) = func->common.scope; call->symbol_table = fci->symbol_table; if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) { @@ -839,6 +840,10 @@ 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; ZVAL_NULL(fci->retval); diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 00fb17451ea61..14f1fcf8b8452 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -29,6 +29,7 @@ #include "zend_interfaces.h" #include "zend_closures.h" #include "zend_compile.h" +#include "zend_vm.h" #include "zend_hash.h" #define DEBUG_OBJECT_HANDLERS 0 @@ -1036,23 +1037,45 @@ 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_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; - //??? 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); + if (ce->type == ZEND_USER_CLASS) { + zend_op_array *call_user_call = ecalloc(1, ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)) + sizeof(zend_op)); + call_user_call->type = ZEND_USER_FUNCTION; + call_user_call->scope = ce; + call_user_call->prototype = ce->__call; + call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; + call_user_call->this_var = -1; + call_user_call->filename = ce->__call->op_array.filename; + call_user_call->opcodes = (zend_op *)((char *)call_user_call + ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array))); + call_user_call->opcodes[0].opcode = ZEND_PROXY_CALL; + call_user_call->opcodes[0].op1_type = IS_UNUSED; + call_user_call->opcodes[0].op2_type = IS_UNUSED; + call_user_call->opcodes[0].result_type = IS_UNUSED; + ZEND_VM_SET_OPCODE_HANDLER(&call_user_call->opcodes[0]); + + 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); + } else { + call_user_call->function_name = zend_string_copy(method_name); + } + return (union _zend_function *)call_user_call; } else { - call_user_call->function_name = zend_string_copy(method_name); + 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; + //??? 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); + } else { + call_user_call->function_name = zend_string_copy(method_name); + } + return (union _zend_function *)call_user_call; } - - return (union _zend_function *)call_user_call; } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 17c2041b3d068..87b9bf5e3e25e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2376,6 +2376,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) } OBJ_RELEASE(object); } + EG(scope) = EX(func)->op_array.scope; if (UNEXPECTED(EG(exception) != NULL)) { @@ -2417,7 +2418,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) EG(current_execute_data) = EX(prev_execute_data); if (EX(func)->op_array.fn_flags & ZEND_ACC_CLOSURE) { OBJ_RELEASE((zend_object*)EX(func)->op_array.prototype); - } + } } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); @@ -3768,8 +3769,8 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) zend_free_op free_op1; SAVE_OPLINE(); - retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); + retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); if (!EX(return_value)) { FREE_OP1(); } else { @@ -7558,3 +7559,42 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) +{ + zval args; + zend_function *fbc = EX(func); + zend_object *obj = Z_OBJ(EX(This)); + zval *return_value = EX(return_value); + zend_call_kind call_kind = EX_CALL_KIND(); + uint32_t num_args = EX_NUM_ARGS(); + zend_execute_data *call, *prev_execute_data = EX(prev_execute_data); + + array_init_size(&args, num_args); + if (num_args) { + zval *p; + zend_hash_real_init(Z_ARRVAL(args), 1); + + p = ZEND_CALL_ARG(execute_data, 1); + ZEND_HASH_FILL_PACKED(Z_ARRVAL(args)) { + uint32_t i; + for (i = 0; i < num_args; ++i) { + ZEND_HASH_FILL_ADD(p); + p++; + } + } ZEND_HASH_FILL_END(); + } + + zend_vm_stack_free_call_frame(execute_data); + call = zend_vm_stack_push_call_frame(call_kind, + fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data); + + ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); + ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); + + efree(fbc); + + call->symbol_table = NULL; + i_init_func_execute_data(call, &call->func->op_array, return_value, 1); + + ZEND_VM_ENTER(); +} diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 8c23e7c5b14b6..370165520b942 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -466,6 +466,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_ } OBJ_RELEASE(object); } + EG(scope) = EX(func)->op_array.scope; if (UNEXPECTED(EG(exception) != NULL)) { @@ -1772,6 +1773,45 @@ 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_PROXY_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval args; + zend_function *fbc = EX(func); + zend_object *obj = Z_OBJ(EX(This)); + zval *return_value = EX(return_value); + zend_call_kind call_kind = EX_CALL_KIND(); + uint32_t num_args = EX_NUM_ARGS(); + zend_execute_data *call, *prev_execute_data = EX(prev_execute_data); + + array_init_size(&args, num_args); + if (num_args) { + zval *p; + zend_hash_real_init(Z_ARRVAL(args), 1); + + p = ZEND_CALL_ARG(execute_data, 1); + ZEND_HASH_FILL_PACKED(Z_ARRVAL(args)) { + uint32_t i; + for (i = 0; i < num_args; ++i) { + ZEND_HASH_FILL_ADD(p); + p++; + } + } ZEND_HASH_FILL_END(); + } + + zend_vm_stack_free_call_frame(execute_data); + call = zend_vm_stack_push_call_frame(call_kind, + fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data); + + ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); + ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); + + efree(fbc); + + call->symbol_table = NULL; + i_init_func_execute_data(call, &call->func->op_array, return_value, 1); + + ZEND_VM_ENTER(); +} static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -2813,8 +2853,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND SAVE_OPLINE(); - retval_ptr = EX_CONSTANT(opline->op1); + retval_ptr = EX_CONSTANT(opline->op1); if (!EX(return_value)) { } else { @@ -10617,8 +10657,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_O zend_free_op free_op1; SAVE_OPLINE(); - retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); if (!EX(return_value)) { zval_ptr_dtor_nogc(free_op1); } else { @@ -13649,8 +13689,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_O zend_free_op free_op1; SAVE_OPLINE(); - retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); if (!EX(return_value)) { zval_ptr_dtor_nogc(free_op1); } else { @@ -27222,8 +27262,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OP SAVE_OPLINE(); - retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); if (!EX(return_value)) { } else { @@ -47346,31 +47386,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_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_SPEC_HANDLER, + ZEND_PROXY_CALL_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..a1c03f3bd0d1e 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_PROXY_CALL", "ZEND_DISCARD_EXCEPTION", "ZEND_YIELD", "ZEND_GENERATOR_RETURN", diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 84b7cd98828b1..fbfafcc4f7cbe 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_PROXY_CALL 158 #define ZEND_DISCARD_EXCEPTION 159 #define ZEND_YIELD 160 #define ZEND_GENERATOR_RETURN 161 From a980fedd5b0e0683713dd4f6eaad62adf4b4732f Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Wed, 8 Apr 2015 18:35:41 +0800 Subject: [PATCH 2/7] opcode-fy magical __callStatic --- Zend/zend_object_handlers.c | 82 +++++++++++++++++++++---------------- Zend/zend_vm_def.h | 5 ++- Zend/zend_vm_execute.h | 5 ++- 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 14f1fcf8b8452..bd1a848dff540 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1035,29 +1035,37 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) } /* }}} */ +static inline zend_op_array *zend_get_proxy_call_function(zend_class_entry *ce, zend_string *method_name, int is_static) /* {{{ */ { + zend_op_array *call_user_call = ecalloc(1, ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)) + sizeof(zend_op)); + + ZEND_ASSERT(ce->type == ZEND_USER_CLASS); + + call_user_call->type = ZEND_USER_FUNCTION; + call_user_call->scope = ce; + call_user_call->prototype = is_static? ce->__callstatic : ce->__call; + call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER | (is_static? (ZEND_ACC_STATIC | ZEND_ACC_PUBLIC) : 0); + call_user_call->this_var = -1; + call_user_call->filename = is_static? ce->__callstatic->op_array. filename : ce->__call->op_array.filename; + call_user_call->opcodes = (zend_op *)((char *)call_user_call + ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array))); + call_user_call->opcodes[0].opcode = ZEND_PROXY_CALL; + call_user_call->opcodes[0].op1_type = IS_UNUSED; + call_user_call->opcodes[0].op2_type = IS_UNUSED; + call_user_call->opcodes[0].result_type = IS_UNUSED; + ZEND_VM_SET_OPCODE_HANDLER(&call_user_call->opcodes[0]); + 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); + } else { + call_user_call->function_name = zend_string_copy(method_name); + } + + return call_user_call; +} +/* }}} */ + static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ { if (ce->type == ZEND_USER_CLASS) { - zend_op_array *call_user_call = ecalloc(1, ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)) + sizeof(zend_op)); - call_user_call->type = ZEND_USER_FUNCTION; - call_user_call->scope = ce; - call_user_call->prototype = ce->__call; - call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; - call_user_call->this_var = -1; - call_user_call->filename = ce->__call->op_array.filename; - call_user_call->opcodes = (zend_op *)((char *)call_user_call + ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array))); - call_user_call->opcodes[0].opcode = ZEND_PROXY_CALL; - call_user_call->opcodes[0].op1_type = IS_UNUSED; - call_user_call->opcodes[0].op2_type = IS_UNUSED; - call_user_call->opcodes[0].result_type = IS_UNUSED; - ZEND_VM_SET_OPCODE_HANDLER(&call_user_call->opcodes[0]); - - 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); - } else { - call_user_call->function_name = zend_string_copy(method_name); - } - return (union _zend_function *)call_user_call; + return (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 0); } else { zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); call_user_call->type = ZEND_INTERNAL_FUNCTION; @@ -1206,23 +1214,27 @@ ZEND_API void zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ static inline union _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); + if (ce->type == ZEND_USER_CLASS) { + return (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 1); } else { - callstatic_user_call->function_name = zend_string_copy(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_function *)callstatic_user_call; + } } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 87b9bf5e3e25e..d593b3fc1cebf 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7591,10 +7591,11 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); - efree(fbc); call->symbol_table = NULL; - i_init_func_execute_data(call, &call->func->op_array, return_value, 1); + i_init_func_execute_data(call, &call->func->op_array, return_value, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + + efree(fbc); ZEND_VM_ENTER(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 370165520b942..df6f74c328ad7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1805,10 +1805,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); - efree(fbc); call->symbol_table = NULL; - i_init_func_execute_data(call, &call->func->op_array, return_value, 1); + i_init_func_execute_data(call, &call->func->op_array, return_value, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + + efree(fbc); ZEND_VM_ENTER(); } From 769d1d59fb48b6f7f93d7412eefbf26135fa3e59 Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Thu, 9 Apr 2015 12:19:23 +0800 Subject: [PATCH 3/7] Support internal magical __call/__callStatic --- Zend/zend_object_handlers.c | 52 ++++++------------------------------- Zend/zend_vm_def.h | 51 ++++++++++++++++++++++++++++++++---- Zend/zend_vm_execute.h | 51 ++++++++++++++++++++++++++++++++---- 3 files changed, 100 insertions(+), 54 deletions(-) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index bd1a848dff540..86c66e84de32c 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1037,15 +1037,18 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) static inline zend_op_array *zend_get_proxy_call_function(zend_class_entry *ce, zend_string *method_name, int is_static) /* {{{ */ { zend_op_array *call_user_call = ecalloc(1, ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)) + sizeof(zend_op)); + zend_function *fbc = is_static? ce->__callstatic : ce->__call; - ZEND_ASSERT(ce->type == ZEND_USER_CLASS); + ZEND_ASSERT(fbc); call_user_call->type = ZEND_USER_FUNCTION; call_user_call->scope = ce; - call_user_call->prototype = is_static? ce->__callstatic : ce->__call; + call_user_call->prototype = fbc; call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER | (is_static? (ZEND_ACC_STATIC | ZEND_ACC_PUBLIC) : 0); call_user_call->this_var = -1; - call_user_call->filename = is_static? ce->__callstatic->op_array. filename : ce->__call->op_array.filename; + call_user_call->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : STR_EMPTY_ALLOC(); + call_user_call->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0; + call_user_call->line_end = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_end : 0; call_user_call->opcodes = (zend_op *)((char *)call_user_call + ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array))); call_user_call->opcodes[0].opcode = ZEND_PROXY_CALL; call_user_call->opcodes[0].op1_type = IS_UNUSED; @@ -1064,26 +1067,7 @@ static inline zend_op_array *zend_get_proxy_call_function(zend_class_entry *ce, static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ { - if (ce->type == ZEND_USER_CLASS) { - return (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 0); - } else { - 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; - //??? 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); - } else { - call_user_call->function_name = zend_string_copy(method_name); - } - return (union _zend_function *)call_user_call; - } + return (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 0); } /* }}} */ @@ -1214,27 +1198,7 @@ ZEND_API void zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ static inline union _zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ { - if (ce->type == ZEND_USER_CLASS) { - return (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 1); - } else { - 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 (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 1); } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d593b3fc1cebf..04bd6cca0cf76 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7564,7 +7564,7 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) zval args; zend_function *fbc = EX(func); zend_object *obj = Z_OBJ(EX(This)); - zval *return_value = EX(return_value); + zval *ret = EX(return_value); zend_call_kind call_kind = EX_CALL_KIND(); uint32_t num_args = EX_NUM_ARGS(); zend_execute_data *call, *prev_execute_data = EX(prev_execute_data); @@ -7585,17 +7585,58 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) } zend_vm_stack_free_call_frame(execute_data); + call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data); ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); + if (call->func->type == ZEND_USER_FUNCTION) { + call->symbol_table = NULL; + i_init_func_execute_data(call, &call->func->op_array, + ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + efree(fbc); - call->symbol_table = NULL; - i_init_func_execute_data(call, &call->func->op_array, return_value, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + ZEND_VM_ENTER(); + } else { + zval retval; + + ZEND_ASSERT(call->func->type == ZEND_INTERNAL_FUNCTION); + SAVE_OPLINE(); + + if (ret == NULL) { + ZVAL_NULL(&retval); + ret = &retval; + } - efree(fbc); + Z_VAR_FLAGS_P(ret) = (call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0; - ZEND_VM_ENTER(); + EG(current_execute_data) = call; + + call->func->internal_function.handler(call, ret); + + execute_data = EG(current_execute_data) = call->prev_execute_data; + + efree(fbc); + + zend_vm_stack_free_args(call); + zend_vm_stack_free_call_frame(call); + + if (ret == &retval) { + zval_ptr_dtor(ret); + } + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL); + if (ret != &retval) { + zval_ptr_dtor(ret); + } + HANDLE_EXCEPTION_LEAVE(); + } + + LOAD_OPLINE(); + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); + } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index df6f74c328ad7..89fb037c94b46 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1778,7 +1778,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O zval args; zend_function *fbc = EX(func); zend_object *obj = Z_OBJ(EX(This)); - zval *return_value = EX(return_value); + zval *ret = EX(return_value); zend_call_kind call_kind = EX_CALL_KIND(); uint32_t num_args = EX_NUM_ARGS(); zend_execute_data *call, *prev_execute_data = EX(prev_execute_data); @@ -1799,19 +1799,60 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O } zend_vm_stack_free_call_frame(execute_data); + call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data); ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); + if (call->func->type == ZEND_USER_FUNCTION) { + call->symbol_table = NULL; + i_init_func_execute_data(call, &call->func->op_array, + ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + efree(fbc); - call->symbol_table = NULL; - i_init_func_execute_data(call, &call->func->op_array, return_value, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + ZEND_VM_ENTER(); + } else { + zval retval; - efree(fbc); + ZEND_ASSERT(call->func->type == ZEND_INTERNAL_FUNCTION); + SAVE_OPLINE(); - ZEND_VM_ENTER(); + if (ret == NULL) { + ZVAL_NULL(&retval); + ret = &retval; + } + + Z_VAR_FLAGS_P(ret) = (call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0; + + EG(current_execute_data) = call; + + call->func->internal_function.handler(call, ret); + + execute_data = EG(current_execute_data) = call->prev_execute_data; + + efree(fbc); + + zend_vm_stack_free_args(call); + zend_vm_stack_free_call_frame(call); + + if (ret == &retval) { + zval_ptr_dtor(ret); + } + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL); + if (ret != &retval) { + zval_ptr_dtor(ret); + } + HANDLE_EXCEPTION_LEAVE(); + } + + LOAD_OPLINE(); + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { From df7fbbf949c99f2c5ae3da2a1199235651c7cc82 Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Thu, 9 Apr 2015 15:10:02 +0800 Subject: [PATCH 4/7] Respect called_scope --- Zend/zend_vm_def.h | 4 ++-- Zend/zend_vm_execute.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 04bd6cca0cf76..2eee43468cc5e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7566,6 +7566,7 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) zend_object *obj = 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, *prev_execute_data = EX(prev_execute_data); @@ -7586,8 +7587,7 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) zend_vm_stack_free_call_frame(execute_data); - call = zend_vm_stack_push_call_frame(call_kind, - fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data); + call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, obj, prev_execute_data); ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 89fb037c94b46..006687576b63a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1780,6 +1780,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O zend_object *obj = 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, *prev_execute_data = EX(prev_execute_data); @@ -1800,8 +1801,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O zend_vm_stack_free_call_frame(execute_data); - call = zend_vm_stack_push_call_frame(call_kind, - fbc->common.prototype, 2, fbc->common.scope, obj, prev_execute_data); + call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, obj, prev_execute_data); ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name); ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, 2), &args); From ec1d9eb592db0c3b7b0e3d21e7f445ed8bccfd4d Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Thu, 9 Apr 2015 18:23:17 +0800 Subject: [PATCH 5/7] Improve performance by using prealloated op_arrray --- Zend/zend.c | 4 +++ Zend/zend_builtin_functions.c | 2 +- Zend/zend_globals.h | 3 ++ Zend/zend_object_handlers.c | 35 ++-------------------- Zend/zend_objects_API.c | 52 +++++++++++++++++++++++++++++++++ Zend/zend_objects_API.h | 13 +++++++++ Zend/zend_vm_def.h | 6 ++-- Zend/zend_vm_execute.h | 6 ++-- ext/reflection/php_reflection.c | 2 +- 9 files changed, 82 insertions(+), 41 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index a7d456876add7..ee9021c414224 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -26,6 +26,7 @@ #include "zend_list.h" #include "zend_API.h" #include "zend_exceptions.h" +#include "zend_objects_API.h" #include "zend_builtin_functions.h" #include "zend_ini.h" #include "zend_vm.h" @@ -531,6 +532,8 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ #ifdef ZEND_WIN32 zend_get_windows_version_info(&executor_globals->windows_version_info); #endif + memset(&EG(proxy_call_func), 0, sizeof(zend_op_array)); + zend_init_proxy_call_func(&EG(proxy_call_func), &EG(proxy_call_op)); } /* }}} */ @@ -722,6 +725,7 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) / #ifndef ZTS zend_init_rsrc_plist(); zend_init_exception_op(); + zend_init_proxy_call_func(&EG(proxy_call_func), &EG(proxy_call_op)); #endif zend_ini_startup(); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 4cec5e6c01ea8..e3b90b3817a56 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1292,7 +1292,7 @@ ZEND_FUNCTION(method_exists) zend_string_release(lcname); zend_string_release(func->common.function_name); - efree(func); + zend_free_proxy_call_func(func); return; } zend_string_release(lcname); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index afd668afb8715..069316774cfdb 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -237,6 +237,9 @@ struct _zend_executor_globals { XPFPA_CW_DATATYPE saved_fpu_cw; #endif + zend_op_array proxy_call_func; + zend_op proxy_call_op; + void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 86c66e84de32c..9d968c3857775 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -29,7 +29,6 @@ #include "zend_interfaces.h" #include "zend_closures.h" #include "zend_compile.h" -#include "zend_vm.h" #include "zend_hash.h" #define DEBUG_OBJECT_HANDLERS 0 @@ -1035,39 +1034,9 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) } /* }}} */ -static inline zend_op_array *zend_get_proxy_call_function(zend_class_entry *ce, zend_string *method_name, int is_static) /* {{{ */ { - zend_op_array *call_user_call = ecalloc(1, ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)) + sizeof(zend_op)); - zend_function *fbc = is_static? ce->__callstatic : ce->__call; - - ZEND_ASSERT(fbc); - - call_user_call->type = ZEND_USER_FUNCTION; - call_user_call->scope = ce; - call_user_call->prototype = fbc; - call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER | (is_static? (ZEND_ACC_STATIC | ZEND_ACC_PUBLIC) : 0); - call_user_call->this_var = -1; - call_user_call->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : STR_EMPTY_ALLOC(); - call_user_call->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0; - call_user_call->line_end = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_end : 0; - call_user_call->opcodes = (zend_op *)((char *)call_user_call + ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array))); - call_user_call->opcodes[0].opcode = ZEND_PROXY_CALL; - call_user_call->opcodes[0].op1_type = IS_UNUSED; - call_user_call->opcodes[0].op2_type = IS_UNUSED; - call_user_call->opcodes[0].result_type = IS_UNUSED; - ZEND_VM_SET_OPCODE_HANDLER(&call_user_call->opcodes[0]); - 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); - } else { - call_user_call->function_name = zend_string_copy(method_name); - } - - return call_user_call; -} -/* }}} */ - static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ { - return (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 0); + return (union _zend_function *)zend_get_proxy_call_func(ce, method_name, 0); } /* }}} */ @@ -1198,7 +1167,7 @@ ZEND_API void zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ static inline union _zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ { - return (union _zend_function *)zend_get_proxy_call_function(ce, method_name, 1); + return (union _zend_function *)zend_get_proxy_call_func(ce, method_name, 1); } /* }}} */ diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 92e36a5558e25..e9d46fb38bb00 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -22,6 +22,7 @@ #include "zend.h" #include "zend_globals.h" #include "zend_variables.h" +#include "zend_vm.h" #include "zend_API.h" #include "zend_objects_API.h" @@ -224,6 +225,57 @@ ZEND_API zend_object_handlers *zend_get_std_object_handlers(void) return &std_object_handlers; } +ZEND_API void zend_init_proxy_call_func(zend_op_array *func, zend_op *opline) +{ + func->type = ZEND_USER_FUNCTION; + + func->fn_flags = ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_PUBLIC; + func->this_var = -1; + + opline->opcode = ZEND_PROXY_CALL; + opline->op1_type = IS_UNUSED; + opline->op2_type = IS_UNUSED; + opline->result_type = IS_UNUSED; + + ZEND_VM_SET_OPCODE_HANDLER(opline); + + func->opcodes = opline; +} + +ZEND_API zend_op_array *zend_get_proxy_call_func(zend_class_entry *ce, zend_string *method_name, int is_static) +{ + zend_op_array *func; + zend_function *fbc = is_static? ce->__callstatic : ce->__call; + + ZEND_ASSERT(fbc); + + if (EXPECTED(EG(proxy_call_func).function_name == NULL)) { + func = &EG(proxy_call_func); + } else { + func = ecalloc(1, ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)) + sizeof(zend_op)); + zend_init_proxy_call_func(func, (zend_op *)((char *)func + ZEND_MM_ALIGNED_SIZE(sizeof(zend_op_array)))); + } + + if (is_static) { + func->fn_flags |= ZEND_ACC_STATIC; + } else { + func->fn_flags &= ~ZEND_ACC_STATIC; + } + + 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; + if (UNEXPECTED(strlen(method_name->val) != method_name->len)) { + func->function_name = zend_string_init(method_name->val, strlen(method_name->val), 0); + } else { + func->function_name = zend_string_copy(method_name); + } + + return func; +} + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 5bc7d774bce22..ce8b3dda2b555 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -70,8 +70,21 @@ ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects ZEND_API zend_object *zend_object_create_proxy(zval *object, zval *member); ZEND_API zend_object_handlers *zend_get_std_object_handlers(void); + +ZEND_API void zend_init_proxy_call_func(zend_op_array *func, zend_op *opline); + +ZEND_API zend_op_array *zend_get_proxy_call_func(zend_class_entry *ce, zend_string *method_name, int is_static); + END_EXTERN_C() +#define zend_free_proxy_call_func(func) do { \ + if (((zend_op_array *)func) == &EG(proxy_call_func)) { \ + ((zend_op_array *)func)->function_name = NULL; \ + } else { \ + efree(func); \ + } \ +} while (0) + static zend_always_inline void zend_object_release(zend_object *obj) { if (--GC_REFCOUNT(obj) == 0) { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 2eee43468cc5e..90d8283131c82 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6940,7 +6940,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) } if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) { zend_string_release(call->func->common.function_name); - efree(call->func); + zend_free_proxy_call_func(call->func); } EX(call) = call->prev_execute_data; @@ -7596,7 +7596,7 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) i_init_func_execute_data(call, &call->func->op_array, ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); - efree(fbc); + zend_free_proxy_call_func(fbc); ZEND_VM_ENTER(); } else { @@ -7618,7 +7618,7 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) execute_data = EG(current_execute_data) = call->prev_execute_data; - efree(fbc); + zend_free_proxy_call_func(fbc); zend_vm_stack_free_args(call); zend_vm_stack_free_call_frame(call); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 006687576b63a..96fc5be90734c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1554,7 +1554,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER( } if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) { zend_string_release(call->func->common.function_name); - efree(call->func); + zend_free_proxy_call_func(call->func); } EX(call) = call->prev_execute_data; @@ -1810,7 +1810,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O i_init_func_execute_data(call, &call->func->op_array, ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); - efree(fbc); + zend_free_proxy_call_func(fbc); ZEND_VM_ENTER(); } else { @@ -1832,7 +1832,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O execute_data = EG(current_execute_data) = call->prev_execute_data; - efree(fbc); + zend_free_proxy_call_func(fbc); zend_vm_stack_free_args(call); zend_vm_stack_free_call_frame(call); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index da208f6c4b5fa..30a1d7b351d6d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2242,7 +2242,7 @@ ZEND_METHOD(reflection_parameter, __construct) if (fptr->type != ZEND_OVERLOADED_FUNCTION) { zend_string_release(fptr->common.function_name); } - efree(fptr); + zend_free_proxy_call_func(fptr); } if (is_closure) { zval_ptr_dtor(reference); From 74f993084627061e783645a866390b68e2981698 Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Thu, 9 Apr 2015 19:03:00 +0800 Subject: [PATCH 6/7] Fixed static call --- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 90d8283131c82..591f4b0411fee 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7594,7 +7594,7 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) if (call->func->type == ZEND_USER_FUNCTION) { call->symbol_table = NULL; i_init_func_execute_data(call, &call->func->op_array, - ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + ret, (call->func->common.fn_flags & ZEND_ACC_STATIC) == 0); zend_free_proxy_call_func(fbc); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 96fc5be90734c..c3dc955e073b1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1808,7 +1808,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O if (call->func->type == ZEND_USER_FUNCTION) { call->symbol_table = NULL; i_init_func_execute_data(call, &call->func->op_array, - ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0); + ret, (call->func->common.fn_flags & ZEND_ACC_STATIC) == 0); zend_free_proxy_call_func(fbc); From 97756d9190e07a072a7b48135304dc25a964845f Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Thu, 9 Apr 2015 19:07:59 +0800 Subject: [PATCH 7/7] Ready for PR --- Zend/zend_vm_def.h | 8 +++++++- Zend/zend_vm_execute.h | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 591f4b0411fee..f708a5ca26e59 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7598,6 +7598,7 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) zend_free_proxy_call_func(fbc); + /* the previously call to current execute_data already check zend_execute_ex */ ZEND_VM_ENTER(); } else { zval retval; @@ -7614,7 +7615,12 @@ ZEND_VM_HANDLER(158, ZEND_PROXY_CALL, ANY, ANY) EG(current_execute_data) = call; - call->func->internal_function.handler(call, ret); + 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); + } execute_data = EG(current_execute_data) = call->prev_execute_data; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index c3dc955e073b1..b304f5c1aa42b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1812,6 +1812,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O zend_free_proxy_call_func(fbc); + /* the previously call to current execute_data already check zend_execute_ex */ ZEND_VM_ENTER(); } else { zval retval; @@ -1828,7 +1829,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PROXY_CALL_SPEC_HANDLER(ZEND_O EG(current_execute_data) = call; - call->func->internal_function.handler(call, ret); + 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); + } execute_data = EG(current_execute_data) = call->prev_execute_data;