Initial export.

git-svn-id: http://v8.googlecode.com/svn/trunk@2 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/frames.cc b/src/frames.cc
new file mode 100644
index 0000000..568295b
--- /dev/null
+++ b/src/frames.cc
@@ -0,0 +1,678 @@
+// Copyright 2006-2008 Google Inc. All Rights Reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "frames-inl.h"
+#include "scopeinfo.h"
+#include "string-stream.h"
+#include "top.h"
+#include "zone-inl.h"
+
+namespace v8 { namespace internal {
+
+
+DEFINE_int(max_stack_trace_source_length, 300,
+           "maximum length of function source code printed in a stack trace.");
+
+
+// -------------------------------------------------------------------------
+
+
+// Iterator that supports traversing the stack handlers of a
+// particular frame. Needs to know the top of the handler chain.
+class StackHandlerIterator BASE_EMBEDDED {
+ public:
+  StackHandlerIterator(const StackFrame* frame, StackHandler* handler)
+      : limit_(frame->fp()), handler_(handler) {
+    // Make sure the handler has already been unwound to this frame.
+    ASSERT(frame->sp() <= handler->address());
+  }
+
+  StackHandler* handler() const { return handler_; }
+
+  bool done() { return handler_->address() > limit_; }
+  void Advance() {
+    ASSERT(!done());
+    handler_ = handler_->next();
+  }
+
+ private:
+  const Address limit_;
+  StackHandler* handler_;
+};
+
+
+// -------------------------------------------------------------------------
+
+
+#define INITIALIZE_SINGLETON(type, field) field##_(this),
+StackFrameIterator::StackFrameIterator()
+    : STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
+      frame_(NULL), handler_(NULL), thread(Top::GetCurrentThread()) {
+  Reset();
+}
+StackFrameIterator::StackFrameIterator(ThreadLocalTop* t)
+    : STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
+      frame_(NULL), handler_(NULL), thread(t) {
+  Reset();
+}
+#undef INITIALIZE_SINGLETON
+
+
+void StackFrameIterator::Advance() {
+  ASSERT(!done());
+  // Compute the state of the calling frame before restoring
+  // callee-saved registers and unwinding handlers. This allows the
+  // frame code that computes the caller state to access the top
+  // handler and the value of any callee-saved register if needed.
+  StackFrame::State state;
+  StackFrame::Type type = frame_->GetCallerState(&state);
+
+  // Restore any callee-saved registers to the register buffer. Avoid
+  // the virtual call if the platform doesn't have any callee-saved
+  // registers.
+  if (kNumJSCalleeSaved > 0) {
+    frame_->RestoreCalleeSavedRegisters(register_buffer());
+  }
+
+  // Unwind handlers corresponding to the current frame.
+  StackHandlerIterator it(frame_, handler_);
+  while (!it.done()) it.Advance();
+  handler_ = it.handler();
+
+  // Advance to the calling frame.
+  frame_ = SingletonFor(type, &state);
+
+  // When we're done iterating over the stack frames, the handler
+  // chain must have been completely unwound.
+  ASSERT(!done() || handler_ == NULL);
+}
+
+
+void StackFrameIterator::Reset() {
+  Address fp = Top::c_entry_fp(thread);
+  StackFrame::State state;
+  StackFrame::Type type = ExitFrame::GetStateForFramePointer(fp, &state);
+  frame_ = SingletonFor(type, &state);
+  handler_ = StackHandler::FromAddress(Top::handler(thread));
+  // Zap the register buffer in debug mode.
+  if (kDebug) {
+    Object** buffer = register_buffer();
+    for (int i = 0; i < kNumJSCalleeSaved; i++) {
+      buffer[i] = reinterpret_cast<Object*>(kZapValue);
+    }
+  }
+}
+
+
+Object** StackFrameIterator::RestoreCalleeSavedForTopHandler(Object** buffer) {
+  ASSERT(kNumJSCalleeSaved > 0);
+  // Traverse the frames until we find the frame containing the top
+  // handler. Such a frame is guaranteed to always exists by the
+  // callers of this function.
+  for (StackFrameIterator it; true; it.Advance()) {
+    StackHandlerIterator handlers(it.frame(), it.handler());
+    if (!handlers.done()) {
+      memcpy(buffer, it.register_buffer(), kNumJSCalleeSaved * kPointerSize);
+      return buffer;
+    }
+  }
+}
+
+
+StackFrame* StackFrameIterator::SingletonFor(StackFrame::Type type,
+                                             StackFrame::State* state) {
+#define FRAME_TYPE_CASE(type, field) \
+  case StackFrame::type: result = &field##_; break;
+
+  StackFrame* result = NULL;
+  switch (type) {
+    case StackFrame::NONE: return NULL;
+    STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE)
+    default: break;
+  }
+  ASSERT(result != NULL);
+  result->state_ = *state;
+  return result;
+
+#undef FRAME_TYPE_CASE
+}
+
+
+// -------------------------------------------------------------------------
+
+
+JavaScriptFrameIterator::JavaScriptFrameIterator(StackFrame::Id id) {
+  while (true) {
+    Advance();
+    if (frame()->id() == id) return;
+  }
+}
+
+
+void JavaScriptFrameIterator::Advance() {
+  do {
+    iterator_.Advance();
+  } while (!iterator_.done() && !iterator_.frame()->is_java_script());
+}
+
+
+void JavaScriptFrameIterator::AdvanceToArgumentsFrame() {
+  if (!frame()->has_adapted_arguments()) return;
+  iterator_.Advance();
+  ASSERT(iterator_.frame()->is_arguments_adaptor());
+}
+
+
+void JavaScriptFrameIterator::Reset() {
+  iterator_.Reset();
+  Advance();
+}
+
+
+// -------------------------------------------------------------------------
+
+
+void StackHandler::Cook(Code* code) {
+  ASSERT(code->contains(pc()));
+  set_pc(AddressFrom<Address>(pc() - code->instruction_start()));
+}
+
+
+void StackHandler::Uncook(Code* code) {
+  set_pc(code->instruction_start() + OffsetFrom(pc()));
+  ASSERT(code->contains(pc()));
+}
+
+
+// -------------------------------------------------------------------------
+
+
+bool StackFrame::HasHandler() const {
+  StackHandlerIterator it(this, top_handler());
+  return !it.done();
+}
+
+
+void StackFrame::CookFramesForThread(ThreadLocalTop* thread) {
+  ASSERT(!thread->stack_is_cooked());
+  for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
+    it.frame()->Cook();
+  }
+  thread->set_stack_is_cooked(true);
+}
+
+
+void StackFrame::UncookFramesForThread(ThreadLocalTop* thread) {
+  ASSERT(thread->stack_is_cooked());
+  for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
+    it.frame()->Uncook();
+  }
+  thread->set_stack_is_cooked(false);
+}
+
+
+void StackFrame::Cook() {
+  Code* code = FindCode();
+  for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
+    it.handler()->Cook(code);
+  }
+  ASSERT(code->contains(pc()));
+  set_pc(AddressFrom<Address>(pc() - code->instruction_start()));
+}
+
+
+void StackFrame::Uncook() {
+  Code* code = FindCode();
+  for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
+    it.handler()->Uncook(code);
+  }
+  set_pc(code->instruction_start() + OffsetFrom(pc()));
+  ASSERT(code->contains(pc()));
+}
+
+
+Code* EntryFrame::FindCode() const {
+  return Heap::js_entry_code();
+}
+
+
+StackFrame::Type EntryFrame::GetCallerState(State* state) const {
+  const int offset = EntryFrameConstants::kCallerFPOffset;
+  Address fp = Memory::Address_at(this->fp() + offset);
+  return ExitFrame::GetStateForFramePointer(fp, state);
+}
+
+
+Code* EntryConstructFrame::FindCode() const {
+  return Heap::js_construct_entry_code();
+}
+
+
+Code* ExitFrame::FindCode() const {
+  return Heap::c_entry_code();
+}
+
+
+StackFrame::Type ExitFrame::GetCallerState(State* state) const {
+  // Setup the caller state.
+  state->sp = pp();
+  state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset);
+#ifdef USE_OLD_CALLING_CONVENTIONS
+  state->pp = Memory::Address_at(fp() + ExitFrameConstants::kCallerPPOffset);
+#endif
+  state->pc_address
+      = reinterpret_cast<Address*>(fp() + ExitFrameConstants::kCallerPCOffset);
+  return ComputeType(state);
+}
+
+
+Address ExitFrame::GetCallerStackPointer() const {
+  return fp() + ExitFrameConstants::kPPDisplacement;
+}
+
+
+Code* ExitDebugFrame::FindCode() const {
+  return Heap::c_entry_debug_break_code();
+}
+
+
+RegList ExitFrame::FindCalleeSavedRegisters() const {
+  // Exit frames save all - if any - callee-saved registers.
+  return kJSCalleeSaved;
+}
+
+
+Address StandardFrame::GetExpressionAddress(int n) const {
+  ASSERT(0 <= n && n < ComputeExpressionsCount());
+  if (kNumJSCalleeSaved > 0 && n < kNumJSCalleeSaved) {
+    return reinterpret_cast<Address>(top_register_buffer() + n);
+  } else {
+    const int offset = StandardFrameConstants::kExpressionsOffset;
+    return fp() + offset - (n - kNumJSCalleeSaved) * kPointerSize;
+  }
+}
+
+
+int StandardFrame::ComputeExpressionsCount() const {
+  const int offset =
+      StandardFrameConstants::kExpressionsOffset + kPointerSize;
+  Address base = fp() + offset;
+  Address limit = sp();
+  ASSERT(base >= limit);  // stack grows downwards
+  // Include register-allocated locals in number of expressions.
+  return (base - limit) / kPointerSize + kNumJSCalleeSaved;
+}
+
+
+StackFrame::Type StandardFrame::GetCallerState(State* state) const {
+  state->sp = caller_sp();
+  state->fp = caller_fp();
+#ifdef USE_OLD_CALLING_CONVENTIONS
+  state->pp = caller_pp();
+#endif
+  state->pc_address = reinterpret_cast<Address*>(ComputePCAddress(fp()));
+  return ComputeType(state);
+}
+
+
+bool StandardFrame::IsExpressionInsideHandler(int n) const {
+  Address address = GetExpressionAddress(n);
+  for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
+    if (it.handler()->includes(address)) return true;
+  }
+  return false;
+}
+
+
+Object* JavaScriptFrame::GetParameter(int index) const {
+  ASSERT(index >= 0 && index < ComputeParametersCount());
+  const int offset = JavaScriptFrameConstants::kParam0Offset;
+  return Memory::Object_at(pp() + offset - (index * kPointerSize));
+}
+
+
+int JavaScriptFrame::ComputeParametersCount() const {
+  Address base  = pp() + JavaScriptFrameConstants::kReceiverOffset;
+  Address limit = fp() + JavaScriptFrameConstants::kSavedRegistersOffset;
+  int result = (base - limit) / kPointerSize;
+  if (kNumJSCalleeSaved > 0) {
+    return result - NumRegs(FindCalleeSavedRegisters());
+  } else {
+    return result;
+  }
+}
+
+
+bool JavaScriptFrame::IsConstructor() const {
+  Address pc = has_adapted_arguments()
+      ? Memory::Address_at(ComputePCAddress(caller_fp()))
+      : caller_pc();
+  return IsConstructTrampolineFrame(pc);
+}
+
+
+Code* ArgumentsAdaptorFrame::FindCode() const {
+  return Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline);
+}
+
+
+Code* InternalFrame::FindCode() const {
+  const int offset = InternalFrameConstants::kCodeOffset;
+  Object* code = Memory::Object_at(fp() + offset);
+  if (code == NULL) {
+    // The code object isn't set; find it and set it.
+    code = Heap::FindCodeObject(pc());
+    ASSERT(!code->IsFailure());
+    Memory::Object_at(fp() + offset) = code;
+  }
+  ASSERT(code != NULL);
+  return Code::cast(code);
+}
+
+
+void StackFrame::PrintIndex(StringStream* accumulator,
+                            PrintMode mode,
+                            int index) {
+  accumulator->Add((mode == OVERVIEW) ? "%5d: " : "[%d]: ", index);
+}
+
+
+void JavaScriptFrame::Print(StringStream* accumulator,
+                            PrintMode mode,
+                            int index) const {
+  HandleScope scope;
+  Object* receiver = this->receiver();
+  Object* function = this->function();
+
+  accumulator->PrintSecurityTokenIfChanged(function);
+  PrintIndex(accumulator, mode, index);
+  Code* code = NULL;
+  if (IsConstructor()) accumulator->Add("new ");
+  accumulator->PrintFunction(function, receiver, &code);
+  accumulator->Add("(this=%o", receiver);
+
+  // Get scope information for nicer output, if possible. If code is
+  // NULL, or doesn't contain scope info, info will return 0 for the
+  // number of parameters, stack slots, or context slots.
+  ScopeInfo<PreallocatedStorage> info(code);
+
+  // Print the parameters.
+  int parameters_count = ComputeParametersCount();
+  for (int i = 0; i < parameters_count; i++) {
+    accumulator->Add(",");
+    // If we have a name for the parameter we print it. Nameless
+    // parameters are either because we have more actual parameters
+    // than formal parameters or because we have no scope information.
+    if (i < info.number_of_parameters()) {
+      accumulator->PrintName(*info.parameter_name(i));
+      accumulator->Add("=");
+    }
+    accumulator->Add("%o", GetParameter(i));
+  }
+
+  accumulator->Add(")");
+  if (mode == OVERVIEW) {
+    accumulator->Add("\n");
+    return;
+  }
+  accumulator->Add(" {\n");
+
+  // Compute the number of locals and expression stack elements.
+  int stack_locals_count = info.number_of_stack_slots();
+  int heap_locals_count = info.number_of_context_slots();
+  int expressions_count = ComputeExpressionsCount();
+
+  // Print stack-allocated local variables.
+  if (stack_locals_count > 0) {
+    accumulator->Add("  // stack-allocated locals\n");
+  }
+  for (int i = 0; i < stack_locals_count; i++) {
+    accumulator->Add("  var ");
+    accumulator->PrintName(*info.stack_slot_name(i));
+    accumulator->Add(" = ");
+    if (i < expressions_count) {
+      accumulator->Add("%o", GetExpression(i));
+    } else {
+      accumulator->Add("// no expression found - inconsistent frame?");
+    }
+    accumulator->Add("\n");
+  }
+
+  // Try to get hold of the context of this frame.
+  Context* context = NULL;
+  if (this->context() != NULL && this->context()->IsContext()) {
+    context = Context::cast(this->context());
+  }
+
+  // Print heap-allocated local variables.
+  if (heap_locals_count > Context::MIN_CONTEXT_SLOTS) {
+    accumulator->Add("  // heap-allocated locals\n");
+  }
+  for (int i = Context::MIN_CONTEXT_SLOTS; i < heap_locals_count; i++) {
+    accumulator->Add("  var ");
+    accumulator->PrintName(*info.context_slot_name(i));
+    accumulator->Add(" = ");
+    if (context != NULL) {
+      if (i < context->length()) {
+        accumulator->Add("%o", context->get(i));
+      } else {
+        accumulator->Add(
+            "// warning: missing context slot - inconsistent frame?");
+      }
+    } else {
+      accumulator->Add("// warning: no context found - inconsistent frame?");
+    }
+    accumulator->Add("\n");
+  }
+
+  // Print the expression stack.
+  int expressions_start = Max(stack_locals_count, kNumJSCalleeSaved);
+  if (expressions_start < expressions_count) {
+    accumulator->Add("  // expression stack (top to bottom)\n");
+  }
+  for (int i = expressions_count - 1; i >= expressions_start; i--) {
+    if (IsExpressionInsideHandler(i)) continue;
+    accumulator->Add("  [%02d] : %o\n", i, GetExpression(i));
+  }
+
+  // Print details about the function.
+  if (FLAG_max_stack_trace_source_length != 0 && code != NULL) {
+    SharedFunctionInfo* shared = JSFunction::cast(function)->shared();
+    accumulator->Add("--------- s o u r c e   c o d e ---------\n");
+    shared->SourceCodePrint(accumulator, FLAG_max_stack_trace_source_length);
+    accumulator->Add("\n-----------------------------------------\n");
+  }
+
+  accumulator->Add("}\n\n");
+}
+
+
+void ArgumentsAdaptorFrame::Print(StringStream* accumulator,
+                                  PrintMode mode,
+                                  int index) const {
+  int actual = ComputeParametersCount();
+  int expected = -1;
+  Object* function = this->function();
+  if (function->IsJSFunction()) {
+    expected = JSFunction::cast(function)->shared()->formal_parameter_count();
+  }
+
+  PrintIndex(accumulator, mode, index);
+  accumulator->Add("arguments adaptor frame: %d->%d", actual, expected);
+  if (mode == OVERVIEW) {
+    accumulator->Add("\n");
+    return;
+  }
+  accumulator->Add(" {\n");
+
+  // Print actual arguments.
+  if (actual > 0) accumulator->Add("  // actual arguments\n");
+  for (int i = 0; i < actual; i++) {
+    accumulator->Add("  [%02d] : %o", i, GetParameter(i));
+    if (expected != -1 && i >= expected) {
+      accumulator->Add("  // not passed to callee");
+    }
+    accumulator->Add("\n");
+  }
+
+  accumulator->Add("}\n\n");
+}
+
+
+void EntryFrame::Iterate(ObjectVisitor* v) const {
+  StackHandlerIterator it(this, top_handler());
+  ASSERT(!it.done());
+  StackHandler* handler = it.handler();
+  ASSERT(handler->is_entry());
+  handler->Iterate(v);
+  // Make sure that there's the entry frame does not contain more than
+  // one stack handler.
+  if (kDebug) {
+    it.Advance();
+    ASSERT(it.done());
+  }
+}
+
+
+void StandardFrame::IterateExpressions(ObjectVisitor* v) const {
+  const int offset = StandardFrameConstants::kContextOffset;
+  Object** base = &Memory::Object_at(sp());
+  Object** limit = &Memory::Object_at(fp() + offset) + 1;
+  for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
+    StackHandler* handler = it.handler();
+    // Traverse pointers down to - but not including - the next
+    // handler in the handler chain. Update the base to skip the
+    // handler and allow the handler to traverse its own pointers.
+    const Address address = handler->address();
+    v->VisitPointers(base, reinterpret_cast<Object**>(address));
+    base = reinterpret_cast<Object**>(address + StackHandlerConstants::kSize);
+    // Traverse the pointers in the handler itself.
+    handler->Iterate(v);
+  }
+  v->VisitPointers(base, limit);
+}
+
+
+void JavaScriptFrame::Iterate(ObjectVisitor* v) const {
+  IterateExpressions(v);
+
+  // Traverse callee-saved registers, receiver, and parameters.
+  const int kBaseOffset = JavaScriptFrameConstants::kSavedRegistersOffset;
+  const int kLimitOffset = JavaScriptFrameConstants::kReceiverOffset;
+  Object** base = &Memory::Object_at(fp() + kBaseOffset);
+  Object** limit = &Memory::Object_at(pp() + kLimitOffset) + 1;
+  v->VisitPointers(base, limit);
+}
+
+
+void InternalFrame::Iterate(ObjectVisitor* v) const {
+  // Internal frames only have object pointers on the expression stack
+  // as they never have any arguments.
+  IterateExpressions(v);
+}
+
+
+// -------------------------------------------------------------------------
+
+
+JavaScriptFrame* StackFrameLocator::FindJavaScriptFrame(int n) {
+  ASSERT(n >= 0);
+  for (int i = 0; i <= n; i++) {
+    while (!iterator_.frame()->is_java_script()) iterator_.Advance();
+    if (i == n) return JavaScriptFrame::cast(iterator_.frame());
+    iterator_.Advance();
+  }
+  UNREACHABLE();
+  return NULL;
+}
+
+
+// -------------------------------------------------------------------------
+
+
+int NumRegs(RegList reglist) {
+  int n = 0;
+  while (reglist != 0) {
+    n++;
+    reglist &= reglist - 1;  // clear one bit
+  }
+  return n;
+}
+
+
+int JSCallerSavedCode(int n) {
+  static int reg_code[kNumJSCallerSaved];
+  static bool initialized = false;
+  if (!initialized) {
+    initialized = true;
+    int i = 0;
+    for (int r = 0; r < kNumRegs; r++)
+      if ((kJSCallerSaved & (1 << r)) != 0)
+        reg_code[i++] = r;
+
+    ASSERT(i == kNumJSCallerSaved);
+  }
+  ASSERT(0 <= n && n < kNumJSCallerSaved);
+  return reg_code[n];
+}
+
+
+int JSCalleeSavedCode(int n) {
+  static int reg_code[kNumJSCalleeSaved + 1];  // avoid zero-size array error
+  static bool initialized = false;
+  if (!initialized) {
+    initialized = true;
+    int i = 0;
+    for (int r = 0; r < kNumRegs; r++)
+      if ((kJSCalleeSaved & (1 << r)) != 0)
+        reg_code[i++] = r;
+
+    ASSERT(i == kNumJSCalleeSaved);
+  }
+  ASSERT(0 <= n && n < kNumJSCalleeSaved);
+  return reg_code[n];
+}
+
+
+RegList JSCalleeSavedList(int n) {
+  // avoid zero-size array error
+  static RegList reg_list[kNumJSCalleeSaved + 1];
+  static bool initialized = false;
+  if (!initialized) {
+    initialized = true;
+    reg_list[0] = 0;
+    for (int i = 0; i < kNumJSCalleeSaved; i++)
+      reg_list[i+1] = reg_list[i] + (1 << JSCalleeSavedCode(i));
+  }
+  ASSERT(0 <= n && n <= kNumJSCalleeSaved);
+  return reg_list[n];
+}
+
+
+} }  // namespace v8::internal