Reviewers: ulan,

Description:
Fix several bugs in error stack trace formatting.

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.

[email protected]
BUG=


Please review this at https://chromiumcodereview.appspot.com/11649037/

SVN Base: https://v8.googlecode.com/svn/branches/bleeding_edge

Affected files:
  M src/handles.cc
  M src/heap.cc
  M src/messages.js
  M test/mjsunit/stack-traces.js


Index: src/handles.cc
diff --git a/src/handles.cc b/src/handles.cc
index 3bc1f4bea03d831cf802c453eb69c15e6a13f49f..f11d104cd88409f9d5abec3e88972d23f36b9f98 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -364,6 +364,9 @@ static void ClearWrapperCache(Persistent<v8::Value> handle, void*) {


 Handle<JSValue> GetScriptWrapper(Handle<Script> script) {
+  // GC could lead to recursion, which this function cannot deal with
+  // (the cache would not yet be set in a recursive call).
+  AlwaysAllocateScope no_gc;
   if (script->wrapper()->foreign_address() != NULL) {
     // Return the script wrapper directly from the cache.
     return Handle<JSValue>(
Index: src/heap.cc
diff --git a/src/heap.cc b/src/heap.cc
index 66512931698aa582f80a6fd39b60b50ec4095f04..ba2d99408d118b2dba5f00e4dc5e901e8d189038 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -7293,36 +7293,40 @@ 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;
+      list_[write_index++] = *object_handle;
       budget = 0;
     }
   }
Index: src/messages.js
diff --git a/src/messages.js b/src/messages.js
index 0bfaf6c6118f636e45a029d0e5dabc4ec02c3ae5..24cd66d1c66ce0ebd4a6866d545209622630a1db 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -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: test/mjsunit/stack-traces.js
diff --git a/test/mjsunit/stack-traces.js b/test/mjsunit/stack-traces.js
index 71d64191c106f979fdf715ba35c36a5c34417d62..b5d58fa0759f3086bce84d139824114d0b80d505 100644
--- a/test/mjsunit/stack-traces.js
+++ b/test/mjsunit/stack-traces.js
@@ -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);


--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to