Revision: 11782
Author:   [email protected]
Date:     Tue Jun 12 08:57:50 2012
Log:      Enable lazy compilation for non-trivial outer contexts.

This changes the compiler to be more aggressive about lazy compilation
of closures with non-trivial outer context. Compilation can only be
triggered with a valid outer context now. One exception is the debugger,
which can request compilation of arbitrary shared code, but it ensures
to trigger compilation only at points where no context is needed.

[email protected]
TEST=mjsunit/debug-script-breakpoints-nested

Review URL: https://chromiumcodereview.appspot.com/10538102
http://code.google.com/p/v8/source/detail?r=11782

Added:
 /branches/bleeding_edge/test/mjsunit/debug-script-breakpoints-nested.js
Modified:
 /branches/bleeding_edge/src/ast.cc
 /branches/bleeding_edge/src/ast.h
 /branches/bleeding_edge/src/compiler.cc
 /branches/bleeding_edge/src/debug.cc
 /branches/bleeding_edge/src/debug.h
 /branches/bleeding_edge/src/full-codegen.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/scopes.cc
 /branches/bleeding_edge/src/scopes.h
 /branches/bleeding_edge/test/cctest/test-debug.cc
 /branches/bleeding_edge/test/cctest/test-heap.cc

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/debug-script-breakpoints-nested.js Tue Jun 12 08:57:50 2012
@@ -0,0 +1,82 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+// Simple debug event handler which just counts the number of break points hit.
+var break_point_hit_count = 0;
+
+function listener(event, exec_state, event_data, data) {
+  if (event == Debug.DebugEvent.Break) {
+    break_point_hit_count++;
+  }
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+eval(
+  "var inner;\n" +
+  "function outer() {\n" +         // Non-trivial outer closure.
+  "  var x = 5;\n" +
+  "  function a() {\n" +
+  "    var foo = 0, y = 7;\n" +
+  "    function b() {\n" +
+  "      var bar = 0, baz = 0, z = 11;\n" +
+  "      function c() {\n" +
+  "        return x + y + z;\n" +  // Breakpoint line ( #8 )
+  "      }\n" +
+  "      inner = c;\n" +
+  "      return c();\n" +
+  "    }\n" +
+  "    return b();\n" +
+  "  }\n" +
+  "  return a();\n" +
+  "}"
+);
+
+var script = Debug.findScript(outer);
+
+// The debugger triggers compilation of inner closures.
+assertEquals(0, Debug.scriptBreakPoints().length);
+var sbp = Debug.setScriptBreakPointById(script.id, 8);
+assertEquals(1, Debug.scriptBreakPoints().length);
+
+// The compiled outer closure should behave correctly.
+assertEquals(23, outer());
+assertEquals(1, break_point_hit_count);
+
+// The compiled inner closure should behave correctly.
+assertEquals(23, inner());
+assertEquals(2, break_point_hit_count);
+
+// Remove script break point.
+assertEquals(1, Debug.scriptBreakPoints().length);
+Debug.clearBreakPoint(sbp);
+assertEquals(0, Debug.scriptBreakPoints().length);
=======================================
--- /branches/bleeding_edge/src/ast.cc  Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/ast.cc  Tue Jun 12 08:57:50 2012
@@ -154,6 +154,11 @@
 bool FunctionLiteral::AllowsLazyCompilation() {
   return scope()->AllowsLazyCompilation();
 }
+
+
+bool FunctionLiteral::AllowsLazyCompilationWithoutContext() {
+  return scope()->AllowsLazyCompilationWithoutContext();
+}


 int FunctionLiteral::start_position() const {
=======================================
--- /branches/bleeding_edge/src/ast.h   Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/ast.h   Tue Jun 12 08:57:50 2012
@@ -2065,6 +2065,7 @@
   int parameter_count() { return parameter_count_; }

   bool AllowsLazyCompilation();
+  bool AllowsLazyCompilationWithoutContext();

   Handle<String> debug_name() const {
     if (name_->length() > 0) return name_;
=======================================
--- /branches/bleeding_edge/src/compiler.cc     Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/compiler.cc     Tue Jun 12 08:57:50 2012
@@ -118,7 +118,7 @@
       FLAG_crankshaft &&
       !function()->flags()->Contains(kDontSelfOptimize) &&
       !function()->flags()->Contains(kDontOptimize) &&
-      function()->scope()->AllowsLazyRecompilation() &&
+      function()->scope()->AllowsLazyCompilation() &&
       (shared_info().is_null() || !shared_info()->optimization_disabled());
 }

@@ -137,9 +137,8 @@
// all. However crankshaft support recompilation of functions, so in this case // the full compiler need not be be used if a debugger is attached, but only if
 // break points has actually been set.
-static bool is_debugging_active() {
+static bool IsDebuggerActive(Isolate* isolate) {
 #ifdef ENABLE_DEBUGGER_SUPPORT
-  Isolate* isolate = Isolate::Current();
   return V8::UseCrankshaft() ?
     isolate->debug()->has_break_points() :
     isolate->debugger()->IsDebuggerActive();
@@ -149,8 +148,8 @@
 }


-static bool AlwaysFullCompiler() {
-  return FLAG_always_full_compiler || is_debugging_active();
+static bool AlwaysFullCompiler(Isolate* isolate) {
+  return FLAG_always_full_compiler || IsDebuggerActive(isolate);
 }


@@ -205,7 +204,7 @@
   // Fall back to using the full code generator if it's not possible
   // to use the Hydrogen-based optimizing compiler. We already have
   // generated code for this from the shared function object.
-  if (AlwaysFullCompiler()) {
+  if (AlwaysFullCompiler(info->isolate())) {
     info->SetCode(code);
     return true;
   }
@@ -719,8 +718,14 @@
   // builtins cannot be handled lazily by the parser, since we have to know
   // if a function uses the special natives syntax, which is something the
   // parser records.
+  // If the debugger requests compilation for break points, we cannot be
+ // aggressive about lazy compilation, because it might trigger compilation + // of functions without an outer context when setting a breakpoint through
+  // Runtime::FindSharedFunctionInfoInScript.
+ bool allow_lazy_without_ctx = literal->AllowsLazyCompilationWithoutContext();
   bool allow_lazy = literal->AllowsLazyCompilation() &&
-      !LiveEditFunctionTracker::IsActive(info.isolate());
+      !LiveEditFunctionTracker::IsActive(info.isolate()) &&
+ (!info.isolate()->DebuggerHasBreakPoints() || allow_lazy_without_ctx);

   Handle<ScopeInfo> scope_info(ScopeInfo::Empty());

@@ -745,6 +750,7 @@
   SetFunctionInfo(result, literal, false, script);
   RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result);
   result->set_allows_lazy_compilation(allow_lazy);
+ result->set_allows_lazy_compilation_without_context(allow_lazy_without_ctx);

   // Set the expected number of properties for instances and return
   // the resulting function.
@@ -777,6 +783,8 @@
       lit->has_only_simple_this_property_assignments(),
       *lit->this_property_assignments());
   function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
+  function_info->set_allows_lazy_compilation_without_context(
+      lit->AllowsLazyCompilationWithoutContext());
   function_info->set_language_mode(lit->language_mode());
   function_info->set_uses_arguments(lit->scope()->arguments() != NULL);
function_info->set_has_duplicate_parameters(lit->has_duplicate_parameters());
=======================================
--- /branches/bleeding_edge/src/debug.cc        Tue Jun 12 03:22:33 2012
+++ /branches/bleeding_edge/src/debug.cc        Tue Jun 12 08:57:50 2012
@@ -1170,14 +1170,16 @@
 }


-void Debug::SetBreakPoint(Handle<SharedFunctionInfo> shared,
+void Debug::SetBreakPoint(Handle<JSFunction> function,
                           Handle<Object> break_point_object,
                           int* source_position) {
   HandleScope scope(isolate_);

   PrepareForBreakPoints();

-  if (!EnsureDebugInfo(shared)) {
+  // Make sure the function is compiled and has set up the debug info.
+  Handle<SharedFunctionInfo> shared(function->shared());
+  if (!EnsureDebugInfo(shared, function)) {
     // Return if retrieving debug info failed.
     return;
   }
@@ -1196,6 +1198,51 @@
   // At least one active break point now.
   ASSERT(debug_info->GetBreakPointCount() > 0);
 }
+
+
+bool Debug::SetBreakPointForScript(Handle<Script> script,
+                                   Handle<Object> break_point_object,
+                                   int* source_position) {
+  HandleScope scope(isolate_);
+
+  // No need to call PrepareForBreakPoints because it will be called
+  // implicitly by Runtime::FindSharedFunctionInfoInScript.
+  Object* result = Runtime::FindSharedFunctionInfoInScript(isolate_,
+                                                           script,
+ *source_position);
+  if (result->IsUndefined()) return false;
+
+  // Make sure the function has set up the debug info.
+  Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
+  if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
+    // Return if retrieving debug info failed.
+    return false;
+  }
+
+  // Find position within function. The script position might be before the
+  // source position of the first function.
+  int position;
+  if (shared->start_position() > *source_position) {
+    position = 0;
+  } else {
+    position = *source_position - shared->start_position();
+  }
+
+  Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+  // Source positions starts with zero.
+  ASSERT(position >= 0);
+
+  // Find the break point and change it.
+  BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
+  it.FindBreakLocationFromPosition(position);
+  it.SetBreakPoint(break_point_object);
+
+  *source_position = it.position() + shared->start_position();
+
+  // At least one active break point now.
+  ASSERT(debug_info->GetBreakPointCount() > 0);
+  return true;
+}


 void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
@@ -1249,10 +1296,12 @@
 }


-void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
+void Debug::FloodWithOneShot(Handle<JSFunction> function) {
   PrepareForBreakPoints();
-  // Make sure the function has set up the debug info.
-  if (!EnsureDebugInfo(shared)) {
+
+  // Make sure the function is compiled and has set up the debug info.
+  Handle<SharedFunctionInfo> shared(function->shared());
+  if (!EnsureDebugInfo(shared, function)) {
     // Return if we failed to retrieve the debug info.
     return;
   }
@@ -1272,8 +1321,8 @@

   if (!bindee.is_null() && bindee->IsJSFunction() &&
       !JSFunction::cast(*bindee)->IsBuiltin()) {
- Handle<SharedFunctionInfo> shared_info(JSFunction::cast(*bindee)->shared());
-    Debug::FloodWithOneShot(shared_info);
+    Handle<JSFunction> bindee_function(JSFunction::cast(*bindee));
+    Debug::FloodWithOneShot(bindee_function);
   }
 }

@@ -1288,11 +1337,9 @@
for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
     JavaScriptFrame* frame = it.frame();
     if (frame->HasHandler()) {
-      Handle<SharedFunctionInfo> shared =
-          Handle<SharedFunctionInfo>(
-              JSFunction::cast(frame->function())->shared());
       // Flood the function with the catch block with break points
-      FloodWithOneShot(shared);
+      JSFunction* function = JSFunction::cast(frame->function());
+      FloodWithOneShot(Handle<JSFunction>(function));
       return;
     }
   }
@@ -1359,14 +1406,14 @@
     frames_it.Advance();
     // Fill the function to return to with one-shot break points.
     JSFunction* function = JSFunction::cast(frames_it.frame()->function());
-    FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
+    FloodWithOneShot(Handle<JSFunction>(function));
     return;
   }

   // Get the debug info (create it if it does not exist).
-  Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
-  if (!EnsureDebugInfo(shared)) {
+  Handle<JSFunction> function(JSFunction::cast(frame->function()));
+  Handle<SharedFunctionInfo> shared(function->shared());
+  if (!EnsureDebugInfo(shared, function)) {
     // Return if ensuring debug info failed.
     return;
   }
@@ -1436,7 +1483,7 @@
     if (!frames_it.done()) {
       // Fill the function to return to with one-shot break points.
JSFunction* function = JSFunction::cast(frames_it.frame()->function());
-      FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
+      FloodWithOneShot(Handle<JSFunction>(function));
       // Set target frame pointer.
       ActivateStepOut(frames_it.frame());
     }
@@ -1446,7 +1493,7 @@
     // Step next or step min.

     // Fill the current function with one-shot break points.
-    FloodWithOneShot(shared);
+    FloodWithOneShot(function);

     // Remember source position and frame to handle step next.
     thread_local_.last_statement_position_ =
@@ -1458,9 +1505,7 @@
     if (is_at_restarted_function) {
       Handle<JSFunction> restarted_function(
JSFunction::cast(*thread_local_.restarter_frame_function_pointer_));
-      Handle<SharedFunctionInfo> restarted_shared(
-          restarted_function->shared());
-      FloodWithOneShot(restarted_shared);
+      FloodWithOneShot(restarted_function);
     } else if (!call_function_stub.is_null()) {
// If it's CallFunction stub ensure target function is compiled and flood
       // it with one shot breakpoints.
@@ -1502,7 +1547,7 @@
         } else if (!js_function->IsBuiltin()) {
           // Don't step into builtins.
           // It will also compile target function if it's not compiled yet.
- FloodWithOneShot(Handle<SharedFunctionInfo>(js_function->shared()));
+          FloodWithOneShot(js_function);
         }
       }
     }
