一.何为frame
frame是一个代码运行期的概念,同时,在编译期又会用到它,它是一个active record,它记录了函数当前运行的信息。我们使用IDE进行调试的时候,用到的call stack,就是通过frame链表得到的。简单来说,每次的函数调用就会在栈上形成一个frame,同时该frame会连接到frame链表中成为当前的frame。一般来说,cpu会有一个专门的寄存器来保存当前frame的地址,在intel cpu中使用ebp。
frame可能包括的内容有:
1.parameters
2.return address
3.previous frame address
4.local variables
需要说明的是,frame并不是一次就创建好的,它的创建分为两部分
1.caller在函数调用前,要把参数分别入栈,然后把调用完成后的返回地址入栈,然后跳转到函数首地址
2.callee首先把frame pointer入栈,同时把sp赋值给fp,这样就把当前frame加入到frame链表中,然后再构建当前函数需要加入到frame中的内容
在函数中只需要通过fp及其偏移量就可以访问frame中的内容。
frame的格式和内容是与CPU,编译器、编译语言相关的,他们是一种convention,例如C/C++就有自己的ABI,但是不同的编译器,不同的CPU的ABI又不完全相同,也就是说,我们完全可以定义自己的frame,只要编译器,调试器能够识别就行,v8就是这么做的
二.v8的frame
我们以execution.cc中的Execution::InstantiateFunction为例,说明v8是如何构建frame的。
Handle<JSFunction>
Execution::InstantiateFunction(
Handle<FunctionTemplateInfo> data,
bool* exc) {
Isolate* isolate = data->GetIsolate();
// Fast case: see if the function has already been instantiated
int serial_number = Smi::cast(data->serial_number())->value();
Object* elm =
isolate->native_context()->function_cache()->
GetElementNoExceptionThrown(serial_number);
if (elm->IsJSFunction()) return Handle<JSFunction>(JSFunction::cast(elm));
// The function has not yet been instantiated in this context; do it.
Handle<Object> args[] = { data };
Handle<FunctionTemplateInfo> data,
bool* exc) {
Isolate* isolate = data->GetIsolate();
// Fast case: see if the function has already been instantiated
int serial_number = Smi::cast(data->serial_number())->value();
Object* elm =
isolate->native_context()->function_cache()->
GetElementNoExceptionThrown(serial_number);
if (elm->IsJSFunction()) return Handle<JSFunction>(JSFunction::cast(elm));
// The function has not yet been instantiated in this context; do it.
Handle<Object> args[] = { data };
Handle<Object> result = Call(isolate->instantiate_fun(),
isolate->js_builtins_object(),
ARRAY_SIZE(args),
args,
exc);
if (*exc) return Handle<JSFunction>::null();
return Handle<JSFunction>::cast(result);
}
isolate->js_builtins_object(),
ARRAY_SIZE(args),
args,
exc);
if (*exc) return Handle<JSFunction>::null();
return Handle<JSFunction>::cast(result);
}
其中重点是Call函数,如下:
Handle<Object>
Execution::Call(Handle<Object> callable,
Handle<Object> receiver,
int argc,
Handle<Object> argv[],
bool* pending_exception,
bool convert_receiver) {
*pending_exception = false;
if (!callable->IsJSFunction()) {
callable = TryGetFunctionDelegate(callable, pending_exception);
if (*pending_exception) return callable;
}
Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
。。。。
return Invoke(false, func, receiver, argc, argv, pending_exception);
}
Handle<Object> receiver,
int argc,
Handle<Object> argv[],
bool* pending_exception,
bool convert_receiver) {
*pending_exception = false;
if (!callable->IsJSFunction()) {
callable = TryGetFunctionDelegate(callable, pending_exception);
if (*pending_exception) return callable;
}
Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
。。。。
return Invoke(false, func, receiver, argc, argv, pending_exception);
}
Invoke
重点是Invoke函数,如下:
static Handle<Object>
Invoke(bool is_construct,
Handle<JSFunction> function,
Handle<Object> receiver,
int argc,
Handle<Object> args[],
bool* has_pending_exception) {
Isolate* isolate = function->GetIsolate();
// Entering JavaScript.
VMState state(isolate, JS);
// Placeholder for return value.
MaybeObject* value = reinterpret_cast<Object*>(kZapValue);
typedef Object* (*JSEntryFunction)(byte* entry,
Object* function,
Object* receiver,
int argc,
Object*** args);
<1>isolate->factory()->js_entry_code()和isolate->factory()->js_construct_entry_code()
Handle<Code> code = is_construct
? isolate->factory()->js_construct_entry_code()
: isolate->factory()->js_entry_code();
// Convert calls on global objects to be calls on the global
// receiver instead to avoid having a 'this' pointer which refers
// directly to a global object.
if (receiver->IsGlobalObject()) {
Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
receiver = Handle<JSObject>(global->global_receiver());
}
// Make sure that the global object of the context we're about to
// make the current one is indeed a global object.
ASSERT(function->context()->global_object()->IsGlobalObject());
{
// Save and restore context around invocation and block the
// allocation of handles without explicit handle scopes.
SaveContext save(isolate);
NoHandleAllocation na(isolate);
JSEntryFunction stub_entry = FUNCTION_CAST<JSEntryFunction>(code->entry());
// Call the function through the right JS entry stub.
byte* function_entry = function->code()->entry();
JSFunction* func = *function;
Object* recv = *receiver;
Object*** argv = reinterpret_cast<Object***>(args);
value =
Handle<JSFunction> function,
Handle<Object> receiver,
int argc,
Handle<Object> args[],
bool* has_pending_exception) {
Isolate* isolate = function->GetIsolate();
// Entering JavaScript.
VMState state(isolate, JS);
// Placeholder for return value.
MaybeObject* value = reinterpret_cast<Object*>(kZapValue);
typedef Object* (*JSEntryFunction)(byte* entry,
Object* function,
Object* receiver,
int argc,
Object*** args);
<1>isolate->factory()->js_entry_code()和isolate->factory()->js_construct_entry_code()
Handle<Code> code = is_construct
? isolate->factory()->js_construct_entry_code()
: isolate->factory()->js_entry_code();
// Convert calls on global objects to be calls on the global
// receiver instead to avoid having a 'this' pointer which refers
// directly to a global object.
if (receiver->IsGlobalObject()) {
Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
receiver = Handle<JSObject>(global->global_receiver());
}
// Make sure that the global object of the context we're about to
// make the current one is indeed a global object.
ASSERT(function->context()->global_object()->IsGlobalObject());
{
// Save and restore context around invocation and block the
// allocation of handles without explicit handle scopes.
SaveContext save(isolate);
NoHandleAllocation na(isolate);
JSEntryFunction stub_entry = FUNCTION_CAST<JSEntryFunction>(code->entry());
// Call the function through the right JS entry stub.
byte* function_entry = function->code()->entry();
JSFunction* func = *function;
Object* recv = *receiver;
Object*** argv = reinterpret_cast<Object***>(args);
value =
<2>#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
(entry(p0, p1, p2, p3, p4))
CALL_GENERATED_CODE(stub_entry, function_entry, func, recv, argc, argv);
}
#ifdef VERIFY_HEAP
value->Verify();
#endif
// Update the pending exception flag and return the value.
*has_pending_exception = value->IsException();
ASSERT(*has_pending_exception == isolate->has_pending_exception());
if (*has_pending_exception) {
isolate->ReportPendingMessages();
if (isolate->pending_exception()->IsOutOfMemory()) {
if (!isolate->ignore_out_of_memory()) {
V8::FatalProcessOutOfMemory("JS", true);
}
}
#ifdef ENABLE_DEBUGGER_SUPPORT
// Reset stepping state when script exits with uncaught exception.
if (isolate->debugger()->IsDebuggerActive()) {
isolate->debug()->ClearStepping();
}
#endif // ENABLE_DEBUGGER_SUPPORT
return Handle<Object>();
} else {
isolate->clear_pending_message();
}
return Handle<Object>(value->ToObjectUnchecked(), isolate);
}
(entry(p0, p1, p2, p3, p4))
CALL_GENERATED_CODE(stub_entry, function_entry, func, recv, argc, argv);
}
#ifdef VERIFY_HEAP
value->Verify();
#endif
// Update the pending exception flag and return the value.
*has_pending_exception = value->IsException();
ASSERT(*has_pending_exception == isolate->has_pending_exception());
if (*has_pending_exception) {
isolate->ReportPendingMessages();
if (isolate->pending_exception()->IsOutOfMemory()) {
if (!isolate->ignore_out_of_memory()) {
V8::FatalProcessOutOfMemory("JS", true);
}
}
#ifdef ENABLE_DEBUGGER_SUPPORT
// Reset stepping state when script exits with uncaught exception.
if (isolate->debugger()->IsDebuggerActive()) {
isolate->debug()->ClearStepping();
}
#endif // ENABLE_DEBUGGER_SUPPORT
return Handle<Object>();
} else {
isolate->clear_pending_message();
}
return Handle<Object>(value->ToObjectUnchecked(), isolate);
}
js_entry_code()
下面我们分析上述的调用序列
<1>isolate->factory()->js_entry_code()和isolate->factory()->js_construct_entry_code()
factory->js_entry_code()函数是在factory.h中通过如下的宏定义的
#define ROOT_ACCESSOR(type, name, camel_name) \
inline Handle<type> name() { \
return Handle<type>(BitCast<type**>( \
&isolate()->heap()->roots_[Heap::k##camel_name##RootIndex])); \
}
ROOT_LIST(ROOT_ACCESSOR)
inline Handle<type> name() { \
return Handle<type>(BitCast<type**>( \
&isolate()->heap()->roots_[Heap::k##camel_name##RootIndex])); \
}
ROOT_LIST(ROOT_ACCESSOR)
ROOT_LIST是在heap.h中定义的,其中
V(Code, js_entry_code, JsEntryCode) \
V(Code, js_construct_entry_code, JsConstructEntryCode) \
V(Code, js_construct_entry_code, JsConstructEntryCode) \
也就是说,该函数返回的是heap中的roots_数组中的Code对象,参看
Heap RootObject的初始化,我们可以知道js_entry_code对象是在
Heap
::
CreateJSEntryStub
()函数中创建的
void
Heap
::
CreateJSEntryStub
() {
JSEntryStub
stub
;
set_js_entry_code
(*
stub
.
GetCode
(
isolate
()));
}
JSEntryStub是CodeStub类的派生类,CodeStub定义了GetCode的流程,JSEntryStub实现GenerateBody虚函数,所以重点在于JSEntryStub::GenerateBody函数的实现
void
JSEntryStub
::
GenerateBody
(
MacroAssembler
*
masm
,
bool
is_construct
) {
Label
invoke
,
handler_entry
,
exit
;
Label
not_outermost_js
,
not_outermost_js_2
;
// Set up frame.
//这里构建frame的后半部分
__
push
(
ebp
);
__
mov
(
ebp
,
esp
);
// Push marker in two places.
int
marker
=
is_construct
?
StackFrame
::
ENTRY_CONSTRUCT
:
StackFrame
::
ENTRY
;
//marker用来表示这是一个frame的类型,同时它又起到占位符的作用,可以作为context和function的slot
__
push
(
Immediate
(
Smi
::
FromInt
(
marker
)));
// context slot
__
push
(
Immediate
(
Smi
::
FromInt
(
marker
)));
// function slot
// Save callee-saved registers (C calling conventions).
__
push
(
edi
);
__
push
(
esi
);
__
push
(
ebx
);
// Save copies of the top frame descriptor on the stack.
<1>ExternalReference c_entry_fp
ExternalReference
c_entry_fp
(
Isolate
::
kCEntryFPAddress
,
masm
->
isolate
());
__
push
(
Operand
::
StaticVariable
(
c_entry_fp
));
// If this is the outermost JS call, set js_entry_sp value.
ExternalReference
js_entry_sp
(
Isolate
::
kJSEntrySPAddress
,
masm
->
isolate
());
//判断js_entry_sp的内容是否为null
__
cmp
(
Operand
::
StaticVariable
(
js_entry_sp
),
Immediate
(0));
//如果不是null,跳转到not_outmost_js
__
j
(
not_equal
, &
not_outermost_js
,
Label
::
kNear
);
//如果是null,说明是outmost js entry
//把ebp赋值给js_entry_sp
__
mov
(
Operand
::
StaticVariable
(
js_entry_sp
),
ebp
);
//push一个marker表示这是一个outmost_jsentry_frame
__
push
(
Immediate
(
Smi
::
FromInt
(
StackFrame
::
OUTERMOST_JSENTRY_FRAME
)));
//跳转到invoke
__
jmp
(&
invoke
,
Label
::
kNear
);
__
bind
(&
not_outermost_js
);
//not
_outmost标签
//push一个marker表示这是一个inner_jsentry_frame
__
push
(
Immediate
(
Smi
::
FromInt
(
StackFrame
::
INNER_JSENTRY_FRAME
)));
// Jump to a faked try block that does the invoke, with a faked catch
// block that sets the pending exception.
//跳转到invoke标签
__
jmp
(&
invoke
);
__
bind
(&
handler_entry
);
handler_offset_
=
handler_entry
.
pos
();
// Caught exception: Store result (exception) in the pending exception
// field in the JSEnv and return a failure sentinel.
ExternalReference
pending_exception
(
Isolate
::
kPendingExceptionAddress
,
masm
->
isolate
());
__
mov
(
Operand
::
StaticVariable
(
pending_exception
),
eax
);
__
mov
(
eax
,
reinterpret_cast
<
int32_t
>(
Failure
::
Exception
()));
__
jmp
(&
exit
);
// Invoke: Link this frame into the handler chain. There's only one
// handler block in this code object, so its index is 0.
__
bind
(&
invoke
);
//这里应该是用来异常处理的,还需要进一步分析
__
PushTryHandler
(
StackHandler
::
JS_ENTRY
, 0);
// Clear any pending exceptions.
__
mov
(
edx
,
Immediate
(
masm
->
isolate
()->
factory
()->
the_hole_value
()));
__
mov
(
Operand
::
StaticVariable
(
pending_exception
),
edx
);
// Fake a receiver (NULL).
__
push
(
Immediate
(0));
// receiver
// Invoke the function by calling through JS entry trampoline builtin and
// pop the faked function when we return. Notice that we cannot store a
// reference to the trampoline code directly in this stub, because the
// builtin stubs may not have been generated yet.
if
(
is_construct
) {
ExternalReference
construct_entry
(
Builtins
::
kJSConstructEntryTrampoline
,
masm
->
isolate
());
__
mov
(
edx
,
Immediate
(
construct_entry
));
}
else
{
<2>ExternalReference entry ( Builtins:: kJSEntryTrampoline
ExternalReference
entry
(
Builtins
::
kJSEntryTrampoline
,
masm
->
isolate
());
//mov指令执行后,edx中存储的是Code**
__
mov
(
edx
,
Immediate
(
entry
));
}
//Operand(edx , 0)对edx进行接引用,mov指令执行后,edx存储的是Code*
__
mov
(
edx
,
Operand
(
edx
, 0));
// deref address
//lea指令是把源操作数的有效地址,即偏移量存储在指定的寄存器中,这里edx+Code::kHeaderSize就是有效地址,它被存储在edx中
__
lea
(
edx
,
FieldOperand
(
edx
,
Code
::
kHeaderSize
));
__
call
(
edx
);
// Unlink this frame from the handler chain.
__
PopTryHandler
();
__
bind
(&
exit
);
// Check if the current stack frame is marked as the outermost JS frame.
__
pop
(
ebx
);
__
cmp
(
ebx
,
Immediate
(
Smi
::
FromInt
(
StackFrame
::
OUTERMOST_JSENTRY_FRAME
)));
__
j
(
not_equal
, &
not_outermost_js_2
);
__
mov
(
Operand
::
StaticVariable
(
js_entry_sp
),
Immediate
(0));
__
bind
(&
not_outermost_js_2
);
// Restore the top frame descriptor from the stack.
__
pop
(
Operand
::
StaticVariable
(
ExternalReference
(
Isolate
::
kCEntryFPAddress
,
masm
->
isolate
())));
// Restore callee-saved registers (C calling conventions).
__
pop
(
ebx
);
__
pop
(
esi
);
__
pop
(
edi
);
__
add
(
esp
,
Immediate
(2 *
kPointerSize
));
// remove markers
// Restore frame pointer and return.
__
pop
(
ebp
);
__
ret
(0);
}
下面分别对标号1和2进行说明
<1>ExternalReference c_entry_fp Isolate:: kCEntryFPAddress
关于ExternalReference类的注释如下:
// An ExternalReference represents a C++ address used in the generated
// code. All references to C++ functions and variables must be encapsulated in
// an ExternalReference instance. This is done in order to track the origin of
// all external references in the code so that they can be bound to the correct
// addresses when deserializing a heap.
通俗来说,在构建该类的时候,传入不同的类型,可以得到一个地址,这个转换过程是被ExternalReference封装的,对于Isolate::kCEntryFPAddress而言,它对应的地址是Isolate::
isolate_addresses_[]数组中一个元素,该数组是在Isolate::init函数中初始化的
#define
ASSIGN_ELEMENT
(
CamelName
,
hacker_name
) \
isolate_addresses_
[
Isolate
::
k
#
#
CamelName
#
#
Address
] = \
reinterpret_cast
<
Address
>(
hacker_name
#
#
_address
());
FOR_EACH_ISOLATE_ADDRESS_NAME
(
ASSIGN_ELEMENT
)
其中
FOR_EACH_ISOLATE_ADDRESS_NAME
的定义如下:
#define
FOR_EACH_ISOLATE_ADDRESS_NAME
(
C
) \
C
(
Handler
,
handler
) \
C
(
CEntryFP
,
c_entry_fp
) \
C
(
Context
,
context
) \
C
(
PendingException
,
pending_exception
) \
C
(
ExternalCaughtException
,
external_caught_exception
) \
C
(
JSEntrySP
,
js_entry_sp
)
对于CEntryFP,对应的调用isolate::c_entry_fp_address()函数,即
inline
Address
*
c_entry_fp_address
() {
return
&
thread_local_top_
.
c_entry_fp_
;
}
其中
thread_local_top_
.
c_entry_fp_是一个Address类型的变量。
通过上面的分析
ExternalReference
c_entry_fp
(
Isolate
::
kCEntryFPAddress
,
masm
->
isolate
());
c_entry_fp中存储的是isolate::thread_local_top_::c_entry_fp_的地址,在isolate初始化的时候,c_entry_fp_的内容为NULL
__
push
(
Operand
::
StaticVariable
(
c_entry_fp
));该指令,取c_entry_fp的内容,然后入栈,也就是NULL入栈
同样的在后面使用到了
ExternalReference
jsentry_sp,它存储的是isolate::thread_local_top_::js_entry_fp_的地址,不同的是后面的代码对其进行了赋值
JSEntryTrampoline
<2>ExternalReference entry Builtins:: kJSEntryTrampoline
这个
ExternalReference获取的是isolate::builtins::buitins_[]中的Code对象指针的地址,参看
builtins的初始化,我们可以知道JSEntryTrampoline是通过
BUILTIN_LIST_A定义的,它对应的Code对象是通过
Generate_JSEntryTrampoline函数生成的,该函数是在builtins_ia32.cc中定义的,在调用Code对象的函数之前的栈状况如下:

生成该Code对象的函数如下:
static
void
Generate_JSEntryTrampolineHelper
(
MacroAssembler
*
masm
,
bool
is_construct
) {
// Clear the context before we push it when entering the internal frame.
__
Set
(
esi
,
Immediate
(0));
{
<1>FrameScope
FrameScope
scope
(
masm
,
StackFrame
::
INTERNAL
);
// Load the previous frame pointer (ebx) to access C arguments
//载入上一个frame的地址
__
mov
(
ebx
,
Operand
(
ebp
, 0));
// Get the function from the frame and setup the context.
<2>这里非常奇怪
__
mov
(
ecx
,
Operand
(
ebx
,
EntryFrameConstants
::
kFunctionArgOffset
));
__
mov
(
esi
,
FieldOperand
(
ecx
,
JSFunction
::
kContextOffset
));
// Push the function and the receiver onto the stack.
__
push
(
ecx
);
__
push
(
Operand
(
ebx
,
EntryFrameConstants
::
kReceiverArgOffset
));
// Load the number of arguments and setup pointer to the arguments.
__
mov
(
eax
,
Operand
(
ebx
,
EntryFrameConstants
::
kArgcOffset
));
__
mov
(
ebx
,
Operand
(
ebx
,
EntryFrameConstants
::
kArgvOffset
));
// Copy arguments to the stack in a loop.
Label
loop
,
entry
;
__
Set
(
ecx
,
Immediate
(0));
__
jmp
(&
entry
);
__
bind
(&
loop
);
__
mov
(
edx
,
Operand
(
ebx
,
ecx
,
times_4
, 0));
// push parameter from argv
__
push
(
Operand
(
edx
, 0));
// dereference handle
__
inc
(
ecx
);
__
bind
(&
entry
);
__
cmp
(
ecx
,
eax
);
__
j
(
not_equal
, &
loop
);
// Get the function from the stack and call it.
// kPointerSize for the receiver.
<3>
__
mov
(
edi
,
Operand
(
esp
,
eax
,
times_4
,
kPointerSize
));
// Invoke the code.
if
(
is_construct
) {
CallConstructStub
stub
(
NO_CALL_FUNCTION_FLAGS
);
__
CallStub
(&
stub
);
}
else
{
ParameterCount
actual
(
eax
);
<4>
__
InvokeFunction
(
edi
,
actual
,
CALL_FUNCTION
,
NullCallWrapper
(),
CALL_AS_METHOD
);
}
// Exit the internal frame. Notice that this also removes the empty.
// context and the function left on the stack by the code
// invocation.
}
__
ret
(
kPointerSize
);
// Remove receiver.
}
<1>FrameScope
在类的构造函数会调用MacroAssembler::EnterFrame构建frame,
void MacroAssembler::EnterFrame(StackFrame::Type type) {
push(ebp);
mov(ebp, esp);
push(esi);
push(ebp);
mov(ebp, esp);
push(esi);
//push一个marker入栈
push(Immediate(Smi::FromInt(type)));
push(Immediate(Smi::FromInt(type)));
//push一个undefined object入栈
push(Immediate(CodeObject()));
if (emit_debug_code()) {
cmp(Operand(esp, 0), Immediate(isolate()->factory()->undefined_value()));
Check(not_equal, "code object not properly patched");
}
}
push(Immediate(CodeObject()));
if (emit_debug_code()) {
cmp(Operand(esp, 0), Immediate(isolate()->factory()->undefined_value()));
Check(not_equal, "code object not properly patched");
}
}
<2>
EntryFrameConstants
::
kFunctionArgOffset
__
mov
(
ecx
,
Operand
(
ebx
,
EntryFrameConstants
::
kFunctionArgOffset
));此时ebx为上一个frame的地址,
EntryFrameConstants
::
kFunctionArgOffset=+3 * kPointerSize,那么
Operand
(
ebx
,
EntryFrameConstants
::
kFunctionArgOffset
)就表示取上一个frame
+3 * kPointerSize的内容,根据我们上面的分析,
JSEntryStub
::
GenerateBody在函数伊始只是构建frame的后半部分,那么这里的所引用到的前部分在哪里呢?
回归到Invoke函数,它调用了
CALL_GENERATED_CODE(stub_entry, function_entry, func, recv, argc, argv);
其中宏
CALL_GENERATED_CODE的定义如下:
#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
(entry(p0, p1, p2, p3, p4))
stub_entry是一个函数指针,它的值就是js_entry_code()的entry,也就是
JSEntryStub
::
GenerateBody生成的那段代码,也就说Invoke函数最后会调用这段代码,既然是函数调用,就必然存在参数入栈,返回地址入栈,然后跳转到指定地址的过程,这样就与
EntryFrameConstants
::
kFunctionArgOffset
的分析联系到一起,按照p4到p0的顺序依次入栈,最后返回地址入栈,然后进入到js_entry_code,ebp入栈。这样我们的栈如下所示:

由此,
EntryFrameConstants
::
kFunctionArgOffset应该对应的是func参数。
__
mov
(
esi
,
FieldOperand
(
ecx
,
JSFunction
::
kContextOffset
));
印证了我们的分析,func参数是一个JSFunction对象,这里取它的context字段,赋值给esi
<3>获取func对象指针,并赋值给edi
<4>此时的栈情况如下

InvokeFunction的函数实现如下:
void MacroAssembler ::InvokeFunction( Register fun ,
const ParameterCount & actual,
InvokeFlag flag ,
const CallWrapper & call_wrapper,
CallKind call_kind ) {
// You can't call a function without a valid frame.
ASSERT(flag == JUMP_FUNCTION || has_frame());
ASSERT(fun .is( edi));
//edi存储的是JSFunction对象指针,FieldOperand( edi , JSFunction :: kSharedFunctionInfoOffset )得到SharedFunctionInfo对象指针
mov(edx , FieldOperand( edi, JSFunction ::kSharedFunctionInfoOffset));
//FieldOperand( edi , JSFunction :: kContextOffset))得到的是Context对象指针
mov(esi , FieldOperand( edi, JSFunction ::kContextOffset));
//FieldOperand( edx , SharedFunctionInfo :: kFormalParameterCountOffset )得到的是SharedFunctionInfo的FormalParameterCount字段的内容
mov(ebx , FieldOperand( edx, SharedFunctionInfo ::kFormalParameterCountOffset));
SmiUntag(ebx );
//现在ebx表示FormalParameterCount也就是函数声明的参数个数
ParameterCount expected (ebx);
//edi是JSFunction,FieldOperand ( edi, JSFunction ::kCodeEntryOffset )得到其CodeEntry
InvokeCode(FieldOperand (edi, JSFunction::kCodeEntryOffset ),
expected, actual , flag, call_wrapper, call_kind );
}
void MacroAssembler ::InvokeCode( const Operand & code,
const ParameterCount & expected,
const ParameterCount & actual,
InvokeFlag flag ,
const CallWrapper & call_wrapper,
CallKind call_kind ) {
// You can't call a function without a valid frame.
ASSERT(flag == JUMP_FUNCTION || has_frame());
Label done ;
bool definitely_mismatches = false;
//这里还需要对参数对最后的整理,函数定义如下所示
InvokePrologue(expected , actual, Handle<Code >::null(), code,
& done, &definitely_mismatches , flag, Label::kNear ,
call_wrapper, call_kind );
if (!definitely_mismatches ) {
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall (CallSize( code));
SetCallKind(ecx , call_kind);
//这里最终会调用Invoke函数中指定的function
call(code );
call_wrapper.AfterCall ();
} else {
ASSERT(flag == JUMP_FUNCTION);
SetCallKind(ecx , call_kind);
jmp(code );
}
bind(&done );
}
}
void MacroAssembler ::InvokePrologue( const ParameterCount & expected,
const ParameterCount & actual,
Handle<Code > code_constant,
const Operand & code_operand,
Label* done ,
bool* definitely_mismatches ,
InvokeFlag flag ,
Label::Distance done_near,
const CallWrapper & call_wrapper,
CallKind call_kind ) {
bool definitely_matches = false;
* definitely_mismatches = false ;
Label invoke ;
if (expected .is_immediate()) {
ASSERT(actual .is_immediate());
if (expected .immediate() == actual.immediate ()) {
definitely_matches = true ;
} else {
mov(eax , actual. immediate());
const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel ;
if (expected .immediate() == sentinel) {
// Don't worry about adapting arguments for builtins that
// don't want that done. Skip adaption code by making it look
// like we have a match between expected and actual number of
// arguments.
definitely_matches = true ;
} else {
* definitely_mismatches = true ;
mov(ebx , expected. immediate());
}
}
} else {
if (actual .is_immediate()) {
// Expected is in register, actual is immediate. This is the
// case when we invoke function values without going through the
// IC mechanism.
cmp(expected .reg(), actual.immediate ());
j(equal , &invoke);
ASSERT(expected .reg(). is(ebx ));
mov(eax , actual. immediate());
} else if (!expected. reg().is (actual. reg())) {
// Both expected and actual are in (different) registers. This
// is the case when we invoke functions using call and apply.
cmp(expected .reg(), actual.reg ());
j(equal , &invoke);
ASSERT(actual .reg(). is(eax ));
ASSERT(expected .reg(). is(ebx ));
}
}
//以上这一段显然是比较expected参数个数与实际参数个数是否一致,如果一致的话,直接跳转到invoke
if (!definitely_matches ) {
//这一句与上面的JSEntryTrampoline是一样的,也是从builtins中获取一个Code对象
Handle<Code > adaptor =
isolate()->builtins ()->ArgumentsAdaptorTrampoline();
if (!code_constant .is_null()) {
mov(edx , Immediate( code_constant));
add(edx , Immediate( Code::kHeaderSize - kHeapObjectTag));
} else if (!code_operand. is_reg(edx )) {
//把code entry设置给edx
mov(edx , code_operand);
}
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall (CallSize( adaptor, RelocInfo ::CODE_TARGET));
//把call_kind设置给ecx
SetCallKind(ecx , call_kind);
//显然这里调用的就是上面获取的Code对象,我们还需要分析生成这个Code对象的代码
call(adaptor , RelocInfo:: CODE_TARGET);
call_wrapper.AfterCall ();
if (!*definitely_mismatches ) {
jmp(done , done_near);
}
} else {
SetCallKind(ecx , call_kind);
jmp(adaptor , RelocInfo:: CODE_TARGET);
}
bind(&invoke );
}
}
ArgumentsAdaptorTrampoline
还是按照老方法,在buitins.h中查找
ArgumentsAdaptorTrampoline,发现它在BUILTIN_LIST_A宏中定义,说明它定义在bultins-ia32.cc中定义,在该文件中找到如下函数
void
Builtins
::
Generate_ArgumentsAdaptorTrampoline
(
MacroAssembler
*
masm
) {
// ----------- S t a t e -------------
// -- eax : actual number of arguments
// -- ebx : expected number of arguments
// -- ecx : call kind information
// -- edx : code entry to call
// -----------------------------------
Label
invoke
,
dont_adapt_arguments
;
__
IncrementCounter
(
masm
->
isolate
()->
counters
()->
arguments_adaptors
(), 1);
Label
enough
,
too_few
;
//比较真实参数个数与期望参数个数
__
cmp
(
eax
,
ebx
);
//如果真实参数个数比期望参数个数小,跳转到too_few
__
j
(
less
, &
too_few
);
//此时属于真实参数个数大于期望参数个数的情况,还需要比较期望参数个数,与SharedFunctionInfo ::kDontAdaptArgumentsSentinel(-1)
__
cmp
(
ebx
,
SharedFunctionInfo
::
kDontAdaptArgumentsSentinel
);
//如果相等,则跳转到dont_adapt_arguments,不再进行参数适配
__
j
(
equal
, &
dont_adapt_arguments
);
{
// Enough parameters: Actual >= expected.
//此时Actual >= expected
__
bind
(&
enough
);
//进入ArmentsAdaptorFrame,该函数如下所示
EnterArgumentsAdaptorFrame
(
masm
);
// Copy receiver and all expected arguments.
// actual >= expected的情况,把receiver和期望的参数入栈,这里是从receiver开始的,然后是arg[0],argv[1],...,可参看下图ArgumentAdaptorFrame部分,也就是说多余的参数将会被忽略
const
int
offset
=
StandardFrameConstants
::
kCallerSPOffset
;
__
lea
(
eax
,
Operand
(
ebp
,
eax
,
times_4
,
offset
));
__
mov
(
edi
, -1);
// account for receiver
Label
copy
;
__
bind
(&
copy
);
__
inc
(
edi
);
__
push
(
Operand
(
eax
, 0));
__
sub
(
eax
,
Immediate
(
kPointerSize
));
__
cmp
(
edi
,
ebx
);
__
j
(
less
, &
copy
);
__
jmp
(&
invoke
);
}
{
// Too few parameters: Actual < expected.
// actual < expected这种情况,则是把所有的receiver和argument入栈之后,在push足够数目的undefined对象
__
bind
(&
too_few
);
EnterArgumentsAdaptorFrame
(
masm
);
// Copy receiver and all actual arguments.
const
int
offset
=
StandardFrameConstants
::
kCallerSPOffset
;
__
lea
(
edi
,
Operand
(
ebp
,
eax
,
times_4
,
offset
));
// ebx = expected - actual.
__
sub
(
ebx
,
eax
);
// eax = -actual - 1
__
neg
(
eax
);
__
sub
(
eax
,
Immediate
(1));
Label
copy
;
__
bind
(&
copy
);
__
inc
(
eax
);
__
push
(
Operand
(
edi
, 0));
__
sub
(
edi
,
Immediate
(
kPointerSize
));
__
test
(
eax
,
eax
);
__
j
(
not_zero
, &
copy
);
// Fill remaining expected arguments with undefined values.
Label
fill
;
__
bind
(&
fill
);
__
inc
(
eax
);
__
push
(
Immediate
(
masm
->
isolate
()->
factory
()->
undefined_value
()));
__
cmp
(
eax
,
ebx
);
__
j
(
less
, &
fill
);
}
// Call the entry point.
__
bind
(&
invoke
);
// Restore function pointer.
__
mov
(
edi
,
Operand
(
ebp
,
JavaScriptFrameConstants
::
kFunctionOffset
));
//调用edx指定的代码,在MacroAssembler :: InvokeFunction中,已经指定了esi,edi的值,edi指向JSFuntion,esi指向Context
__
call
(
edx
);
// Store offset of return address for deoptimizer.
masm
->
isolate
()->
heap
()->
SetArgumentsAdaptorDeoptPCOffset
(
masm
->
pc_offset
());
// Leave frame and return.
LeaveArgumentsAdaptorFrame
(
masm
);
__
ret
(0);
// -------------------------------------------
// Dont adapt arguments.
// -------------------------------------------
__
bind
(&
dont_adapt_arguments
);
__
jmp
(
edx
);
}
static
void
EnterArgumentsAdaptorFrame
(
MacroAssembler
*
masm
) {
__
push
(
ebp
);
__
mov
(
ebp
,
esp
);
// Store the arguments adaptor context sentinel.
__
push
(
Immediate
(
Smi
::
FromInt
(
StackFrame
::
ARGUMENTS_ADAPTOR
)));
// Push the function on the stack.
__
push
(
edi
);
// Preserve the number of arguments on the stack. Must preserve eax,
// ebx and ecx because these registers are used when copying the
// arguments and the receiver.
STATIC_ASSERT
(
kSmiTagSize
== 1);
__
lea
(
edi
,
Operand
(
eax
,
eax
,
times_1
,
kSmiTag
));
__
push
(
edi
);
}
调用该函数后,栈的情况如下:

full-codegen-ia32.cc中FullCodeGenerator::Generate()用于生成javascript的code,其注释如下:
// Generate code for a JS function. On entry to the function the receiver
// and arguments have been pushed on the stack left to right, with the
// return address on top of them. The actual argument count matches the
// formal parameter count expected by the function.
//
// The live registers are:
// o edi: the JS function object being called (i.e. ourselves)
// o esi: our context
// o ebp: our caller's frame pointer
// o esp: stack pointer (pointing to return address)
//
// The function builds a JS frame. Please see JavaScriptFrameConstants in
// frames-ia32.h for its layout.
// and arguments have been pushed on the stack left to right, with the
// return address on top of them. The actual argument count matches the
// formal parameter count expected by the function.
//
// The live registers are:
// o edi: the JS function object being called (i.e. ourselves)
// o esi: our context
// o ebp: our caller's frame pointer
// o esp: stack pointer (pointing to return address)
//
// The function builds a JS frame. Please see JavaScriptFrameConstants in
// frames-ia32.h for its layout.
这里清楚的说明,此时参数是与实际函数声明匹配的,这就对应了我们通过ArgumentAdaptorFrame所做的事情,同时edi和esi也指向了各自所需要的值。
至此,我们就清楚了从C函数调用javascript函数的整个过程。上图的栈情况已经很清楚的说明了函数调用的流程,让我们再回顾一下要点
->Invoke
->js_entry_code()
这段代码是存储在Code对象中的,该对象指针是存储在heap的root数组中的,同时它又是放在Stub中cache的,它是通过JSEntryStub创建的,这段代码创建了EntryFrame
->JSEntryTrampoline
这段代码是存储在Code对象中的,该对象指针存储在isolate::builtin::builtins[]数组中,它是在builtin初始化的时候,通过Generate函数生成的,这段代码重新整理了C的参数,为参数适配做准备
->ArgumentAdaptor
这段代码是存储在Code对象中的,该对象指针存储在isolate::builtin::builtins[]数组中,它是在builtin初始化的时候,通过Generate函数生成的,这段代码是为了使得实际参数与期望参数匹配
在上面的调用过程中,使用到的Frame有:
EntryFrame
在frame-ia32.h中有如下的定义
class EntryFrameConstants : public AllStatic {
public:
static const int kCallerFPOffset = -6 * kPointerSize;
static const int kFunctionArgOffset = +3 * kPointerSize;
static const int kReceiverArgOffset = +4 * kPointerSize;
static const int kArgcOffset = +5 * kPointerSize;
static const int kArgvOffset = +6 * kPointerSize;
};
static const int kCallerFPOffset = -6 * kPointerSize;
static const int kFunctionArgOffset = +3 * kPointerSize;
static const int kReceiverArgOffset = +4 * kPointerSize;
static const int kArgcOffset = +5 * kPointerSize;
static const int kArgvOffset = +6 * kPointerSize;
};
我们可以看到这里的offset与上图中绿色部分是吻合的
ArgumentAdaptorFrame
在frame-ia32.h中有如下的定义
class ArgumentsAdaptorFrameConstants : public AllStatic {
public:
// FP-relative.
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
static const int kFrameSize =
StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
public:
// FP-relative.
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
static const int kFrameSize =
StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
我们可以看到这里的offset与上图中土色部分是吻合的
CEntry
Heap::CreateFixStub
->
CEntryStub
::
GenerateAheadOfTime
该函数会生成一个Stub存入factory的stub dict中