Index: src/debug/debug.cc |
diff --git a/src/debug/debug.cc b/src/debug/debug.cc |
index bbeefff486d34b5923b41f8f5f831fd278d6924f..e23f0f172380756bfa55eaf4a5310ae865121c7d 100644 |
--- a/src/debug/debug.cc |
+++ b/src/debug/debug.cc |
@@ -14,6 +14,7 @@ |
#include "src/compilation-cache.h" |
#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" |
#include "src/compiler.h" |
+#include "src/debug/debug-evaluate.h" |
#include "src/debug/liveedit.h" |
#include "src/deoptimizer.h" |
#include "src/execution.h" |
@@ -42,6 +43,7 @@ Debug::Debug(Isolate* isolate) |
command_received_(0), |
command_queue_(isolate->logger(), kQueueInitialSize), |
is_active_(false), |
+ hook_on_function_call_(false), |
is_suppressed_(false), |
live_edit_enabled_(true), // TODO(yangguo): set to false by default. |
break_disabled_(false), |
@@ -49,6 +51,7 @@ Debug::Debug(Isolate* isolate) |
in_debug_event_listener_(false), |
break_on_exception_(false), |
break_on_uncaught_exception_(false), |
+ side_effect_check_failed_(false), |
debug_info_list_(NULL), |
feature_tracker_(isolate), |
isolate_(isolate) { |
@@ -406,6 +409,7 @@ void Debug::ThreadInit() { |
// TODO(isolates): frames_are_dropped_? |
base::NoBarrier_Store(&thread_local_.current_debug_scope_, |
static_cast<base::AtomicWord>(0)); |
+ UpdateHookOnFunctionCall(); |
} |
@@ -905,16 +909,19 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { |
void Debug::PrepareStepIn(Handle<JSFunction> function) { |
CHECK(last_step_action() >= StepIn); |
- if (!is_active()) return; |
+ if (ignore_events()) return; |
if (in_debug_scope()) return; |
+ if (break_disabled()) return; |
FloodWithOneShot(function); |
} |
void Debug::PrepareStepInSuspendedGenerator() { |
CHECK(has_suspended_generator()); |
- if (!is_active()) return; |
+ if (ignore_events()) return; |
if (in_debug_scope()) return; |
+ if (break_disabled()) return; |
thread_local_.last_step_action_ = StepIn; |
+ UpdateHookOnFunctionCall(); |
Handle<JSFunction> function( |
JSGeneratorObject::cast(thread_local_.suspended_generator_)->function()); |
FloodWithOneShot(function); |
@@ -922,9 +929,10 @@ void Debug::PrepareStepInSuspendedGenerator() { |
} |
void Debug::PrepareStepOnThrow() { |
- if (!is_active()) return; |
if (last_step_action() == StepNone) return; |
+ if (ignore_events()) return; |
if (in_debug_scope()) return; |
+ if (break_disabled()) return; |
ClearOneShot(); |
@@ -975,6 +983,7 @@ void Debug::PrepareStep(StepAction step_action) { |
feature_tracker()->Track(DebugFeatureTracker::kStepping); |
thread_local_.last_step_action_ = step_action; |
+ UpdateHookOnFunctionCall(); |
// If the function on the top frame is unresolved perform step out. This will |
// be the case when calling unknown function and having the debugger stopped |
@@ -1105,6 +1114,7 @@ void Debug::ClearStepping() { |
thread_local_.last_statement_position_ = kNoSourcePosition; |
thread_local_.last_fp_ = 0; |
thread_local_.target_fp_ = 0; |
+ UpdateHookOnFunctionCall(); |
} |
@@ -2135,6 +2145,13 @@ void Debug::UpdateState() { |
is_active_ = is_active; |
} |
+void Debug::UpdateHookOnFunctionCall() { |
+ STATIC_ASSERT(StepFrame > StepIn); |
+ STATIC_ASSERT(LastStepAction == StepFrame); |
+ hook_on_function_call_ = thread_local_.last_step_action_ >= StepIn || |
+ isolate_->needs_side_effect_check(); |
+} |
+ |
// Calls the registered debug message handler. This callback is part of the |
// public API. |
void Debug::InvokeMessageHandler(MessageImpl message) { |
@@ -2332,6 +2349,50 @@ DebugScope::~DebugScope() { |
debug_->UpdateState(); |
} |
+bool Debug::PerformSideEffectCheck(Handle<JSFunction> function) { |
+ DCHECK(isolate_->needs_side_effect_check()); |
+ DisallowJavascriptExecution no_js(isolate_); |
+ if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) return false; |
+ Deoptimizer::DeoptimizeFunction(*function); |
+ if (!function->shared()->HasNoSideEffect()) { |
+ if (FLAG_trace_side_effect_free_debug_evaluate) { |
+ PrintF("[debug-evaluate] Function %s failed side effect check.\n", |
+ function->shared()->DebugName()->ToCString().get()); |
+ } |
+ side_effect_check_failed_ = true; |
+ // Throw an uncatchable termination exception. |
+ isolate_->TerminateExecution(); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+bool Debug::PerformSideEffectCheckForCallback(Address function) { |
+ DCHECK(isolate_->needs_side_effect_check()); |
+ if (DebugEvaluate::CallbackHasNoSideEffect(function)) return true; |
+ side_effect_check_failed_ = true; |
+ // Throw an uncatchable termination exception. |
+ isolate_->TerminateExecution(); |
+ isolate_->OptionalRescheduleException(false); |
+ return false; |
+} |
+ |
+NoSideEffectScope::~NoSideEffectScope() { |
+ if (isolate_->needs_side_effect_check() && |
+ isolate_->debug()->side_effect_check_failed_) { |
+ DCHECK(isolate_->has_pending_exception()); |
+ DCHECK_EQ(isolate_->heap()->termination_exception(), |
+ isolate_->pending_exception()); |
+ // Convert the termination exception into a regular exception. |
+ isolate_->CancelTerminateExecution(); |
+ isolate_->Throw(*isolate_->factory()->NewEvalError( |
+ MessageTemplate::kNoSideEffectDebugEvaluate)); |
+ } |
+ isolate_->set_needs_side_effect_check(old_needs_side_effect_check_); |
+ isolate_->debug()->UpdateHookOnFunctionCall(); |
+ isolate_->debug()->side_effect_check_failed_ = false; |
+} |
+ |
MessageImpl MessageImpl::NewEvent(DebugEvent event, bool running, |
Handle<JSObject> exec_state, |
Handle<JSObject> event_data) { |