]> granicus.if.org Git - php/commitdiff
Implement call_user_func() and call_user_func_array() using special opcodes.
authorDmitry Stogov <dmitry@zend.com>
Thu, 10 Jul 2014 20:32:18 +0000 (00:32 +0400)
committerDmitry Stogov <dmitry@zend.com>
Thu, 10 Jul 2014 20:32:18 +0000 (00:32 +0400)
In some rare cases it leads to insignificant changes in error messages.

15 files changed:
Zend/tests/001.phpt
Zend/tests/002.phpt
Zend/tests/003.phpt
Zend/tests/call_user_func_004.phpt
Zend/tests/call_user_func_005.phpt
Zend/tests/fr47160.phpt
Zend/tests/objects_027.phpt
Zend/zend_compile.c
Zend/zend_execute.c
Zend/zend_execute_API.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/optimize_func_calls.c

index 78d982a7adb7f327719cf07591dd2a7016650ef8..bec7d8adbcd4f31667f79afc64808ccacfa026da 100644 (file)
@@ -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)
index 73c8d3ec43ec038e429b03788ef8ff71de8e58eb..b01c7fa329d8bd65be2ce20cf7dfa1139815cf03 100644 (file)
@@ -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
index 81f70b8d872aceeb6fdcd866b4e5e9f2517e3130..5c3b83d25e1d11d7e0d818552a7a55224f81c7e8 100644 (file)
@@ -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)
index 4885c4d3faea31d8331db19e8258e9a33cb5f53c..9df650ec9d55681a3008669d81db32568e086c0c 100644 (file)
@@ -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
index 6c1fa197335c1ab76890513360b7bb4dd17044dc..a602f59a12f6c582b6acfa2d5409c758bc0e1101 100644 (file)
@@ -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]=>
index 809e1f160360753b6dbaf9652272448aa0b2e0dd..898e0eda97b98c56aa30d0121ed0ab5049dd2aa1 100644 (file)
@@ -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
index 184b471cb47a4ffe231e4fffaa8c62c9495fd8dd..e3d399d3bcfbff6ffed5f026dcaccb549c96934e 100644 (file)
@@ -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) {
index 6b40dbb35d1713df0febad46e647ba6a4e47b93a..132def4b1491485beb73d095112b634fb8e79c95 100644 (file)
@@ -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);
index 9ca6d9c9c7888ede974656de9adb3773e6f6d70b..7fca79613787a09e1f61d4fe2bcb0a98b11d58da 100644 (file)
@@ -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)
index 509cb9111326202810efaf465ca5f2c467f7debf..308d54b31d7282d56a4a762cc05378a5648b9c88 100644 (file)
@@ -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 {
index d571919cb27ed870701aa950ae38536330fe6f7e..e6a69a10b64fb9adebbebc206b3b4170ee2b9c6f 100644 (file)
@@ -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
index 6a7c47f2f940055e08f2c09778f34bb42bb81594..95cd54f49415b7af6c9cbfd9ad65910ad061d708 100644 (file)
@@ -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,
index 70d7e716bcf9f91085752cf883b09d3c7d19aa70..87d6a56b365bdb6aa3cda11d4ddd67d90bbb4ab6 100644 (file)
@@ -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,
index 8aae4ae74cb23cf01034d68f1c3e5e6253a2ddbc..cd448f51de48f8a661ff3a27fff72c4b546f8e6a 100644 (file)
@@ -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
index 65897425be2199568faace0ff4dfdf49d8f89b2a..b93fc3667c9d6608d12a4bafc9d33a477c0e40a8 100644 (file)
@@ -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;