com php-src: MFH: Zend/zend_vm_def.h Zend/zend_vm_e xecute.h
| From: | Tjerk Anne Meesters | Date: | Sat, 16 Aug 2014 15:38:20 +0000 |
| Subject: | com php-src: MFH: Zend/zend_vm_def.h Zend/zend_vm_e xecute.h | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-82128@lists.php.net to get a copy of this message | ||
Commit: c3d214aa0822d00b9e2171f148126966340ef534
Author: Timm Friebe <thekid@thekid.de> Sat, 16 Aug 2014 17:38:20 +0200
Parents: a49ca6fde80021ae4a95b6b483d51e871850a0a8 d790eceb01009489358c3781c94ebea3b3400f67
Branches: master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=c3d214aa0822d00b9e2171f148126966340ef534
Log:
MFH
Changed paths:
MM Zend/zend_vm_def.h
MM Zend/zend_vm_execute.h
diff --cc Zend/zend_vm_def.h index 5346f86,7749197..378e3f1 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@@ -2427,89 -2208,50 +2208,91 @@@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_C HANDLE_EXCEPTION(); } zend_error_noreturn(E_ERROR, "Method name must be a string"); - } - - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (OP2_TYPE != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((OP2_TYPE == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (OP2_TYPE == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + } + + object = GET_OP1_OBJ_ZVAL_PTR_DEREF(BP_VAR_R); + + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { FREE_OP2(); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + FREE_OP2(); + FREE_OP1_IF_VAR(); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (OP2_TYPE != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((OP2_TYPE == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (OP2_TYPE == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); FREE_OP2(); FREE_OP1_IF_VAR(); diff --cc Zend/zend_vm_execute.h index 52a9988,c2e59c1..51a5476 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@@ -9286,80 -9543,37 +9543,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -10177,80 -10380,37 +10420,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -11069,80 -11220,37 +11300,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -12541,80 -12612,37 +12732,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -15805,89 -15987,50 +16147,91 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zval_ptr_dtor_nogc(&free_op1.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); - zval_ptr_dtor_nogc(&free_op1.var); + zval_ptr_dtor_nogc(free_op1.var); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@@ -18178,90 -18221,51 +18422,92 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); - zval_ptr_dtor_nogc(&free_op1.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); zval_dtor(free_op2.var); - zval_ptr_dtor_nogc(&free_op1.var); + zval_ptr_dtor_nogc(free_op1.var); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@@ -20512,81 -20427,37 +20669,78 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); - zval_ptr_dtor_nogc(&free_op1.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -23996,81 -23812,37 +24095,78 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zval_ptr_dtor_nogc(&free_op1.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -25665,80 -25396,37 +25720,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_obj_zval_ptr_unused(TSRMLS_C); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -27099,80 -26772,37 +27136,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_obj_zval_ptr_unused(TSRMLS_C); - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -28439,80 -28054,37 +28458,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } + object = _get_obj_zval_ptr_unused(TSRMLS_C); - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -30207,80 -29846,37 +30290,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_obj_zval_ptr_unused(TSRMLS_C); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -33472,86 -33194,48 +33678,88 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); CHECK_EXCEPTION(); @@@ -35610,80 -35242,37 +35766,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -37803,80 -37328,37 +37892,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -40997,86 -40457,48 +41061,88 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); CHECK_EXCEPTION();
diff --cc Zend/zend_vm_def.h index 5346f86,7749197..378e3f1 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@@ -2427,89 -2208,50 +2208,91 @@@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_C HANDLE_EXCEPTION(); } zend_error_noreturn(E_ERROR, "Method name must be a string"); - } - - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (OP2_TYPE != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((OP2_TYPE == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (OP2_TYPE == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + } + + object = GET_OP1_OBJ_ZVAL_PTR_DEREF(BP_VAR_R); + + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { FREE_OP2(); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + FREE_OP2(); + FREE_OP1_IF_VAR(); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (OP2_TYPE != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((OP2_TYPE == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (OP2_TYPE == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); FREE_OP2(); FREE_OP1_IF_VAR(); diff --cc Zend/zend_vm_execute.h index 52a9988,c2e59c1..51a5476 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@@ -9286,80 -9543,37 +9543,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -10177,80 -10380,37 +10420,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -11069,80 -11220,37 +11300,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -12541,80 -12612,37 +12732,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -15805,89 -15987,50 +16147,91 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zval_ptr_dtor_nogc(&free_op1.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); - zval_ptr_dtor_nogc(&free_op1.var); + zval_ptr_dtor_nogc(free_op1.var); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@@ -18178,90 -18221,51 +18422,92 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); - zval_ptr_dtor_nogc(&free_op1.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); zval_dtor(free_op2.var); - zval_ptr_dtor_nogc(&free_op1.var); + zval_ptr_dtor_nogc(free_op1.var); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@@ -20512,81 -20427,37 +20669,78 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); - zval_ptr_dtor_nogc(&free_op1.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -23996,81 -23812,37 +24095,78 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zval_ptr_dtor_nogc(&free_op1.var); ++ zval_ptr_dtor_nogc(free_op1.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -25665,80 -25396,37 +25720,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_obj_zval_ptr_unused(TSRMLS_C); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -27099,80 -26772,37 +27136,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_obj_zval_ptr_unused(TSRMLS_C); - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -28439,80 -28054,37 +28458,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } + object = _get_obj_zval_ptr_unused(TSRMLS_C); - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -30207,80 -29846,37 +30290,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_obj_zval_ptr_unused(TSRMLS_C); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; + object = _get_obj_zval_ptr_unused(TSRMLS_C); - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -33472,86 -33194,48 +33678,88 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CONST != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CONST == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CONST != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_CONST == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); CHECK_EXCEPTION(); @@@ -35610,80 -35242,37 +35766,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_TMP_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_TMP_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_TMP_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { zval_dtor(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + zval_dtor(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_TMP_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_TMP_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_TMP_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -37803,80 -37328,37 +37892,77 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); - - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - if (IS_VAR != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_VAR == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_VAR == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor_nogc(&free_op2.var); + zval_ptr_dtor_nogc(free_op2.var); HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); - zval_ptr_dtor_nogc(&free_op2.var); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); ++ zval_ptr_dtor_nogc(free_op2.var); + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_VAR != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_VAR == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); + } + if (IS_VAR == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } } @@@ -40997,86 -40457,48 +41061,88 @@@ static int ZEND_FASTCALL ZEND_INIT_MET zend_error_noreturn(E_ERROR, "Method name must be a string"); } - function_name_strval = Z_STRVAL_P(function_name); - function_name_strlen = Z_STRLEN_P(function_name); + object = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - - if (EXPECTED(call->object != NULL) && - EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) { - call->called_scope = Z_OBJCE_P(call->object); - - if (IS_CV != IS_CONST || - (call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) { - zval *object = call->object; - - if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) { - zend_error_noreturn(E_ERROR, "Object does not support method calls"); - } - - /* First, locate the function. */ - call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CV == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC); - if (UNEXPECTED(call->fbc == NULL)) { - zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval); - } - if (IS_CV == IS_CONST && - EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) && - EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && - EXPECTED(call->object == object)) { - CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc); - } - } - } else { - zend_uint nesting = opline->result.num; + if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { ++ zend_uint nesting = 1; + if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + - zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object))); ++ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); + + + if (EG(exception) != NULL) { + HANDLE_EXCEPTION(); + } + + /* No exception raised: Skip over arguments until fcall opcode with correct + * nesting level. Return NULL (except when return value unused) */ + do { ++ /* fprintf(stderr, "SKIP %s => ", zend_get_opcode_name(opline->opcode)); */ + opline++; - } while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting); ++ ++ if (opline->opcode == ZEND_INIT_FCALL || ++ opline->opcode == ZEND_INIT_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || ++ opline->opcode == ZEND_INIT_METHOD_CALL || ++ opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || ++ opline->opcode == ZEND_INIT_USER_CALL || ++ opline->opcode == ZEND_NEW ++ ) { ++ nesting++; ++ } else if (opline->opcode == ZEND_DO_FCALL) { ++ nesting--; ++ } ++ ++ /* fprintf(stderr, "%s %d\n", zend_get_opcode_name(opline->opcode), nesting); */ ++ } while (nesting); + + if (RETURN_VALUE_USED(opline)) { - ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr); - EX_T(opline->result.var).var.fcall_returned_reference = 0; - EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr; ++ ZVAL_NULL(EX_VAR(opline->result.var)); + } + + /* We've skipped EXT_FCALL_BEGIND, so also skip the ending opcode */ + if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) { + opline++; + } + ZEND_VM_JMP(++opline); } - if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - call->object = NULL; - } else { - if (!PZVAL_IS_REF(call->object)) { - Z_ADDREF_P(call->object); /* For $this pointer */ - } else { - zval *this_ptr; - ALLOC_ZVAL(this_ptr); - INIT_PZVAL_COPY(this_ptr, call->object); - zval_copy_ctor(this_ptr); - call->object = this_ptr; + obj = Z_OBJ_P(object); + called_scope = zend_get_class_entry(obj TSRMLS_CC); + + if (IS_CV != IS_CONST || + (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope)) == NULL) { + zend_object *orig_obj = obj; + + if (UNEXPECTED(obj->handlers->get_method == NULL)) { + zend_error_noreturn(E_ERROR, "Object does not support method calls"); + } + + /* First, locate the function. */ + fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (opline->op2.zv + 1) : NULL) TSRMLS_CC); + if (UNEXPECTED(fbc == NULL)) { + zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(obj), Z_STRVAL_P(function_name)); } + if (IS_CV == IS_CONST && + EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && + EXPECTED((fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) && + EXPECTED(obj == orig_obj)) { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); + } + } + + if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { + obj = NULL; + } else { + GC_REFCOUNT(obj)++; /* For $this pointer */ } - call->num_additional_args = 0; - call->is_ctor_call = 0; - EX(call) = call; + EX(call) = zend_vm_stack_push_call_frame( + fbc, opline->extended_value, 0, called_scope, obj, EX(call) TSRMLS_CC); CHECK_EXCEPTION();