summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2014-07-11 00:32:18 +0400
committerDmitry Stogov <dmitry@zend.com>2014-07-11 00:32:18 +0400
commitde306e70882dd7dd04ea952ef3c665304837c3be (patch)
tree550152c8ade9c90ffb232430c4661ee66dab1e89
parent4e291fab81eb8c7517d673705ae8a46bf178724b (diff)
downloadphp-git-de306e70882dd7dd04ea952ef3c665304837c3be.tar.gz
Implement call_user_func() and call_user_func_array() using special opcodes.
In some rare cases it leads to insignificant changes in error messages.
-rw-r--r--Zend/tests/001.phpt2
-rw-r--r--Zend/tests/002.phpt2
-rw-r--r--Zend/tests/003.phpt2
-rw-r--r--Zend/tests/call_user_func_004.phpt2
-rw-r--r--Zend/tests/call_user_func_005.phpt2
-rw-r--r--Zend/tests/fr47160.phpt2
-rw-r--r--Zend/tests/objects_027.phpt2
-rw-r--r--Zend/zend_compile.c184
-rw-r--r--Zend/zend_execute.c17
-rw-r--r--Zend/zend_execute_API.c1
-rw-r--r--Zend/zend_vm_def.h237
-rw-r--r--Zend/zend_vm_execute.h521
-rw-r--r--Zend/zend_vm_opcodes.c6
-rw-r--r--Zend/zend_vm_opcodes.h3
-rw-r--r--ext/opcache/Optimizer/optimize_func_calls.c1
15 files changed, 930 insertions, 54 deletions
diff --git a/Zend/tests/001.phpt b/Zend/tests/001.phpt
index 78d982a7ad..bec7d8adbc 100644
--- a/Zend/tests/001.phpt
+++ b/Zend/tests/001.phpt
@@ -44,7 +44,7 @@ int(0)
int(2)
int(0)
-Warning: Missing argument 2 for test3() in %s on line %d
+Warning: Missing argument 2 for test3()%s
int(1)
int(2)
int(1)
diff --git a/Zend/tests/002.phpt b/Zend/tests/002.phpt
index 73c8d3ec43..b01c7fa329 100644
--- a/Zend/tests/002.phpt
+++ b/Zend/tests/002.phpt
@@ -85,7 +85,7 @@ bool(false)
Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)
-Warning: Missing argument 2 for test3() in %s on line %d
+Warning: Missing argument 2 for test3()%s
int(1)
Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
diff --git a/Zend/tests/003.phpt b/Zend/tests/003.phpt
index 81f70b8d87..5c3b83d25e 100644
--- a/Zend/tests/003.phpt
+++ b/Zend/tests/003.phpt
@@ -60,7 +60,7 @@ array(2) {
array(0) {
}
-Warning: Missing argument 2 for test3() in %s on line %d
+Warning: Missing argument 2 for test3()%s
array(1) {
[0]=>
int(1)
diff --git a/Zend/tests/call_user_func_004.phpt b/Zend/tests/call_user_func_004.phpt
index 4885c4d3fa..9df650ec9d 100644
--- a/Zend/tests/call_user_func_004.phpt
+++ b/Zend/tests/call_user_func_004.phpt
@@ -13,6 +13,6 @@ call_user_func(array('foo', 'teste'));
?>
--EXPECTF--
-Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method foo::teste() should not be called statically in %s on line %d
+Strict Standards: %son-static method foo::teste() should not be called statically in %s on line %d
Fatal error: Using $this when not in object context in %s on line %d
diff --git a/Zend/tests/call_user_func_005.phpt b/Zend/tests/call_user_func_005.phpt
index 6c1fa19733..a602f59a12 100644
--- a/Zend/tests/call_user_func_005.phpt
+++ b/Zend/tests/call_user_func_005.phpt
@@ -18,7 +18,7 @@ var_dump(call_user_func(array('foo', 'teste')));
?>
--EXPECTF--
-Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method foo::teste() should not be called statically in %s on line %d
+Strict Standards: %son-static method foo::teste() should not be called statically in %s on line %d
%string|unicode%(1) "x"
array(1) {
[0]=>
diff --git a/Zend/tests/fr47160.phpt b/Zend/tests/fr47160.phpt
index 809e1f1603..898e0eda97 100644
--- a/Zend/tests/fr47160.phpt
+++ b/Zend/tests/fr47160.phpt
@@ -105,7 +105,7 @@ Hello, you
Notice: Undefined variable: this in %s on line %d
NULL
-Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method Hello::world() should not be called statically in %s on line %d
+Strict Standards: %son-static method Hello::world() should not be called statically in %s on line %d
Hello, you
Notice: Undefined variable: this in %s on line %d
diff --git a/Zend/tests/objects_027.phpt b/Zend/tests/objects_027.phpt
index 184b471cb4..e3d399d3bc 100644
--- a/Zend/tests/objects_027.phpt
+++ b/Zend/tests/objects_027.phpt
@@ -35,7 +35,7 @@ object(foo)#%d (0) {
object(foo)#%d (0) {
}
-Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method foo::test() should not be called statically in %s on line %d
+Strict Standards: %son-static method foo::test() should not be called statically in %s on line %d
Strict Standards: Non-static method bar::show() should not be called statically in %s on line %d
object(foo)#%d (0) {
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 6b40dbb35d..132def4b14 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2539,6 +2539,170 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na
}
/* }}} */
+static int zend_do_convert_call(zend_op *init_opline, zend_op *opline, long num_args, zend_function **func_ptr TSRMLS_CC) /* {{{ */
+{
+ zval *function_name;
+ zend_string *lcname;
+ zend_function *function;
+
+ *func_ptr = NULL;
+ if (opline->op1_type == IS_CONST && Z_TYPE(CONSTANT(opline->op1.constant)) == IS_STRING) {
+ function_name = &CONSTANT(opline->op1.constant);
+ lcname = STR_ALLOC(Z_STRLEN_P(function_name), 0);
+ zend_str_tolower_copy(lcname->val, Z_STRVAL_P(function_name), Z_STRLEN_P(function_name));
+ if (((function = zend_hash_find_ptr(CG(function_table), lcname)) == NULL) ||
+ ((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) &&
+ (function->type == ZEND_INTERNAL_FUNCTION))) {
+ function = NULL;
+ STR_RELEASE(lcname);
+ opline->opcode = ZEND_INIT_USER_CALL;
+ opline->extended_value = num_args;
+ opline->op2_type = opline->op1_type;
+ opline->op2 = opline->op1;
+ opline->op1_type = IS_CONST;
+ opline->op1 = init_opline->op2;
+ SET_UNUSED(opline->result);
+ MAKE_NOP(init_opline);
+ return 1;
+ } else {
+ STR_RELEASE(Z_STR_P(function_name));
+ Z_STR_P(function_name) = zend_new_interned_string(lcname TSRMLS_CC);
+ opline->opcode = ZEND_INIT_FCALL;
+ opline->extended_value = num_args;
+ opline->op2_type = IS_CONST;
+ opline->op2.constant = opline->op1.constant;
+ GET_CACHE_SLOT(opline->op2.constant);
+ SET_UNUSED(opline->op1);
+ SET_UNUSED(opline->result);
+ MAKE_NOP(init_opline);
+ *func_ptr = function;
+ return 1;
+ }
+ } else {
+ opline->opcode = ZEND_INIT_USER_CALL;
+ opline->extended_value = num_args;
+ opline->op2_type = opline->op1_type;
+ opline->op2 = opline->op1;
+ opline->op1_type = IS_CONST;
+ opline->op1 = init_opline->op2;
+ SET_UNUSED(opline->result);
+ MAKE_NOP(init_opline);
+ return 1;
+ }
+ return 0;
+}
+/* }}} */
+
+static int zend_do_convert_call_user_func(zend_op *init_opline, zend_uint num_args TSRMLS_DC) /* {{{ */
+{
+ zend_op *opline = init_opline + 1;
+ int level = 0;
+ int converted = 0;
+ zend_uint arg_num = 0;
+ zend_function *func = NULL;
+
+ while (1) {
+ switch (opline->opcode) {
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_UNPACK:
+ if (level == 0) {
+ if (!converted) {
+ if (opline->opcode == ZEND_SEND_UNPACK) {
+ return 0;
+ }
+ if (!zend_do_convert_call(init_opline, opline, init_opline->extended_value - 1, &func TSRMLS_CC)) {
+ return 0;
+ }
+ converted = 1;
+ } else {
+ /* Use ZEND_SEND_USER instruction for parameters that may pass by reference */
+ if (opline->opcode != ZEND_SEND_VAL &&
+ (func == NULL ||
+ ARG_SHOULD_BE_SENT_BY_REF(func, opline->op2.num-1))) {
+ opline->opcode = ZEND_SEND_USER;
+ }
+ opline->op2.num--;
+ }
+ if (++arg_num == num_args) {
+ return 1;
+ }
+ }
+ break;
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_NEW:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ case ZEND_INIT_FCALL:
+ level++;
+ break;
+ case ZEND_DO_FCALL:
+ level--;
+ break;
+ }
+ opline++;
+ }
+}
+/* }}} */
+
+static int zend_do_convert_call_user_func_array(zend_op *init_opline TSRMLS_DC) /* {{{ */
+{
+ zend_op *opline = init_opline + 1;
+ zend_op *send1 = NULL, *send2 = NULL;
+ int level = 0;
+ zend_function *func;
+
+ do {
+ switch (opline->opcode) {
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_UNPACK:
+ if (level == 0) {
+ if (opline->opcode == ZEND_SEND_UNPACK) {
+ return 0;
+ }
+ if (send1 == NULL) {
+ send1 = opline;
+ } else {
+ send2 = opline;
+ }
+ }
+ break;
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_NEW:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ case ZEND_INIT_FCALL:
+ level++;
+ break;
+ case ZEND_DO_FCALL:
+ level--;
+ break;
+ }
+ opline++;
+ } while (send2 == NULL);
+
+ if (!zend_do_convert_call(init_opline, send1, 0, &func TSRMLS_CC)) {
+ return 0;
+ }
+
+ send2->opcode = ZEND_SEND_ARRAY;
+ send2->extended_value = 0;
+ send2->op2.num--;
+ return 1;
+}
+/* }}} */
+
void zend_do_end_function_call(znode *function_name, znode *result, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */
{
zend_op *opline;
@@ -2558,6 +2722,26 @@ void zend_do_end_function_call(znode *function_name, znode *result, int is_metho
if (opline->opcode == ZEND_NEW) {
call_flags = ZEND_CALL_CTOR;
+ } else {
+ zend_function *func = fcall->fbc;
+ if (func && func->type == ZEND_INTERNAL_FUNCTION) {
+ /* Convert calls to some internal functions into built-ins */
+ if (func->common.function_name->len == sizeof("call_user_func")-1 &&
+ memcmp(func->common.function_name->val, "call_user_func", sizeof("call_user_func")-1) == 0) {
+ if (fcall->arg_num > 0) {
+ if (zend_do_convert_call_user_func(opline, fcall->arg_num TSRMLS_CC)) {
+ fcall->arg_num--;
+ }
+ }
+ } else if (func->common.function_name->len == sizeof("call_user_func_array")-1 &&
+ memcmp(func->common.function_name->val, "call_user_func_array", sizeof("call_user_func_array")-1) == 0) {
+ if (fcall->arg_num == 2) {
+ if (zend_do_convert_call_user_func_array(opline TSRMLS_CC)) {
+ fcall->arg_num = 0;
+ }
+ }
+ }
+ }
}
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 9ca6d9c9c7..7fca796137 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -76,6 +76,23 @@ static zend_always_inline void zend_pzval_unlock_func(zval *z, zend_free_op *sho
}
}
+static ZEND_FUNCTION(pass)
+{
+}
+
+static const zend_internal_function zend_pass_function = {
+ ZEND_INTERNAL_FUNCTION, /* type */
+ NULL, /* name */
+ NULL, /* scope */
+ 0, /* fn_flags */
+ NULL, /* prototype */
+ 0, /* num_args */
+ 0, /* required_num_args */
+ NULL, /* arg_info */
+ ZEND_FN(pass), /* handler */
+ NULL /* module */
+};
+
#undef zval_ptr_dtor
#define zval_ptr_dtor(zv) i_zval_ptr_dtor(zv ZEND_FILE_LINE_CC TSRMLS_CC)
#define zval_ptr_dtor_nogc(zv) i_zval_ptr_dtor_nogc(zv ZEND_FILE_LINE_CC TSRMLS_CC)
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 509cb91113..308d54b31d 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -825,7 +825,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
} else if (Z_ISREF(fci->params[i]) &&
/* don't separate references for __call */
(func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0 ) {
- param = &tmp;
param = ZEND_CALL_ARG(call, i+1);
ZVAL_DUP(param, Z_REFVAL(fci->params[i]));
} else {
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index d571919cb2..e6a69a10b6 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -2507,6 +2507,47 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
}
}
+ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMP|VAR|CV)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *function_name = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
+ zend_fcall_info_cache fcc;
+ char *error = NULL;
+ zend_function *func;
+ zend_class_entry *called_scope;
+ zend_object *object;
+
+ if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
+ if (error) {
+ efree(error);
+ }
+ func = fcc.function_handler;
+ if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ /* Delay closure destruction until its invocation */
+ func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
+ Z_ADDREF_P(function_name);
+ }
+ called_scope = fcc.called_scope;
+ object = fcc.object;
+ if (object) {
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ } else {
+ zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
+ efree(error);
+ func = (zend_function*)&zend_pass_function;
+ called_scope = NULL;
+ object = NULL;
+ }
+
+ EX(call) = zend_vm_stack_push_call_frame(
+ func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
+
+ FREE_OP2();
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST)
{
@@ -3287,6 +3328,202 @@ ZEND_VM_C_LABEL(unpack_iter_dtor):
ZEND_VM_NEXT_OPCODE();
}
+ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *args;
+ SAVE_OPLINE();
+
+ args = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
+
+ if (Z_TYPE_P(args) != IS_ARRAY) {
+ zend_error(E_WARNING, "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args)));
+ if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
+ }
+ if (EX(call)->object) {
+ OBJ_RELEASE(EX(call)->object);
+ }
+ EX(call)->func = (zend_function*)&zend_pass_function;
+ EX(call)->called_scope = NULL;
+ EX(call)->object = NULL;
+ } else {
+ zend_uint arg_num = 1;
+
+ HashTable *ht = Z_ARRVAL_P(args);
+ zval *arg, *param, tmp;
+
+ zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht) TSRMLS_CC);
+
+ if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_TMP_VAR && Z_IMMUTABLE_P(args)) {
+ zend_uint i;
+ int separate = 0;
+
+ /* check if any of arguments are going to be passed by reference */
+ for (i = 0; i < zend_hash_num_elements(ht); i++) {
+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) {
+ separate = 1;
+ break;
+ }
+ }
+ if (separate) {
+ zval_copy_ctor(args);
+ ht = Z_ARRVAL_P(args);
+ }
+ }
+
+ param = ZEND_CALL_ARG(EX(call), arg_num);
+ ZEND_HASH_FOREACH_VAL(ht, arg) {
+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ // TODO: Scalar values don't have reference counters anymore.
+ // They are assumed to be 1, and they may be easily passed by
+ // reference now. However, previously scalars with refcount==1
+ // might be passed and with refcount>1 might not. We can support
+ // only single behavior ???
+#if 0
+ if (Z_REFCOUNTED_P(arg) &&
+ // This solution breaks the following test (omit warning message) ???
+ // Zend/tests/bug61273.phpt
+ // ext/reflection/tests/bug42976.phpt
+ // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
+#else
+ if (!Z_REFCOUNTED_P(arg) ||
+ // This solution breaks the following test (emit warning message) ???
+ // ext/pdo_sqlite/tests/pdo_005.phpt
+#endif
+ (!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
+
+ if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+
+ zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
+ arg_num,
+ EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
+ EX(call)->func->common.scope ? "::" : "",
+ EX(call)->func->common.function_name->val);
+
+ if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
+ }
+ if (EX(call)->object) {
+ OBJ_RELEASE(EX(call)->object);
+ }
+ EX(call)->func = (zend_function*)&zend_pass_function;
+ EX(call)->called_scope = NULL;
+ EX(call)->object = NULL;
+
+ break;
+ }
+
+ if (Z_REFCOUNTED_P(arg)) {
+ Z_DELREF_P(arg);
+ }
+ ZVAL_DUP(&tmp, arg);
+ ZVAL_NEW_REF(arg, &tmp);
+ Z_ADDREF_P(arg);
+ } else if (!Z_ISREF_P(arg)) {
+ ZVAL_NEW_REF(arg, arg);
+ Z_ADDREF_P(arg);
+ } else if (Z_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
+ ZVAL_COPY_VALUE(param, arg);
+ } else if (Z_ISREF_P(arg) &&
+ /* don't separate references for __call */
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ ZVAL_DUP(param, Z_REFVAL_P(arg));
+ } else {
+ ZVAL_COPY(param, arg);
+ }
+ EX(call)->num_args++;
+ arg_num++;
+ param++;
+ } ZEND_HASH_FOREACH_END();
+ }
+ FREE_OP1();
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
+{
+ USE_OPLINE
+ zval *arg, *param, tmp;
+ zend_free_op free_op1;
+
+ arg = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ param = ZEND_CALL_ARG(EX(call), opline->op2.num);
+
+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+ // TODO: Scalar values don't have reference counters anymore.
+ // They are assumed to be 1, and they may be easily passed by
+ // reference now. However, previously scalars with refcount==1
+ // might be passed and with refcount>1 might not. We can support
+ // only single behavior ???
+#if 0
+ if (Z_REFCOUNTED_P(arg) &&
+ // This solution breaks the following test (omit warning message) ???
+ // Zend/tests/bug61273.phpt
+ // ext/reflection/tests/bug42976.phpt
+ // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
+#else
+ if (!Z_REFCOUNTED_P(arg) ||
+ // This solution breaks the following test (emit warning message) ???
+ // ext/pdo_sqlite/tests/pdo_005.phpt
+#endif
+ (!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
+
+ if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+
+ zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
+ opline->op2.num,
+ EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
+ EX(call)->func->common.scope ? "::" : "",
+ EX(call)->func->common.function_name->val);
+
+ if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
+ }
+ if (EX(call)->object) {
+ OBJ_RELEASE(EX(call)->object);
+ }
+ EX(call)->func = (zend_function*)&zend_pass_function;
+ EX(call)->called_scope = NULL;
+ EX(call)->object = NULL;
+
+ FREE_OP1();
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ if (Z_REFCOUNTED_P(arg)) {
+ Z_DELREF_P(arg);
+ }
+ ZVAL_DUP(&tmp, arg);
+ ZVAL_NEW_REF(arg, &tmp);
+ Z_ADDREF_P(arg);
+ } else if (!Z_ISREF_P(arg)) {
+ ZVAL_NEW_REF(arg, arg);
+ Z_ADDREF_P(arg);
+ } else if (Z_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
+ ZVAL_COPY_VALUE(param, arg);
+ } else if (Z_ISREF_P(arg) &&
+ /* don't separate references for __call */
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ ZVAL_DUP(param, Z_REFVAL_P(arg));
+ } else {
+ ZVAL_COPY(param, arg);
+ }
+
+ EX(call)->num_args = opline->op2.num;
+
+ FREE_OP1();
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY)
{
USE_OPLINE
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 6a7c47f2f9..95cd54f494 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -888,6 +888,123 @@ unpack_iter_dtor:
ZEND_VM_NEXT_OPCODE();
}
+static int ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *args;
+ SAVE_OPLINE();
+
+ args = get_zval_ptr_deref(opline->op1_type, &opline->op1, execute_data, &free_op1, BP_VAR_R);
+
+ if (Z_TYPE_P(args) != IS_ARRAY) {
+ zend_error(E_WARNING, "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args)));
+ if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
+ }
+ if (EX(call)->object) {
+ OBJ_RELEASE(EX(call)->object);
+ }
+ EX(call)->func = (zend_function*)&zend_pass_function;
+ EX(call)->called_scope = NULL;
+ EX(call)->object = NULL;
+ } else {
+ zend_uint arg_num = 1;
+
+ HashTable *ht = Z_ARRVAL_P(args);
+ zval *arg, *param, tmp;
+
+ zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht) TSRMLS_CC);
+
+ if (opline->op1_type != IS_CONST && opline->op1_type != IS_TMP_VAR && Z_IMMUTABLE_P(args)) {
+ zend_uint i;
+ int separate = 0;
+
+ /* check if any of arguments are going to be passed by reference */
+ for (i = 0; i < zend_hash_num_elements(ht); i++) {
+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) {
+ separate = 1;
+ break;
+ }
+ }
+ if (separate) {
+ zval_copy_ctor(args);
+ ht = Z_ARRVAL_P(args);
+ }
+ }
+
+ param = ZEND_CALL_ARG(EX(call), arg_num);
+ ZEND_HASH_FOREACH_VAL(ht, arg) {
+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ // TODO: Scalar values don't have reference counters anymore.
+ // They are assumed to be 1, and they may be easily passed by
+ // reference now. However, previously scalars with refcount==1
+ // might be passed and with refcount>1 might not. We can support
+ // only single behavior ???
+#if 0
+ if (Z_REFCOUNTED_P(arg) &&
+ // This solution breaks the following test (omit warning message) ???
+ // Zend/tests/bug61273.phpt
+ // ext/reflection/tests/bug42976.phpt
+ // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
+#else
+ if (!Z_REFCOUNTED_P(arg) ||
+ // This solution breaks the following test (emit warning message) ???
+ // ext/pdo_sqlite/tests/pdo_005.phpt
+#endif
+ (!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
+
+ if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+
+ zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
+ arg_num,
+ EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
+ EX(call)->func->common.scope ? "::" : "",
+ EX(call)->func->common.function_name->val);
+
+ if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
+ }
+ if (EX(call)->object) {
+ OBJ_RELEASE(EX(call)->object);
+ }
+ EX(call)->func = (zend_function*)&zend_pass_function;
+ EX(call)->called_scope = NULL;
+ EX(call)->object = NULL;
+
+ break;
+ }
+
+ if (Z_REFCOUNTED_P(arg)) {
+ Z_DELREF_P(arg);
+ }
+ ZVAL_DUP(&tmp, arg);
+ ZVAL_NEW_REF(arg, &tmp);
+ Z_ADDREF_P(arg);
+ } else if (!Z_ISREF_P(arg)) {
+ ZVAL_NEW_REF(arg, arg);
+ Z_ADDREF_P(arg);
+ } else if (Z_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
+ ZVAL_COPY_VALUE(param, arg);
+ } else if (Z_ISREF_P(arg) &&
+ /* don't separate references for __call */
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ ZVAL_DUP(param, Z_REFVAL_P(arg));
+ } else {
+ ZVAL_COPY(param, arg);
+ }
+ EX(call)->num_args++;
+ arg_num++;
+ param++;
+ } ZEND_HASH_FOREACH_END();
+ }
+ FREE_OP(free_op1);
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -1566,7 +1683,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
}
}
-
static int ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -1896,7 +2012,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
}
}
-
static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -2071,7 +2186,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
}
}
-
static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -2284,7 +2398,6 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
}
}
-
static int ZEND_FASTCALL ZEND_BW_NOT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -3828,6 +3941,47 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER(
ZEND_VM_NEXT_OPCODE();
}
+static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *function_name = opline->op2.zv;
+ zend_fcall_info_cache fcc;
+ char *error = NULL;
+ zend_function *func;
+ zend_class_entry *called_scope;
+ zend_object *object;
+
+ if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
+ if (error) {
+ efree(error);
+ }
+ func = fcc.function_handler;
+ if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ /* Delay closure destruction until its invocation */
+ func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
+ Z_ADDREF_P(function_name);
+ }
+ called_scope = fcc.called_scope;
+ object = fcc.object;
+ if (object) {
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ } else {
+ zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
+ efree(error);
+ func = (zend_function*)&zend_pass_function;
+ called_scope = NULL;
+ object = NULL;
+ }
+
+ EX(call) = zend_vm_stack_push_call_frame(
+ func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
+
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static int ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -4796,6 +4950,48 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMP_HANDLER(ZE
ZEND_VM_NEXT_OPCODE();
}
+static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *function_name = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2 TSRMLS_CC);
+ zend_fcall_info_cache fcc;
+ char *error = NULL;
+ zend_function *func;
+ zend_class_entry *called_scope;
+ zend_object *object;
+
+ if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
+ if (error) {
+ efree(error);
+ }
+ func = fcc.function_handler;
+ if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ /* Delay closure destruction until its invocation */
+ func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
+ Z_ADDREF_P(function_name);
+ }
+ called_scope = fcc.called_scope;
+ object = fcc.object;
+ if (object) {
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ } else {
+ zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
+ efree(error);
+ func = (zend_function*)&zend_pass_function;
+ called_scope = NULL;
+ object = NULL;
+ }
+
+ EX(call) = zend_vm_stack_push_call_frame(
+ func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
+
+ zval_dtor(free_op2.var);
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static int ZEND_FASTCALL ZEND_CASE_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -5634,6 +5830,48 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_VAR_HANDLER(ZE
ZEND_VM_NEXT_OPCODE();
}
+static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *function_name = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2 TSRMLS_CC);
+ zend_fcall_info_cache fcc;
+ char *error = NULL;
+ zend_function *func;
+ zend_class_entry *called_scope;
+ zend_object *object;
+
+ if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
+ if (error) {
+ efree(error);
+ }
+ func = fcc.function_handler;
+ if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ /* Delay closure destruction until its invocation */
+ func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
+ Z_ADDREF_P(function_name);
+ }
+ called_scope = fcc.called_scope;
+ object = fcc.object;
+ if (object) {
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ } else {
+ zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
+ efree(error);
+ func = (zend_function*)&zend_pass_function;
+ called_scope = NULL;
+ object = NULL;
+ }
+
+ EX(call) = zend_vm_stack_push_call_frame(
+ func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
+
+ zval_ptr_dtor_nogc(free_op2.var);
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static int ZEND_FASTCALL ZEND_CASE_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -7164,6 +7402,47 @@ static int ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CV_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE();
}
+static int ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *function_name = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var TSRMLS_CC);
+ zend_fcall_info_cache fcc;
+ char *error = NULL;
+ zend_function *func;
+ zend_class_entry *called_scope;
+ zend_object *object;
+
+ if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error TSRMLS_CC)) {
+ if (error) {
+ efree(error);
+ }
+ func = fcc.function_handler;
+ if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ /* Delay closure destruction until its invocation */
+ func->common.prototype = (zend_function*)Z_OBJ_P(function_name);
+ Z_ADDREF_P(function_name);
+ }
+ called_scope = fcc.called_scope;
+ object = fcc.object;
+ if (object) {
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ } else {
+ zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(opline->op1.zv), error);
+ efree(error);
+ func = (zend_function*)&zend_pass_function;
+ called_scope = NULL;
+ object = NULL;
+ }
+
+ EX(call) = zend_vm_stack_push_call_frame(
+ func, opline->extended_value, 0, called_scope, object, EX(call) TSRMLS_CC);
+
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static int ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -13127,6 +13406,85 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
ZEND_VM_NEXT_OPCODE();
}
+static int ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *arg, *param, tmp;
+ zend_free_op free_op1;
+
+ arg = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
+ param = ZEND_CALL_ARG(EX(call), opline->op2.num);
+
+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+ // TODO: Scalar values don't have reference counters anymore.
+ // They are assumed to be 1, and they may be easily passed by
+ // reference now. However, previously scalars with refcount==1
+ // might be passed and with refcount>1 might not. We can support
+ // only single behavior ???
+#if 0
+ if (Z_REFCOUNTED_P(arg) &&
+ // This solution breaks the following test (omit warning message) ???
+ // Zend/tests/bug61273.phpt
+ // ext/reflection/tests/bug42976.phpt
+ // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
+#else
+ if (!Z_REFCOUNTED_P(arg) ||
+ // This solution breaks the following test (emit warning message) ???
+ // ext/pdo_sqlite/tests/pdo_005.phpt
+#endif
+ (!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
+
+ if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+
+ zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
+ opline->op2.num,
+ EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
+ EX(call)->func->common.scope ? "::" : "",
+ EX(call)->func->common.function_name->val);
+
+ if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
+ }
+ if (EX(call)->object) {
+ OBJ_RELEASE(EX(call)->object);
+ }
+ EX(call)->func = (zend_function*)&zend_pass_function;
+ EX(call)->called_scope = NULL;
+ EX(call)->object = NULL;
+
+ zval_ptr_dtor_nogc(free_op1.var);
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ if (Z_REFCOUNTED_P(arg)) {
+ Z_DELREF_P(arg);
+ }
+ ZVAL_DUP(&tmp, arg);
+ ZVAL_NEW_REF(arg, &tmp);
+ Z_ADDREF_P(arg);
+ } else if (!Z_ISREF_P(arg)) {
+ ZVAL_NEW_REF(arg, arg);
+ Z_ADDREF_P(arg);
+ } else if (Z_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
+ ZVAL_COPY_VALUE(param, arg);
+ } else if (Z_ISREF_P(arg) &&
+ /* don't separate references for __call */
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ ZVAL_DUP(param, Z_REFVAL_P(arg));
+ } else {
+ ZVAL_COPY(param, arg);
+ }
+
+ EX(call)->num_args = opline->op2.num;
+
+ zval_ptr_dtor_nogc(free_op1.var);
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static int ZEND_FASTCALL ZEND_BOOL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -30286,6 +30644,83 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
ZEND_VM_NEXT_OPCODE();
}
+static int ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *arg, *param, tmp;
+
+
+ arg = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC);
+ param = ZEND_CALL_ARG(EX(call), opline->op2.num);
+
+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+ // TODO: Scalar values don't have reference counters anymore.
+ // They are assumed to be 1, and they may be easily passed by
+ // reference now. However, previously scalars with refcount==1
+ // might be passed and with refcount>1 might not. We can support
+ // only single behavior ???
+#if 0
+ if (Z_REFCOUNTED_P(arg) &&
+ // This solution breaks the following test (omit warning message) ???
+ // Zend/tests/bug61273.phpt
+ // ext/reflection/tests/bug42976.phpt
+ // ext/standard/tests/general_functions/call_user_func_array_variation_001.phpt
+#else
+ if (!Z_REFCOUNTED_P(arg) ||
+ // This solution breaks the following test (emit warning message) ???
+ // ext/pdo_sqlite/tests/pdo_005.phpt
+#endif
+ (!Z_ISREF_P(arg) && Z_REFCOUNT_P(arg) > 1)) {
+
+ if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+
+ zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
+ opline->op2.num,
+ EX(call)->func->common.scope ? EX(call)->func->common.scope->name->val : "",
+ EX(call)->func->common.scope ? "::" : "",
+ EX(call)->func->common.function_name->val);
+
+ if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
+ }
+ if (EX(call)->object) {
+ OBJ_RELEASE(EX(call)->object);
+ }
+ EX(call)->func = (zend_function*)&zend_pass_function;
+ EX(call)->called_scope = NULL;
+ EX(call)->object = NULL;
+
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ if (Z_REFCOUNTED_P(arg)) {
+ Z_DELREF_P(arg);
+ }
+ ZVAL_DUP(&tmp, arg);
+ ZVAL_NEW_REF(arg, &tmp);
+ Z_ADDREF_P(arg);
+ } else if (!Z_ISREF_P(arg)) {
+ ZVAL_NEW_REF(arg, arg);
+ Z_ADDREF_P(arg);
+ } else if (Z_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
+ ZVAL_COPY_VALUE(param, arg);
+ } else if (Z_ISREF_P(arg) &&
+ /* don't separate references for __call */
+ (EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) == 0) {
+ ZVAL_DUP(param, Z_REFVAL_P(arg));
+ } else {
+ ZVAL_COPY(param, arg);
+ }
+
+ EX(call)->num_args = opline->op2.num;
+
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static int ZEND_FASTCALL ZEND_BOOL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -43109,7 +43544,11 @@ void zend_init_opcodes_handlers(void)
ZEND_SEND_VAR_SPEC_CV_HANDLER,
ZEND_SEND_VAR_SPEC_CV_HANDLER,
ZEND_SEND_VAR_SPEC_CV_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_CONST_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_TMP_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_VAR_HANDLER,
ZEND_NULL_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_CV_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
@@ -43130,6 +43569,31 @@ void zend_init_opcodes_handlers(void)
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
@@ -43140,50 +43604,21 @@ void zend_init_opcodes_handlers(void)
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
+ ZEND_SEND_USER_SPEC_VAR_HANDLER,
+ ZEND_SEND_USER_SPEC_VAR_HANDLER,
+ ZEND_SEND_USER_SPEC_VAR_HANDLER,
+ ZEND_SEND_USER_SPEC_VAR_HANDLER,
+ ZEND_SEND_USER_SPEC_VAR_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_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_SEND_USER_SPEC_CV_HANDLER,
+ ZEND_SEND_USER_SPEC_CV_HANDLER,
+ ZEND_SEND_USER_SPEC_CV_HANDLER,
+ ZEND_SEND_USER_SPEC_CV_HANDLER,
+ ZEND_SEND_USER_SPEC_CV_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index 70d7e716bc..87d6a56b36 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -140,9 +140,9 @@ const char *zend_vm_opcodes_map[169] = {
"ZEND_ISSET_ISEMPTY_DIM_OBJ",
"ZEND_SEND_VAL_EX",
"ZEND_SEND_VAR",
- NULL,
- NULL,
- NULL,
+ "ZEND_INIT_USER_CALL",
+ "ZEND_SEND_ARRAY",
+ "ZEND_SEND_USER",
NULL,
NULL,
NULL,
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index 8aae4ae74c..cd448f51de 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -141,6 +141,9 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);
#define ZEND_ISSET_ISEMPTY_DIM_OBJ 115
#define ZEND_SEND_VAL_EX 116
#define ZEND_SEND_VAR 117
+#define ZEND_INIT_USER_CALL 118
+#define ZEND_SEND_ARRAY 119
+#define ZEND_SEND_USER 120
#define ZEND_PRE_INC_OBJ 132
#define ZEND_PRE_DEC_OBJ 133
#define ZEND_POST_INC_OBJ 134
diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c
index 65897425be..b93fc3667c 100644
--- a/ext/opcache/Optimizer/optimize_func_calls.c
+++ b/ext/opcache/Optimizer/optimize_func_calls.c
@@ -38,6 +38,7 @@ static void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx
case ZEND_INIT_METHOD_CALL:
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_FCALL:
+ case ZEND_INIT_USER_CALL:
call_stack[call].opline = opline;
call++;
break;