Index: src/isolate.cc |
diff --git a/src/isolate.cc b/src/isolate.cc |
index 75e15a454196573fce28a438032a7fe68e966ddb..04d9c4cd9b9f4641234acd4c44f4d0bb663cc538 100644 |
--- a/src/isolate.cc |
+++ b/src/isolate.cc |
@@ -553,7 +553,101 @@ void Isolate::PushStackTraceAndDie(unsigned int magic, |
} |
-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. |
+bool ShowFrameInStackTrace(StackFrame* raw_frame, |
Sven Panne
2012/11/09 12:25:52
Make this static or a method of StackFrame. Furthe
|
+ Object* caller, |
+ bool* seen_caller) { |
+ // Only display JS frames. |
+ if (!raw_frame->is_java_script()) { |
Sven Panne
2012/11/09 12:25:52
Just for consistency: One-liner if...
|
+ 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()) { |
Sven Panne
2012/11/09 12:25:52
Again just for consistency: One-liner if...
|
+ 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); |
+ |
+ StackFrameIterator iter(this); |
+ // 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; |
+ while (!iter.done() && frames_seen < limit) { |
Sven Panne
2012/11/09 12:25:52
A for-loop with a conditional break in it might be
|
+ StackFrame* raw_frame = iter.frame(); |
+ if (ShowFrameInStackTrace(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); |
+ } |
+ } |
+ iter.Advance(); |
+ } |
+ 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(); |
@@ -926,12 +1020,24 @@ Failure* Isolate::StackOverflow() { |
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()); |
+ |
+ // Store the raw stack trace as hidden property so that the stack trace |
+ // string can be lazily formatted in javascript. |
+ Handle<JSArray> stack_trace = CaptureSimpleStackTrace( |
+ exception, factory()->undefined_value(), limit); |
+ JSObject::SetHiddenProperty(exception, |
+ factory()->hidden_stack_trace_symbol(), |
+ stack_trace); |
return Failure::Exception(); |
} |