summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-04-10 23:01:00 +0300
committerDmitry Stogov <dmitry@zend.com>2015-04-10 23:01:00 +0300
commit5e8133f4537fa303acee0240fd245d2303eb972d (patch)
tree724b99c4b00ed9bfa7e3217251106ddc69e7b7e7
parent8eaa09887c9b6fc13217fe32aa95fb8230453f1b (diff)
downloadphp-git-5e8133f4537fa303acee0240fd245d2303eb972d.tar.gz
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
-rw-r--r--NEWS3
-rw-r--r--Zend/tests/bug50383.phpt26
-rw-r--r--Zend/tests/bug68412.phpt19
-rw-r--r--Zend/zend.c14
-rw-r--r--Zend/zend_API.c34
-rw-r--r--Zend/zend_builtin_functions.c9
-rw-r--r--Zend/zend_compile.h7
-rw-r--r--Zend/zend_execute_API.c9
-rw-r--r--Zend/zend_globals.h4
-rw-r--r--Zend/zend_object_handlers.c160
-rw-r--r--Zend/zend_object_handlers.h12
-rw-r--r--Zend/zend_vm_def.h151
-rw-r--r--Zend/zend_vm_execute.h233
-rw-r--r--Zend/zend_vm_opcodes.c2
-rw-r--r--Zend/zend_vm_opcodes.h1
-rw-r--r--ext/reflection/php_reflection.c23
16 files changed, 465 insertions, 242 deletions
diff --git a/NEWS b/NEWS
index fbdf82474f..fc8cf3556b 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 2210c4bc35..de81facd05 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 0000000000..c6f413cc32
--- /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 38b5bd6bc2..77e0b624c0 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 273e9e9f6a..5e1a8185fd 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 c7d12bf128..be6b11f6a8 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 322a7d32e7..57b234a12a 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 d37c2cd1d8..f79169232d 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 afd668afb8..c1b1531eab 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 00fb17451e..753c2abc78 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 c537a16173..77ac85039f 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 17c2041b3d..8859d674eb 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 8c23e7c5b1..f1ad2aa7d6 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 b234c2f33e..2214cd9dc4 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 84b7cd9882..8b9321a991 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 da208f6c4b..47cf107b19 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);
}