@@ -1511,7 +1556,7 @@
     // a call target as the function called might be a native function for
     // which step in will not stop. It also prepares for stepping in
     // getters/setters.
-    FloodWithOneShot(shared);
+    FloodWithOneShot(function);

     if (is_load_or_store) {
// Remember source position and frame to handle step in getter/setter. If
@@ -1711,12 +1756,11 @@
         // function.
         if (!holder.is_null() && holder->IsJSFunction() &&
             !JSFunction::cast(*holder)->IsBuiltin()) {
-          Handle<SharedFunctionInfo> shared_info(
-              JSFunction::cast(*holder)->shared());
-          Debug::FloodWithOneShot(shared_info);
+ Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder);
+          Debug::FloodWithOneShot(js_function);
         }
       } else {
- Debug::FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
+        Debug::FloodWithOneShot(function);
       }
     }
   }
@@ -1956,6 +2000,9 @@
     Handle<Code> lazy_compile =
Handle<Code>(isolate_->builtins()->builtin(Builtins::kLazyCompile));

+    // There will be at least one break point when we are done.
+    has_break_points_ = true;
+
     // Keep the list of activated functions in a handlified list as it
     // is used both in GC and non-GC code.
     List<Handle<JSFunction> > active_functions(100);
@@ -2063,15 +2110,20 @@


 // Ensures the debug information is present for shared.
-bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
+bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+                            Handle<JSFunction> function) {
   // Return if we already have the debug info for shared.
   if (HasDebugInfo(shared)) {
     ASSERT(shared->is_compiled());
     return true;
   }

-  // Ensure shared in compiled. Return false if this failed.
-  if (!SharedFunctionInfo::EnsureCompiled(shared, CLEAR_EXCEPTION)) {
+  // Make sure we are prepared to handle breakpoints.
+  ASSERT(has_break_points_);
+
+  // Ensure function is compiled. Return false if this failed.
+  if (!function.is_null() &&
+      !JSFunction::EnsureCompiled(function, CLEAR_EXCEPTION)) {
     return false;
   }

@@ -2083,9 +2135,6 @@
   node->set_next(debug_info_list_);
   debug_info_list_ = node;

-  // Now there is at least one break point.
-  has_break_points_ = true;
-
   return true;
 }

@@ -2127,9 +2176,9 @@
   PrepareForBreakPoints();

   // Get the executing function in which the debug break occurred.
-  Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
-  if (!EnsureDebugInfo(shared)) {
+  Handle<JSFunction> function(JSFunction::cast(frame->function()));
+  Handle<SharedFunctionInfo> shared(function->shared());
+  if (!EnsureDebugInfo(shared, function)) {
     // Return if we failed to retrieve the debug info.
     return;
   }
@@ -2219,9 +2268,9 @@
   PrepareForBreakPoints();

   // Get the executing function in which the debug break occurred.
-  Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
-  if (!EnsureDebugInfo(shared)) {
+  Handle<JSFunction> function(JSFunction::cast(frame->function()));
+  Handle<SharedFunctionInfo> shared(function->shared());
+  if (!EnsureDebugInfo(shared, function)) {
     // Return if we failed to retrieve the debug info.
     return false;
   }
=======================================
--- /branches/bleeding_edge/src/debug.h Fri May  4 06:20:41 2012
+++ /branches/bleeding_edge/src/debug.h Tue Jun 12 08:57:50 2012
@@ -239,12 +239,15 @@
                                         int count,
                                         int end));
   Object* Break(Arguments args);
-  void SetBreakPoint(Handle<SharedFunctionInfo> shared,
+  void SetBreakPoint(Handle<JSFunction> function,
                      Handle<Object> break_point_object,
                      int* source_position);
+  bool SetBreakPointForScript(Handle<Script> script,
+                              Handle<Object> break_point_object,
+                              int* source_position);
   void ClearBreakPoint(Handle<Object> break_point_object);
   void ClearAllBreakPoints();
-  void FloodWithOneShot(Handle<SharedFunctionInfo> shared);
+  void FloodWithOneShot(Handle<JSFunction> function);
   void FloodBoundFunctionWithOneShot(Handle<JSFunction> function);
   void FloodHandlerWithOneShot();
   void ChangeBreakOnException(ExceptionBreakType type, bool enable);
@@ -260,8 +263,11 @@

   void PrepareForBreakPoints();

-  // Returns whether the operation succeeded.
-  bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
+ // Returns whether the operation succeeded. Compilation can only be triggered + // if a valid closure is passed as the second argument, otherwise the shared
+  // function needs to be compiled already.
+  bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+                       Handle<JSFunction> function);

   // Returns true if the current stub call is patched to call the debugger.
   static bool IsDebugBreak(Address addr);
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/full-codegen.cc Tue Jun 12 08:57:50 2012
@@ -315,7 +315,7 @@
   Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
   code->set_optimizable(info->IsOptimizable() &&
!info->function()->flags()->Contains(kDontOptimize) && - info->function()->scope()->AllowsLazyRecompilation()); + info->function()->scope()->AllowsLazyCompilation());
   cgen.PopulateDeoptimizationData(code);
   cgen.PopulateTypeFeedbackInfo(code);
   cgen.PopulateTypeFeedbackCells(code);
