Version 3.15.3

Changed sample shell to send non-JS output (e.g. errors) to stderr instead of stdout.

Correctly check for stack overflow even when interrupt is pending. (issue 214)

Collect stack trace on stack overflow. (issue 2394)

Performance and stability improvements on all platforms.

git-svn-id: http://v8.googlecode.com/svn/trunk@12947 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/isolate.cc b/src/isolate.cc
index 00979f7..85ae32a 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -553,7 +553,97 @@
 }
 
 
-void Isolate::CaptureAndSetCurrentStackTraceFor(Handle<JSObject> error_object) {
+// Determines whether the given stack frame should be displayed in
+// a stack trace.  The caller is the error constructor that asked
+// for the stack trace to be collected.  The first time a construct
+// call to this function is encountered it is skipped.  The seen_caller
+// in/out parameter is used to remember if the caller has been seen
+// yet.
+static bool IsVisibleInStackTrace(StackFrame* raw_frame,
+                                  Object* caller,
+                                  bool* seen_caller) {
+  // Only display JS frames.
+  if (!raw_frame->is_java_script()) return false;
+  JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
+  Object* raw_fun = frame->function();
+  // Not sure when this can happen but skip it just in case.
+  if (!raw_fun->IsJSFunction()) return false;
+  if ((raw_fun == caller) && !(*seen_caller)) {
+    *seen_caller = true;
+    return false;
+  }
+  // Skip all frames until we've seen the caller.
+  if (!(*seen_caller)) return false;
+  // Also, skip non-visible built-in functions and any call with the builtins
+  // object as receiver, so as to not reveal either the builtins object or
+  // an internal function.
+  // The --builtins-in-stack-traces command line flag allows including
+  // internal call sites in the stack trace for debugging purposes.
+  if (!FLAG_builtins_in_stack_traces) {
+    JSFunction* fun = JSFunction::cast(raw_fun);
+    if (frame->receiver()->IsJSBuiltinsObject() ||
+        (fun->IsBuiltin() && !fun->shared()->native())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+
+Handle<JSArray> Isolate::CaptureSimpleStackTrace(Handle<JSObject> error_object,
+                                                 Handle<Object> caller,
+                                                 int limit) {
+  limit = Max(limit, 0);  // Ensure that limit is not negative.
+  int initial_size = Min(limit, 10);
+  Handle<FixedArray> elements =
+      factory()->NewFixedArrayWithHoles(initial_size * 4);
+
+  // If the caller parameter is a function we skip frames until we're
+  // under it before starting to collect.
+  bool seen_caller = !caller->IsJSFunction();
+  int cursor = 0;
+  int frames_seen = 0;
+  for (StackFrameIterator iter(this);
+       !iter.done() && frames_seen < limit;
+       iter.Advance()) {
+    StackFrame* raw_frame = iter.frame();
+    if (IsVisibleInStackTrace(raw_frame, *caller, &seen_caller)) {
+      frames_seen++;
+      JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
+      // Set initial size to the maximum inlining level + 1 for the outermost
+      // function.
+      List<FrameSummary> frames(Compiler::kMaxInliningLevels + 1);
+      frame->Summarize(&frames);
+      for (int i = frames.length() - 1; i >= 0; i--) {
+        if (cursor + 4 > elements->length()) {
+          int new_capacity = JSObject::NewElementsCapacity(elements->length());
+          Handle<FixedArray> new_elements =
+              factory()->NewFixedArrayWithHoles(new_capacity);
+          for (int i = 0; i < cursor; i++) {
+            new_elements->set(i, elements->get(i));
+          }
+          elements = new_elements;
+        }
+        ASSERT(cursor + 4 <= elements->length());
+
+        Handle<Object> recv = frames[i].receiver();
+        Handle<JSFunction> fun = frames[i].function();
+        Handle<Code> code = frames[i].code();
+        Handle<Smi> offset(Smi::FromInt(frames[i].offset()));
+        elements->set(cursor++, *recv);
+        elements->set(cursor++, *fun);
+        elements->set(cursor++, *code);
+        elements->set(cursor++, *offset);
+      }
+    }
+  }
+  Handle<JSArray> result = factory()->NewJSArrayWithElements(elements);
+  result->set_length(Smi::FromInt(cursor));
+  return result;
+}
+
+
+void Isolate::CaptureAndSetDetailedStackTrace(Handle<JSObject> error_object) {
   if (capture_stack_trace_for_uncaught_exceptions_) {
     // Capture stack trace for a detailed exception message.
     Handle<String> key = factory()->hidden_stack_trace_symbol();
@@ -910,15 +1000,28 @@
 
 Failure* Isolate::StackOverflow() {
   HandleScope scope;
+  // At this point we cannot create an Error object using its javascript
+  // constructor.  Instead, we copy the pre-constructed boilerplate and
+  // attach the stack trace as a hidden property.
   Handle<String> key = factory()->stack_overflow_symbol();
   Handle<JSObject> boilerplate =
       Handle<JSObject>::cast(GetProperty(js_builtins_object(), key));
-  Handle<Object> exception = Copy(boilerplate);
-  // TODO(1240995): To avoid having to call JavaScript code to compute
-  // the message for stack overflow exceptions which is very likely to
-  // double fault with another stack overflow exception, we use a
-  // precomputed message.
+  Handle<JSObject> exception = Copy(boilerplate);
   DoThrow(*exception, NULL);
+
+  // Get stack trace limit.
+  Handle<Object> error = GetProperty(js_builtins_object(), "$Error");
+  if (!error->IsJSObject()) return Failure::Exception();
+  Handle<Object> stack_trace_limit =
+      GetProperty(Handle<JSObject>::cast(error), "stackTraceLimit");
+  if (!stack_trace_limit->IsNumber()) return Failure::Exception();
+  int limit = static_cast<int>(stack_trace_limit->Number());
+
+  Handle<JSArray> stack_trace = CaptureSimpleStackTrace(
+      exception, factory()->undefined_value(), limit);
+  JSObject::SetHiddenProperty(exception,
+                              factory()->hidden_stack_trace_symbol(),
+                              stack_trace);
   return Failure::Exception();
 }
 
@@ -1125,18 +1228,22 @@
               stack_trace_for_uncaught_exceptions_options_);
         }
       }
-      // Stringify custom error objects for the message object.
-      if (exception_handle->IsJSObject() && !IsErrorObject(exception_handle)) {
+
+      Handle<Object> exception_arg = exception_handle;
+      // If the exception argument is a custom object, turn it into a string
+      // before throwing as uncaught exception.  Note that the pending
+      // exception object to be set later must not be turned into a string.
+      if (exception_arg->IsJSObject() && !IsErrorObject(exception_arg)) {
         bool failed = false;
-        exception_handle = Execution::ToString(exception_handle, &failed);
+        exception_arg = Execution::ToDetailString(exception_arg, &failed);
         if (failed) {
-          exception_handle = factory()->LookupAsciiSymbol("exception");
+          exception_arg = factory()->LookupAsciiSymbol("exception");
         }
       }
       Handle<Object> message_obj = MessageHandler::MakeMessageObject(
           "uncaught_exception",
           location,
-          HandleVector<Object>(&exception_handle, 1),
+          HandleVector<Object>(&exception_arg, 1),
           stack_trace,
           stack_trace_object);
       thread_local_top()->pending_message_obj_ = *message_obj;