Add option to run ScopeIterator faster giving up nested scope chain.
authorulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 21 Mar 2014 12:30:58 +0000 (12:30 +0000)
committerulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 21 Mar 2014 12:30:58 +0000 (12:30 +0000)
We'd like to be able to trade nested scope chain info (consisting of with, block and catch scopes) in favor of speed in some cases.

BUG=chromium:340285
LOG=N
R=ulan@chromium.org, pfeldman, ulan, yangguo

Review URL: https://codereview.chromium.org/203463011

Patch from Andrey Adaykin <aandrey@chromium.org>.

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

src/mirror-debugger.js
src/runtime.cc
src/runtime.h
test/mjsunit/debug-scopes.js

index f288123..b07d1fe 100644 (file)
@@ -1692,10 +1692,11 @@ FrameMirror.prototype.scope = function(index) {
 };
 
 
-FrameMirror.prototype.allScopes = function() {
+FrameMirror.prototype.allScopes = function(opt_ignore_nested_scopes) {
   var scopeDetails = %GetAllScopesDetails(this.break_id_,
                                           this.details_.frameId(),
-                                          this.details_.inlinedFrameIndex());
+                                          this.details_.inlinedFrameIndex(),
+                                          !!opt_ignore_nested_scopes);
   var result = [];
   for (var i = 0; i < scopeDetails.length; ++i) {
     result.push(new ScopeMirror(this, UNDEFINED, i, scopeDetails[i]));
index dcb9dc4..b4c34ef 100644 (file)
@@ -11815,7 +11815,8 @@ class ScopeIterator {
 
   ScopeIterator(Isolate* isolate,
                 JavaScriptFrame* frame,
-                int inlined_jsframe_index)
+                int inlined_jsframe_index,
+                bool ignore_nested_scopes = false)
     : isolate_(isolate),
       frame_(frame),
       inlined_jsframe_index_(inlined_jsframe_index),
@@ -11839,19 +11840,31 @@ class ScopeIterator {
       // Return if ensuring debug info failed.
       return;
     }
-    Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info);
 
-    // Find the break point where execution has stopped.
-    BreakLocationIterator break_location_iterator(debug_info,
-                                                  ALL_BREAK_LOCATIONS);
-    // pc points to the instruction after the current one, possibly a break
-    // location as well. So the "- 1" to exclude it from the search.
-    break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
-    if (break_location_iterator.IsExit()) {
-      // We are within the return sequence. At the momemt it is not possible to
+    // Currently it takes too much time to find nested scopes due to script
+    // parsing. Sometimes we want to run the ScopeIterator as fast as possible
+    // (for example, while collecting async call stacks on every
+    // addEventListener call), even if we drop some nested scopes.
+    // Later we may optimize getting the nested scopes (cache the result?)
+    // and include nested scopes into the "fast" iteration case as well.
+    if (!ignore_nested_scopes) {
+      Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info);
+
+      // Find the break point where execution has stopped.
+      BreakLocationIterator break_location_iterator(debug_info,
+                                                    ALL_BREAK_LOCATIONS);
+      // pc points to the instruction after the current one, possibly a break
+      // location as well. So the "- 1" to exclude it from the search.
+      break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
+
+      // Within the return sequence at the moment it is not possible to
       // get a source position which is consistent with the current scope chain.
       // Thus all nested with, catch and block contexts are skipped and we only
       // provide the function scope.
+      ignore_nested_scopes = break_location_iterator.IsExit();
+    }
+
+    if (ignore_nested_scopes) {
       if (scope_info->HasContext()) {
         context_ = Handle<Context>(context_->declaration_context(), isolate_);
       } else {
@@ -11859,7 +11872,7 @@ class ScopeIterator {
           context_ = Handle<Context>(context_->previous(), isolate_);
         }
       }
-      if (scope_info->scope_type() != EVAL_SCOPE) {
+      if (scope_info->scope_type() == FUNCTION_SCOPE) {
         nested_scope_chain_.Add(scope_info);
       }
     } else {
@@ -12327,13 +12340,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
 // args[0]: number: break id
 // args[1]: number: frame index
 // args[2]: number: inlined frame index
+// args[3]: boolean: ignore nested scopes
 //
 // The array returned contains arrays with the following information:
 // 0: Scope type
 // 1: Scope object
 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetAllScopesDetails) {
   HandleScope scope(isolate);
-  ASSERT(args.length() == 3);
+  ASSERT(args.length() == 3 || args.length() == 4);
 
   // Check arguments.
   Object* check;
@@ -12344,13 +12358,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetAllScopesDetails) {
   CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
   CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
 
+  bool ignore_nested_scopes = false;
+  if (args.length() == 4) {
+    CONVERT_BOOLEAN_ARG_CHECKED(flag, 3);
+    ignore_nested_scopes = flag;
+  }
+
   // Get the frame where the debugging is performed.
   StackFrame::Id id = UnwrapFrameId(wrapped_id);
   JavaScriptFrameIterator frame_it(isolate, id);
   JavaScriptFrame* frame = frame_it.frame();
 
   List<Handle<JSObject> > result(4);
-  ScopeIterator it(isolate, frame, inlined_jsframe_index);
+  ScopeIterator it(isolate, frame, inlined_jsframe_index, ignore_nested_scopes);
   for (; !it.Done(); it.Next()) {
     Handle<JSObject> details = MaterializeScopeDetails(isolate, &it);
     RETURN_IF_EMPTY_HANDLE(isolate, details);
index 3c8077f..aa811a6 100644 (file)
@@ -495,7 +495,7 @@ namespace internal {
   F(GetScopeCount, 2, 1) \
   F(GetStepInPositions, 2, 1) \
   F(GetScopeDetails, 4, 1) \
-  F(GetAllScopesDetails, 3, 1) \
+  F(GetAllScopesDetails, 4, 1) \
   F(GetFunctionScopeCount, 1, 1) \
   F(GetFunctionScopeDetails, 2, 1) \
   F(SetScopeVariableValue, 6, 1) \
index 57adf23..f5b5ec9 100644 (file)
@@ -71,16 +71,29 @@ function BeginTest(name) {
 // Check result of a test.
 function EndTest() {
   assertTrue(listener_called, "listerner not called for " + test_name);
-  assertNull(exception, test_name);
+  assertNull(exception, test_name + " / " + exception);
   end_test_count++;
 }
 
 
 // Check that two scope are the same.
-function CheckScopeMirrors(scope1, scope2) {
+function assertScopeMirrorEquals(scope1, scope2) {
   assertEquals(scope1.scopeType(), scope2.scopeType());
   assertEquals(scope1.frameIndex(), scope2.frameIndex());
   assertEquals(scope1.scopeIndex(), scope2.scopeIndex());
+  assertPropertiesEqual(scope1.scopeObject().value(), scope2.scopeObject().value());
+}
+
+function CheckFastAllScopes(scopes, exec_state)
+{
+  var fast_all_scopes = exec_state.frame().allScopes(true);
+  var length = fast_all_scopes.length;
+  assertTrue(scopes.length >= length);
+  for (var i = 0; i < scopes.length && i < length; i++) {
+    var scope = fast_all_scopes[length - i - 1];
+    assertTrue(scope.isScope());
+    assertEquals(scopes[scopes.length - i - 1], scope.scopeType());
+  }
 }
 
 
@@ -93,7 +106,7 @@ function CheckScopeChain(scopes, exec_state) {
     var scope = exec_state.frame().scope(i);
     assertTrue(scope.isScope());
     assertEquals(scopes[i], scope.scopeType());
-    CheckScopeMirrors(all_scopes[i], scope);
+    assertScopeMirrorEquals(all_scopes[i], scope);
 
     // Check the global object when hitting the global scope.
     if (scopes[i] == debug.ScopeType.Global) {
@@ -102,6 +115,7 @@ function CheckScopeChain(scopes, exec_state) {
       assertPropertiesEqual(this, scope.scopeObject().value());
     }
   }
+  CheckFastAllScopes(scopes, exec_state);
 
   // Get the debug command processor.
   var dcp = exec_state.debugCommandProcessor("unspecified_running_state");