=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Mon Jun 11 09:57:27 2012
+++ /branches/bleeding_edge/src/objects-inl.h   Tue Jun 12 08:57:50 2012
@@ -3697,6 +3697,10 @@
                kAllowLazyCompilation)
 BOOL_ACCESSORS(SharedFunctionInfo,
                compiler_hints,
+               allows_lazy_compilation_without_context,
+               kAllowLazyCompilationWithoutContext)
+BOOL_ACCESSORS(SharedFunctionInfo,
+               compiler_hints,
                uses_arguments,
                kUsesArguments)
 BOOL_ACCESSORS(SharedFunctionInfo,
=======================================
--- /branches/bleeding_edge/src/objects.cc      Tue Jun 12 08:30:16 2012
+++ /branches/bleeding_edge/src/objects.cc      Tue Jun 12 08:57:50 2012
@@ -7433,12 +7433,6 @@
   Builtins* builtins = GetIsolate()->builtins();
   ReplaceCode(builtins->builtin(Builtins::kLazyRecompile));
 }
-
-
-bool SharedFunctionInfo::EnsureCompiled(Handle<SharedFunctionInfo> shared,
-                                        ClearExceptionFlag flag) {
-  return shared->is_compiled() || CompileLazy(shared, flag);
-}


 static bool CompileLazyHelper(CompilationInfo* info,
@@ -7457,6 +7451,7 @@

 bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared,
                                      ClearExceptionFlag flag) {
+  ASSERT(shared->allows_lazy_compilation_without_context());
   CompilationInfo info(shared);
   return CompileLazyHelper(&info, flag);
 }
