ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, NUM|CACHE_SLOT)
{
	USE_OPLINE
	zval *function_name;
	zend_free_op free_op1, free_op2;
	zval *object;
	zend_function *fbc;
	zend_class_entry *called_scope;
	zend_object *obj;
	zend_execute_data *call;
	uint32_t call_info;

	SAVE_OPLINE();

	object = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);

	if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
		ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
	}

	if (OP2_TYPE != IS_CONST) {
		function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
	}

	if (OP2_TYPE != IS_CONST &&
	    UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) {
		do {
			if ((OP2_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(function_name)) {
				function_name = Z_REFVAL_P(function_name);
				if (EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) {
					break;
				}
			} else if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) {
				ZVAL_UNDEFINED_OP2();
				if (UNEXPECTED(EG(exception) != NULL)) {
					FREE_OP1();
					HANDLE_EXCEPTION();
				}
			}
			zend_throw_error(NULL, "Method name must be a string");
			FREE_OP2();
			FREE_OP1();
			HANDLE_EXCEPTION();
		} while (0);
	}

	if (OP1_TYPE != IS_UNUSED) {
		do {
			if (OP1_TYPE == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
				if ((OP1_TYPE & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) {
					object = Z_REFVAL_P(object);
					if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
						break;
					}
				}
				if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
					object = ZVAL_UNDEFINED_OP1();
					if (UNEXPECTED(EG(exception) != NULL)) {
						if (OP2_TYPE != IS_CONST) {
							FREE_OP2();
						}
						HANDLE_EXCEPTION();
					}
				}
				if (OP2_TYPE == IS_CONST) {
					function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
				}
				zend_invalid_method_call(object, function_name);
				FREE_OP2();
				FREE_OP1();
				HANDLE_EXCEPTION();
			}
		} while (0);
	}

	obj = Z_OBJ_P(object);
	called_scope = obj->ce;

	if (OP2_TYPE == IS_CONST &&
	    EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) {
	    fbc = CACHED_PTR(opline->result.num + sizeof(void*));
	} else {
	    zend_object *orig_obj = obj;

		if (OP2_TYPE == IS_CONST) {
			function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
		}

		/* First, locate the function. */
		fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((OP2_TYPE == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL));
		if (UNEXPECTED(fbc == NULL)) {
			if (EXPECTED(!EG(exception))) {
				zend_undefined_method(obj->ce, Z_STR_P(function_name));
			}
			FREE_OP2();
			FREE_OP1();
			HANDLE_EXCEPTION();
		}
		if (OP2_TYPE == IS_CONST &&
		    EXPECTED(fbc->type <= ZEND_USER_FUNCTION) &&
		    EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) &&
		    EXPECTED(obj == orig_obj)) {
			CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc);
		}
		if ((OP1_TYPE & (IS_VAR|IS_TMP_VAR)) && UNEXPECTED(obj != orig_obj)) {
			/* Reset "object" to trigger reference counting */
			object = NULL;
		}
		if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
			init_func_run_time_cache(&fbc->op_array);
		}
	}

	if (OP2_TYPE != IS_CONST) {
		FREE_OP2();
	}

	call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_HAS_THIS;
	if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) {
		FREE_OP1();

		if ((OP1_TYPE & (IS_VAR|IS_TMP_VAR)) && UNEXPECTED(EG(exception))) {
			HANDLE_EXCEPTION();
		}
		/* call static method */
		obj = (zend_object*)called_scope;
		call_info = ZEND_CALL_NESTED_FUNCTION;
	} else if (OP1_TYPE & (IS_VAR|IS_TMP_VAR|IS_CV)) {
		if (OP1_TYPE == IS_CV) {
			GC_ADDREF(obj); /* For $this pointer */
		} else if (free_op1 != object) {
			GC_ADDREF(obj); /* For $this pointer */
			FREE_OP1();
		}
		/* CV may be changed indirectly (e.g. when it's a reference) */
		call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_HAS_THIS | ZEND_CALL_RELEASE_THIS;
	}

	call = zend_vm_stack_push_call_frame(call_info,
		fbc, opline->extended_value, obj);
	call->prev_execute_data = EX(call);
	EX(call) = call;

	ZEND_VM_NEXT_OPCODE();
}