JavaCalls::call为hotspot调用java方法的实现之一。其调用os::os_exception_wrapper(call_helper, result, &method, args, THREAD);这个还是比较好看懂,通过传入call_helper函数指针,在call_helper上面封装了异常的处理,典型的回调函数用法。call_helper的实现里面的具体调用java函数的方法相对难看明白,下面进行进一步分析。
....
//函数的具体调用
{ JavaCallWrapper link(method, receiver, result, CHECK);
{ HandleMark hm(thread); // HandleMark used by HandleMarkCleaner
StubRoutines::call_stub()(
(address)&link,
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
result_val_address, // see NOTE above (compiler problem)
result_type,
method(),
entry_point,
args->parameters(),
args->size_of_parameters(),
CHECK
);
result = link.result(); // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)
// Preserve oop return value across possible gc points
if (oop_result_flag) {
thread->set_vm_result((oop) result->get_jobject());
}
}
上面代码中StubRoutines::call_stub()返回的是一个函数指针,在执行上面的call_stub()时,会先将参数先压入堆栈。
这个函数指针指向什么地方呢,这是和机器类型有关的,以X86-32为例,看stubGenerator_x86_32.cpp里面
StubGenerator::generate_call_stub。返回的是__pc()的值,这个值其实是内存的的一个动态生态的机器码的一个位置。
address generate_call_stub(address& return_address) {
StubCodeMark mark(this, "StubRoutines", "call_stub");
address start = __ pc(); //注意,这是返回的指针值,下面还继续生成机器码,也就是说在上面的javaCall
//会调用下面产生的机器码,这些才是真正调用java方法的内容。
bool sse_save = false;
const Address rsp_after_call(rbp, -4 * wordSize); // same as in generate_catch_exception()!
const int locals_count_in_bytes (4*wordSize);
const Address mxcsr_save (rbp, -4 * wordSize);
const Address saved_rbx (rbp, -3 * wordSize);
const Address saved_rsi (rbp, -2 * wordSize);
const Address saved_rdi (rbp, -1 * wordSize);
const Address result (rbp, 3 * wordSize);
const Address result_type (rbp, 4 * wordSize);
const Address method (rbp, 5 * wordSize);
const Address entry_point (rbp, 6 * wordSize);
const Address parameters (rbp, 7 * wordSize);
const Address parameter_size(rbp, 8 * wordSize);
const Address thread (rbp, 9 * wordSize); // same as in generate_catch_exception()!
sse_save = UseSSE > 0;
//上面为对调用压入的堆栈进行的处理,将具体的值赋给寄存器
//下面就是残端代码,主要是产生机器码,用类似汇编语言的格式产生
// stub code
__ enter();
__ movl(rcx, parameter_size); // parameter counter
__ shll(rcx, Interpreter::logStackElementSize()); // convert parameter count to bytes
__ addl(rcx, locals_count_in_bytes); // reserve space for register saves
__ subl(rsp, rcx);
__ andl(rsp, -(StackAlignmentInBytes)); // Align stack
.....
采用类的方法,模拟汇编语言产生机器码还是很体现了大牛们的水平。
汇编调用java方法的具体代码
// call Java function
__ BIND(parameters_done);
__ movl(rbx, method); // get methodOop
__ movl(rax, entry_point); // get entry_point
__ movl(rsi, rsp); // set sender sp
BLOCK_COMMENT("call Java function");
__ call(rax);
上面就将cpu控制转向了entry_point,这正是java方法的入口。
这个entry_point从何而来,从method->from_interpreted_entry(),从methodOopDesc::link_method中获取address entry = Interpreter::entry_for_method(h_method),也就是说如果不用jit的,直接调用解析器的入口,由解释器再进行操作。