v8的Frame

一.何爲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<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);
}
其中重點是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);
}
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 =
        <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);
}
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)
ROOT_LIST是在heap.h中定義的,其中
V(Code, js_entry_code, JsEntryCode)                                          \
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);
  //把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);
  //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一個marker入棧
  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");
  }
}
<2>EntryFrameConstants:: kFunctionArgOffset
__ mov ( ecxOperand (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 ( esiFieldOperand (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 ( ediJSFunction ::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.
這裏清楚的說明,此時參數是與實際函數聲明匹配的,這就對應了我們通過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;
};
我們可以看到這裏的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;
};
我們可以看到這裏的offset與上圖中土色部分是吻合的

CEntry
Heap::CreateFixStub
->CEntryStub ::GenerateAheadOfTime
該函數會生成一個Stub存入factory的stub dict中






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章