@@ -7469,6 +7464,7 @@
     function->ReplaceCode(function->shared()->code());
     function->shared()->set_code_age(0);
   } else {
+    ASSERT(function->shared()->allows_lazy_compilation());
     CompilationInfo info(function);
     result = CompileLazyHelper(&info, flag);
     ASSERT(!result || function->is_compiled());
@@ -7484,6 +7480,12 @@
   info.SetOptimizing(osr_ast_id);
   return CompileLazyHelper(&info, flag);
 }
+
+
+bool JSFunction::EnsureCompiled(Handle<JSFunction> function,
+                                ClearExceptionFlag flag) {
+  return function->is_compiled() || CompileLazy(function, flag);
+}


 bool JSFunction::IsInlineable() {
=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Jun 12 08:45:08 2012
+++ /branches/bleeding_edge/src/objects.h       Tue Jun 12 08:57:50 2012
@@ -5439,6 +5439,12 @@
   // when doing GC if we expect that the function will no longer be used.
   DECL_BOOLEAN_ACCESSORS(allows_lazy_compilation)

+  // Indicates if this function can be lazy compiled without a context.
+  // This is used to determine if we can force compilation without reaching
+ // the function through program execution but through other means (e.g. heap
+  // iteration by the debugger).
+  DECL_BOOLEAN_ACCESSORS(allows_lazy_compilation_without_context)
+
   // Indicates how many full GCs this function has survived with assigned
   // code object. Used to determine when it is relatively safe to flush
   // this code object and replace it with lazy compilation stub.
@@ -5585,10 +5591,9 @@

   void ResetForNewContext(int new_ic_age);

-  // Helpers to compile the shared code.  Returns true on success, false on
-  // failure (e.g., stack overflow during compilation).
-  static bool EnsureCompiled(Handle<SharedFunctionInfo> shared,
-                             ClearExceptionFlag flag);
+  // Helper to compile the shared code.  Returns true on success, false on
+ // failure (e.g., stack overflow during compilation). This is only used by + // the debugger, it is not possible to compile without a context otherwise.
   static bool CompileLazy(Handle<SharedFunctionInfo> shared,
                           ClearExceptionFlag flag);

@@ -5722,6 +5727,7 @@
   enum CompilerHints {
     kHasOnlySimpleThisPropertyAssignments,
     kAllowLazyCompilation,
+    kAllowLazyCompilationWithoutContext,
     kLiveObjectsMayExist,
     kCodeAgeShift,
     kOptimizationDisabled = kCodeAgeShift + kCodeAgeSize,
@@ -5870,6 +5876,8 @@

   // Helpers to compile this function.  Returns true on success, false on
   // failure (e.g., stack overflow during compilation).
+  static bool EnsureCompiled(Handle<JSFunction> function,
+                             ClearExceptionFlag flag);
   static bool CompileLazy(Handle<JSFunction> function,
                           ClearExceptionFlag flag);
   static bool CompileOptimized(Handle<JSFunction> function,
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/runtime.cc      Tue Jun 12 08:57:50 2012
@@ -2183,8 +2183,7 @@
   Handle<SharedFunctionInfo> target_shared(target->shared());
   Handle<SharedFunctionInfo> source_shared(source->shared());

-  if (!source->is_compiled() &&
-      !JSFunction::CompileLazy(source, KEEP_EXCEPTION)) {
+  if (!JSFunction::EnsureCompiled(source, KEEP_EXCEPTION)) {
     return Failure::Exception();
   }

@@ -4855,14 +4854,13 @@
 RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrepareStepInIfStepping) {
   Debug* debug = isolate->debug();
   if (!debug->IsStepping()) return NULL;
-  CONVERT_ARG_CHECKED(Object, callback, 0);
+  CONVERT_ARG_HANDLE_CHECKED(JSFunction, callback, 0);
   HandleScope scope(isolate);
- Handle<SharedFunctionInfo> shared_info(JSFunction::cast(callback)->shared()); // When leaving the callback, step out has been activated, but not performed
   // if we do not leave the builtin.  To be able to step into the callback
   // again, we need to clear the step out at this point.
   debug->ClearStepOut();
-  debug->FloodWithOneShot(shared_info);
+  debug->FloodWithOneShot(callback);
   return NULL;
 }

@@ -8140,12 +8138,8 @@
   }

   // The function should be compiled for the optimization hints to be
-  // available. We cannot use EnsureCompiled because that forces a
-  // compilation through the shared function info which makes it
-  // impossible for us to optimize.
-  if (!function->is_compiled()) {
-    JSFunction::CompileLazy(function, CLEAR_EXCEPTION);
-  }
+  // available.
+  JSFunction::EnsureCompiled(function, CLEAR_EXCEPTION);

   Handle<SharedFunctionInfo> shared(function->shared(), isolate);
   if (!function->has_initial_map() &&
@@ -11155,7 +11149,7 @@
     }

     // Get the debug info (create it if it does not exist).
-    if (!isolate->debug()->EnsureDebugInfo(shared_info)) {
+    if (!isolate->debug()->EnsureDebugInfo(shared_info, function_)) {
       // Return if ensuring debug info failed.
       return;
     }
@@ -11676,32 +11670,16 @@
   return *isolate->factory()->NewJSArrayWithElements(
       Handle<FixedArray>::cast(break_locations));
 }
-
-
-// Set a break point in a function
-// args[0]: function
-// args[1]: number: break source position (within the function source)
-// args[2]: number: break point object
-RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFunctionBreakPoint) {
-  HandleScope scope(isolate);
-  ASSERT(args.length() == 3);
-  CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
-  Handle<SharedFunctionInfo> shared(fun->shared());
-  CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
-  RUNTIME_ASSERT(source_position >= 0);
-  Handle<Object> break_point_object_arg = args.at<Object>(2);
-
-  // Set break point.
-  isolate->debug()->SetBreakPoint(shared, break_point_object_arg,
-                                  &source_position);
-
-  return Smi::FromInt(source_position);
-}


 Object* Runtime::FindSharedFunctionInfoInScript(Isolate* isolate,
                                                 Handle<Script> script,
                                                 int position) {
+  // 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.
+  isolate->debug()->PrepareForBreakPoints();
+
   // 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.
@@ -11723,6 +11701,12 @@
            obj != NULL; obj = iterator.next()) {
         if (obj->IsSharedFunctionInfo()) {
           Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
+          if (!shared->allows_lazy_compilation_without_context() &&
+              !shared->is_compiled()) {
+ // Skip functions that we cannot compile lazily without a context,
+            // which is not available here.
+            continue;
+          }
           if (shared->script() == *script) {
// If the SharedFunctionInfo found has the requested script data and
             // contains the source position it is a candidate.
@@ -11767,19 +11751,40 @@
       return isolate->heap()->undefined_value();
     }

-    // If the candidate found is compiled we are done. NOTE: when lazy
- // compilation of inner functions is introduced some additional checking
-    // needs to be done here to compile inner functions.
+    // 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.
+      // 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::BuildFunctionInfo checks whether the
+      // debugger is active.
       SharedFunctionInfo::CompileLazy(target, KEEP_EXCEPTION);
     }
   }  // End while loop.

   return *target;
 }
