} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
} else if (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence() &&
- isolate->debug()->has_break_points()) {
+ IsPatchedDebugBreakSlotSequence()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
visitor->VisitRuntimeEntry(this);
StaticVisitor::VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
- } else if (heap->isolate()->debug()->has_break_points() &&
- RelocInfo::IsDebugBreakSlot(mode) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
} else if (mode == RelocInfo::INTERNAL_REFERENCE) {
visitor->VisitInternalReference(this);
} else if (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence() &&
- isolate->debug()->has_break_points()) {
+ IsPatchedDebugBreakSlotSequence()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
visitor->VisitRuntimeEntry(this);
StaticVisitor::VisitExternalReference(this);
} else if (mode == RelocInfo::INTERNAL_REFERENCE) {
StaticVisitor::VisitInternalReference(this);
- } else if (heap->isolate()->debug()->has_break_points() &&
- RelocInfo::IsDebugBreakSlot(mode) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
// found in the LICENSE file.
#include "src/background-parsing-task.h"
+#include "src/debug.h"
namespace v8 {
namespace internal {
info->set_global();
info->set_unicode_cache(&source_->unicode_cache);
- bool disable_lazy = Compiler::DebuggerWantsEagerCompilation(isolate);
+ bool disable_lazy = isolate->debug()->RequiresEagerCompilation();
if (disable_lazy && options == ScriptCompiler::kProduceParserCache) {
// Producing cached data while parsing eagerly is not supported.
options = ScriptCompiler::kNoCompileOptions;
V(kCopyBuffersOverlap, "Copy buffers overlap") \
V(kCouldNotGenerateZero, "Could not generate +0.0") \
V(kCouldNotGenerateNegativeZero, "Could not generate -0.0") \
- V(kDebuggerHasBreakPoints, "Debugger has break points") \
V(kDebuggerStatement, "DebuggerStatement") \
V(kDeclarationInCatchContext, "Declaration in catch context") \
V(kDeclarationInWithContext, "Declaration in with context") \
"ForInStatement with non-local each variable") \
V(kForOfStatement, "ForOfStatement") \
V(kFrameIsExpectedToBeAligned, "Frame is expected to be aligned") \
+ V(kFunctionBeingDebugged, "Function is being debugged") \
V(kFunctionCallsEval, "Function calls eval") \
V(kFunctionWithIllegalRedeclaration, "Function with illegal redeclaration") \
V(kGeneratedCodeIsTooLarge, "Generated code is too large") \
OptimizedCompileJob::Status OptimizedCompileJob::CreateGraph() {
DCHECK(info()->IsOptimizing());
- DCHECK(!info()->IsCompilingForDebugging());
// Do not use Crankshaft/TurboFan if we need to be able to set break points.
- if (isolate()->debug()->has_break_points()) {
- return RetryOptimization(kDebuggerHasBreakPoints);
+ if (info()->shared_info()->HasDebugInfo()) {
+ return AbortOptimization(kFunctionBeingDebugged);
}
// Limit the number of times we try to optimize functions.
}
-MaybeHandle<Code> Compiler::GetUnoptimizedCode(
- Handle<SharedFunctionInfo> shared) {
- DCHECK(!shared->GetIsolate()->has_pending_exception());
- DCHECK(!shared->is_compiled());
-
- Zone zone;
- ParseInfo parse_info(&zone, shared);
- CompilationInfo info(&parse_info);
- return GetUnoptimizedCodeCommon(&info);
-}
-
-
bool Compiler::EnsureCompiled(Handle<JSFunction> function,
ClearExceptionFlag flag) {
if (function->is_compiled()) return true;
}
-// Compile full code for debugging. This code will have debug break slots
-// and deoptimization information. Deoptimization information is required
-// in case that an optimized version of this function is still activated on
-// the stack. It will also make sure that the full code is compiled with
-// the same flags as the previous version, that is flags which can change
-// the code generated. The current method of mapping from already compiled
-// full code without debug break slots to full code with debug break slots
-// depends on the generated code is otherwise exactly the same.
-// If compilation fails, just keep the existing code.
-MaybeHandle<Code> Compiler::GetDebugCode(Handle<JSFunction> function) {
- CompilationInfoWithZone info(function);
- Isolate* isolate = info.isolate();
- VMState<COMPILER> state(isolate);
-
- info.MarkAsDebug();
-
- DCHECK(!isolate->has_pending_exception());
- Handle<Code> old_code(function->shared()->code());
- DCHECK(old_code->kind() == Code::FUNCTION);
- DCHECK(!old_code->has_debug_break_slots());
-
- info.MarkCompilingForDebugging();
- if (old_code->is_compiled_optimizable()) {
- info.EnableDeoptimizationSupport();
- } else {
- info.MarkNonOptimizable();
+MaybeHandle<Code> CompileForDebugging(CompilationInfo* info) {
+ info->MarkAsDebug();
+ VMState<COMPILER> state(info->isolate());
+ if (info->shared_info()->is_compiled()) {
+ if (info->shared_info()->code()->is_compiled_optimizable()) {
+ info->EnableDeoptimizationSupport();
+ } else {
+ info->MarkNonOptimizable();
+ }
}
- MaybeHandle<Code> maybe_new_code = GetUnoptimizedCodeCommon(&info);
+ MaybeHandle<Code> maybe_new_code = GetUnoptimizedCodeCommon(info);
Handle<Code> new_code;
if (!maybe_new_code.ToHandle(&new_code)) {
- isolate->clear_pending_exception();
- } else {
- DCHECK_EQ(old_code->is_compiled_optimizable(),
- new_code->is_compiled_optimizable());
+ info->isolate()->clear_pending_exception();
}
return maybe_new_code;
}
+MaybeHandle<Code> Compiler::GetDebugCode(Handle<JSFunction> function) {
+ CompilationInfoWithZone info(function);
+ VMState<COMPILER> state(info.isolate());
+ return CompileForDebugging(&info);
+}
+
+
+MaybeHandle<Code> Compiler::GetDebugCode(Handle<SharedFunctionInfo> shared) {
+ DCHECK(shared->allows_lazy_compilation_without_context());
+ Zone zone;
+ ParseInfo parse_info(&zone, shared);
+ CompilationInfo info(&parse_info);
+ return CompileForDebugging(&info);
+}
+
+
void Compiler::CompileForLiveEdit(Handle<Script> script) {
// TODO(635): support extensions.
Zone zone;
{ VMState<COMPILER> state(info->isolate());
if (parse_info->literal() == NULL) {
// Parse the script if needed (if it's already parsed, function() is
- // non-NULL).
+ // non-NULL). If compiling for debugging, we may eagerly compile inner
+ // functions, so do not parse lazily in that case.
ScriptCompiler::CompileOptions options = parse_info->compile_options();
bool parse_allow_lazy = (options == ScriptCompiler::kConsumeParserCache ||
String::cast(script->source())->length() >
FLAG_min_preparse_length) &&
- !Compiler::DebuggerWantsEagerCompilation(isolate);
+ !info->is_debug();
parse_info->set_allow_lazy_parsing(parse_allow_lazy);
if (!parse_allow_lazy &&
parse_info.set_parse_restriction(restriction);
parse_info.set_context(context);
+ // If we eval from debug code, compile for debugging as well.
+ if (outer_info->HasDebugCode()) info.MarkAsDebug();
Debug::RecordEvalCaller(script);
shared_info = CompileToplevel(&info);
// We found an existing shared function info. If it's already compiled,
// don't worry about compiling it, and simply return it. If it's not yet
// compiled, continue to decide whether to eagerly compile.
+ // Carry on if we are compiling eager to obtain code for debugging,
+ // unless we already have code with debut break slots.
Handle<SharedFunctionInfo> existing;
if (maybe_existing.ToHandle(&existing) && existing->is_compiled()) {
- return existing;
+ if (!outer_info->is_debug() || existing->HasDebugCode()) {
+ return existing;
+ }
}
Zone zone;
parse_info.set_language_mode(literal->scope()->language_mode());
if (outer_info->will_serialize()) info.PrepareForSerializing();
if (outer_info->is_first_compile()) info.MarkAsFirstCompile();
+ if (outer_info->is_debug()) info.MarkAsDebug();
LiveEditFunctionTracker live_edit_tracker(isolate, literal);
// Determine if the function can be lazily compiled. This is necessary to
// of functions without an outer context when setting a breakpoint through
// Debug::FindSharedFunctionInfoInScript.
bool allow_lazy_without_ctx = literal->AllowsLazyCompilationWithoutContext();
- bool allow_lazy =
- literal->AllowsLazyCompilation() &&
- !DebuggerWantsEagerCompilation(isolate, allow_lazy_without_ctx);
+ // Compile eagerly for live edit. When compiling debug code, eagerly compile
+ // unless we can lazily compile without the context.
+ bool allow_lazy = literal->AllowsLazyCompilation() &&
+ !LiveEditFunctionTracker::IsActive(isolate) &&
+ (!info.is_debug() || allow_lazy_without_ctx);
if (outer_info->parse_info()->is_toplevel() && outer_info->will_serialize()) {
// Make sure that if the toplevel code (possibly to be serialized),
live_edit_tracker.RecordFunctionInfo(result, literal, info.zone());
return result;
} else if (!lazy) {
- // We have additional data from compilation now.
- DCHECK(!existing->is_compiled());
+ // Assert that we are not overwriting (possibly patched) debug code.
+ DCHECK(!existing->HasDebugCode());
existing->ReplaceCode(*info.code());
existing->set_scope_info(*scope_info);
existing->set_feedback_vector(*info.feedback_vector());
ConcurrencyMode mode,
BailoutId osr_ast_id,
JavaScriptFrame* osr_frame) {
+ Isolate* isolate = function->GetIsolate();
+ Handle<SharedFunctionInfo> shared(function->shared(), isolate);
+ if (shared->HasDebugInfo()) return MaybeHandle<Code>();
+
Handle<Code> cached_code;
if (GetCodeFromOptimizedCodeMap(
function, osr_ast_id).ToHandle(&cached_code)) {
return cached_code;
}
- Isolate* isolate = function->GetIsolate();
DCHECK(AllowCompilation::IsAllowed(isolate));
- Handle<SharedFunctionInfo> shared(function->shared(), isolate);
if (!shared->is_compiled() ||
shared->scope_info() == ScopeInfo::Empty(isolate)) {
// The function was never compiled. Compile it unoptimized first.
Handle<SharedFunctionInfo> shared = info->shared_info();
shared->code()->set_profiler_ticks(0);
+ DCHECK(!shared->HasDebugInfo());
+
// 1) Optimization on the concurrent thread may have failed.
// 2) The function may have already been optimized by OSR. Simply continue.
// Except when OSR already disabled optimization for some reason.
// 3) The code may have already been invalidated due to dependency change.
- // 4) Debugger may have been activated.
- // 5) Code generation may have failed.
+ // 4) Code generation may have failed.
if (job->last_status() == OptimizedCompileJob::SUCCEEDED) {
if (shared->optimization_disabled()) {
job->RetryOptimization(kOptimizationDisabled);
} else if (info->dependencies()->HasAborted()) {
job->RetryOptimization(kBailedOutDueToDependencyChange);
- } else if (isolate->debug()->has_break_points()) {
- job->RetryOptimization(kDebuggerHasBreakPoints);
} else if (job->GenerateCode() == OptimizedCompileJob::SUCCEEDED) {
RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info.get(), shared);
if (shared->SearchOptimizedCodeMap(info->context()->native_context(),
}
-bool Compiler::DebuggerWantsEagerCompilation(Isolate* isolate,
- bool allow_lazy_without_ctx) {
- if (LiveEditFunctionTracker::IsActive(isolate)) return true;
- Debug* debug = isolate->debug();
- bool debugging = debug->is_active() || debug->has_break_points();
- return debugging && !allow_lazy_without_ctx;
-}
-
-
CompilationPhase::CompilationPhase(const char* name, CompilationInfo* info)
: name_(name), info_(info) {
if (FLAG_hydrogen_stats) {
kMustNotHaveEagerFrame = 1 << 4,
kDeoptimizationSupport = 1 << 5,
kDebug = 1 << 6,
- kCompilingForDebugging = 1 << 7,
- kSerializing = 1 << 8,
- kContextSpecializing = 1 << 9,
- kFrameSpecializing = 1 << 10,
- kInliningEnabled = 1 << 11,
- kTypingEnabled = 1 << 12,
- kDisableFutureOptimization = 1 << 13,
- kSplittingEnabled = 1 << 14,
- kTypeFeedbackEnabled = 1 << 15,
- kDeoptimizationEnabled = 1 << 16,
- kSourcePositionsEnabled = 1 << 17,
- kFirstCompile = 1 << 18,
+ kSerializing = 1 << 7,
+ kContextSpecializing = 1 << 8,
+ kFrameSpecializing = 1 << 9,
+ kInliningEnabled = 1 << 10,
+ kTypingEnabled = 1 << 11,
+ kDisableFutureOptimization = 1 << 12,
+ kSplittingEnabled = 1 << 13,
+ kTypeFeedbackEnabled = 1 << 14,
+ kDeoptimizationEnabled = 1 << 15,
+ kSourcePositionsEnabled = 1 << 16,
+ kFirstCompile = 1 << 17,
};
explicit CompilationInfo(ParseInfo* parse_info);
return GetFlag(kMustNotHaveEagerFrame);
}
+ // Compiles marked as debug produce unoptimized code with debug break slots.
+ // Inner functions that cannot be compiled w/o context are compiled eagerly.
void MarkAsDebug() { SetFlag(kDebug); }
bool is_debug() const { return GetFlag(kDebug); }
}
void SetCode(Handle<Code> code) { code_ = code; }
- void MarkCompilingForDebugging() { SetFlag(kCompilingForDebugging); }
- bool IsCompilingForDebugging() { return GetFlag(kCompilingForDebugging); }
void MarkNonOptimizable() {
SetMode(CompilationInfo::NONOPT);
}
Handle<JSFunction> function);
MUST_USE_RESULT static MaybeHandle<Code> GetLazyCode(
Handle<JSFunction> function);
- MUST_USE_RESULT static MaybeHandle<Code> GetUnoptimizedCode(
- Handle<SharedFunctionInfo> shared);
+
MUST_USE_RESULT static MaybeHandle<Code> GetDebugCode(
Handle<JSFunction> function);
+ MUST_USE_RESULT static MaybeHandle<Code> GetDebugCode(
+ Handle<SharedFunctionInfo> shared);
// Parser::Parse, then Compiler::Analyze.
static bool ParseAndAnalyze(ParseInfo* info);
// Generate and return code from previously queued optimization job.
// On failure, return the empty handle.
static Handle<Code> GetConcurrentlyOptimizedCode(OptimizedCompileJob* job);
-
- // TODO(titzer): move this method out of the compiler.
- static bool DebuggerWantsEagerCompilation(
- Isolate* isolate, bool allow_lazy_without_ctx = false);
};
return NoChange();
}
+ if (function->shared()->HasDebugInfo()) {
+ // Function contains break points.
+ TRACE("Not inlining %s into %s because callee may contain break points\n",
+ function->shared()->DebugName()->ToCString().get(),
+ info_->shared_info()->DebugName()->ToCString().get());
+ return NoChange();
+ }
+
// Disallow cross native-context inlining for now. This means that all parts
// of the resulting code will operate on the same global object.
// This also prevents cross context leaks for asm.js code, where we could
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),
// 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();
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);
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;
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.
// 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
}
-// 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)) {
return true;
}
- Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
// Source positions starts with zero.
DCHECK(*source_position >= 0);
BreakPositionAlignment alignment) {
HandleScope scope(isolate_);
- PrepareForBreakPoints();
-
// Obtain shared function info for the function.
Handle<Object> result =
FindSharedFunctionInfoInScript(script, *source_position);
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);
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)) {
}
// 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();
}
}
}
+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.
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.
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) {
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);
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);
}
}
-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;
// 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;
}
-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;
}
};
-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) {
}
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;
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();
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.
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;
}
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.
// 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;
}
+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
void HandleStepIn(Handle<Object> function_obj, bool is_constructor);
bool StepOutActive() { return thread_local_.step_out_fp_ != 0; }
- // Purge all code objects that have no debug break slots.
- void PrepareForBreakPoints();
+ void GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id,
+ List<int>* results_out);
+
+ bool PrepareFunctionForBreakPoints(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
bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
Handle<JSFunction> function);
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
- static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
template <typename C>
bool CompileToRevealInnerFunctions(C* compilable);
break_id() == id;
}
+ bool RequiresEagerCompilation(bool allows_lazy_without_ctx = false) {
+ return LiveEditFunctionTracker::IsActive(isolate_) ||
+ (is_active() && !allows_lazy_without_ctx);
+ }
+
// Flags and states.
DebugScope* debugger_entry() {
return reinterpret_cast<DebugScope*>(
inline bool is_active() const { return is_active_; }
inline bool is_loaded() const { return !debug_context_.is_null(); }
- inline bool has_break_points() const { return has_break_points_; }
inline bool in_debug_scope() const {
return !!base::NoBarrier_Load(&thread_local_.current_debug_scope_);
}
void MarkCompactCollector::EnableCodeFlushing(bool enable) {
- if (isolate()->debug()->is_loaded() ||
- isolate()->debug()->has_break_points()) {
- enable = false;
- }
+ if (isolate()->debug()->is_active()) enable = false;
if (enable) {
if (code_flusher_ != NULL) return;
CompilationInfo target_info(&parse_info);
Handle<SharedFunctionInfo> target_shared(target->shared());
+ if (target_shared->HasDebugInfo()) {
+ TraceInline(target, caller, "target is being debugged");
+ return false;
+ }
if (!Compiler::ParseAndAnalyze(target_info.parse_info())) {
if (target_info.isolate()->has_pending_exception()) {
// Parse or scope error, never optimize this function.
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
} else if (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence() &&
- isolate->debug()->has_break_points()) {
+ IsPatchedDebugBreakSlotSequence()) {
visitor->VisitDebugTarget(this);
} else if (IsRuntimeEntry(mode)) {
visitor->VisitRuntimeEntry(this);
StaticVisitor::VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
- } else if (heap->isolate()->debug()->has_break_points() &&
- RelocInfo::IsDebugBreakSlot(mode) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (IsRuntimeEntry(mode)) {
if (!getter->IsJSFunction()) break;
if (!holder->HasFastProperties()) break;
// When debugging we need to go the slow path to flood the accessor.
- if (!GetSharedFunctionInfo()->debug_info()->IsUndefined()) break;
+ if (GetSharedFunctionInfo()->HasDebugInfo()) break;
Handle<JSFunction> function = Handle<JSFunction>::cast(getter);
if (!receiver->IsJSObject() && !function->IsBuiltin() &&
is_sloppy(function->shared()->language_mode())) {
break;
}
// When debugging we need to go the slow path to flood the accessor.
- if (!GetSharedFunctionInfo()->debug_info()->IsUndefined()) break;
+ if (GetSharedFunctionInfo()->HasDebugInfo()) break;
Handle<JSFunction> function = Handle<JSFunction>::cast(setter);
CallOptimization call_optimization(function);
NamedStoreHandlerCompiler compiler(isolate(), receiver_map(), holder);
};
-namespace {
-
-// Check whether the code is natural function code (not a lazy-compile stub
-// code).
-bool IsJSFunctionCode(Code* code) { return code->kind() == Code::FUNCTION; }
-
-
-// Returns true if an instance of candidate were inlined into function's code.
-bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) {
- DisallowHeapAllocation no_gc;
-
- if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
-
- DeoptimizationInputData* const data =
- DeoptimizationInputData::cast(function->code()->deoptimization_data());
- if (data != function->GetIsolate()->heap()->empty_fixed_array()) {
- FixedArray* const literals = data->LiteralArray();
- int const inlined_count = data->InlinedFunctionCount()->value();
- for (int i = 0; i < inlined_count; ++i) {
- if (SharedFunctionInfo::cast(literals->get(i)) == candidate) {
- return true;
- }
- }
- }
-
- return false;
-}
-
-} // namespace
-
-
// Marks code that shares the same shared function info or has inlined
// code that shares the same function info.
class DependentFunctionMarker: public OptimizedFunctionVisitor {
virtual void VisitFunction(JSFunction* function) {
// It should be guaranteed by the iterator that everything is optimized.
DCHECK(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
- if (shared_info_ == function->shared() ||
- IsInlined(function, shared_info_)) {
+ if (function->Inlines(shared_info_)) {
// Mark the code for deoptimization.
function->code()->set_marked_for_deoptimization(true);
found_ = true;
Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
- if (IsJSFunctionCode(shared_info->code())) {
+ if (shared_info->code()->kind() == Code::FUNCTION) {
Handle<Code> code = compile_info_wrapper.GetFunctionCode();
ReplaceCodeObject(Handle<Code>(shared_info->code()), code);
Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
info->set_end_position(new_function_end);
info->set_function_token_position(new_function_token_pos);
- if (IsJSFunctionCode(info->code())) {
+ if (info->code()->kind() == Code::FUNCTION) {
// Patch relocation info section of the code.
Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
position_change_array);
Handle<SharedFunctionInfo> shared =
UnwrapSharedFunctionInfoFromJSValue(jsvalue);
- if (function->shared() == *shared || IsInlined(*function, *shared)) {
+ if (function->Inlines(*shared)) {
SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
return true;
}
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
} else if (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence() &&
- isolate->debug()->has_break_points()) {
+ IsPatchedDebugBreakSlotSequence()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
visitor->VisitRuntimeEntry(this);
StaticVisitor::VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
- } else if (heap->isolate()->debug()->has_break_points() &&
- RelocInfo::IsDebugBreakSlot(mode) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
} else if (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence() &&
- isolate->debug()->has_break_points()) {
+ IsPatchedDebugBreakSlotSequence()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
visitor->VisitRuntimeEntry(this);
StaticVisitor::VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
- } else if (heap->isolate()->debug()->has_break_points() &&
- RelocInfo::IsDebugBreakSlot(mode) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
}
+bool SharedFunctionInfo::HasDebugInfo() {
+ bool has_debug_info = debug_info()->IsStruct();
+ DCHECK(!has_debug_info || HasDebugCode());
+ return has_debug_info;
+}
+
+
+DebugInfo* SharedFunctionInfo::GetDebugInfo() {
+ DCHECK(HasDebugInfo());
+ return DebugInfo::cast(debug_info());
+}
+
+
+bool SharedFunctionInfo::HasDebugCode() {
+ return code()->kind() == Code::FUNCTION && code()->has_debug_break_slots();
+}
+
+
bool SharedFunctionInfo::IsApiFunction() {
return function_data()->IsFunctionTemplateInfo();
}
}
+bool JSFunction::Inlines(SharedFunctionInfo* candidate) {
+ DisallowHeapAllocation no_gc;
+ if (shared() == candidate) return true;
+ if (code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
+ DeoptimizationInputData* const data =
+ DeoptimizationInputData::cast(code()->deoptimization_data());
+ if (data->length() == 0) return false;
+ FixedArray* const literals = data->LiteralArray();
+ int const inlined_count = data->InlinedFunctionCount()->value();
+ for (int i = 0; i < inlined_count; ++i) {
+ if (SharedFunctionInfo::cast(literals->get(i)) == candidate) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
void JSFunction::MarkForOptimization() {
Isolate* isolate = GetIsolate();
+ // Do not optimize if function contains break points.
+ if (shared()->HasDebugInfo()) return;
DCHECK(!IsOptimized());
DCHECK(shared()->allows_lazy_compilation() ||
!shared()->optimization_disabled());
+ DCHECK(!shared()->HasDebugInfo());
set_code_no_write_barrier(
isolate->builtins()->builtin(Builtins::kCompileOptimized));
// No write barrier required, since the builtin is part of the root set.
inline int start_position_and_type() const;
inline void set_start_position_and_type(int value);
+ // The function is subject to debugging if a debug info is attached.
+ inline bool HasDebugInfo();
+ inline DebugInfo* GetDebugInfo();
+
+ // A function has debug code if the compiled code has debug break slots.
+ inline bool HasDebugCode();
+
// [debug info]: Debug information.
DECL_ACCESSORS(debug_info, Object)
// Tells whether this function is builtin.
inline bool IsBuiltin();
+ // Tells whether this function inlines the given shared function info.
+ bool Inlines(SharedFunctionInfo* candidate);
+
// Tells whether this function should be subject to debugging.
inline bool IsSubjectToDebugging();
// 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 && !shared_info->debug_info()->IsUndefined()) {
+ if (!ignore_nested_scopes && shared_info->HasDebugInfo()) {
// The source position at return is always the end of the function,
// which is not consistent with the current scope chain. Therefore all
// nested with, catch and block contexts are skipped, and we can only
// inspect the function scope.
// This can only happen if we set a break point inside right before the
// return, which requires a debug info to be available.
- Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info);
+ Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
// PC points to the instruction after the current one, possibly a break
// location as well. So the "- 1" to exclude it from the search.
JavaScriptFrameIterator frame_it(isolate, id);
RUNTIME_ASSERT(!frame_it.done());
- List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
- frame_it.frame()->Summarize(&frames);
- FrameSummary summary = frames.first();
-
- Handle<JSFunction> fun = Handle<JSFunction>(summary.function());
- Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared());
-
- if (!isolate->debug()->EnsureDebugInfo(shared, fun)) {
- return isolate->heap()->undefined_value();
- }
-
- Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared);
-
- // 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);
-
- Handle<JSArray> array = isolate->factory()->NewJSArray(locations.length());
-
- int index = 0;
- for (BreakLocation location : locations) {
- bool accept;
- if (location.pc() > summary.pc()) {
- accept = true;
- } else {
- StackFrame::Id break_frame_id = isolate->debug()->break_frame_id();
- // 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) {
- // We are not stepping.
- accept = false;
- } else {
- JavaScriptFrameIterator additional_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.
- accept = additional_frame_it.frame()->id() == id;
- }
- }
- if (accept) {
- if (location.IsStepInLocation()) {
- Smi* position_value = Smi::FromInt(location.position());
- RETURN_FAILURE_ON_EXCEPTION(
- isolate,
- Object::SetElement(isolate, array, index,
- handle(position_value, isolate), SLOPPY));
- index++;
- }
- }
+ List<int> positions;
+ isolate->debug()->GetStepinPositions(frame_it.frame(), id, &positions);
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> array = factory->NewFixedArray(positions.length());
+ for (int i = 0; i < positions.length(); ++i) {
+ array->set(i, Smi::FromInt(positions[i]));
}
- return *array;
+ return *factory->NewJSArrayWithElements(array, FAST_SMI_ELEMENTS);
}
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
} else if (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence() &&
- isolate->debug()->has_break_points()) {
+ IsPatchedDebugBreakSlotSequence()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
visitor->VisitRuntimeEntry(this);
StaticVisitor::VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
- } else if (heap->isolate()->debug()->has_break_points() &&
- RelocInfo::IsDebugBreakSlot(mode) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
static bool HasDebugInfo(v8::Handle<v8::Function> fun) {
Handle<v8::internal::JSFunction> f = v8::Utils::OpenHandle(*fun);
Handle<v8::internal::SharedFunctionInfo> shared(f->shared());
- return Debug::HasDebugInfo(shared);
+ return shared->HasDebugInfo();
}
CHECK_NE(0, func_pos);
// Obtain SharedFunctionInfo for the function.
- isolate->debug()->PrepareForBreakPoints();
Handle<SharedFunctionInfo> shared_func_info =
Handle<SharedFunctionInfo>::cast(
isolate->debug()->FindSharedFunctionInfoInScript(i_script, func_pos));
function g() {
var a = 2;
f();
+ return a; // Use the value to prevent it being removed by DCE.
};
a = 1;
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --expose-debug-as debug --allow-natives-syntax --use-inlining
+
+var Debug = debug.Debug;
+
+function f1() {
+ return 1;
+}
+
+function f2() {
+ return 2;
+}
+
+function f3() {
+ return f1();
+}
+
+function f4() {
+ return 4;
+}
+
+
+function optimize(f) {
+ f();
+ f();
+ %OptimizeFunctionOnNextCall(f);
+ f();
+}
+
+optimize(f1);
+optimize(f2);
+optimize(f3);
+
+Debug.setListener(function() {});
+
+assertOptimized(f1);
+assertOptimized(f2);
+assertOptimized(f3);
+
+Debug.setBreakPoint(f1, 1);
+
+// Setting break point deoptimizes f1 and f3 (which inlines f1).
+assertUnoptimized(f1);
+assertOptimized(f2);
+assertUnoptimized(f3);
+
+// We can optimize with break points set.
+optimize(f4);
+assertOptimized(f4);
+
+Debug.setListener(null);