Debugger: use list to find shared function info in a script.
authoryangguo <yangguo@chromium.org>
Fri, 26 Jun 2015 13:53:26 +0000 (06:53 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 26 Jun 2015 13:53:33 +0000 (13:53 +0000)
Now that we keep tabs on shared function infos from a script, we can speed up finding shared function infos for debugging. However, in case we have to compile a function that cannot be lazily compiled without context, we fall back to the slow heap iteration.

R=mstarzinger@chromium.org
BUG=v8:4132,v8:4052
LOG=N

Committed: https://crrev.com/cfe89a71a332ef9ed481c8210bc3ad6d2822034b
Cr-Commit-Position: refs/heads/master@{#29296}

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

Cr-Commit-Position: refs/heads/master@{#29327}

src/compiler.cc
src/debug.cc
src/debug.h

index c62d8d6..2fa5ff0 100644 (file)
@@ -1022,8 +1022,7 @@ void Compiler::CompileForLiveEdit(Handle<Script> script) {
   VMState<COMPILER> state(info.isolate());
 
   // Get rid of old list of shared function infos.
-  script->set_shared_function_infos(Smi::FromInt(0));
-
+  info.MarkAsFirstCompile();
   info.parse_info()->set_global();
   if (!Parser::ParseStatic(info.parse_info())) return;
 
@@ -1346,11 +1345,14 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
     FunctionLiteral* literal, Handle<Script> script,
     CompilationInfo* outer_info) {
   // Precondition: code has been parsed and scopes have been analyzed.
+  Isolate* isolate = outer_info->isolate();
   MaybeHandle<SharedFunctionInfo> maybe_existing;
   if (outer_info->is_first_compile()) {
     // On the first compile, there are no existing shared function info for
-    // inner functions yet, so do not try to find them.
-    DCHECK(script->FindSharedFunctionInfo(literal).is_null());
+    // inner functions yet, so do not try to find them. All bets are off for
+    // live edit though.
+    DCHECK(script->FindSharedFunctionInfo(literal).is_null() ||
+           isolate->debug()->live_edit_enabled());
   } else {
     maybe_existing = script->FindSharedFunctionInfo(literal);
   }
@@ -1371,8 +1373,6 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
   if (outer_info->will_serialize()) info.PrepareForSerializing();
   if (outer_info->is_first_compile()) info.MarkAsFirstCompile();
 
-  Isolate* isolate = info.isolate();
-  Factory* factory = isolate->factory();
   LiveEditFunctionTracker live_edit_tracker(isolate, literal);
   // Determine if the function can be lazily compiled. This is necessary to
   // allow some of our builtin JS files to be lazily compiled. These
@@ -1427,9 +1427,10 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
 
   if (maybe_existing.is_null()) {
     // Create a shared function info object.
-    Handle<SharedFunctionInfo> result = factory->NewSharedFunctionInfo(
-        literal->name(), literal->materialized_literal_count(), literal->kind(),
-        info.code(), scope_info, info.feedback_vector());
+    Handle<SharedFunctionInfo> result =
+        isolate->factory()->NewSharedFunctionInfo(
+            literal->name(), literal->materialized_literal_count(),
+            literal->kind(), info.code(), scope_info, info.feedback_vector());
 
     SharedFunctionInfo::InitFromFunctionLiteral(result, literal);
     SharedFunctionInfo::SetScript(result, script);
index f814db8..fefccc8 100644 (file)
@@ -1984,126 +1984,125 @@ void Debug::PrepareForBreakPoints() {
 }
 
 
-Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
-                                                     int position) {
-  // Iterate the heap looking for SharedFunctionInfo generated from the
-  // script. The inner most SharedFunctionInfo containing the source position
-  // for the requested break point is found.
-  // NOTE: This might require several heap iterations. If the SharedFunctionInfo
-  // which is found is not compiled it is compiled and the heap is iterated
-  // again as the compilation might create inner functions from the newly
-  // compiled function and the actual requested break point might be in one of
-  // these functions.
-  // NOTE: The below fix-point iteration depends on all functions that cannot be
-  // compiled lazily without a context to not be compiled at all. Compilation
-  // will be triggered at points where we do not need a context.
-  bool done = false;
-  // The current candidate for the source position:
-  int target_start_position = RelocInfo::kNoPosition;
-  Handle<JSFunction> target_function;
-  Handle<SharedFunctionInfo> target;
-  Heap* heap = isolate_->heap();
-  while (!done) {
-    { // Extra scope for iterator.
-      // If lazy compilation is off, we won't have duplicate shared function
-      // infos that need to be filtered.
-      HeapIterator iterator(heap, FLAG_lazy ? HeapIterator::kNoFiltering
-                                            : HeapIterator::kFilterUnreachable);
-      for (HeapObject* obj = iterator.next();
-           obj != NULL; obj = iterator.next()) {
-        bool found_next_candidate = false;
-        Handle<JSFunction> function;
-        Handle<SharedFunctionInfo> shared;
-        if (obj->IsJSFunction()) {
-          function = Handle<JSFunction>(JSFunction::cast(obj));
-          shared = Handle<SharedFunctionInfo>(function->shared());
-          DCHECK(shared->allows_lazy_compilation() || shared->is_compiled());
-          found_next_candidate = true;
-        } else if (obj->IsSharedFunctionInfo()) {
-          shared = Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(obj));
-          // Skip functions that we cannot compile lazily without a context,
-          // which is not available here, because there is no closure.
-          found_next_candidate = shared->is_compiled() ||
-              shared->allows_lazy_compilation_without_context();
-        }
-        if (!found_next_candidate) continue;
-        if (shared->script() == *script) {
-          // If the SharedFunctionInfo found has the requested script data and
-          // contains the source position it is a candidate.
-          int start_position = shared->function_token_position();
-          if (start_position == RelocInfo::kNoPosition) {
-            start_position = shared->start_position();
-          }
-          if (start_position <= position &&
-              position <= shared->end_position()) {
-            // If there is no candidate or this function is within the current
-            // candidate this is the new candidate.
-            if (target.is_null()) {
-              target_start_position = start_position;
-              target_function = function;
-              target = shared;
-            } else {
-              if (target_start_position == start_position &&
-                  shared->end_position() == target->end_position()) {
-                // If a top-level function contains only one function
-                // declaration the source for the top-level and the function
-                // is the same. In that case prefer the non top-level function.
-                if (!shared->is_toplevel()) {
-                  target_start_position = start_position;
-                  target_function = function;
-                  target = shared;
-                }
-              } else if (target_start_position <= start_position &&
-                         shared->end_position() <= target->end_position()) {
-                // This containment check includes equality as a function
-                // inside a top-level function can share either start or end
-                // position with the top-level function.
-                target_start_position = start_position;
-                target_function = function;
-                target = shared;
-              }
-            }
-          }
-        }
-      }  // End for loop.
-    }  // End no-allocation scope.
+class SharedFunctionInfoFinder {
+ public:
+  explicit SharedFunctionInfoFinder(int target_position)
+      : current_candidate_(NULL),
+        current_candidate_closure_(NULL),
+        current_start_position_(RelocInfo::kNoPosition),
+        target_position_(target_position) {}
+
+  void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) {
+    int start_position = shared->function_token_position();
+    if (start_position == RelocInfo::kNoPosition) {
+      start_position = shared->start_position();
+    }
 
-    if (target.is_null()) return isolate_->factory()->undefined_value();
+    if (start_position > target_position_) return;
+    if (target_position_ > shared->end_position()) return;
+
+    if (current_candidate_ != NULL) {
+      if (current_start_position_ == start_position &&
+          shared->end_position() == current_candidate_->end_position()) {
+        // If a top-level function contains only one function
+        // declaration the source for the top-level and the function
+        // is the same. In that case prefer the non top-level function.
+        if (shared->is_toplevel()) return;
+      } else if (start_position < current_start_position_ ||
+                 current_candidate_->end_position() < shared->end_position()) {
+        return;
+      }
+    }
 
-    // There will be at least one break point when we are done.
-    has_break_points_ = true;
+    current_start_position_ = start_position;
+    current_candidate_ = shared;
+    current_candidate_closure_ = closure;
+  }
+
+  SharedFunctionInfo* Result() { return current_candidate_; }
+
+  JSFunction* ResultClosure() { return current_candidate_closure_; }
+
+ private:
+  SharedFunctionInfo* current_candidate_;
+  JSFunction* current_candidate_closure_;
+  int current_start_position_;
+  int target_position_;
+  DisallowHeapAllocation no_gc_;
+};
 
-    // If the candidate found is compiled we are done.
-    done = target->is_compiled();
-    if (!done) {
-      // If the candidate is not compiled, compile it to reveal any inner
-      // functions which might contain the requested source position. This
-      // will compile all inner functions that cannot be compiled without a
-      // context, because Compiler::GetSharedFunctionInfo checks whether the
-      // debugger is active.
-      MaybeHandle<Code> maybe_result = target_function.is_null()
-          ? Compiler::GetUnoptimizedCode(target)
-          : Compiler::GetUnoptimizedCode(target_function);
-      if (maybe_result.is_null()) return isolate_->factory()->undefined_value();
+
+template <typename C>
+bool Debug::CompileToRevealInnerFunctions(C* compilable) {
+  HandleScope scope(isolate_);
+  // Force compiling inner functions that require context.
+  // TODO(yangguo): remove this hack.
+  bool has_break_points = has_break_points_;
+  has_break_points_ = true;
+  Handle<C> compilable_handle(compilable);
+  bool result = !Compiler::GetUnoptimizedCode(compilable_handle).is_null();
+  has_break_points_ = has_break_points;
+  return result;
+}
+
+
+Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
+                                                     int position) {
+  while (true) {
+    // Go through all shared function infos associated with this script to
+    // find the inner most function containing this position.
+    if (!script->shared_function_infos()->IsWeakFixedArray()) break;
+    WeakFixedArray* array =
+        WeakFixedArray::cast(script->shared_function_infos());
+
+    SharedFunctionInfo* shared;
+    {
+      SharedFunctionInfoFinder finder(position);
+      for (int i = 0; i < array->Length(); i++) {
+        Object* item = array->Get(i);
+        if (!item->IsSharedFunctionInfo()) continue;
+        SharedFunctionInfo* shared = SharedFunctionInfo::cast(item);
+        finder.NewCandidate(shared);
+      }
+      shared = finder.Result();
+      if (shared == NULL) break;
+      // We found it if it's already compiled.
+      if (shared->is_compiled()) return handle(shared);
     }
-  }  // End while loop.
-
-  // JSFunctions from the same literal may not have the same shared function
-  // info. Find those JSFunctions and deduplicate the shared function info.
-  HeapIterator iterator(heap, FLAG_lazy ? HeapIterator::kNoFiltering
-                                        : HeapIterator::kFilterUnreachable);
-  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
-    if (!obj->IsJSFunction()) continue;
-    JSFunction* function = JSFunction::cast(obj);
-    SharedFunctionInfo* shared = function->shared();
-    if (shared != *target && shared->script() == target->script() &&
-        shared->start_position_and_type() ==
-            target->start_position_and_type()) {
-      function->set_shared(*target);
+    // If not, compile to reveal inner functions, if possible.
+    if (shared->allows_lazy_compilation_without_context()) {
+      if (!CompileToRevealInnerFunctions(shared)) break;
+      continue;
     }
-  }
 
-  return target;
+    // If not possible, comb the heap for the best suitable compile target.
+    JSFunction* closure;
+    {
+      HeapIterator it(isolate_->heap(), HeapIterator::kNoFiltering);
+      SharedFunctionInfoFinder finder(position);
+      while (HeapObject* object = it.next()) {
+        JSFunction* closure = NULL;
+        SharedFunctionInfo* shared = NULL;
+        if (object->IsJSFunction()) {
+          closure = JSFunction::cast(object);
+          shared = closure->shared();
+        } else if (object->IsSharedFunctionInfo()) {
+          shared = SharedFunctionInfo::cast(object);
+          if (!shared->allows_lazy_compilation_without_context()) continue;
+        } else {
+          continue;
+        }
+        if (shared->script() == *script) finder.NewCandidate(shared, closure);
+      }
+      closure = finder.ResultClosure();
+      shared = finder.Result();
+    }
+    if (closure == NULL ? !CompileToRevealInnerFunctions(shared)
+                        : !CompileToRevealInnerFunctions(closure)) {
+      break;
+    }
+  }
+  return isolate_->factory()->undefined_value();
 }
 
 
index 82d37bb..fe951a3 100644 (file)
@@ -481,6 +481,9 @@ class Debug {
   static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
   static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
 
+  template <typename C>
+  bool CompileToRevealInnerFunctions(C* compilable);
+
   // This function is used in FunctionNameUsing* tests.
   Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script,
                                                 int position);