+
+
+// Set a break point in a function.
+// args[0]: function
+// args[1]: number: break source position (within the function source)
+// args[2]: number: break point object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFunctionBreakPoint) {
+  HandleScope scope(isolate);
+  ASSERT(args.length() == 3);
+  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+  CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+  RUNTIME_ASSERT(source_position >= 0);
+  Handle<Object> break_point_object_arg = args.at<Object>(2);
+
+  // Set break point.
+  isolate->debug()->SetBreakPoint(function, break_point_object_arg,
+                                  &source_position);
+
+  return Smi::FromInt(source_position);
+}


// Changes the state of a break point in a script and returns source position
@@ -11800,23 +11805,13 @@
   RUNTIME_ASSERT(wrapper->value()->IsScript());
   Handle<Script> script(Script::cast(wrapper->value()));

-  Object* result = Runtime::FindSharedFunctionInfoInScript(
-      isolate, script, source_position);
-  if (!result->IsUndefined()) {
-    Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
- // Find position within function. The script position might be before the
-    // source position of the first function.
-    int position;
-    if (shared->start_position() > source_position) {
-      position = 0;
-    } else {
-      position = source_position - shared->start_position();
-    }
- isolate->debug()->SetBreakPoint(shared, break_point_object_arg, &position);
-    position += shared->start_position();
-    return Smi::FromInt(position);
-  }
-  return  isolate->heap()->undefined_value();
+  // Set break point.
+ if (!isolate->debug()->SetBreakPointForScript(script, break_point_object_arg,
+                                                &source_position)) {
+    return  isolate->heap()->undefined_value();
+  }
+
+  return Smi::FromInt(source_position);
 }


