Index: src/debug.cc |
diff --git a/src/debug.cc b/src/debug.cc |
index 1d90c2999a2bd0efb19080b1fac1ccca6beffaa7..a71315ad6976ce6003482c5af31d9e00af997b6d 100644 |
--- a/src/debug.cc |
+++ b/src/debug.cc |
@@ -36,7 +36,6 @@ Debug::Debug(Isolate* isolate) |
is_active_(false), |
is_suppressed_(false), |
live_edit_enabled_(true), // TODO(yangguo): set to false by default. |
- has_break_points_(false), |
break_disabled_(false), |
in_debug_event_listener_(false), |
break_on_exception_(false), |
@@ -146,7 +145,6 @@ void BreakLocation::Iterator::Next() { |
// the address. |
BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info, |
BreakLocatorType type, Address pc) { |
- DCHECK(debug_info->code()->has_debug_break_slots()); |
Iterator it(debug_info, type); |
it.SkipTo(BreakIndexFromAddress(debug_info, type, pc)); |
return it.GetBreakLocation(); |
@@ -158,7 +156,6 @@ BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info, |
void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info, |
BreakLocatorType type, Address pc, |
List<BreakLocation>* result_out) { |
- DCHECK(debug_info->code()->has_debug_break_slots()); |
int break_index = BreakIndexFromAddress(debug_info, type, pc); |
Iterator it(debug_info, type); |
it.SkipTo(break_index); |
@@ -191,7 +188,6 @@ int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info, |
BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info, |
BreakLocatorType type, int position, |
BreakPositionAlignment alignment) { |
- DCHECK(debug_info->code()->has_debug_break_slots()); |
// Run through all break points to locate the one closest to the source |
// position. |
int closest_break = 0; |
@@ -222,7 +218,6 @@ BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info, |
void BreakLocation::SetBreakPoint(Handle<Object> break_point_object) { |
// If there is not already a real break point here patch code with debug |
// break. |
- DCHECK(code()->has_debug_break_slots()); |
if (!HasBreakPoint()) SetDebugBreak(); |
DCHECK(IsDebugBreak() || IsDebuggerStatement()); |
// Set the break point information. |
@@ -607,7 +602,7 @@ void Debug::Break(Arguments args, JavaScriptFrame* frame) { |
// Get the debug info (create it if it does not exist). |
Handle<SharedFunctionInfo> shared = |
Handle<SharedFunctionInfo>(frame->function()->shared()); |
- Handle<DebugInfo> debug_info = GetDebugInfo(shared); |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
// Find the break point where execution has stopped. |
// PC points to the instruction after the current one, possibly a break |
@@ -780,27 +775,11 @@ bool Debug::CheckBreakPoint(Handle<Object> break_point_object) { |
} |
-// Check whether the function has debug information. |
-bool Debug::HasDebugInfo(Handle<SharedFunctionInfo> shared) { |
- return !shared->debug_info()->IsUndefined(); |
-} |
- |
- |
-// Return the debug info for this function. EnsureDebugInfo must be called |
-// prior to ensure the debug info has been generated for shared. |
-Handle<DebugInfo> Debug::GetDebugInfo(Handle<SharedFunctionInfo> shared) { |
- DCHECK(HasDebugInfo(shared)); |
- return Handle<DebugInfo>(DebugInfo::cast(shared->debug_info())); |
-} |
- |
- |
bool Debug::SetBreakPoint(Handle<JSFunction> function, |
Handle<Object> break_point_object, |
int* source_position) { |
HandleScope scope(isolate_); |
- PrepareForBreakPoints(); |
- |
// Make sure the function is compiled and has set up the debug info. |
Handle<SharedFunctionInfo> shared(function->shared()); |
if (!EnsureDebugInfo(shared, function)) { |
@@ -808,7 +787,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function, |
return true; |
} |
- Handle<DebugInfo> debug_info = GetDebugInfo(shared); |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
// Source positions starts with zero. |
DCHECK(*source_position >= 0); |
@@ -829,8 +808,6 @@ bool Debug::SetBreakPointForScript(Handle<Script> script, |
BreakPositionAlignment alignment) { |
HandleScope scope(isolate_); |
- PrepareForBreakPoints(); |
- |
// Obtain shared function info for the function. |
Handle<Object> result = |
FindSharedFunctionInfoInScript(script, *source_position); |
@@ -852,7 +829,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script, |
position = *source_position - shared->start_position(); |
} |
- Handle<DebugInfo> debug_info = GetDebugInfo(shared); |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
// Source positions starts with zero. |
DCHECK(position >= 0); |
@@ -926,8 +903,6 @@ void Debug::ClearAllBreakPoints() { |
void Debug::FloodWithOneShot(Handle<JSFunction> function, |
BreakLocatorType type) { |
- PrepareForBreakPoints(); |
- |
// Make sure the function is compiled and has set up the debug info. |
Handle<SharedFunctionInfo> shared(function->shared()); |
if (!EnsureDebugInfo(shared, function)) { |
@@ -936,8 +911,8 @@ void Debug::FloodWithOneShot(Handle<JSFunction> function, |
} |
// Flood the function with break points. |
- for (BreakLocation::Iterator it(GetDebugInfo(shared), type); !it.Done(); |
- it.Next()) { |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
+ for (BreakLocation::Iterator it(debug_info, type); !it.Done(); it.Next()) { |
it.GetBreakLocation().SetOneShot(); |
} |
} |
@@ -1033,13 +1008,18 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { |
} |
+FrameSummary GetFirstFrameSummary(JavaScriptFrame* frame) { |
+ List<FrameSummary> frames(FLAG_max_inlining_levels + 1); |
+ frame->Summarize(&frames); |
+ return frames.first(); |
+} |
+ |
+ |
void Debug::PrepareStep(StepAction step_action, |
int step_count, |
StackFrame::Id frame_id) { |
HandleScope scope(isolate_); |
- PrepareForBreakPoints(); |
- |
DCHECK(in_debug_scope()); |
// Remember this step action and count. |
@@ -1084,22 +1064,18 @@ void Debug::PrepareStep(StepAction step_action, |
return; |
} |
- List<FrameSummary> frames(FLAG_max_inlining_levels + 1); |
- frames_it.frame()->Summarize(&frames); |
- FrameSummary summary = frames.first(); |
- |
// Get the debug info (create it if it does not exist). |
+ FrameSummary summary = GetFirstFrameSummary(frame); |
Handle<JSFunction> function(summary.function()); |
Handle<SharedFunctionInfo> shared(function->shared()); |
if (!EnsureDebugInfo(shared, function)) { |
// Return if ensuring debug info failed. |
return; |
} |
- Handle<DebugInfo> debug_info = GetDebugInfo(shared); |
- // Compute whether or not the target is a call target. |
- bool is_at_restarted_function = false; |
- Handle<Code> call_function_stub; |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
+ // Refresh frame summary if the code has been recompiled for debugging. |
+ if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame); |
// PC points to the instruction after the current one, possibly a break |
// location as well. So the "- 1" to exclude it from the search. |
@@ -1107,10 +1083,6 @@ void Debug::PrepareStep(StepAction step_action, |
BreakLocation location = |
BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc); |
- if (thread_local_.restarter_frame_function_pointer_ != NULL) { |
- is_at_restarted_function = true; |
- } |
- |
// If this is the last break code target step out is the only possibility. |
if (location.IsReturn() || step_action == StepOut) { |
if (step_action == StepOut) { |
@@ -1142,7 +1114,7 @@ void Debug::PrepareStep(StepAction step_action, |
if (step_action != StepNext && step_action != StepMin) { |
// If there's restarter frame on top of the stack, just get the pointer |
// to function which is going to be restarted. |
- if (is_at_restarted_function) { |
+ if (thread_local_.restarter_frame_function_pointer_ != NULL) { |
Handle<JSFunction> restarted_function( |
JSFunction::cast(*thread_local_.restarter_frame_function_pointer_)); |
FloodWithOneShot(restarted_function); |
@@ -1253,10 +1225,10 @@ Handle<Object> Debug::GetSourceBreakLocations( |
BreakPositionAlignment position_alignment) { |
Isolate* isolate = shared->GetIsolate(); |
Heap* heap = isolate->heap(); |
- if (!HasDebugInfo(shared)) { |
+ if (!shared->HasDebugInfo()) { |
return Handle<Object>(heap->undefined_value(), isolate); |
} |
- Handle<DebugInfo> debug_info = GetDebugInfo(shared); |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
if (debug_info->GetBreakPointCount() == 0) { |
return Handle<Object>(heap->undefined_value(), isolate); |
} |
@@ -1369,42 +1341,12 @@ void Debug::ClearStepNext() { |
} |
-static void CollectActiveFunctionsFromThread( |
- Isolate* isolate, |
- ThreadLocalTop* top, |
- List<Handle<JSFunction> >* active_functions, |
- Object* active_code_marker) { |
- // Find all non-optimized code functions with activation frames |
- // on the stack. This includes functions which have optimized |
- // activations (including inlined functions) on the stack as the |
- // non-optimized code is needed for the lazy deoptimization. |
- for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { |
- JavaScriptFrame* frame = it.frame(); |
- if (frame->is_optimized()) { |
- List<JSFunction*> functions(FLAG_max_inlining_levels + 1); |
- frame->GetFunctions(&functions); |
- for (int i = 0; i < functions.length(); i++) { |
- JSFunction* function = functions[i]; |
- active_functions->Add(Handle<JSFunction>(function)); |
- function->shared()->code()->set_gc_metadata(active_code_marker); |
- } |
- } else if (frame->function()->IsJSFunction()) { |
- JSFunction* function = frame->function(); |
- DCHECK(frame->LookupCode()->kind() == Code::FUNCTION); |
- active_functions->Add(Handle<JSFunction>(function)); |
- function->shared()->code()->set_gc_metadata(active_code_marker); |
- } |
- } |
-} |
- |
- |
// Count the number of calls before the current frame PC to find the |
// corresponding PC in the newly recompiled code. |
static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code, |
Address old_pc) { |
DCHECK_EQ(old_code->kind(), Code::FUNCTION); |
DCHECK_EQ(new_code->kind(), Code::FUNCTION); |
- DCHECK(!old_code->has_debug_break_slots()); |
DCHECK(new_code->has_debug_break_slots()); |
int mask = RelocInfo::kCodeTargetMask; |
int index = 0; |
@@ -1427,7 +1369,6 @@ static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code, |
// Count the number of continuations at which the current pc offset is at. |
static int ComputeContinuationIndexFromPcOffset(Code* code, int pc_offset) { |
DCHECK_EQ(code->kind(), Code::FUNCTION); |
- DCHECK(!code->has_debug_break_slots()); |
Address pc = code->instruction_start() + pc_offset; |
int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION); |
int index = 0; |
@@ -1453,301 +1394,120 @@ static int ComputePcOffsetFromContinuationIndex(Code* code, int index) { |
} |
-static void RedirectActivationsToRecompiledCodeOnThread( |
- Isolate* isolate, |
- ThreadLocalTop* top) { |
- for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { |
- JavaScriptFrame* frame = it.frame(); |
- |
- if (frame->is_optimized() || !frame->function()->IsJSFunction()) continue; |
- |
- JSFunction* function = frame->function(); |
- |
- DCHECK(frame->LookupCode()->kind() == Code::FUNCTION); |
- |
- Handle<Code> frame_code(frame->LookupCode()); |
- if (frame_code->has_debug_break_slots()) continue; |
- |
- Handle<Code> new_code(function->shared()->code()); |
- if (new_code->kind() != Code::FUNCTION || |
- !new_code->has_debug_break_slots()) { |
- continue; |
- } |
- |
- Address new_pc = |
- ComputeNewPcForRedirect(*new_code, *frame_code, frame->pc()); |
- |
- if (FLAG_trace_deopt) { |
- PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " |
- "with %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " |
- "for debugging, " |
- "changing pc from %08" V8PRIxPTR " to %08" V8PRIxPTR "\n", |
- reinterpret_cast<intptr_t>( |
- frame_code->instruction_start()), |
- reinterpret_cast<intptr_t>( |
- frame_code->instruction_start()) + |
- frame_code->instruction_size(), |
- frame_code->instruction_size(), |
- reinterpret_cast<intptr_t>(new_code->instruction_start()), |
- reinterpret_cast<intptr_t>(new_code->instruction_start()) + |
- new_code->instruction_size(), |
- new_code->instruction_size(), |
- reinterpret_cast<intptr_t>(frame->pc()), |
- reinterpret_cast<intptr_t>(new_pc)); |
- } |
- |
- if (FLAG_enable_embedded_constant_pool) { |
- // Update constant pool pointer for new code. |
- frame->set_constant_pool(new_code->constant_pool()); |
- } |
- |
- // Patch the return address to return into the code with |
- // debug break slots. |
- frame->set_pc(new_pc); |
+class RedirectActiveFunctions : public ThreadVisitor { |
+ public: |
+ explicit RedirectActiveFunctions(SharedFunctionInfo* shared) |
+ : shared_(shared) { |
+ DCHECK(shared->HasDebugCode()); |
} |
-} |
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) { |
+ for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { |
+ JavaScriptFrame* frame = it.frame(); |
+ JSFunction* function = frame->function(); |
+ if (frame->is_optimized()) continue; |
+ if (!function->Inlines(shared_)) continue; |
+ |
+ Code* frame_code = frame->LookupCode(); |
+ DCHECK(frame_code->kind() == Code::FUNCTION); |
+ if (frame_code->has_debug_break_slots()) continue; |
+ |
+ Code* new_code = function->shared()->code(); |
+ Address old_pc = frame->pc(); |
+ Address new_pc = ComputeNewPcForRedirect(new_code, frame_code, old_pc); |
+ |
+ if (FLAG_trace_deopt) { |
+ PrintF("Replacing pc for debugging: %08" V8PRIxPTR " => %08" V8PRIxPTR |
+ "\n", |
+ reinterpret_cast<intptr_t>(old_pc), |
+ reinterpret_cast<intptr_t>(new_pc)); |
+ } |
-class ActiveFunctionsCollector : public ThreadVisitor { |
- public: |
- explicit ActiveFunctionsCollector(List<Handle<JSFunction> >* active_functions, |
- Object* active_code_marker) |
- : active_functions_(active_functions), |
- active_code_marker_(active_code_marker) { } |
+ if (FLAG_enable_embedded_constant_pool) { |
+ // Update constant pool pointer for new code. |
+ frame->set_constant_pool(new_code->constant_pool()); |
+ } |
- void VisitThread(Isolate* isolate, ThreadLocalTop* top) { |
- CollectActiveFunctionsFromThread(isolate, |
- top, |
- active_functions_, |
- active_code_marker_); |
+ // Patch the return address to return into the code with |
+ // debug break slots. |
+ frame->set_pc(new_pc); |
+ } |
} |
private: |
- List<Handle<JSFunction> >* active_functions_; |
- Object* active_code_marker_; |
+ SharedFunctionInfo* shared_; |
+ DisallowHeapAllocation no_gc_; |
}; |
-class ActiveFunctionsRedirector : public ThreadVisitor { |
- public: |
- void VisitThread(Isolate* isolate, ThreadLocalTop* top) { |
- RedirectActivationsToRecompiledCodeOnThread(isolate, top); |
- } |
-}; |
- |
+bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { |
+ DCHECK(shared->is_compiled()); |
-static void EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function) { |
- if (function->code()->kind() == Code::FUNCTION && |
- function->code()->has_debug_break_slots()) { |
- // Nothing to do. Function code already had debug break slots. |
- return; |
+ if (isolate_->concurrent_recompilation_enabled()) { |
+ isolate_->optimizing_compile_dispatcher()->Flush(); |
} |
- // Make sure that the shared full code is compiled with debug |
- // break slots. |
- if (!function->shared()->code()->has_debug_break_slots()) { |
- MaybeHandle<Code> code = Compiler::GetDebugCode(function); |
- // Recompilation can fail. In that case leave the code as it was. |
- if (!code.is_null()) function->ReplaceCode(*code.ToHandleChecked()); |
- } else { |
- // Simply use shared code if it has debug break slots. |
- function->ReplaceCode(function->shared()->code()); |
- } |
-} |
- |
-static void RecompileAndRelocateSuspendedGenerators( |
- const List<Handle<JSGeneratorObject> > &generators) { |
- for (int i = 0; i < generators.length(); i++) { |
- Handle<JSFunction> fun(generators[i]->function()); |
+ List<Handle<JSFunction> > functions; |
+ List<Handle<JSGeneratorObject> > suspended_generators; |
- EnsureFunctionHasDebugBreakSlots(fun); |
- |
- int index = generators[i]->continuation(); |
- int pc_offset = ComputePcOffsetFromContinuationIndex(fun->code(), index); |
- generators[i]->set_continuation(pc_offset); |
+ if (!shared->optimized_code_map()->IsSmi()) { |
+ shared->ClearOptimizedCodeMap(); |
} |
-} |
- |
- |
-static bool SkipSharedFunctionInfo(SharedFunctionInfo* shared, |
- Object* active_code_marker) { |
- if (!shared->allows_lazy_compilation()) return true; |
- Object* script = shared->script(); |
- if (!script->IsScript()) return true; |
- if (Script::cast(script)->type()->value() == Script::TYPE_NATIVE) return true; |
- Code* shared_code = shared->code(); |
- return shared_code->gc_metadata() == active_code_marker; |
-} |
- |
- |
-static inline bool HasDebugBreakSlots(Code* code) { |
- return code->kind() == Code::FUNCTION && code->has_debug_break_slots(); |
-} |
- |
- |
-void Debug::PrepareForBreakPoints() { |
- // If preparing for the first break point make sure to deoptimize all |
- // functions as debugging does not work with optimized code. |
- if (!has_break_points_) { |
- if (isolate_->concurrent_recompilation_enabled()) { |
- isolate_->optimizing_compile_dispatcher()->Flush(); |
- } |
- |
- Deoptimizer::DeoptimizeAll(isolate_); |
- |
- Handle<Code> lazy_compile = isolate_->builtins()->CompileLazy(); |
- |
- // 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); |
- |
- // A list of all suspended generators. |
- List<Handle<JSGeneratorObject> > suspended_generators; |
- // A list of all generator functions. We need to recompile all functions, |
- // but we don't know until after visiting the whole heap which generator |
- // functions have suspended activations and which do not. As in the case of |
- // functions with activations on the stack, we need to be careful with |
- // generator functions with suspended activations because although they |
- // should be recompiled, recompilation can fail, and we need to avoid |
- // leaving the heap in an inconsistent state. |
- // |
- // We could perhaps avoid this list and instead re-use the GC metadata |
- // links. |
- List<Handle<JSFunction> > generator_functions; |
+ // Make sure we abort incremental marking. |
+ isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
+ "prepare for break points"); |
- { |
- // We are going to iterate heap to find all functions without |
- // debug break slots. |
- Heap* heap = isolate_->heap(); |
- heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
- "preparing for breakpoints"); |
- HeapIterator iterator(heap); |
- |
- // Ensure no GC in this scope as we are going to use gc_metadata |
- // field in the Code object to mark active functions. |
- DisallowHeapAllocation no_allocation; |
- |
- Object* active_code_marker = heap->the_hole_value(); |
- |
- CollectActiveFunctionsFromThread(isolate_, |
- isolate_->thread_local_top(), |
- &active_functions, |
- active_code_marker); |
- ActiveFunctionsCollector active_functions_collector(&active_functions, |
- active_code_marker); |
- isolate_->thread_manager()->IterateArchivedThreads( |
- &active_functions_collector); |
- |
- // Scan the heap for all non-optimized functions which have no |
- // debug break slots and are not active or inlined into an active |
- // function and mark them for lazy compilation. |
- HeapObject* obj = NULL; |
- while (((obj = iterator.next()) != NULL)) { |
- if (obj->IsJSFunction()) { |
- JSFunction* function = JSFunction::cast(obj); |
- SharedFunctionInfo* shared = function->shared(); |
- if (SkipSharedFunctionInfo(shared, active_code_marker)) continue; |
- if (shared->is_generator()) { |
- generator_functions.Add(Handle<JSFunction>(function, isolate_)); |
- continue; |
- } |
- if (HasDebugBreakSlots(function->code())) continue; |
- Code* fallback = HasDebugBreakSlots(shared->code()) ? shared->code() |
- : *lazy_compile; |
- Code::Kind kind = function->code()->kind(); |
- if (kind == Code::FUNCTION || |
- (kind == Code::BUILTIN && // Abort in-flight compilation. |
- (function->IsInOptimizationQueue() || |
- function->IsMarkedForOptimization() || |
- function->IsMarkedForConcurrentOptimization()))) { |
- function->ReplaceCode(fallback); |
- } |
- if (kind == Code::OPTIMIZED_FUNCTION) { |
- // Optimized code can only get here if DeoptimizeAll did not |
- // deoptimize turbo fan code. |
- DCHECK(!FLAG_turbo_asm_deoptimization); |
- DCHECK(function->shared()->asm_function()); |
- DCHECK(function->code()->is_turbofanned()); |
- function->ReplaceCode(fallback); |
- } |
- } else if (obj->IsJSGeneratorObject()) { |
- JSGeneratorObject* gen = JSGeneratorObject::cast(obj); |
- if (!gen->is_suspended()) continue; |
- |
- JSFunction* fun = gen->function(); |
- DCHECK_EQ(fun->code()->kind(), Code::FUNCTION); |
- if (fun->code()->has_debug_break_slots()) continue; |
- |
- int pc_offset = gen->continuation(); |
- DCHECK_LT(0, pc_offset); |
- |
- int index = |
- ComputeContinuationIndexFromPcOffset(fun->code(), pc_offset); |
- |
- // This will be fixed after we recompile the functions. |
- gen->set_continuation(index); |
- |
- suspended_generators.Add(Handle<JSGeneratorObject>(gen, isolate_)); |
- } else if (obj->IsSharedFunctionInfo()) { |
- SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj); |
- if (SkipSharedFunctionInfo(shared, active_code_marker)) continue; |
- if (shared->is_generator()) continue; |
- if (HasDebugBreakSlots(shared->code())) continue; |
- shared->ReplaceCode(*lazy_compile); |
+ { |
+ HeapIterator iterator(isolate_->heap()); |
+ HeapObject* obj; |
+ bool include_generators = shared->is_generator(); |
+ |
+ while ((obj = iterator.next())) { |
+ if (obj->IsJSFunction()) { |
+ JSFunction* function = JSFunction::cast(obj); |
+ if (!function->Inlines(*shared)) continue; |
+ if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) { |
+ Deoptimizer::DeoptimizeFunction(function); |
} |
- } |
- |
- // Clear gc_metadata field. |
- for (int i = 0; i < active_functions.length(); i++) { |
- Handle<JSFunction> function = active_functions[i]; |
- function->shared()->code()->set_gc_metadata(Smi::FromInt(0)); |
+ functions.Add(handle(function)); |
+ } else if (include_generators && obj->IsJSGeneratorObject()) { |
+ JSGeneratorObject* generator_obj = JSGeneratorObject::cast(obj); |
+ if (!generator_obj->is_suspended()) continue; |
+ JSFunction* function = generator_obj->function(); |
+ if (!function->Inlines(*shared)) continue; |
+ int pc_offset = generator_obj->continuation(); |
+ int index = |
+ ComputeContinuationIndexFromPcOffset(function->code(), pc_offset); |
+ generator_obj->set_continuation(index); |
+ suspended_generators.Add(handle(generator_obj)); |
} |
} |
+ } |
- // Recompile generator functions that have suspended activations, and |
- // relocate those activations. |
- RecompileAndRelocateSuspendedGenerators(suspended_generators); |
- |
- // Mark generator functions that didn't have suspended activations for lazy |
- // recompilation. Note that this set does not include any active functions. |
- for (int i = 0; i < generator_functions.length(); i++) { |
- Handle<JSFunction> &function = generator_functions[i]; |
- if (function->code()->kind() != Code::FUNCTION) continue; |
- if (function->code()->has_debug_break_slots()) continue; |
- function->ReplaceCode(*lazy_compile); |
- function->shared()->ReplaceCode(*lazy_compile); |
+ if (!shared->HasDebugCode()) { |
+ DCHECK(functions.length() > 0); |
+ if (Compiler::GetDebugCode(functions.first()).is_null()) { |
+ return false; |
} |
+ } |
- // Now recompile all functions with activation frames and and |
- // patch the return address to run in the new compiled code. It could be |
- // that some active functions were recompiled already by the suspended |
- // generator recompilation pass above; a generator with suspended |
- // activations could also have active activations. That's fine. |
- for (int i = 0; i < active_functions.length(); i++) { |
- Handle<JSFunction> function = active_functions[i]; |
- Handle<SharedFunctionInfo> shared(function->shared()); |
- if (!shared->allows_lazy_compilation()) { |
- // Ignore functions that cannot be recompiled. Fortunately, those are |
- // only ones that are not subject to debugging in the first place. |
- DCHECK(!function->IsSubjectToDebugging()); |
- continue; |
- } |
- if (shared->code()->kind() == Code::BUILTIN) continue; |
+ for (Handle<JSFunction> const function : functions) { |
+ function->ReplaceCode(shared->code()); |
+ } |
- EnsureFunctionHasDebugBreakSlots(function); |
- } |
+ for (Handle<JSGeneratorObject> const generator_obj : suspended_generators) { |
+ int index = generator_obj->continuation(); |
+ int pc_offset = ComputePcOffsetFromContinuationIndex(shared->code(), index); |
+ generator_obj->set_continuation(pc_offset); |
+ } |
- RedirectActivationsToRecompiledCodeOnThread(isolate_, |
- isolate_->thread_local_top()); |
+ // Update PCs on the stack to point to recompiled code. |
+ RedirectActiveFunctions redirect_visitor(*shared); |
+ redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top()); |
+ isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor); |
- ActiveFunctionsRedirector active_functions_redirector; |
- isolate_->thread_manager()->IterateArchivedThreads( |
- &active_functions_redirector); |
- } |
+ return true; |
} |
@@ -1799,20 +1559,12 @@ class SharedFunctionInfoFinder { |
}; |
-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; |
-} |
- |
- |
+// We need to find a SFI for a literal that may not yet have been compiled yet, |
+// and there may not be a JSFunction referencing it. Find the SFI closest to |
+// the given position, compile it to reveal possible inner SFIs and repeat. |
+// While we are at this, also ensure code with debug break slots so that we do |
+// not have to compile a SFI without JSFunction, which is paifu for those that |
+// cannot be compiled without context (need to find outer compilable SFI etc.) |
Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, |
int position) { |
while (true) { |
@@ -1832,19 +1584,20 @@ Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, |
} |
shared = finder.Result(); |
if (shared == NULL) break; |
- // We found it if it's already compiled. |
- if (shared->is_compiled()) return handle(shared); |
+ // We found it if it's already compiled and has debug code. |
+ if (shared->HasDebugCode()) return handle(shared); |
} |
// If not, compile to reveal inner functions, if possible. |
if (shared->allows_lazy_compilation_without_context()) { |
- if (!CompileToRevealInnerFunctions(shared)) break; |
+ HandleScope scope(isolate_); |
+ if (Compiler::GetDebugCode(handle(shared)).is_null()) break; |
continue; |
} |
// If not possible, comb the heap for the best suitable compile target. |
JSFunction* closure; |
{ |
- HeapIterator it(isolate_->heap(), HeapIterator::kNoFiltering); |
+ HeapIterator it(isolate_->heap()); |
SharedFunctionInfoFinder finder(position); |
while (HeapObject* object = it.next()) { |
JSFunction* candidate_closure = NULL; |
@@ -1865,9 +1618,11 @@ Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, |
closure = finder.ResultClosure(); |
shared = finder.Result(); |
} |
- if (closure == NULL ? !CompileToRevealInnerFunctions(shared) |
- : !CompileToRevealInnerFunctions(closure)) { |
- break; |
+ HandleScope scope(isolate_); |
+ if (closure == NULL) { |
+ if (Compiler::GetDebugCode(handle(shared)).is_null()) break; |
+ } else { |
+ if (Compiler::GetDebugCode(handle(closure)).is_null()) break; |
} |
} |
return isolate_->factory()->undefined_value(); |
@@ -1880,30 +1635,23 @@ bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared, |
if (!shared->IsSubjectToDebugging()) return false; |
// Return if we already have the debug info for shared. |
- if (HasDebugInfo(shared)) { |
- DCHECK(shared->is_compiled()); |
- DCHECK(shared->code()->has_debug_break_slots()); |
- return true; |
- } |
- |
- // There will be at least one break point when we are done. |
- has_break_points_ = true; |
+ if (shared->HasDebugInfo()) return true; |
if (function.is_null()) { |
- DCHECK(shared->is_compiled()); |
- DCHECK(shared->code()->has_debug_break_slots()); |
+ DCHECK(shared->HasDebugCode()); |
} else if (!Compiler::EnsureCompiled(function, CLEAR_EXCEPTION)) { |
return false; |
} |
+ if (!PrepareFunctionForBreakPoints(shared)) return false; |
+ |
// Make sure IC state is clean. This is so that we correctly flood |
// accessor pairs when stepping in. |
shared->code()->ClearInlineCaches(); |
shared->feedback_vector()->ClearICSlots(*shared); |
// Create the debug info object. |
- DCHECK(shared->is_compiled()); |
- DCHECK(shared->code()->has_debug_break_slots()); |
+ DCHECK(shared->HasDebugCode()); |
Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared); |
// Add debug info to the list. |
@@ -1923,10 +1671,6 @@ void Debug::RemoveDebugInfo(DebugInfoListNode* prev, DebugInfoListNode* node) { |
prev->set_next(node->next()); |
} |
delete node; |
- |
- // If there are no more debug info objects there are not more break |
- // points. |
- has_break_points_ = debug_info_list_ != NULL; |
} |
@@ -1989,21 +1733,13 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) { |
bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { |
HandleScope scope(isolate_); |
- // If there are no break points this cannot be break at return, as |
- // the debugger statement and stack guard debug break cannot be at |
- // return. |
- if (!has_break_points_) return false; |
- |
- PrepareForBreakPoints(); |
- |
// Get the executing function in which the debug break occurred. |
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; |
- } |
- Handle<DebugInfo> debug_info = GetDebugInfo(shared); |
+ |
+ // With no debug info there are no break points, so we can't be at a return. |
+ if (!shared->HasDebugInfo()) return false; |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
Handle<Code> code(debug_info->code()); |
#ifdef DEBUG |
// Get the code which is actually executing. |
@@ -2014,7 +1750,7 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { |
// Find the reloc info matching the start of the debug break slot. |
Address slot_pc = frame->pc() - Assembler::kDebugBreakSlotLength; |
int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN); |
- for (RelocIterator it(debug_info->code(), mask); !it.done(); it.next()) { |
+ for (RelocIterator it(*code, mask); !it.done(); it.next()) { |
if (it.rinfo()->pc() == slot_pc) return true; |
} |
return false; |
@@ -2068,6 +1804,44 @@ Handle<FixedArray> Debug::GetLoadedScripts() { |
} |
+void Debug::GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id, |
+ List<int>* results_out) { |
+ FrameSummary summary = GetFirstFrameSummary(frame); |
+ |
+ Handle<JSFunction> fun = Handle<JSFunction>(summary.function()); |
+ Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared()); |
+ |
+ if (!EnsureDebugInfo(shared, fun)) return; |
+ |
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
+ // Refresh frame summary if the code has been recompiled for debugging. |
+ if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame); |
+ |
+ // Find range of break points starting from the break point where execution |
+ // has stopped. |
+ Address call_pc = summary.pc() - 1; |
+ List<BreakLocation> locations; |
+ BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS, |
+ call_pc, &locations); |
+ |
+ for (BreakLocation location : locations) { |
+ if (location.pc() <= summary.pc()) { |
+ // The break point is near our pc. Could be a step-in possibility, |
+ // that is currently taken by active debugger call. |
+ if (break_frame_id() == StackFrame::NO_ID) { |
+ continue; // We are not stepping. |
+ } else { |
+ JavaScriptFrameIterator frame_it(isolate_, break_frame_id()); |
+ // If our frame is a top frame and we are stepping, we can do step-in |
+ // at this place. |
+ if (frame_it.frame()->id() != frame_id) continue; |
+ } |
+ } |
+ if (location.IsStepInLocation()) results_out->Add(location.position()); |
+ } |
+} |
+ |
+ |
void Debug::RecordEvalCaller(Handle<Script> script) { |
script->set_compilation_type(Script::COMPILATION_TYPE_EVAL); |
// For eval scripts add information on the function from which eval was |