一.何为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