@@ -12540,7 +12535,7 @@
   ASSERT(args.length() == 1);
   // Get the function and make sure it is compiled.
   CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
-  if (!JSFunction::CompileLazy(func, KEEP_EXCEPTION)) {
+  if (!JSFunction::EnsureCompiled(func, KEEP_EXCEPTION)) {
     return Failure::Exception();
   }
   func->code()->PrintLn();
@@ -12555,7 +12550,7 @@
   ASSERT(args.length() == 1);
   // Get the function and make sure it is compiled.
   CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
-  if (!JSFunction::CompileLazy(func, KEEP_EXCEPTION)) {
+  if (!JSFunction::EnsureCompiled(func, KEEP_EXCEPTION)) {
     return Failure::Exception();
   }
   func->shared()->construct_stub()->PrintLn();
=======================================
--- /branches/bleeding_edge/src/scopes.cc       Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/scopes.cc       Tue Jun 12 08:57:50 2012
@@ -635,11 +635,6 @@

   return true;
 }
-
-
-bool Scope::AllowsLazyCompilation() const {
-  return !force_eager_compilation_ && HasTrivialOuterContext();
-}


 bool Scope::HasTrivialContext() const {
@@ -666,10 +661,15 @@
 }


-bool Scope::AllowsLazyRecompilation() const {
+bool Scope::AllowsLazyCompilation() const {
   return !force_eager_compilation_ &&
          !TrivialDeclarationScopesBeforeWithScope();
 }
+
+
+bool Scope::AllowsLazyCompilationWithoutContext() const {
+  return !force_eager_compilation_ && HasTrivialOuterContext();
+}


 bool Scope::TrivialDeclarationScopesBeforeWithScope() const {
=======================================
--- /branches/bleeding_edge/src/scopes.h        Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/scopes.h        Tue Jun 12 08:57:50 2012
@@ -374,8 +374,8 @@
   // Determine if we can use lazy compilation for this scope.
   bool AllowsLazyCompilation() const;

-  // True if we can lazily recompile functions with this scope.
-  bool AllowsLazyRecompilation() const;
+ // Determine if we can use lazy compilation for this scope without a context.
+  bool AllowsLazyCompilationWithoutContext() const;

   // True if the outer context of this scope is always the global context.
   bool HasTrivialOuterContext() const;
=======================================
--- /branches/bleeding_edge/test/cctest/test-debug.cc Fri Apr 20 03:43:02 2012 +++ /branches/bleeding_edge/test/cctest/test-debug.cc Tue Jun 12 08:57:50 2012
@@ -197,10 +197,9 @@
 // number.
static int SetBreakPoint(Handle<v8::internal::JSFunction> fun, int position) {
   static int break_point = 0;
-  Handle<v8::internal::SharedFunctionInfo> shared(fun->shared());
   v8::internal::Debug* debug = v8::internal::Isolate::Current()->debug();
   debug->SetBreakPoint(
-      shared,
+      fun,
       Handle<Object>(v8::internal::Smi::FromInt(++break_point)),
       &position);
   return break_point;
@@ -515,7 +514,7 @@
   // there
   ClearBreakPoint(bp);
   CHECK(!debug->HasDebugInfo(shared));
-  CHECK(debug->EnsureDebugInfo(shared));
+  CHECK(debug->EnsureDebugInfo(shared, fun));
   TestBreakLocationIterator it2(Debug::GetDebugInfo(shared));
   it2.FindBreakLocationFromPosition(position);
   actual_mode = it2.it()->rinfo()->rmode();
=======================================
--- /branches/bleeding_edge/test/cctest/test-heap.cc Wed Jun 6 03:17:26 2012 +++ /branches/bleeding_edge/test/cctest/test-heap.cc Tue Jun 12 08:57:50 2012
@@ -1198,6 +1198,7 @@
   HEAP->CollectAllGarbage(Heap::kNoGCFlags);
   HEAP->CollectAllGarbage(Heap::kNoGCFlags);
   HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
   CHECK(HEAP->old_pointer_space()->IsSweepingComplete());
   int initial_size = static_cast<int>(HEAP->SizeOfObjects());

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

Reply via email to