diff options
author | Dmitry Stogov <dmitry@zend.com> | 2014-07-11 00:32:18 +0400 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2014-07-11 00:32:18 +0400 |
commit | de306e70882dd7dd04ea952ef3c665304837c3be (patch) | |
tree | 550152c8ade9c90ffb232430c4661ee66dab1e89 | |
parent | 4e291fab81eb8c7517d673705ae8a46bf178724b (diff) | |
download | php-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.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/002.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/003.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/call_user_func_004.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/call_user_func_005.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/fr47160.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/objects_027.phpt | 2 | ||||
-rw-r--r-- | Zend/zend_compile.c | 184 | ||||
-rw-r--r-- | Zend/zend_execute.c | 17 | ||||
-rw-r--r-- | Zend/zend_execute_API.c | 1 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 237 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 521 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.c | 6 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.h | 3 | ||||
-rw-r--r-- | ext/opcache/Optimizer/optimize_func_calls.c | 1 |
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; |