Fix several bugs in error stack trace formatting.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 20 Dec 2012 16:25:26 +0000 (16:25 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 20 Dec 2012 16:25:26 +0000 (16:25 +0000)
GetScriptWrapper can be called recursively:
GetScriptWrapper -> GC -> DeferredFormatStackTrace -> GetScriptWrapper

GC-unsafe code in ErrorObjectList::DeferredFormatStackTrace

Enable overwriting Error.prepareStackTrace by itself while not
causing infinity recursion when it triggers an exception.

R=ulan@chromium.org
BUG=

Review URL: https://chromiumcodereview.appspot.com/11649037

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13256 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/handles.cc
src/heap.cc
src/messages.js
test/mjsunit/stack-traces.js

index 3bc1f4bea03d831cf802c453eb69c15e6a13f49f..855997ec3fca354ccdd3c027bfc0819e21b95e08 100644 (file)
@@ -375,6 +375,15 @@ Handle<JSValue> GetScriptWrapper(Handle<Script> script) {
   Handle<JSFunction> constructor = isolate->script_function();
   Handle<JSValue> result =
       Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
+
+  // The allocation might have triggered a GC, which could have called this
+  // function recursively, and a wrapper has already been created and cached.
+  // In that case, simply return the cached wrapper.
+  if (script->wrapper()->foreign_address() != NULL) {
+    return Handle<JSValue>(
+        reinterpret_cast<JSValue**>(script->wrapper()->foreign_address()));
+  }
+
   result->set_value(*script);
 
   // Create a new weak global handle and use it to cache the wrapper
index 66512931698aa582f80a6fd39b60b50ec4095f04..ec98ca5267aa3f113f33b47ed83ff8b553c9d015 100644 (file)
@@ -7293,36 +7293,41 @@ void ErrorObjectList::DeferredFormatStackTrace(Isolate* isolate) {
   int budget = kBudgetPerGC;
   for (int i = 0; i < list_.length(); i++) {
     Object* object = list_[i];
-    // Skip possible holes in the list.
-    if (object->IsTheHole()) continue;
-    if (isolate->heap()->InNewSpace(object) || budget == 0) {
-      list_[write_index++] = object;
-      continue;
+    JSFunction* getter_fun;
+
+    { AssertNoAllocation assert;
+      // Skip possible holes in the list.
+      if (object->IsTheHole()) continue;
+      if (isolate->heap()->InNewSpace(object) || budget == 0) {
+        list_[write_index++] = object;
+        continue;
+      }
+
+      // Check whether the stack property is backed by the original getter.
+      LookupResult lookup(isolate);
+      JSObject::cast(object)->LocalLookupRealNamedProperty(*stack_key, &lookup);
+      if (!lookup.IsFound() || lookup.type() != CALLBACKS) continue;
+      Object* callback = lookup.GetCallbackObject();
+      if (!callback->IsAccessorPair()) continue;
+      Object* getter_obj = AccessorPair::cast(callback)->getter();
+      if (!getter_obj->IsJSFunction()) continue;
+      getter_fun = JSFunction::cast(getter_obj);
+      String* key = isolate->heap()->hidden_stack_trace_symbol();
+      if (key != getter_fun->GetHiddenProperty(key)) continue;
     }
 
-    // Fire the stack property getter, if it is the original marked getter.
-    LookupResult lookup(isolate);
-    JSObject::cast(object)->LocalLookupRealNamedProperty(*stack_key, &lookup);
-    if (!lookup.IsFound() || lookup.type() != CALLBACKS) continue;
-    Object* callback = lookup.GetCallbackObject();
-    if (!callback->IsAccessorPair()) continue;
-    Object* getter_obj = AccessorPair::cast(callback)->getter();
-    if (!getter_obj->IsJSFunction()) continue;
-    JSFunction* getter_fun = JSFunction::cast(getter_obj);
-    String* key = isolate->heap()->hidden_stack_trace_symbol();
-    if (key != getter_fun->GetHiddenProperty(key)) continue;
     budget--;
+    HandleScope scope(isolate);
     bool has_exception = false;
-    Execution::Call(Handle<Object>(getter_fun, isolate),
-                    Handle<Object>(object, isolate),
-                    0,
-                    NULL,
-                    &has_exception);
+    Handle<Object> object_handle(object, isolate);
+    Handle<Object> getter_handle(getter_fun, isolate);
+    Execution::Call(getter_handle, object_handle, 0, NULL, &has_exception);
     if (has_exception) {
       // Hit an exception (most likely a stack overflow).
       // Wrap up this pass and retry after another GC.
       isolate->clear_pending_exception();
-      list_[write_index++] = object;
+      // We use the handle since calling the getter might have caused a GC.
+      list_[write_index++] = *object_handle;
       budget = 0;
     }
   }
index 0bfaf6c6118f636e45a029d0e5dabc4ec02c3ae5..24cd66d1c66ce0ebd4a6866d545209622630a1db 100644 (file)
@@ -1082,6 +1082,10 @@ function GetTypeName(obj, requireConstructor) {
 }
 
 
+// Flag to prevent recursive call of Error.prepareStackTrace.
+var formatting_custom_stack_trace = false;
+
+
 function captureStackTrace(obj, cons_opt) {
   var stackTraceLimit = $Error.stackTraceLimit;
   if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
@@ -1093,14 +1097,17 @@ function captureStackTrace(obj, cons_opt) {
                                  stackTraceLimit);
 
   // Don't be lazy if the error stack formatting is custom (observable).
-  if (IS_FUNCTION($Error.prepareStackTrace)) {
-    var custom_stacktrace_fun = $Error.prepareStackTrace;
-    // Use default error formatting for the case that custom formatting throws.
-    $Error.prepareStackTrace = null;
+  if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
     var array = [];
     %MoveArrayContents(GetStackFrames(stack), array);
-    obj.stack = custom_stacktrace_fun(obj, array);
-    $Error.prepareStackTrace = custom_stacktrace_fun;
+    formatting_custom_stack_trace = true;
+    try {
+      obj.stack = $Error.prepareStackTrace(obj, array);
+    } catch (e) {
+      throw e;  // The custom formatting function threw.  Rethrow.
+    } finally {
+      formatting_custom_stack_trace = false;
+    }
     return;
   }
 
index 71d64191c106f979fdf715ba35c36a5c34417d62..b5d58fa0759f3086bce84d139824114d0b80d505 100644 (file)
@@ -310,9 +310,9 @@ assertTrue(fired);
 error.stack;
 assertTrue(fired);
 
-//Check that throwing exception in a custom stack trace formatting function
-//does not lead to recursion.
-Error.prepareStackTrace = function() { throw new Error("abc"); }
+// Check that throwing exception in a custom stack trace formatting function
+// does not lead to recursion.
+Error.prepareStackTrace = function() { throw new Error("abc"); };
 var message;
 try {
   throw new Error();
@@ -321,3 +321,9 @@ try {
 }
 
 assertEquals("abc", message);
+
+// Test that modifying Error.prepareStackTrace by itself works.
+Error.prepareStackTrace = function() { Error.prepareStackTrace = "custom"; };
+new Error();
+
+assertEquals("custom", Error.prepareStackTrace);