1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/debug/debug.h"
6
7 #include <memory>
8
9 #include "src/api.h"
10 #include "src/arguments.h"
11 #include "src/assembler-inl.h"
12 #include "src/bootstrapper.h"
13 #include "src/code-stubs.h"
14 #include "src/codegen.h"
15 #include "src/compilation-cache.h"
16 #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
17 #include "src/compiler.h"
18 #include "src/debug/debug-evaluate.h"
19 #include "src/debug/liveedit.h"
20 #include "src/deoptimizer.h"
21 #include "src/execution.h"
22 #include "src/frames-inl.h"
23 #include "src/full-codegen/full-codegen.h"
24 #include "src/global-handles.h"
25 #include "src/globals.h"
26 #include "src/interpreter/interpreter.h"
27 #include "src/isolate-inl.h"
28 #include "src/list.h"
29 #include "src/log.h"
30 #include "src/messages.h"
31 #include "src/snapshot/natives.h"
32 #include "src/wasm/wasm-module.h"
33 #include "src/wasm/wasm-objects.h"
34
35 #include "include/v8-debug.h"
36
37 namespace v8 {
38 namespace internal {
39
Debug(Isolate * isolate)40 Debug::Debug(Isolate* isolate)
41 : debug_context_(Handle<Context>()),
42 is_active_(false),
43 hook_on_function_call_(false),
44 is_suppressed_(false),
45 live_edit_enabled_(true), // TODO(yangguo): set to false by default.
46 break_disabled_(false),
47 break_points_active_(true),
48 break_on_exception_(false),
49 break_on_uncaught_exception_(false),
50 side_effect_check_failed_(false),
51 debug_info_list_(NULL),
52 feature_tracker_(isolate),
53 isolate_(isolate) {
54 ThreadInit();
55 }
56
FromFrame(Handle<DebugInfo> debug_info,JavaScriptFrame * frame)57 BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
58 JavaScriptFrame* frame) {
59 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
60 int offset = summary.code_offset();
61 Handle<AbstractCode> abstract_code = summary.abstract_code();
62 if (abstract_code->IsCode()) offset = offset - 1;
63 auto it = BreakIterator::GetIterator(debug_info, abstract_code);
64 it->SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
65 return it->GetBreakLocation();
66 }
67
AllAtCurrentStatement(Handle<DebugInfo> debug_info,JavaScriptFrame * frame,List<BreakLocation> * result_out)68 void BreakLocation::AllAtCurrentStatement(Handle<DebugInfo> debug_info,
69 JavaScriptFrame* frame,
70 List<BreakLocation>* result_out) {
71 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
72 int offset = summary.code_offset();
73 Handle<AbstractCode> abstract_code = summary.abstract_code();
74 if (abstract_code->IsCode()) offset = offset - 1;
75 int statement_position;
76 {
77 auto it = BreakIterator::GetIterator(debug_info, abstract_code);
78 it->SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
79 statement_position = it->statement_position();
80 }
81 for (auto it = BreakIterator::GetIterator(debug_info, abstract_code);
82 !it->Done(); it->Next()) {
83 if (it->statement_position() == statement_position) {
84 result_out->Add(it->GetBreakLocation());
85 }
86 }
87 }
88
BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,Handle<AbstractCode> abstract_code,int offset)89 int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,
90 Handle<AbstractCode> abstract_code,
91 int offset) {
92 // Run through all break points to locate the one closest to the address.
93 int closest_break = 0;
94 int distance = kMaxInt;
95 DCHECK(0 <= offset && offset < abstract_code->Size());
96 for (auto it = BreakIterator::GetIterator(debug_info, abstract_code);
97 !it->Done(); it->Next()) {
98 // Check if this break point is closer that what was previously found.
99 if (it->code_offset() <= offset && offset - it->code_offset() < distance) {
100 closest_break = it->break_index();
101 distance = offset - it->code_offset();
102 // Check whether we can't get any closer.
103 if (distance == 0) break;
104 }
105 }
106 return closest_break;
107 }
108
HasBreakPoint(Handle<DebugInfo> debug_info) const109 bool BreakLocation::HasBreakPoint(Handle<DebugInfo> debug_info) const {
110 // First check whether there is a break point with the same source position.
111 if (!debug_info->HasBreakPoint(position_)) return false;
112 // Then check whether a break point at that source position would have
113 // the same code offset. Otherwise it's just a break location that we can
114 // step to, but not actually a location where we can put a break point.
115 if (abstract_code_->IsCode()) {
116 DCHECK_EQ(debug_info->DebugCode(), abstract_code_->GetCode());
117 CodeBreakIterator it(debug_info);
118 it.SkipToPosition(position_, BREAK_POSITION_ALIGNED);
119 return it.code_offset() == code_offset_;
120 } else {
121 DCHECK(abstract_code_->IsBytecodeArray());
122 BytecodeArrayBreakIterator it(debug_info);
123 it.SkipToPosition(position_, BREAK_POSITION_ALIGNED);
124 return it.code_offset() == code_offset_;
125 }
126 }
127
GetIterator(Handle<DebugInfo> debug_info,Handle<AbstractCode> abstract_code)128 std::unique_ptr<BreakIterator> BreakIterator::GetIterator(
129 Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code) {
130 if (abstract_code->IsBytecodeArray()) {
131 DCHECK(debug_info->HasDebugBytecodeArray());
132 return std::unique_ptr<BreakIterator>(
133 new BytecodeArrayBreakIterator(debug_info));
134 } else {
135 DCHECK(abstract_code->IsCode());
136 DCHECK(debug_info->HasDebugCode());
137 return std::unique_ptr<BreakIterator>(new CodeBreakIterator(debug_info));
138 }
139 }
140
BreakIterator(Handle<DebugInfo> debug_info)141 BreakIterator::BreakIterator(Handle<DebugInfo> debug_info)
142 : debug_info_(debug_info), break_index_(-1) {
143 position_ = debug_info->shared()->start_position();
144 statement_position_ = position_;
145 }
146
BreakIndexFromPosition(int source_position,BreakPositionAlignment alignment)147 int BreakIterator::BreakIndexFromPosition(int source_position,
148 BreakPositionAlignment alignment) {
149 int distance = kMaxInt;
150 int closest_break = break_index();
151 while (!Done()) {
152 int next_position;
153 if (alignment == STATEMENT_ALIGNED) {
154 next_position = statement_position();
155 } else {
156 DCHECK(alignment == BREAK_POSITION_ALIGNED);
157 next_position = position();
158 }
159 if (source_position <= next_position &&
160 next_position - source_position < distance) {
161 closest_break = break_index();
162 distance = next_position - source_position;
163 // Check whether we can't get any closer.
164 if (distance == 0) break;
165 }
166 Next();
167 }
168 return closest_break;
169 }
170
CodeBreakIterator(Handle<DebugInfo> debug_info)171 CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info)
172 : BreakIterator(debug_info),
173 reloc_iterator_(debug_info->DebugCode(), GetModeMask()),
174 source_position_iterator_(
175 debug_info->DebugCode()->source_position_table()) {
176 // There is at least one break location.
177 DCHECK(!Done());
178 Next();
179 }
180
GetModeMask()181 int CodeBreakIterator::GetModeMask() {
182 int mask = 0;
183 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
184 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
185 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL);
186 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
187 return mask;
188 }
189
Next()190 void CodeBreakIterator::Next() {
191 DisallowHeapAllocation no_gc;
192 DCHECK(!Done());
193
194 // Iterate through reloc info stopping at each breakable code target.
195 bool first = break_index_ == -1;
196
197 if (!first) reloc_iterator_.next();
198 first = false;
199 if (Done()) return;
200
201 int offset = code_offset();
202 while (!source_position_iterator_.done() &&
203 source_position_iterator_.code_offset() <= offset) {
204 position_ = source_position_iterator_.source_position().ScriptOffset();
205 if (source_position_iterator_.is_statement()) {
206 statement_position_ = position_;
207 }
208 source_position_iterator_.Advance();
209 }
210
211 DCHECK(RelocInfo::IsDebugBreakSlot(rmode()));
212 break_index_++;
213 }
214
GetDebugBreakType()215 DebugBreakType CodeBreakIterator::GetDebugBreakType() {
216 if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) {
217 return DEBUG_BREAK_SLOT_AT_RETURN;
218 } else if (RelocInfo::IsDebugBreakSlotAtCall(rmode())) {
219 return DEBUG_BREAK_SLOT_AT_CALL;
220 } else if (RelocInfo::IsDebugBreakSlotAtTailCall(rmode())) {
221 return isolate()->is_tail_call_elimination_enabled()
222 ? DEBUG_BREAK_SLOT_AT_TAIL_CALL
223 : DEBUG_BREAK_SLOT_AT_CALL;
224 } else if (RelocInfo::IsDebugBreakSlot(rmode())) {
225 return DEBUG_BREAK_SLOT;
226 } else {
227 return NOT_DEBUG_BREAK;
228 }
229 }
230
SkipToPosition(int position,BreakPositionAlignment alignment)231 void CodeBreakIterator::SkipToPosition(int position,
232 BreakPositionAlignment alignment) {
233 CodeBreakIterator it(debug_info_);
234 SkipTo(it.BreakIndexFromPosition(position, alignment));
235 }
236
SetDebugBreak()237 void CodeBreakIterator::SetDebugBreak() {
238 DebugBreakType debug_break_type = GetDebugBreakType();
239 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
240 Builtins* builtins = isolate()->builtins();
241 Handle<Code> target = debug_break_type == DEBUG_BREAK_SLOT_AT_RETURN
242 ? builtins->Return_DebugBreak()
243 : builtins->Slot_DebugBreak();
244 DebugCodegen::PatchDebugBreakSlot(isolate(), rinfo()->pc(), target);
245 }
246
ClearDebugBreak()247 void CodeBreakIterator::ClearDebugBreak() {
248 DCHECK(GetDebugBreakType() >= DEBUG_BREAK_SLOT);
249 DebugCodegen::ClearDebugBreakSlot(isolate(), rinfo()->pc());
250 }
251
IsDebugBreak()252 bool CodeBreakIterator::IsDebugBreak() {
253 DCHECK(GetDebugBreakType() >= DEBUG_BREAK_SLOT);
254 return DebugCodegen::DebugBreakSlotIsPatched(rinfo()->pc());
255 }
256
GetBreakLocation()257 BreakLocation CodeBreakIterator::GetBreakLocation() {
258 Handle<AbstractCode> code(AbstractCode::cast(debug_info_->DebugCode()));
259 return BreakLocation(code, GetDebugBreakType(), code_offset(), position_);
260 }
261
BytecodeArrayBreakIterator(Handle<DebugInfo> debug_info)262 BytecodeArrayBreakIterator::BytecodeArrayBreakIterator(
263 Handle<DebugInfo> debug_info)
264 : BreakIterator(debug_info),
265 source_position_iterator_(
266 debug_info->DebugBytecodeArray()->source_position_table()) {
267 // There is at least one break location.
268 DCHECK(!Done());
269 Next();
270 }
271
Next()272 void BytecodeArrayBreakIterator::Next() {
273 DisallowHeapAllocation no_gc;
274 DCHECK(!Done());
275 bool first = break_index_ == -1;
276 while (!Done()) {
277 if (!first) source_position_iterator_.Advance();
278 first = false;
279 if (Done()) return;
280 position_ = source_position_iterator_.source_position().ScriptOffset();
281 if (source_position_iterator_.is_statement()) {
282 statement_position_ = position_;
283 }
284 DCHECK(position_ >= 0);
285 DCHECK(statement_position_ >= 0);
286
287 DebugBreakType type = GetDebugBreakType();
288 if (type != NOT_DEBUG_BREAK) break;
289 }
290 break_index_++;
291 }
292
GetDebugBreakType()293 DebugBreakType BytecodeArrayBreakIterator::GetDebugBreakType() {
294 BytecodeArray* bytecode_array = debug_info_->OriginalBytecodeArray();
295 interpreter::Bytecode bytecode =
296 interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
297
298 if (bytecode == interpreter::Bytecode::kDebugger) {
299 return DEBUGGER_STATEMENT;
300 } else if (bytecode == interpreter::Bytecode::kReturn) {
301 return DEBUG_BREAK_SLOT_AT_RETURN;
302 } else if (bytecode == interpreter::Bytecode::kTailCall) {
303 return isolate()->is_tail_call_elimination_enabled()
304 ? DEBUG_BREAK_SLOT_AT_TAIL_CALL
305 : DEBUG_BREAK_SLOT_AT_CALL;
306 } else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) {
307 return DEBUG_BREAK_SLOT_AT_CALL;
308 } else if (source_position_iterator_.is_statement()) {
309 return DEBUG_BREAK_SLOT;
310 } else {
311 return NOT_DEBUG_BREAK;
312 }
313 }
314
SkipToPosition(int position,BreakPositionAlignment alignment)315 void BytecodeArrayBreakIterator::SkipToPosition(
316 int position, BreakPositionAlignment alignment) {
317 BytecodeArrayBreakIterator it(debug_info_);
318 SkipTo(it.BreakIndexFromPosition(position, alignment));
319 }
320
SetDebugBreak()321 void BytecodeArrayBreakIterator::SetDebugBreak() {
322 DebugBreakType debug_break_type = GetDebugBreakType();
323 if (debug_break_type == DEBUGGER_STATEMENT) return;
324 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
325 BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
326 interpreter::Bytecode bytecode =
327 interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
328 if (interpreter::Bytecodes::IsDebugBreak(bytecode)) return;
329 interpreter::Bytecode debugbreak =
330 interpreter::Bytecodes::GetDebugBreak(bytecode);
331 bytecode_array->set(code_offset(),
332 interpreter::Bytecodes::ToByte(debugbreak));
333 }
334
ClearDebugBreak()335 void BytecodeArrayBreakIterator::ClearDebugBreak() {
336 DebugBreakType debug_break_type = GetDebugBreakType();
337 if (debug_break_type == DEBUGGER_STATEMENT) return;
338 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
339 BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
340 BytecodeArray* original = debug_info_->OriginalBytecodeArray();
341 bytecode_array->set(code_offset(), original->get(code_offset()));
342 }
343
IsDebugBreak()344 bool BytecodeArrayBreakIterator::IsDebugBreak() {
345 DebugBreakType debug_break_type = GetDebugBreakType();
346 if (debug_break_type == DEBUGGER_STATEMENT) return false;
347 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
348 BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
349 interpreter::Bytecode bytecode =
350 interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
351 return interpreter::Bytecodes::IsDebugBreak(bytecode);
352 }
353
GetBreakLocation()354 BreakLocation BytecodeArrayBreakIterator::GetBreakLocation() {
355 Handle<AbstractCode> code(
356 AbstractCode::cast(debug_info_->DebugBytecodeArray()));
357 return BreakLocation(code, GetDebugBreakType(), code_offset(), position_);
358 }
359
360
Track(DebugFeatureTracker::Feature feature)361 void DebugFeatureTracker::Track(DebugFeatureTracker::Feature feature) {
362 uint32_t mask = 1 << feature;
363 // Only count one sample per feature and isolate.
364 if (bitfield_ & mask) return;
365 isolate_->counters()->debug_feature_usage()->AddSample(feature);
366 bitfield_ |= mask;
367 }
368
369
370 // Threading support.
ThreadInit()371 void Debug::ThreadInit() {
372 thread_local_.break_count_ = 0;
373 thread_local_.break_id_ = 0;
374 thread_local_.break_frame_id_ = StackFrame::NO_ID;
375 thread_local_.last_step_action_ = StepNone;
376 thread_local_.last_statement_position_ = kNoSourcePosition;
377 thread_local_.last_frame_count_ = -1;
378 thread_local_.target_frame_count_ = -1;
379 thread_local_.return_value_ = Smi::kZero;
380 thread_local_.async_task_count_ = 0;
381 clear_suspended_generator();
382 thread_local_.restart_fp_ = nullptr;
383 base::NoBarrier_Store(&thread_local_.current_debug_scope_,
384 static_cast<base::AtomicWord>(0));
385 UpdateHookOnFunctionCall();
386 }
387
388
ArchiveDebug(char * storage)389 char* Debug::ArchiveDebug(char* storage) {
390 // Simply reset state. Don't archive anything.
391 ThreadInit();
392 return storage + ArchiveSpacePerThread();
393 }
394
395
RestoreDebug(char * storage)396 char* Debug::RestoreDebug(char* storage) {
397 // Simply reset state. Don't restore anything.
398 ThreadInit();
399 return storage + ArchiveSpacePerThread();
400 }
401
ArchiveSpacePerThread()402 int Debug::ArchiveSpacePerThread() { return 0; }
403
Iterate(ObjectVisitor * v)404 void Debug::Iterate(ObjectVisitor* v) {
405 v->VisitPointer(&thread_local_.return_value_);
406 v->VisitPointer(&thread_local_.suspended_generator_);
407 }
408
DebugInfoListNode(DebugInfo * debug_info)409 DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) {
410 // Globalize the request debug info object and make it weak.
411 GlobalHandles* global_handles = debug_info->GetIsolate()->global_handles();
412 debug_info_ =
413 Handle<DebugInfo>::cast(global_handles->Create(debug_info)).location();
414 }
415
416
~DebugInfoListNode()417 DebugInfoListNode::~DebugInfoListNode() {
418 if (debug_info_ == nullptr) return;
419 GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_));
420 debug_info_ = nullptr;
421 }
422
423
Load()424 bool Debug::Load() {
425 // Return if debugger is already loaded.
426 if (is_loaded()) return true;
427
428 // Bail out if we're already in the process of compiling the native
429 // JavaScript source code for the debugger.
430 if (is_suppressed_) return false;
431 SuppressDebug while_loading(this);
432
433 // Disable breakpoints and interrupts while compiling and running the
434 // debugger scripts including the context creation code.
435 DisableBreak disable(this);
436 PostponeInterruptsScope postpone(isolate_);
437
438 // Create the debugger context.
439 HandleScope scope(isolate_);
440 ExtensionConfiguration no_extensions;
441 // TODO(yangguo): we rely on the fact that first context snapshot is usable
442 // as debug context. This dependency is gone once we remove
443 // debug context completely.
444 static const int kFirstContextSnapshotIndex = 0;
445 Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment(
446 MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), &no_extensions,
447 kFirstContextSnapshotIndex, v8::DeserializeInternalFieldsCallback(),
448 DEBUG_CONTEXT);
449
450 // Fail if no context could be created.
451 if (context.is_null()) return false;
452
453 debug_context_ = Handle<Context>::cast(
454 isolate_->global_handles()->Create(*context));
455
456 feature_tracker()->Track(DebugFeatureTracker::kActive);
457
458 return true;
459 }
460
461
Unload()462 void Debug::Unload() {
463 ClearAllBreakPoints();
464 ClearStepping();
465 RemoveDebugDelegate();
466
467 // Return debugger is not loaded.
468 if (!is_loaded()) return;
469
470 // Clear debugger context global handle.
471 GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location());
472 debug_context_ = Handle<Context>();
473 }
474
Break(JavaScriptFrame * frame)475 void Debug::Break(JavaScriptFrame* frame) {
476 // Initialize LiveEdit.
477 LiveEdit::InitializeThreadLocal(this);
478
479 // Just continue if breaks are disabled or debugger cannot be loaded.
480 if (break_disabled()) return;
481
482 // Enter the debugger.
483 DebugScope debug_scope(this);
484 if (debug_scope.failed()) return;
485
486 // Postpone interrupt during breakpoint processing.
487 PostponeInterruptsScope postpone(isolate_);
488 DisableBreak no_recursive_break(this);
489
490 // Return if we fail to retrieve debug info.
491 Handle<JSFunction> function(frame->function());
492 Handle<SharedFunctionInfo> shared(function->shared());
493 if (!EnsureDebugInfo(shared)) return;
494 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
495
496 // Find the break location where execution has stopped.
497 BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
498
499 // Find actual break points, if any, and trigger debug break event.
500 MaybeHandle<FixedArray> break_points_hit =
501 CheckBreakPoints(debug_info, &location);
502 if (!break_points_hit.is_null()) {
503 // Clear all current stepping setup.
504 ClearStepping();
505 // Notify the debug event listeners.
506 Handle<JSArray> jsarr = isolate_->factory()->NewJSArrayWithElements(
507 break_points_hit.ToHandleChecked());
508 OnDebugBreak(jsarr);
509 return;
510 }
511
512 // No break point. Check for stepping.
513 StepAction step_action = last_step_action();
514 int current_frame_count = CurrentFrameCount();
515 int target_frame_count = thread_local_.target_frame_count_;
516 int last_frame_count = thread_local_.last_frame_count_;
517
518 bool step_break = false;
519 switch (step_action) {
520 case StepNone:
521 return;
522 case StepOut:
523 // Step out should not break in a deeper frame than target frame.
524 if (current_frame_count > target_frame_count) return;
525 step_break = true;
526 break;
527 case StepNext:
528 // Step next should not break in a deeper frame than target frame.
529 if (current_frame_count > target_frame_count) return;
530 // For step-next, a tail call is like a return and should break.
531 step_break = location.IsTailCall();
532 // Fall through.
533 case StepIn: {
534 FrameSummary summary = FrameSummary::GetTop(frame);
535 step_break = step_break || location.IsReturn() ||
536 current_frame_count != last_frame_count ||
537 thread_local_.last_statement_position_ !=
538 summary.SourceStatementPosition();
539 break;
540 }
541 }
542
543 // Clear all current stepping setup.
544 ClearStepping();
545
546 if (step_break) {
547 // Notify the debug event listeners.
548 OnDebugBreak(isolate_->factory()->undefined_value());
549 } else {
550 // Re-prepare to continue.
551 PrepareStep(step_action);
552 }
553 }
554
555
556 // Find break point objects for this location, if any, and evaluate them.
557 // Return an array of break point objects that evaluated true, or an empty
558 // handle if none evaluated true.
CheckBreakPoints(Handle<DebugInfo> debug_info,BreakLocation * location,bool * has_break_points)559 MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info,
560 BreakLocation* location,
561 bool* has_break_points) {
562 bool has_break_points_to_check =
563 break_points_active_ && location->HasBreakPoint(debug_info);
564 if (has_break_points) *has_break_points = has_break_points_to_check;
565 if (!has_break_points_to_check) return {};
566
567 Handle<Object> break_point_objects =
568 debug_info->GetBreakPointObjects(location->position());
569 return Debug::GetHitBreakPointObjects(break_point_objects);
570 }
571
572
IsMutedAtCurrentLocation(JavaScriptFrame * frame)573 bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
574 HandleScope scope(isolate_);
575 // A break location is considered muted if break locations on the current
576 // statement have at least one break point, and all of these break points
577 // evaluate to false. Aside from not triggering a debug break event at the
578 // break location, we also do not trigger one for debugger statements, nor
579 // an exception event on exception at this location.
580 FrameSummary summary = FrameSummary::GetTop(frame);
581 DCHECK(!summary.IsWasm());
582 Handle<JSFunction> function = summary.AsJavaScript().function();
583 if (!function->shared()->HasDebugInfo()) return false;
584 Handle<DebugInfo> debug_info(function->shared()->GetDebugInfo());
585 // Enter the debugger.
586 DebugScope debug_scope(this);
587 if (debug_scope.failed()) return false;
588 List<BreakLocation> break_locations;
589 BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations);
590 bool has_break_points_at_all = false;
591 for (int i = 0; i < break_locations.length(); i++) {
592 bool has_break_points;
593 MaybeHandle<FixedArray> check_result =
594 CheckBreakPoints(debug_info, &break_locations[i], &has_break_points);
595 has_break_points_at_all |= has_break_points;
596 if (has_break_points && !check_result.is_null()) return false;
597 }
598 return has_break_points_at_all;
599 }
600
601
CallFunction(const char * name,int argc,Handle<Object> args[])602 MaybeHandle<Object> Debug::CallFunction(const char* name, int argc,
603 Handle<Object> args[]) {
604 PostponeInterruptsScope no_interrupts(isolate_);
605 AssertDebugContext();
606 Handle<JSReceiver> holder =
607 Handle<JSReceiver>::cast(isolate_->natives_utils_object());
608 Handle<JSFunction> fun = Handle<JSFunction>::cast(
609 JSReceiver::GetProperty(isolate_, holder, name).ToHandleChecked());
610 Handle<Object> undefined = isolate_->factory()->undefined_value();
611 MaybeHandle<Object> maybe_exception;
612 return Execution::TryCall(isolate_, fun, undefined, argc, args,
613 Execution::MessageHandling::kReport,
614 &maybe_exception);
615 }
616
617
618 // Check whether a single break point object is triggered.
CheckBreakPoint(Handle<Object> break_point_object)619 bool Debug::CheckBreakPoint(Handle<Object> break_point_object) {
620 Factory* factory = isolate_->factory();
621 HandleScope scope(isolate_);
622
623 // Ignore check if break point object is not a JSObject.
624 if (!break_point_object->IsJSObject()) return true;
625
626 // Get the break id as an object.
627 Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id());
628
629 // Call IsBreakPointTriggered.
630 Handle<Object> argv[] = { break_id, break_point_object };
631 Handle<Object> result;
632 if (!CallFunction("IsBreakPointTriggered", arraysize(argv), argv)
633 .ToHandle(&result)) {
634 return false;
635 }
636
637 // Return whether the break point is triggered.
638 return result->IsTrue(isolate_);
639 }
640
641
SetBreakPoint(Handle<JSFunction> function,Handle<Object> break_point_object,int * source_position)642 bool Debug::SetBreakPoint(Handle<JSFunction> function,
643 Handle<Object> break_point_object,
644 int* source_position) {
645 HandleScope scope(isolate_);
646
647 // Make sure the function is compiled and has set up the debug info.
648 Handle<SharedFunctionInfo> shared(function->shared());
649 if (!EnsureDebugInfo(shared)) return true;
650 Handle<DebugInfo> debug_info(shared->GetDebugInfo());
651 // Source positions starts with zero.
652 DCHECK(*source_position >= 0);
653
654 // Find the break point and change it.
655 *source_position =
656 FindBreakablePosition(debug_info, *source_position, STATEMENT_ALIGNED);
657 DebugInfo::SetBreakPoint(debug_info, *source_position, break_point_object);
658 // At least one active break point now.
659 DCHECK(debug_info->GetBreakPointCount() > 0);
660
661 ClearBreakPoints(debug_info);
662 ApplyBreakPoints(debug_info);
663
664 feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
665 return true;
666 }
667
668
SetBreakPointForScript(Handle<Script> script,Handle<Object> break_point_object,int * source_position,BreakPositionAlignment alignment)669 bool Debug::SetBreakPointForScript(Handle<Script> script,
670 Handle<Object> break_point_object,
671 int* source_position,
672 BreakPositionAlignment alignment) {
673 if (script->type() == Script::TYPE_WASM) {
674 Handle<WasmCompiledModule> compiled_module(
675 WasmCompiledModule::cast(script->wasm_compiled_module()), isolate_);
676 return WasmCompiledModule::SetBreakPoint(compiled_module, source_position,
677 break_point_object);
678 }
679
680 HandleScope scope(isolate_);
681
682 // Obtain shared function info for the function.
683 Handle<Object> result =
684 FindSharedFunctionInfoInScript(script, *source_position);
685 if (result->IsUndefined(isolate_)) return false;
686
687 // Make sure the function has set up the debug info.
688 Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
689 if (!EnsureDebugInfo(shared)) return false;
690
691 // Find position within function. The script position might be before the
692 // source position of the first function.
693 if (shared->start_position() > *source_position) {
694 *source_position = shared->start_position();
695 }
696
697 Handle<DebugInfo> debug_info(shared->GetDebugInfo());
698
699 // Find the break point and change it.
700 *source_position =
701 FindBreakablePosition(debug_info, *source_position, alignment);
702 DebugInfo::SetBreakPoint(debug_info, *source_position, break_point_object);
703 // At least one active break point now.
704 DCHECK(debug_info->GetBreakPointCount() > 0);
705
706 ClearBreakPoints(debug_info);
707 ApplyBreakPoints(debug_info);
708
709 feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
710 return true;
711 }
712
FindBreakablePosition(Handle<DebugInfo> debug_info,int source_position,BreakPositionAlignment alignment)713 int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
714 int source_position,
715 BreakPositionAlignment alignment) {
716 int statement_position;
717 int position;
718 if (debug_info->HasDebugCode()) {
719 CodeBreakIterator it(debug_info);
720 it.SkipToPosition(source_position, alignment);
721 statement_position = it.statement_position();
722 position = it.position();
723 } else {
724 DCHECK(debug_info->HasDebugBytecodeArray());
725 BytecodeArrayBreakIterator it(debug_info);
726 it.SkipToPosition(source_position, alignment);
727 statement_position = it.statement_position();
728 position = it.position();
729 }
730 return alignment == STATEMENT_ALIGNED ? statement_position : position;
731 }
732
ApplyBreakPoints(Handle<DebugInfo> debug_info)733 void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
734 DisallowHeapAllocation no_gc;
735 if (debug_info->break_points()->IsUndefined(isolate_)) return;
736 FixedArray* break_points = debug_info->break_points();
737 for (int i = 0; i < break_points->length(); i++) {
738 if (break_points->get(i)->IsUndefined(isolate_)) continue;
739 BreakPointInfo* info = BreakPointInfo::cast(break_points->get(i));
740 if (info->GetBreakPointCount() == 0) continue;
741 if (debug_info->HasDebugCode()) {
742 CodeBreakIterator it(debug_info);
743 it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED);
744 it.SetDebugBreak();
745 }
746 if (debug_info->HasDebugBytecodeArray()) {
747 BytecodeArrayBreakIterator it(debug_info);
748 it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED);
749 it.SetDebugBreak();
750 }
751 }
752 }
753
ClearBreakPoints(Handle<DebugInfo> debug_info)754 void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
755 DisallowHeapAllocation no_gc;
756 if (debug_info->HasDebugCode()) {
757 for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) {
758 it.ClearDebugBreak();
759 }
760 }
761 if (debug_info->HasDebugBytecodeArray()) {
762 for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) {
763 it.ClearDebugBreak();
764 }
765 }
766 }
767
ClearBreakPoint(Handle<Object> break_point_object)768 void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
769 HandleScope scope(isolate_);
770
771 for (DebugInfoListNode* node = debug_info_list_; node != NULL;
772 node = node->next()) {
773 Handle<Object> result =
774 DebugInfo::FindBreakPointInfo(node->debug_info(), break_point_object);
775 if (result->IsUndefined(isolate_)) continue;
776 Handle<DebugInfo> debug_info = node->debug_info();
777 if (DebugInfo::ClearBreakPoint(debug_info, break_point_object)) {
778 ClearBreakPoints(debug_info);
779 if (debug_info->GetBreakPointCount() == 0) {
780 RemoveDebugInfoAndClearFromShared(debug_info);
781 } else {
782 ApplyBreakPoints(debug_info);
783 }
784 return;
785 }
786 }
787 }
788
789 // Clear out all the debug break code. This is ONLY supposed to be used when
790 // shutting down the debugger as it will leave the break point information in
791 // DebugInfo even though the code is patched back to the non break point state.
ClearAllBreakPoints()792 void Debug::ClearAllBreakPoints() {
793 for (DebugInfoListNode* node = debug_info_list_; node != NULL;
794 node = node->next()) {
795 ClearBreakPoints(node->debug_info());
796 }
797 // Remove all debug info.
798 while (debug_info_list_ != NULL) {
799 RemoveDebugInfoAndClearFromShared(debug_info_list_->debug_info());
800 }
801 }
802
FloodWithOneShot(Handle<SharedFunctionInfo> shared)803 void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
804 if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) return;
805 // Make sure the function is compiled and has set up the debug info.
806 if (!EnsureDebugInfo(shared)) return;
807 Handle<DebugInfo> debug_info(shared->GetDebugInfo());
808 // Flood the function with break points.
809 if (debug_info->HasDebugCode()) {
810 for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) {
811 it.SetDebugBreak();
812 }
813 }
814 if (debug_info->HasDebugBytecodeArray()) {
815 for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) {
816 it.SetDebugBreak();
817 }
818 }
819 }
820
ChangeBreakOnException(ExceptionBreakType type,bool enable)821 void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
822 if (type == BreakUncaughtException) {
823 break_on_uncaught_exception_ = enable;
824 } else {
825 break_on_exception_ = enable;
826 }
827 }
828
829
IsBreakOnException(ExceptionBreakType type)830 bool Debug::IsBreakOnException(ExceptionBreakType type) {
831 if (type == BreakUncaughtException) {
832 return break_on_uncaught_exception_;
833 } else {
834 return break_on_exception_;
835 }
836 }
837
GetHitBreakPointObjects(Handle<Object> break_point_objects)838 MaybeHandle<FixedArray> Debug::GetHitBreakPointObjects(
839 Handle<Object> break_point_objects) {
840 DCHECK(!break_point_objects->IsUndefined(isolate_));
841 if (!break_point_objects->IsFixedArray()) {
842 if (!CheckBreakPoint(break_point_objects)) return {};
843 Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1);
844 break_points_hit->set(0, *break_point_objects);
845 return break_points_hit;
846 }
847
848 Handle<FixedArray> array(FixedArray::cast(*break_point_objects));
849 int num_objects = array->length();
850 Handle<FixedArray> break_points_hit =
851 isolate_->factory()->NewFixedArray(num_objects);
852 int break_points_hit_count = 0;
853 for (int i = 0; i < num_objects; ++i) {
854 Handle<Object> break_point_object(array->get(i), isolate_);
855 if (CheckBreakPoint(break_point_object)) {
856 break_points_hit->set(break_points_hit_count++, *break_point_object);
857 }
858 }
859 if (break_points_hit_count == 0) return {};
860 break_points_hit->Shrink(break_points_hit_count);
861 return break_points_hit;
862 }
863
PrepareStepIn(Handle<JSFunction> function)864 void Debug::PrepareStepIn(Handle<JSFunction> function) {
865 CHECK(last_step_action() >= StepIn);
866 if (ignore_events()) return;
867 if (in_debug_scope()) return;
868 if (break_disabled()) return;
869 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
870 }
871
PrepareStepInSuspendedGenerator()872 void Debug::PrepareStepInSuspendedGenerator() {
873 CHECK(has_suspended_generator());
874 if (ignore_events()) return;
875 if (in_debug_scope()) return;
876 if (break_disabled()) return;
877 thread_local_.last_step_action_ = StepIn;
878 UpdateHookOnFunctionCall();
879 Handle<JSFunction> function(
880 JSGeneratorObject::cast(thread_local_.suspended_generator_)->function());
881 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
882 clear_suspended_generator();
883 }
884
PrepareStepOnThrow()885 void Debug::PrepareStepOnThrow() {
886 if (last_step_action() == StepNone) return;
887 if (ignore_events()) return;
888 if (in_debug_scope()) return;
889 if (break_disabled()) return;
890
891 ClearOneShot();
892
893 int current_frame_count = CurrentFrameCount();
894
895 // Iterate through the JavaScript stack looking for handlers.
896 JavaScriptFrameIterator it(isolate_);
897 while (!it.done()) {
898 JavaScriptFrame* frame = it.frame();
899 if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break;
900 List<SharedFunctionInfo*> infos;
901 frame->GetFunctions(&infos);
902 current_frame_count -= infos.length();
903 it.Advance();
904 }
905
906 // No handler found. Nothing to instrument.
907 if (it.done()) return;
908
909 bool found_handler = false;
910 // Iterate frames, including inlined frames. First, find the handler frame.
911 // Then skip to the frame we want to break in, then instrument for stepping.
912 for (; !it.done(); it.Advance()) {
913 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
914 if (last_step_action() == StepIn) {
915 // Deoptimize frame to ensure calls are checked for step-in.
916 Deoptimizer::DeoptimizeFunction(frame->function());
917 }
918 List<FrameSummary> summaries;
919 frame->Summarize(&summaries);
920 for (int i = summaries.length() - 1; i >= 0; i--, current_frame_count--) {
921 if (!found_handler) {
922 // We have yet to find the handler. If the frame inlines multiple
923 // functions, we have to check each one for the handler.
924 // If it only contains one function, we already found the handler.
925 if (summaries.length() > 1) {
926 Handle<AbstractCode> code =
927 summaries[i].AsJavaScript().abstract_code();
928 CHECK_EQ(AbstractCode::INTERPRETED_FUNCTION, code->kind());
929 BytecodeArray* bytecode = code->GetBytecodeArray();
930 HandlerTable* table = HandlerTable::cast(bytecode->handler_table());
931 int code_offset = summaries[i].code_offset();
932 HandlerTable::CatchPrediction prediction;
933 int index = table->LookupRange(code_offset, nullptr, &prediction);
934 if (index > 0) found_handler = true;
935 } else {
936 found_handler = true;
937 }
938 }
939
940 if (found_handler) {
941 // We found the handler. If we are stepping next or out, we need to
942 // iterate until we found the suitable target frame to break in.
943 if ((last_step_action() == StepNext || last_step_action() == StepOut) &&
944 current_frame_count > thread_local_.target_frame_count_) {
945 continue;
946 }
947 Handle<SharedFunctionInfo> info(
948 summaries[i].AsJavaScript().function()->shared());
949 if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue;
950 FloodWithOneShot(info);
951 return;
952 }
953 }
954 }
955 }
956
957
PrepareStep(StepAction step_action)958 void Debug::PrepareStep(StepAction step_action) {
959 HandleScope scope(isolate_);
960
961 DCHECK(in_debug_scope());
962
963 // Get the frame where the execution has stopped and skip the debug frame if
964 // any. The debug frame will only be present if execution was stopped due to
965 // hitting a break point. In other situations (e.g. unhandled exception) the
966 // debug frame is not present.
967 StackFrame::Id frame_id = break_frame_id();
968 // If there is no JavaScript stack don't do anything.
969 if (frame_id == StackFrame::NO_ID) return;
970
971 feature_tracker()->Track(DebugFeatureTracker::kStepping);
972
973 thread_local_.last_step_action_ = step_action;
974 UpdateHookOnFunctionCall();
975
976 StackTraceFrameIterator frames_it(isolate_, frame_id);
977 StandardFrame* frame = frames_it.frame();
978
979 // Handle stepping in wasm functions via the wasm interpreter.
980 if (frame->is_wasm()) {
981 // If the top frame is compiled, we cannot step.
982 if (frame->is_wasm_compiled()) return;
983 WasmInterpreterEntryFrame* wasm_frame =
984 WasmInterpreterEntryFrame::cast(frame);
985 wasm_frame->wasm_instance()->debug_info()->PrepareStep(step_action);
986 return;
987 }
988
989 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
990 DCHECK(js_frame->function()->IsJSFunction());
991
992 // Get the debug info (create it if it does not exist).
993 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
994 Handle<JSFunction> function(summary.function());
995 Handle<SharedFunctionInfo> shared(function->shared());
996 if (!EnsureDebugInfo(shared)) return;
997 Handle<DebugInfo> debug_info(shared->GetDebugInfo());
998
999 BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame);
1000
1001 // Any step at a return is a step-out.
1002 if (location.IsReturn()) step_action = StepOut;
1003 // A step-next at a tail call is a step-out.
1004 if (location.IsTailCall() && step_action == StepNext) step_action = StepOut;
1005 // A step-next in blackboxed function is a step-out.
1006 if (step_action == StepNext && IsBlackboxed(shared)) step_action = StepOut;
1007
1008 thread_local_.last_statement_position_ =
1009 summary.abstract_code()->SourceStatementPosition(summary.code_offset());
1010 int current_frame_count = CurrentFrameCount();
1011 thread_local_.last_frame_count_ = current_frame_count;
1012 // No longer perform the current async step.
1013 clear_suspended_generator();
1014
1015 switch (step_action) {
1016 case StepNone:
1017 UNREACHABLE();
1018 break;
1019 case StepOut: {
1020 // Clear last position info. For stepping out it does not matter.
1021 thread_local_.last_statement_position_ = kNoSourcePosition;
1022 thread_local_.last_frame_count_ = -1;
1023 // Skip the current frame, find the first frame we want to step out to
1024 // and deoptimize every frame along the way.
1025 bool in_current_frame = true;
1026 for (; !frames_it.done(); frames_it.Advance()) {
1027 // TODO(clemensh): Implement stepping out from JS to WASM.
1028 if (frames_it.frame()->is_wasm()) continue;
1029 JavaScriptFrame* frame = JavaScriptFrame::cast(frames_it.frame());
1030 if (last_step_action() == StepIn) {
1031 // Deoptimize frame to ensure calls are checked for step-in.
1032 Deoptimizer::DeoptimizeFunction(frame->function());
1033 }
1034 HandleScope scope(isolate_);
1035 List<Handle<SharedFunctionInfo>> infos;
1036 frame->GetFunctions(&infos);
1037 for (; !infos.is_empty(); current_frame_count--) {
1038 Handle<SharedFunctionInfo> info = infos.RemoveLast();
1039 if (in_current_frame) {
1040 // We want to skip out, so skip the current frame.
1041 in_current_frame = false;
1042 continue;
1043 }
1044 if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue;
1045 FloodWithOneShot(info);
1046 thread_local_.target_frame_count_ = current_frame_count;
1047 return;
1048 }
1049 }
1050 break;
1051 }
1052 case StepNext:
1053 thread_local_.target_frame_count_ = current_frame_count;
1054 // Fall through.
1055 case StepIn:
1056 // TODO(clemensh): Implement stepping from JS into WASM.
1057 FloodWithOneShot(shared);
1058 break;
1059 }
1060 }
1061
1062 // Simple function for returning the source positions for active break points.
GetSourceBreakLocations(Handle<SharedFunctionInfo> shared,BreakPositionAlignment position_alignment)1063 Handle<Object> Debug::GetSourceBreakLocations(
1064 Handle<SharedFunctionInfo> shared,
1065 BreakPositionAlignment position_alignment) {
1066 Isolate* isolate = shared->GetIsolate();
1067 if (!shared->HasDebugInfo()) {
1068 return isolate->factory()->undefined_value();
1069 }
1070 Handle<DebugInfo> debug_info(shared->GetDebugInfo());
1071 if (debug_info->GetBreakPointCount() == 0) {
1072 return isolate->factory()->undefined_value();
1073 }
1074 Handle<FixedArray> locations =
1075 isolate->factory()->NewFixedArray(debug_info->GetBreakPointCount());
1076 int count = 0;
1077 for (int i = 0; i < debug_info->break_points()->length(); ++i) {
1078 if (!debug_info->break_points()->get(i)->IsUndefined(isolate)) {
1079 BreakPointInfo* break_point_info =
1080 BreakPointInfo::cast(debug_info->break_points()->get(i));
1081 int break_points = break_point_info->GetBreakPointCount();
1082 if (break_points == 0) continue;
1083 Smi* position = NULL;
1084 if (position_alignment == STATEMENT_ALIGNED) {
1085 if (debug_info->HasDebugCode()) {
1086 CodeBreakIterator it(debug_info);
1087 it.SkipToPosition(break_point_info->source_position(),
1088 BREAK_POSITION_ALIGNED);
1089 position = Smi::FromInt(it.statement_position());
1090 } else {
1091 DCHECK(debug_info->HasDebugBytecodeArray());
1092 BytecodeArrayBreakIterator it(debug_info);
1093 it.SkipToPosition(break_point_info->source_position(),
1094 BREAK_POSITION_ALIGNED);
1095 position = Smi::FromInt(it.statement_position());
1096 }
1097 } else {
1098 DCHECK_EQ(BREAK_POSITION_ALIGNED, position_alignment);
1099 position = Smi::FromInt(break_point_info->source_position());
1100 }
1101 for (int j = 0; j < break_points; ++j) locations->set(count++, position);
1102 }
1103 }
1104 return locations;
1105 }
1106
ClearStepping()1107 void Debug::ClearStepping() {
1108 // Clear the various stepping setup.
1109 ClearOneShot();
1110
1111 thread_local_.last_step_action_ = StepNone;
1112 thread_local_.last_statement_position_ = kNoSourcePosition;
1113 thread_local_.last_frame_count_ = -1;
1114 thread_local_.target_frame_count_ = -1;
1115 UpdateHookOnFunctionCall();
1116 }
1117
1118
1119 // Clears all the one-shot break points that are currently set. Normally this
1120 // function is called each time a break point is hit as one shot break points
1121 // are used to support stepping.
ClearOneShot()1122 void Debug::ClearOneShot() {
1123 // The current implementation just runs through all the breakpoints. When the
1124 // last break point for a function is removed that function is automatically
1125 // removed from the list.
1126 for (DebugInfoListNode* node = debug_info_list_; node != NULL;
1127 node = node->next()) {
1128 Handle<DebugInfo> debug_info = node->debug_info();
1129 ClearBreakPoints(debug_info);
1130 ApplyBreakPoints(debug_info);
1131 }
1132 }
1133
1134
MatchingCodeTargets(Code * target1,Code * target2)1135 bool MatchingCodeTargets(Code* target1, Code* target2) {
1136 if (target1 == target2) return true;
1137 if (target1->kind() != target2->kind()) return false;
1138 return target1->is_handler() || target1->is_inline_cache_stub();
1139 }
1140
1141
1142 // Count the number of calls before the current frame PC to find the
1143 // corresponding PC in the newly recompiled code.
ComputeNewPcForRedirect(Code * new_code,Code * old_code,Address old_pc)1144 static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code,
1145 Address old_pc) {
1146 DCHECK_EQ(old_code->kind(), Code::FUNCTION);
1147 DCHECK_EQ(new_code->kind(), Code::FUNCTION);
1148 DCHECK(new_code->has_debug_break_slots());
1149 static const int mask = RelocInfo::kCodeTargetMask;
1150
1151 // Find the target of the current call.
1152 Code* target = NULL;
1153 intptr_t delta = 0;
1154 for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
1155 RelocInfo* rinfo = it.rinfo();
1156 Address current_pc = rinfo->pc();
1157 // The frame PC is behind the call instruction by the call instruction size.
1158 if (current_pc > old_pc) break;
1159 delta = old_pc - current_pc;
1160 target = Code::GetCodeFromTargetAddress(rinfo->target_address());
1161 }
1162
1163 // Count the number of calls to the same target before the current call.
1164 int index = 0;
1165 for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
1166 RelocInfo* rinfo = it.rinfo();
1167 Address current_pc = rinfo->pc();
1168 if (current_pc > old_pc) break;
1169 Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
1170 if (MatchingCodeTargets(target, current)) index++;
1171 }
1172
1173 DCHECK(index > 0);
1174
1175 // Repeat the count on the new code to find corresponding call.
1176 for (RelocIterator it(new_code, mask); !it.done(); it.next()) {
1177 RelocInfo* rinfo = it.rinfo();
1178 Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
1179 if (MatchingCodeTargets(target, current)) index--;
1180 if (index == 0) return rinfo->pc() + delta;
1181 }
1182
1183 UNREACHABLE();
1184 return NULL;
1185 }
1186
1187
1188 class RedirectActiveFunctions : public ThreadVisitor {
1189 public:
RedirectActiveFunctions(SharedFunctionInfo * shared)1190 explicit RedirectActiveFunctions(SharedFunctionInfo* shared)
1191 : shared_(shared) {
1192 DCHECK(shared->HasDebugCode());
1193 }
1194
VisitThread(Isolate * isolate,ThreadLocalTop * top)1195 void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
1196 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
1197 JavaScriptFrame* frame = it.frame();
1198 JSFunction* function = frame->function();
1199 if (frame->is_optimized()) continue;
1200 if (!function->Inlines(shared_)) continue;
1201
1202 if (frame->is_interpreted()) {
1203 InterpretedFrame* interpreted_frame =
1204 reinterpret_cast<InterpretedFrame*>(frame);
1205 BytecodeArray* debug_copy =
1206 shared_->GetDebugInfo()->DebugBytecodeArray();
1207 interpreted_frame->PatchBytecodeArray(debug_copy);
1208 continue;
1209 }
1210
1211 Code* frame_code = frame->LookupCode();
1212 DCHECK(frame_code->kind() == Code::FUNCTION);
1213 if (frame_code->has_debug_break_slots()) continue;
1214
1215 Code* new_code = function->shared()->code();
1216 Address old_pc = frame->pc();
1217 Address new_pc = ComputeNewPcForRedirect(new_code, frame_code, old_pc);
1218
1219 if (FLAG_trace_deopt) {
1220 PrintF("Replacing pc for debugging: %08" V8PRIxPTR " => %08" V8PRIxPTR
1221 "\n",
1222 reinterpret_cast<intptr_t>(old_pc),
1223 reinterpret_cast<intptr_t>(new_pc));
1224 }
1225
1226 if (FLAG_enable_embedded_constant_pool) {
1227 // Update constant pool pointer for new code.
1228 frame->set_constant_pool(new_code->constant_pool());
1229 }
1230
1231 // Patch the return address to return into the code with
1232 // debug break slots.
1233 frame->set_pc(new_pc);
1234 }
1235 }
1236
1237 private:
1238 SharedFunctionInfo* shared_;
1239 DisallowHeapAllocation no_gc_;
1240 };
1241
1242
PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared)1243 bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
1244 DCHECK(shared->is_compiled());
1245
1246 if (isolate_->concurrent_recompilation_enabled()) {
1247 isolate_->optimizing_compile_dispatcher()->Flush(
1248 OptimizingCompileDispatcher::BlockingBehavior::kBlock);
1249 }
1250
1251 List<Handle<JSFunction> > functions;
1252
1253 // Flush all optimized code maps. Note that the below heap iteration does not
1254 // cover this, because the given function might have been inlined into code
1255 // for which no JSFunction exists.
1256 {
1257 SharedFunctionInfo::GlobalIterator iterator(isolate_);
1258 while (SharedFunctionInfo* shared = iterator.Next()) {
1259 shared->ClearCodeFromOptimizedCodeMap();
1260 }
1261 }
1262
1263 // The native context also has a list of OSR'd optimized code. Clear it.
1264 isolate_->ClearOSROptimizedCode();
1265
1266 // Make sure we abort incremental marking.
1267 isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
1268 GarbageCollectionReason::kDebugger);
1269
1270 DCHECK(shared->is_compiled());
1271 bool baseline_exists = shared->HasBaselineCode();
1272
1273 {
1274 // TODO(yangguo): with bytecode, we still walk the heap to find all
1275 // optimized code for the function to deoptimize. We can probably be
1276 // smarter here and avoid the heap walk.
1277 HeapIterator iterator(isolate_->heap());
1278 HeapObject* obj;
1279
1280 while ((obj = iterator.next())) {
1281 if (obj->IsJSFunction()) {
1282 JSFunction* function = JSFunction::cast(obj);
1283 if (!function->Inlines(*shared)) continue;
1284 if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
1285 Deoptimizer::DeoptimizeFunction(function);
1286 }
1287 if (baseline_exists && function->shared() == *shared) {
1288 functions.Add(handle(function));
1289 }
1290 }
1291 }
1292 }
1293
1294 // We do not need to replace code to debug bytecode.
1295 DCHECK(baseline_exists || functions.is_empty());
1296
1297 // We do not need to recompile to debug bytecode.
1298 if (baseline_exists && !shared->code()->has_debug_break_slots()) {
1299 if (!Compiler::CompileDebugCode(shared)) return false;
1300 }
1301
1302 for (Handle<JSFunction> const function : functions) {
1303 function->ReplaceCode(shared->code());
1304 JSFunction::EnsureLiterals(function);
1305 }
1306
1307 // Update PCs on the stack to point to recompiled code.
1308 RedirectActiveFunctions redirect_visitor(*shared);
1309 redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
1310 isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
1311
1312 return true;
1313 }
1314
1315 namespace {
1316 template <typename Iterator>
GetBreakablePositions(Iterator * it,int start_position,int end_position,BreakPositionAlignment alignment,std::set<int> * positions)1317 void GetBreakablePositions(Iterator* it, int start_position, int end_position,
1318 BreakPositionAlignment alignment,
1319 std::set<int>* positions) {
1320 it->SkipToPosition(start_position, alignment);
1321 while (!it->Done() && it->position() < end_position &&
1322 it->position() >= start_position) {
1323 positions->insert(alignment == STATEMENT_ALIGNED ? it->statement_position()
1324 : it->position());
1325 it->Next();
1326 }
1327 }
1328
FindBreakablePositions(Handle<DebugInfo> debug_info,int start_position,int end_position,BreakPositionAlignment alignment,std::set<int> * positions)1329 void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
1330 int end_position, BreakPositionAlignment alignment,
1331 std::set<int>* positions) {
1332 if (debug_info->HasDebugCode()) {
1333 CodeBreakIterator it(debug_info);
1334 GetBreakablePositions(&it, start_position, end_position, alignment,
1335 positions);
1336 } else {
1337 DCHECK(debug_info->HasDebugBytecodeArray());
1338 BytecodeArrayBreakIterator it(debug_info);
1339 GetBreakablePositions(&it, start_position, end_position, alignment,
1340 positions);
1341 }
1342 }
1343 } // namespace
1344
GetPossibleBreakpoints(Handle<Script> script,int start_position,int end_position,std::set<int> * positions)1345 bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
1346 int end_position, std::set<int>* positions) {
1347 while (true) {
1348 HandleScope scope(isolate_);
1349 List<Handle<SharedFunctionInfo>> candidates;
1350 SharedFunctionInfo::ScriptIterator iterator(script);
1351 for (SharedFunctionInfo* info = iterator.Next(); info != nullptr;
1352 info = iterator.Next()) {
1353 if (info->end_position() < start_position ||
1354 info->start_position() >= end_position) {
1355 continue;
1356 }
1357 if (!info->IsSubjectToDebugging()) continue;
1358 if (!info->HasDebugCode() && !info->allows_lazy_compilation()) continue;
1359 candidates.Add(i::handle(info));
1360 }
1361
1362 bool was_compiled = false;
1363 for (int i = 0; i < candidates.length(); ++i) {
1364 // Code that cannot be compiled lazily are internal and not debuggable.
1365 DCHECK(candidates[i]->allows_lazy_compilation());
1366 if (!candidates[i]->HasDebugCode()) {
1367 if (!Compiler::CompileDebugCode(candidates[i])) {
1368 return false;
1369 } else {
1370 was_compiled = true;
1371 }
1372 }
1373 if (!EnsureDebugInfo(candidates[i])) return false;
1374 }
1375 if (was_compiled) continue;
1376
1377 for (int i = 0; i < candidates.length(); ++i) {
1378 CHECK(candidates[i]->HasDebugInfo());
1379 Handle<DebugInfo> debug_info(candidates[i]->GetDebugInfo());
1380 FindBreakablePositions(debug_info, start_position, end_position,
1381 BREAK_POSITION_ALIGNED, positions);
1382 }
1383 return true;
1384 }
1385 UNREACHABLE();
1386 return false;
1387 }
1388
RecordGenerator(Handle<JSGeneratorObject> generator_object)1389 void Debug::RecordGenerator(Handle<JSGeneratorObject> generator_object) {
1390 if (last_step_action() <= StepOut) return;
1391
1392 if (last_step_action() == StepNext) {
1393 // Only consider this generator a step-next target if not stepping in.
1394 if (thread_local_.target_frame_count_ < CurrentFrameCount()) return;
1395 }
1396
1397 DCHECK(!has_suspended_generator());
1398 thread_local_.suspended_generator_ = *generator_object;
1399 ClearStepping();
1400 }
1401
1402 class SharedFunctionInfoFinder {
1403 public:
SharedFunctionInfoFinder(int target_position)1404 explicit SharedFunctionInfoFinder(int target_position)
1405 : current_candidate_(NULL),
1406 current_candidate_closure_(NULL),
1407 current_start_position_(kNoSourcePosition),
1408 target_position_(target_position) {}
1409
NewCandidate(SharedFunctionInfo * shared,JSFunction * closure=NULL)1410 void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) {
1411 if (!shared->IsSubjectToDebugging()) return;
1412 int start_position = shared->function_token_position();
1413 if (start_position == kNoSourcePosition) {
1414 start_position = shared->start_position();
1415 }
1416
1417 if (start_position > target_position_) return;
1418 if (target_position_ > shared->end_position()) return;
1419
1420 if (current_candidate_ != NULL) {
1421 if (current_start_position_ == start_position &&
1422 shared->end_position() == current_candidate_->end_position()) {
1423 // If we already have a matching closure, do not throw it away.
1424 if (current_candidate_closure_ != NULL && closure == NULL) return;
1425 // If a top-level function contains only one function
1426 // declaration the source for the top-level and the function
1427 // is the same. In that case prefer the non top-level function.
1428 if (!current_candidate_->is_toplevel() && shared->is_toplevel()) return;
1429 } else if (start_position < current_start_position_ ||
1430 current_candidate_->end_position() < shared->end_position()) {
1431 return;
1432 }
1433 }
1434
1435 current_start_position_ = start_position;
1436 current_candidate_ = shared;
1437 current_candidate_closure_ = closure;
1438 }
1439
Result()1440 SharedFunctionInfo* Result() { return current_candidate_; }
1441
ResultClosure()1442 JSFunction* ResultClosure() { return current_candidate_closure_; }
1443
1444 private:
1445 SharedFunctionInfo* current_candidate_;
1446 JSFunction* current_candidate_closure_;
1447 int current_start_position_;
1448 int target_position_;
1449 DisallowHeapAllocation no_gc_;
1450 };
1451
1452
1453 // We need to find a SFI for a literal that may not yet have been compiled yet,
1454 // and there may not be a JSFunction referencing it. Find the SFI closest to
1455 // the given position, compile it to reveal possible inner SFIs and repeat.
1456 // While we are at this, also ensure code with debug break slots so that we do
1457 // not have to compile a SFI without JSFunction, which is paifu for those that
1458 // cannot be compiled without context (need to find outer compilable SFI etc.)
FindSharedFunctionInfoInScript(Handle<Script> script,int position)1459 Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
1460 int position) {
1461 for (int iteration = 0;; iteration++) {
1462 // Go through all shared function infos associated with this script to
1463 // find the inner most function containing this position.
1464 // If there is no shared function info for this script at all, there is
1465 // no point in looking for it by walking the heap.
1466
1467 SharedFunctionInfo* shared;
1468 {
1469 SharedFunctionInfoFinder finder(position);
1470 SharedFunctionInfo::ScriptIterator iterator(script);
1471 for (SharedFunctionInfo* info = iterator.Next(); info != nullptr;
1472 info = iterator.Next()) {
1473 finder.NewCandidate(info);
1474 }
1475 shared = finder.Result();
1476 if (shared == NULL) break;
1477 // We found it if it's already compiled and has debug code.
1478 if (shared->HasDebugCode()) {
1479 Handle<SharedFunctionInfo> shared_handle(shared);
1480 // If the iteration count is larger than 1, we had to compile the outer
1481 // function in order to create this shared function info. So there can
1482 // be no JSFunction referencing it. We can anticipate creating a debug
1483 // info while bypassing PrepareFunctionForBreakpoints.
1484 if (iteration > 1) {
1485 AllowHeapAllocation allow_before_return;
1486 CreateDebugInfo(shared_handle);
1487 }
1488 return shared_handle;
1489 }
1490 }
1491 // If not, compile to reveal inner functions.
1492 HandleScope scope(isolate_);
1493 // Code that cannot be compiled lazily are internal and not debuggable.
1494 DCHECK(shared->allows_lazy_compilation());
1495 if (!Compiler::CompileDebugCode(handle(shared))) break;
1496 }
1497 return isolate_->factory()->undefined_value();
1498 }
1499
1500
1501 // Ensures the debug information is present for shared.
EnsureDebugInfo(Handle<SharedFunctionInfo> shared)1502 bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
1503 // Return if we already have the debug info for shared.
1504 if (shared->HasDebugInfo()) return true;
1505 if (!shared->IsSubjectToDebugging()) return false;
1506 if (!shared->is_compiled() && !Compiler::CompileDebugCode(shared)) {
1507 return false;
1508 }
1509
1510 // To prepare bytecode for debugging, we already need to have the debug
1511 // info (containing the debug copy) upfront, but since we do not recompile,
1512 // preparing for break points cannot fail.
1513 CreateDebugInfo(shared);
1514 CHECK(PrepareFunctionForBreakPoints(shared));
1515 return true;
1516 }
1517
1518
CreateDebugInfo(Handle<SharedFunctionInfo> shared)1519 void Debug::CreateDebugInfo(Handle<SharedFunctionInfo> shared) {
1520 // Create the debug info object.
1521 Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
1522
1523 // Add debug info to the list.
1524 DebugInfoListNode* node = new DebugInfoListNode(*debug_info);
1525 node->set_next(debug_info_list_);
1526 debug_info_list_ = node;
1527 }
1528
1529
RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info)1530 void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) {
1531 HandleScope scope(isolate_);
1532 Handle<SharedFunctionInfo> shared(debug_info->shared());
1533
1534 DCHECK_NOT_NULL(debug_info_list_);
1535 // Run through the debug info objects to find this one and remove it.
1536 DebugInfoListNode* prev = NULL;
1537 DebugInfoListNode* current = debug_info_list_;
1538 while (current != NULL) {
1539 if (current->debug_info().is_identical_to(debug_info)) {
1540 // Unlink from list. If prev is NULL we are looking at the first element.
1541 if (prev == NULL) {
1542 debug_info_list_ = current->next();
1543 } else {
1544 prev->set_next(current->next());
1545 }
1546 shared->set_debug_info(Smi::FromInt(debug_info->debugger_hints()));
1547 delete current;
1548 return;
1549 }
1550 // Move to next in list.
1551 prev = current;
1552 current = current->next();
1553 }
1554
1555 UNREACHABLE();
1556 }
1557
IsBreakAtReturn(JavaScriptFrame * frame)1558 bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
1559 HandleScope scope(isolate_);
1560
1561 // Get the executing function in which the debug break occurred.
1562 Handle<SharedFunctionInfo> shared(frame->function()->shared());
1563
1564 // With no debug info there are no break points, so we can't be at a return.
1565 if (!shared->HasDebugInfo()) return false;
1566
1567 DCHECK(!frame->is_optimized());
1568 Handle<DebugInfo> debug_info(shared->GetDebugInfo());
1569 BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
1570 return location.IsReturn() || location.IsTailCall();
1571 }
1572
ScheduleFrameRestart(StackFrame * frame)1573 void Debug::ScheduleFrameRestart(StackFrame* frame) {
1574 // Set a target FP for the FrameDropperTrampoline builtin to drop to once
1575 // we return from the debugger.
1576 DCHECK(frame->is_java_script());
1577 // Only reschedule to a frame further below a frame we already scheduled for.
1578 if (frame->fp() <= thread_local_.restart_fp_) return;
1579 // If the frame is optimized, trigger a deopt and jump into the
1580 // FrameDropperTrampoline in the deoptimizer.
1581 thread_local_.restart_fp_ = frame->fp();
1582
1583 // Reset break frame ID to the frame below the restarted frame.
1584 StackTraceFrameIterator it(isolate_);
1585 thread_local_.break_frame_id_ = StackFrame::NO_ID;
1586 for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
1587 if (it.frame()->fp() > thread_local_.restart_fp_) {
1588 thread_local_.break_frame_id_ = it.frame()->id();
1589 return;
1590 }
1591 }
1592 }
1593
1594
IsDebugGlobal(JSGlobalObject * global)1595 bool Debug::IsDebugGlobal(JSGlobalObject* global) {
1596 return is_loaded() && global == debug_context()->global_object();
1597 }
1598
1599
GetLoadedScripts()1600 Handle<FixedArray> Debug::GetLoadedScripts() {
1601 isolate_->heap()->CollectAllGarbage(Heap::kFinalizeIncrementalMarkingMask,
1602 GarbageCollectionReason::kDebugger);
1603 Factory* factory = isolate_->factory();
1604 if (!factory->script_list()->IsWeakFixedArray()) {
1605 return factory->empty_fixed_array();
1606 }
1607 Handle<WeakFixedArray> array =
1608 Handle<WeakFixedArray>::cast(factory->script_list());
1609 Handle<FixedArray> results = factory->NewFixedArray(array->Length());
1610 int length = 0;
1611 {
1612 Script::Iterator iterator(isolate_);
1613 Script* script;
1614 while ((script = iterator.Next())) {
1615 if (script->HasValidSource()) results->set(length++, script);
1616 }
1617 }
1618 results->Shrink(length);
1619 return results;
1620 }
1621
1622
MakeExecutionState()1623 MaybeHandle<Object> Debug::MakeExecutionState() {
1624 // Create the execution state object.
1625 Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()) };
1626 return CallFunction("MakeExecutionState", arraysize(argv), argv);
1627 }
1628
1629
MakeBreakEvent(Handle<Object> break_points_hit)1630 MaybeHandle<Object> Debug::MakeBreakEvent(Handle<Object> break_points_hit) {
1631 // Create the new break event object.
1632 Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
1633 break_points_hit };
1634 return CallFunction("MakeBreakEvent", arraysize(argv), argv);
1635 }
1636
1637
MakeExceptionEvent(Handle<Object> exception,bool uncaught,Handle<Object> promise)1638 MaybeHandle<Object> Debug::MakeExceptionEvent(Handle<Object> exception,
1639 bool uncaught,
1640 Handle<Object> promise) {
1641 // Create the new exception event object.
1642 Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
1643 exception,
1644 isolate_->factory()->ToBoolean(uncaught),
1645 promise };
1646 return CallFunction("MakeExceptionEvent", arraysize(argv), argv);
1647 }
1648
1649
MakeCompileEvent(Handle<Script> script,v8::DebugEvent type)1650 MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
1651 v8::DebugEvent type) {
1652 // Create the compile event object.
1653 Handle<Object> script_wrapper = Script::GetWrapper(script);
1654 Handle<Object> argv[] = { script_wrapper,
1655 isolate_->factory()->NewNumberFromInt(type) };
1656 return CallFunction("MakeCompileEvent", arraysize(argv), argv);
1657 }
1658
MakeAsyncTaskEvent(v8::debug::PromiseDebugActionType type,int id)1659 MaybeHandle<Object> Debug::MakeAsyncTaskEvent(
1660 v8::debug::PromiseDebugActionType type, int id) {
1661 // Create the async task event object.
1662 Handle<Object> argv[] = {Handle<Smi>(Smi::FromInt(type), isolate_),
1663 Handle<Smi>(Smi::FromInt(id), isolate_)};
1664 return CallFunction("MakeAsyncTaskEvent", arraysize(argv), argv);
1665 }
1666
1667
OnThrow(Handle<Object> exception)1668 void Debug::OnThrow(Handle<Object> exception) {
1669 if (in_debug_scope() || ignore_events()) return;
1670 // Temporarily clear any scheduled_exception to allow evaluating
1671 // JavaScript from the debug event handler.
1672 HandleScope scope(isolate_);
1673 Handle<Object> scheduled_exception;
1674 if (isolate_->has_scheduled_exception()) {
1675 scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
1676 isolate_->clear_scheduled_exception();
1677 }
1678 OnException(exception, isolate_->GetPromiseOnStackOnThrow());
1679 if (!scheduled_exception.is_null()) {
1680 isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
1681 }
1682 PrepareStepOnThrow();
1683 }
1684
OnPromiseReject(Handle<Object> promise,Handle<Object> value)1685 void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
1686 if (in_debug_scope() || ignore_events()) return;
1687 HandleScope scope(isolate_);
1688 // Check whether the promise has been marked as having triggered a message.
1689 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
1690 if (!promise->IsJSObject() ||
1691 JSReceiver::GetDataProperty(Handle<JSObject>::cast(promise), key)
1692 ->IsUndefined(isolate_)) {
1693 OnException(value, promise);
1694 }
1695 }
1696
1697 namespace {
GetDebugEventContext(Isolate * isolate)1698 v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) {
1699 Handle<Context> context = isolate->debug()->debugger_entry()->GetContext();
1700 // Isolate::context() may have been NULL when "script collected" event
1701 // occured.
1702 if (context.is_null()) return v8::Local<v8::Context>();
1703 Handle<Context> native_context(context->native_context());
1704 return v8::Utils::ToLocal(native_context);
1705 }
1706 } // anonymous namespace
1707
IsExceptionBlackboxed(bool uncaught)1708 bool Debug::IsExceptionBlackboxed(bool uncaught) {
1709 JavaScriptFrameIterator it(isolate_);
1710 if (it.done()) return false;
1711 // Uncaught exception is blackboxed if all current frames are blackboxed,
1712 // caught exception if top frame is blackboxed.
1713 bool is_top_frame_blackboxed = IsFrameBlackboxed(it.frame());
1714 if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
1715 it.Advance();
1716 while (!it.done()) {
1717 if (!IsFrameBlackboxed(it.frame())) return false;
1718 it.Advance();
1719 }
1720 return true;
1721 }
1722
IsFrameBlackboxed(JavaScriptFrame * frame)1723 bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
1724 HandleScope scope(isolate_);
1725 if (!frame->HasInlinedFrames()) {
1726 Handle<SharedFunctionInfo> shared(frame->function()->shared(), isolate_);
1727 return IsBlackboxed(shared);
1728 }
1729 List<Handle<SharedFunctionInfo>> infos;
1730 frame->GetFunctions(&infos);
1731 for (const auto& info : infos)
1732 if (!IsBlackboxed(info)) return false;
1733 return true;
1734 }
1735
OnException(Handle<Object> exception,Handle<Object> promise)1736 void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
1737 // We cannot generate debug events when JS execution is disallowed.
1738 // TODO(5530): Reenable debug events within DisallowJSScopes once relevant
1739 // code (MakeExceptionEvent and ProcessDebugEvent) have been moved to C++.
1740 if (!AllowJavascriptExecution::IsAllowed(isolate_)) return;
1741
1742 Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
1743
1744 // Don't notify listener of exceptions that are internal to a desugaring.
1745 if (catch_type == Isolate::CAUGHT_BY_DESUGARING) return;
1746
1747 bool uncaught = catch_type == Isolate::NOT_CAUGHT;
1748 if (promise->IsJSObject()) {
1749 Handle<JSObject> jspromise = Handle<JSObject>::cast(promise);
1750 // Mark the promise as already having triggered a message.
1751 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
1752 JSObject::SetProperty(jspromise, key, key, STRICT).Assert();
1753 // Check whether the promise reject is considered an uncaught exception.
1754 uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
1755 }
1756
1757 if (!debug_delegate_) return;
1758
1759 // Bail out if exception breaks are not active
1760 if (uncaught) {
1761 // Uncaught exceptions are reported by either flags.
1762 if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
1763 } else {
1764 // Caught exceptions are reported is activated.
1765 if (!break_on_exception_) return;
1766 }
1767
1768 {
1769 JavaScriptFrameIterator it(isolate_);
1770 // Check whether the top frame is blackboxed or the break location is muted.
1771 if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) ||
1772 IsExceptionBlackboxed(uncaught))) {
1773 return;
1774 }
1775 if (it.done()) return; // Do not trigger an event with an empty stack.
1776 }
1777
1778 DebugScope debug_scope(this);
1779 if (debug_scope.failed()) return;
1780 HandleScope scope(isolate_);
1781 PostponeInterruptsScope postpone(isolate_);
1782 DisableBreak no_recursive_break(this);
1783
1784 // Create the execution state.
1785 Handle<Object> exec_state;
1786 // Bail out and don't call debugger if exception.
1787 if (!MakeExecutionState().ToHandle(&exec_state)) return;
1788
1789 debug_delegate_->ExceptionThrown(
1790 GetDebugEventContext(isolate_),
1791 v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
1792 v8::Utils::ToLocal(exception), v8::Utils::ToLocal(promise), uncaught);
1793 }
1794
OnDebugBreak(Handle<Object> break_points_hit)1795 void Debug::OnDebugBreak(Handle<Object> break_points_hit) {
1796 // The caller provided for DebugScope.
1797 AssertDebugContext();
1798 // Bail out if there is no listener for this event
1799 if (ignore_events()) return;
1800
1801 #ifdef DEBUG
1802 PrintBreakLocation();
1803 #endif // DEBUG
1804
1805 if (!debug_delegate_) return;
1806 HandleScope scope(isolate_);
1807 PostponeInterruptsScope no_interrupts(isolate_);
1808 DisableBreak no_recursive_break(this);
1809
1810 // Create the execution state.
1811 Handle<Object> exec_state;
1812 // Bail out and don't call debugger if exception.
1813 if (!MakeExecutionState().ToHandle(&exec_state)) return;
1814
1815 debug_delegate_->BreakProgramRequested(
1816 GetDebugEventContext(isolate_),
1817 v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
1818 v8::Utils::ToLocal(break_points_hit));
1819 }
1820
1821
OnCompileError(Handle<Script> script)1822 void Debug::OnCompileError(Handle<Script> script) {
1823 ProcessCompileEvent(v8::CompileError, script);
1824 }
1825
1826
1827 // Handle debugger actions when a new script is compiled.
OnAfterCompile(Handle<Script> script)1828 void Debug::OnAfterCompile(Handle<Script> script) {
1829 ProcessCompileEvent(v8::AfterCompile, script);
1830 }
1831
1832 namespace {
1833 struct CollectedCallbackData {
1834 Object** location;
1835 int id;
1836 Debug* debug;
1837 Isolate* isolate;
1838
CollectedCallbackDatav8::internal::__anond9ba0ad30311::CollectedCallbackData1839 CollectedCallbackData(Object** location, int id, Debug* debug,
1840 Isolate* isolate)
1841 : location(location), id(id), debug(debug), isolate(isolate) {}
1842 };
1843
SendAsyncTaskEventCancel(const v8::WeakCallbackInfo<void> & info)1844 void SendAsyncTaskEventCancel(const v8::WeakCallbackInfo<void>& info) {
1845 std::unique_ptr<CollectedCallbackData> data(
1846 reinterpret_cast<CollectedCallbackData*>(info.GetParameter()));
1847 if (!data->debug->is_active()) return;
1848 HandleScope scope(data->isolate);
1849 data->debug->OnAsyncTaskEvent(debug::kDebugPromiseCollected, data->id, 0);
1850 }
1851
ResetPromiseHandle(const v8::WeakCallbackInfo<void> & info)1852 void ResetPromiseHandle(const v8::WeakCallbackInfo<void>& info) {
1853 CollectedCallbackData* data =
1854 reinterpret_cast<CollectedCallbackData*>(info.GetParameter());
1855 GlobalHandles::Destroy(data->location);
1856 info.SetSecondPassCallback(&SendAsyncTaskEventCancel);
1857 }
1858
1859 // In an async function, reuse the existing stack related to the outer
1860 // Promise. Otherwise, e.g. in a direct call to then, save a new stack.
1861 // Promises with multiple reactions with one or more of them being async
1862 // functions will not get a good stack trace, as async functions require
1863 // different stacks from direct Promise use, but we save and restore a
1864 // stack once for all reactions.
1865 //
1866 // If this isn't a case of async function, we return false, otherwise
1867 // we set the correct id and return true.
1868 //
1869 // TODO(littledan): Improve this case.
GetReferenceAsyncTaskId(Isolate * isolate,Handle<JSPromise> promise)1870 int GetReferenceAsyncTaskId(Isolate* isolate, Handle<JSPromise> promise) {
1871 Handle<Symbol> handled_by_symbol =
1872 isolate->factory()->promise_handled_by_symbol();
1873 Handle<Object> handled_by_promise =
1874 JSObject::GetDataProperty(promise, handled_by_symbol);
1875 if (!handled_by_promise->IsJSPromise()) {
1876 return isolate->debug()->NextAsyncTaskId(promise);
1877 }
1878 Handle<JSPromise> handled_by_promise_js =
1879 Handle<JSPromise>::cast(handled_by_promise);
1880 Handle<Symbol> async_stack_id_symbol =
1881 isolate->factory()->promise_async_stack_id_symbol();
1882 Handle<Object> async_task_id =
1883 JSObject::GetDataProperty(handled_by_promise_js, async_stack_id_symbol);
1884 if (!async_task_id->IsSmi()) {
1885 return isolate->debug()->NextAsyncTaskId(promise);
1886 }
1887 return Handle<Smi>::cast(async_task_id)->value();
1888 }
1889 } // namespace
1890
RunPromiseHook(PromiseHookType type,Handle<JSPromise> promise,Handle<Object> parent)1891 void Debug::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
1892 Handle<Object> parent) {
1893 if (!debug_delegate_) return;
1894 int id = GetReferenceAsyncTaskId(isolate_, promise);
1895 switch (type) {
1896 case PromiseHookType::kInit:
1897 OnAsyncTaskEvent(debug::kDebugPromiseCreated, id,
1898 parent->IsJSPromise()
1899 ? GetReferenceAsyncTaskId(
1900 isolate_, Handle<JSPromise>::cast(parent))
1901 : 0);
1902 return;
1903 case PromiseHookType::kResolve:
1904 // We can't use this hook because it's called before promise object will
1905 // get resolved status.
1906 return;
1907 case PromiseHookType::kBefore:
1908 OnAsyncTaskEvent(debug::kDebugWillHandle, id, 0);
1909 return;
1910 case PromiseHookType::kAfter:
1911 OnAsyncTaskEvent(debug::kDebugDidHandle, id, 0);
1912 return;
1913 }
1914 }
1915
NextAsyncTaskId(Handle<JSObject> promise)1916 int Debug::NextAsyncTaskId(Handle<JSObject> promise) {
1917 LookupIterator it(promise, isolate_->factory()->promise_async_id_symbol());
1918 Maybe<bool> maybe = JSReceiver::HasProperty(&it);
1919 if (maybe.ToChecked()) {
1920 MaybeHandle<Object> result = Object::GetProperty(&it);
1921 return Handle<Smi>::cast(result.ToHandleChecked())->value();
1922 }
1923 Handle<Smi> async_id =
1924 handle(Smi::FromInt(++thread_local_.async_task_count_), isolate_);
1925 Object::SetProperty(&it, async_id, SLOPPY, Object::MAY_BE_STORE_FROM_KEYED)
1926 .ToChecked();
1927 Handle<Object> global_handle = isolate_->global_handles()->Create(*promise);
1928 // We send EnqueueRecurring async task event when promise is fulfilled or
1929 // rejected, WillHandle and DidHandle for every scheduled microtask for this
1930 // promise.
1931 // We need to send a cancel event when no other microtasks can be
1932 // started for this promise and all current microtasks are finished.
1933 // Since we holding promise when at least one microtask is scheduled (inside
1934 // PromiseReactionJobInfo), we can send cancel event in weak callback.
1935 GlobalHandles::MakeWeak(
1936 global_handle.location(),
1937 new CollectedCallbackData(global_handle.location(), async_id->value(),
1938 this, isolate_),
1939 &ResetPromiseHandle, v8::WeakCallbackType::kParameter);
1940 return async_id->value();
1941 }
1942
1943 namespace {
GetDebugLocation(Handle<Script> script,int source_position)1944 debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
1945 Script::PositionInfo info;
1946 Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
1947 return debug::Location(info.line, info.column);
1948 }
1949 } // namespace
1950
IsBlackboxed(Handle<SharedFunctionInfo> shared)1951 bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
1952 if (!debug_delegate_) return false;
1953 if (!shared->computed_debug_is_blackboxed()) {
1954 bool is_blackboxed = false;
1955 if (shared->script()->IsScript()) {
1956 SuppressDebug while_processing(this);
1957 HandleScope handle_scope(isolate_);
1958 PostponeInterruptsScope no_interrupts(isolate_);
1959 DisableBreak no_recursive_break(this);
1960 Handle<Script> script(Script::cast(shared->script()));
1961 if (script->type() == i::Script::TYPE_NORMAL) {
1962 debug::Location start =
1963 GetDebugLocation(script, shared->start_position());
1964 debug::Location end = GetDebugLocation(script, shared->end_position());
1965 is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
1966 ToApiHandle<debug::Script>(script), start, end);
1967 }
1968 }
1969 shared->set_debug_is_blackboxed(is_blackboxed);
1970 shared->set_computed_debug_is_blackboxed(true);
1971 }
1972 return shared->debug_is_blackboxed();
1973 }
1974
OnAsyncTaskEvent(debug::PromiseDebugActionType type,int id,int parent_id)1975 void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
1976 int parent_id) {
1977 if (in_debug_scope() || ignore_events()) return;
1978 if (!debug_delegate_) return;
1979 SuppressDebug while_processing(this);
1980 DebugScope debug_scope(isolate_->debug());
1981 if (debug_scope.failed()) return;
1982 HandleScope scope(isolate_);
1983 PostponeInterruptsScope no_interrupts(isolate_);
1984 DisableBreak no_recursive_break(this);
1985 debug_delegate_->PromiseEventOccurred(type, id, parent_id);
1986 }
1987
ProcessCompileEvent(v8::DebugEvent event,Handle<Script> script)1988 void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
1989 if (ignore_events()) return;
1990 if (script->type() != i::Script::TYPE_NORMAL &&
1991 script->type() != i::Script::TYPE_WASM) {
1992 return;
1993 }
1994 if (!debug_delegate_) return;
1995 SuppressDebug while_processing(this);
1996 DebugScope debug_scope(this);
1997 if (debug_scope.failed()) return;
1998 HandleScope scope(isolate_);
1999 PostponeInterruptsScope postpone(isolate_);
2000 DisableBreak no_recursive_break(this);
2001 debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
2002 event != v8::AfterCompile);
2003 }
2004
2005
GetDebugContext()2006 Handle<Context> Debug::GetDebugContext() {
2007 if (!is_loaded()) return Handle<Context>();
2008 DebugScope debug_scope(this);
2009 if (debug_scope.failed()) return Handle<Context>();
2010 // The global handle may be destroyed soon after. Return it reboxed.
2011 return handle(*debug_context(), isolate_);
2012 }
2013
CurrentFrameCount()2014 int Debug::CurrentFrameCount() {
2015 StackTraceFrameIterator it(isolate_);
2016 if (break_frame_id() != StackFrame::NO_ID) {
2017 // Skip to break frame.
2018 DCHECK(in_debug_scope());
2019 while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance();
2020 }
2021 int counter = 0;
2022 while (!it.done()) {
2023 if (it.frame()->is_optimized()) {
2024 List<SharedFunctionInfo*> infos;
2025 OptimizedFrame::cast(it.frame())->GetFunctions(&infos);
2026 counter += infos.length();
2027 } else {
2028 counter++;
2029 }
2030 it.Advance();
2031 }
2032 return counter;
2033 }
2034
SetDebugDelegate(debug::DebugDelegate * delegate,bool pass_ownership)2035 void Debug::SetDebugDelegate(debug::DebugDelegate* delegate,
2036 bool pass_ownership) {
2037 RemoveDebugDelegate();
2038 debug_delegate_ = delegate;
2039 owns_debug_delegate_ = pass_ownership;
2040 UpdateState();
2041 }
2042
RemoveDebugDelegate()2043 void Debug::RemoveDebugDelegate() {
2044 if (debug_delegate_ == nullptr) return;
2045 if (owns_debug_delegate_) {
2046 owns_debug_delegate_ = false;
2047 delete debug_delegate_;
2048 }
2049 debug_delegate_ = nullptr;
2050 }
2051
UpdateState()2052 void Debug::UpdateState() {
2053 bool is_active = debug_delegate_ != nullptr;
2054 if (is_active || in_debug_scope()) {
2055 // Note that the debug context could have already been loaded to
2056 // bootstrap test cases.
2057 isolate_->compilation_cache()->Disable();
2058 is_active = Load();
2059 } else if (is_loaded()) {
2060 isolate_->compilation_cache()->Enable();
2061 Unload();
2062 }
2063 is_active_ = is_active;
2064 isolate_->DebugStateUpdated();
2065 }
2066
UpdateHookOnFunctionCall()2067 void Debug::UpdateHookOnFunctionCall() {
2068 STATIC_ASSERT(LastStepAction == StepIn);
2069 hook_on_function_call_ = thread_local_.last_step_action_ == StepIn ||
2070 isolate_->needs_side_effect_check();
2071 }
2072
Call(Handle<Object> fun,Handle<Object> data)2073 MaybeHandle<Object> Debug::Call(Handle<Object> fun, Handle<Object> data) {
2074 DebugScope debug_scope(this);
2075 if (debug_scope.failed()) return isolate_->factory()->undefined_value();
2076
2077 // Create the execution state.
2078 Handle<Object> exec_state;
2079 if (!MakeExecutionState().ToHandle(&exec_state)) {
2080 return isolate_->factory()->undefined_value();
2081 }
2082
2083 Handle<Object> argv[] = { exec_state, data };
2084 return Execution::Call(
2085 isolate_,
2086 fun,
2087 Handle<Object>(debug_context()->global_proxy(), isolate_),
2088 arraysize(argv),
2089 argv);
2090 }
2091
2092
HandleDebugBreak()2093 void Debug::HandleDebugBreak() {
2094 // Initialize LiveEdit.
2095 LiveEdit::InitializeThreadLocal(this);
2096 // Ignore debug break during bootstrapping.
2097 if (isolate_->bootstrapper()->IsActive()) return;
2098 // Just continue if breaks are disabled.
2099 if (break_disabled()) return;
2100 // Ignore debug break if debugger is not active.
2101 if (!is_active()) return;
2102
2103 StackLimitCheck check(isolate_);
2104 if (check.HasOverflowed()) return;
2105
2106 { JavaScriptFrameIterator it(isolate_);
2107 DCHECK(!it.done());
2108 Object* fun = it.frame()->function();
2109 if (fun && fun->IsJSFunction()) {
2110 HandleScope scope(isolate_);
2111 // Don't stop in builtin and blackboxed functions.
2112 Handle<SharedFunctionInfo> shared(JSFunction::cast(fun)->shared(),
2113 isolate_);
2114 if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) {
2115 // Inspector uses pause on next statement for asynchronous breakpoints.
2116 // When breakpoint is fired we try to break on first not blackboxed
2117 // statement. To achieve this goal we need to deoptimize current
2118 // function and don't clear requested DebugBreak even if it's blackboxed
2119 // to be able to break on not blackboxed function call.
2120 // TODO(yangguo): introduce break_on_function_entry since current
2121 // implementation is slow.
2122 Deoptimizer::DeoptimizeFunction(JSFunction::cast(fun));
2123 return;
2124 }
2125 JSGlobalObject* global =
2126 JSFunction::cast(fun)->context()->global_object();
2127 // Don't stop in debugger functions.
2128 if (IsDebugGlobal(global)) return;
2129 // Don't stop if the break location is muted.
2130 if (IsMutedAtCurrentLocation(it.frame())) return;
2131 }
2132 }
2133
2134 isolate_->stack_guard()->ClearDebugBreak();
2135
2136 // Clear stepping to avoid duplicate breaks.
2137 ClearStepping();
2138
2139 HandleScope scope(isolate_);
2140 DebugScope debug_scope(this);
2141 if (debug_scope.failed()) return;
2142
2143 OnDebugBreak(isolate_->factory()->undefined_value());
2144 }
2145
2146 #ifdef DEBUG
PrintBreakLocation()2147 void Debug::PrintBreakLocation() {
2148 if (!FLAG_print_break_location) return;
2149 HandleScope scope(isolate_);
2150 StackTraceFrameIterator iterator(isolate_);
2151 if (iterator.done()) return;
2152 StandardFrame* frame = iterator.frame();
2153 FrameSummary summary = FrameSummary::GetTop(frame);
2154 int source_position = summary.SourcePosition();
2155 Handle<Object> script_obj = summary.script();
2156 PrintF("[debug] break in function '");
2157 summary.FunctionName()->PrintOn(stdout);
2158 PrintF("'.\n");
2159 if (script_obj->IsScript()) {
2160 Handle<Script> script = Handle<Script>::cast(script_obj);
2161 Handle<String> source(String::cast(script->source()));
2162 Script::InitLineEnds(script);
2163 int line =
2164 Script::GetLineNumber(script, source_position) - script->line_offset();
2165 int column = Script::GetColumnNumber(script, source_position) -
2166 (line == 0 ? script->column_offset() : 0);
2167 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
2168 int line_start =
2169 line == 0 ? 0 : Smi::cast(line_ends->get(line - 1))->value() + 1;
2170 int line_end = Smi::cast(line_ends->get(line))->value();
2171 DisallowHeapAllocation no_gc;
2172 String::FlatContent content = source->GetFlatContent();
2173 if (content.IsOneByte()) {
2174 PrintF("[debug] %.*s\n", line_end - line_start,
2175 content.ToOneByteVector().start() + line_start);
2176 PrintF("[debug] ");
2177 for (int i = 0; i < column; i++) PrintF(" ");
2178 PrintF("^\n");
2179 } else {
2180 PrintF("[debug] at line %d column %d\n", line, column);
2181 }
2182 }
2183 }
2184 #endif // DEBUG
2185
DebugScope(Debug * debug)2186 DebugScope::DebugScope(Debug* debug)
2187 : debug_(debug),
2188 prev_(debug->debugger_entry()),
2189 save_(debug_->isolate_),
2190 no_termination_exceptons_(debug_->isolate_,
2191 StackGuard::TERMINATE_EXECUTION) {
2192 // Link recursive debugger entry.
2193 base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
2194 reinterpret_cast<base::AtomicWord>(this));
2195
2196 // Store the previous break id, frame id and return value.
2197 break_id_ = debug_->break_id();
2198 break_frame_id_ = debug_->break_frame_id();
2199
2200 // Create the new break info. If there is no proper frames there is no break
2201 // frame id.
2202 StackTraceFrameIterator it(isolate());
2203 bool has_frames = !it.done();
2204 debug_->thread_local_.break_frame_id_ =
2205 has_frames ? it.frame()->id() : StackFrame::NO_ID;
2206 debug_->SetNextBreakId();
2207
2208 debug_->UpdateState();
2209 // Make sure that debugger is loaded and enter the debugger context.
2210 // The previous context is kept in save_.
2211 failed_ = !debug_->is_loaded();
2212 if (!failed_) isolate()->set_context(*debug->debug_context());
2213 }
2214
2215
~DebugScope()2216 DebugScope::~DebugScope() {
2217 // Leaving this debugger entry.
2218 base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
2219 reinterpret_cast<base::AtomicWord>(prev_));
2220
2221 // Restore to the previous break state.
2222 debug_->thread_local_.break_frame_id_ = break_frame_id_;
2223 debug_->thread_local_.break_id_ = break_id_;
2224
2225 debug_->UpdateState();
2226 }
2227
ReturnValueScope(Debug * debug)2228 ReturnValueScope::ReturnValueScope(Debug* debug) : debug_(debug) {
2229 return_value_ = debug_->return_value_handle();
2230 }
2231
~ReturnValueScope()2232 ReturnValueScope::~ReturnValueScope() {
2233 debug_->set_return_value(*return_value_);
2234 }
2235
PerformSideEffectCheck(Handle<JSFunction> function)2236 bool Debug::PerformSideEffectCheck(Handle<JSFunction> function) {
2237 DCHECK(isolate_->needs_side_effect_check());
2238 DisallowJavascriptExecution no_js(isolate_);
2239 if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) return false;
2240 Deoptimizer::DeoptimizeFunction(*function);
2241 if (!function->shared()->HasNoSideEffect()) {
2242 if (FLAG_trace_side_effect_free_debug_evaluate) {
2243 PrintF("[debug-evaluate] Function %s failed side effect check.\n",
2244 function->shared()->DebugName()->ToCString().get());
2245 }
2246 side_effect_check_failed_ = true;
2247 // Throw an uncatchable termination exception.
2248 isolate_->TerminateExecution();
2249 return false;
2250 }
2251 return true;
2252 }
2253
PerformSideEffectCheckForCallback(Address function)2254 bool Debug::PerformSideEffectCheckForCallback(Address function) {
2255 DCHECK(isolate_->needs_side_effect_check());
2256 if (DebugEvaluate::CallbackHasNoSideEffect(function)) return true;
2257 side_effect_check_failed_ = true;
2258 // Throw an uncatchable termination exception.
2259 isolate_->TerminateExecution();
2260 isolate_->OptionalRescheduleException(false);
2261 return false;
2262 }
2263
PromiseEventOccurred(v8::debug::PromiseDebugActionType type,int id,int parent_id)2264 void LegacyDebugDelegate::PromiseEventOccurred(
2265 v8::debug::PromiseDebugActionType type, int id, int parent_id) {
2266 Handle<Object> event_data;
2267 if (isolate_->debug()->MakeAsyncTaskEvent(type, id).ToHandle(&event_data)) {
2268 ProcessDebugEvent(v8::AsyncTaskEvent, Handle<JSObject>::cast(event_data));
2269 }
2270 }
2271
ScriptCompiled(v8::Local<v8::debug::Script> script,bool is_compile_error)2272 void LegacyDebugDelegate::ScriptCompiled(v8::Local<v8::debug::Script> script,
2273 bool is_compile_error) {
2274 Handle<Object> event_data;
2275 v8::DebugEvent event = is_compile_error ? v8::CompileError : v8::AfterCompile;
2276 if (isolate_->debug()
2277 ->MakeCompileEvent(v8::Utils::OpenHandle(*script), event)
2278 .ToHandle(&event_data)) {
2279 ProcessDebugEvent(event, Handle<JSObject>::cast(event_data));
2280 }
2281 }
2282
BreakProgramRequested(v8::Local<v8::Context> paused_context,v8::Local<v8::Object> exec_state,v8::Local<v8::Value> break_points_hit)2283 void LegacyDebugDelegate::BreakProgramRequested(
2284 v8::Local<v8::Context> paused_context, v8::Local<v8::Object> exec_state,
2285 v8::Local<v8::Value> break_points_hit) {
2286 Handle<Object> event_data;
2287 if (isolate_->debug()
2288 ->MakeBreakEvent(v8::Utils::OpenHandle(*break_points_hit))
2289 .ToHandle(&event_data)) {
2290 ProcessDebugEvent(
2291 v8::Break, Handle<JSObject>::cast(event_data),
2292 Handle<JSObject>::cast(v8::Utils::OpenHandle(*exec_state)));
2293 }
2294 }
2295
ExceptionThrown(v8::Local<v8::Context> paused_context,v8::Local<v8::Object> exec_state,v8::Local<v8::Value> exception,v8::Local<v8::Value> promise,bool is_uncaught)2296 void LegacyDebugDelegate::ExceptionThrown(v8::Local<v8::Context> paused_context,
2297 v8::Local<v8::Object> exec_state,
2298 v8::Local<v8::Value> exception,
2299 v8::Local<v8::Value> promise,
2300 bool is_uncaught) {
2301 Handle<Object> event_data;
2302 if (isolate_->debug()
2303 ->MakeExceptionEvent(v8::Utils::OpenHandle(*exception), is_uncaught,
2304 v8::Utils::OpenHandle(*promise))
2305 .ToHandle(&event_data)) {
2306 ProcessDebugEvent(
2307 v8::Exception, Handle<JSObject>::cast(event_data),
2308 Handle<JSObject>::cast(v8::Utils::OpenHandle(*exec_state)));
2309 }
2310 }
2311
ProcessDebugEvent(v8::DebugEvent event,Handle<JSObject> event_data)2312 void LegacyDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
2313 Handle<JSObject> event_data) {
2314 Handle<Object> exec_state;
2315 if (isolate_->debug()->MakeExecutionState().ToHandle(&exec_state)) {
2316 ProcessDebugEvent(event, event_data, Handle<JSObject>::cast(exec_state));
2317 }
2318 }
2319
JavaScriptDebugDelegate(Isolate * isolate,Handle<JSFunction> listener,Handle<Object> data)2320 JavaScriptDebugDelegate::JavaScriptDebugDelegate(Isolate* isolate,
2321 Handle<JSFunction> listener,
2322 Handle<Object> data)
2323 : LegacyDebugDelegate(isolate) {
2324 GlobalHandles* global_handles = isolate->global_handles();
2325 listener_ = Handle<JSFunction>::cast(global_handles->Create(*listener));
2326 data_ = global_handles->Create(*data);
2327 }
2328
~JavaScriptDebugDelegate()2329 JavaScriptDebugDelegate::~JavaScriptDebugDelegate() {
2330 GlobalHandles::Destroy(Handle<Object>::cast(listener_).location());
2331 GlobalHandles::Destroy(data_.location());
2332 }
2333
ProcessDebugEvent(v8::DebugEvent event,Handle<JSObject> event_data,Handle<JSObject> exec_state)2334 void JavaScriptDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
2335 Handle<JSObject> event_data,
2336 Handle<JSObject> exec_state) {
2337 Handle<Object> argv[] = {Handle<Object>(Smi::FromInt(event), isolate_),
2338 exec_state, event_data, data_};
2339 Handle<JSReceiver> global = isolate_->global_proxy();
2340 // Listener must not throw.
2341 Execution::Call(isolate_, listener_, global, arraysize(argv), argv)
2342 .ToHandleChecked();
2343 }
2344
NativeDebugDelegate(Isolate * isolate,v8::Debug::EventCallback callback,Handle<Object> data)2345 NativeDebugDelegate::NativeDebugDelegate(Isolate* isolate,
2346 v8::Debug::EventCallback callback,
2347 Handle<Object> data)
2348 : LegacyDebugDelegate(isolate), callback_(callback) {
2349 data_ = isolate->global_handles()->Create(*data);
2350 }
2351
~NativeDebugDelegate()2352 NativeDebugDelegate::~NativeDebugDelegate() {
2353 GlobalHandles::Destroy(data_.location());
2354 }
2355
EventDetails(DebugEvent event,Handle<JSObject> exec_state,Handle<JSObject> event_data,Handle<Object> callback_data)2356 NativeDebugDelegate::EventDetails::EventDetails(DebugEvent event,
2357 Handle<JSObject> exec_state,
2358 Handle<JSObject> event_data,
2359 Handle<Object> callback_data)
2360 : event_(event),
2361 exec_state_(exec_state),
2362 event_data_(event_data),
2363 callback_data_(callback_data) {}
2364
GetEvent() const2365 DebugEvent NativeDebugDelegate::EventDetails::GetEvent() const {
2366 return event_;
2367 }
2368
GetExecutionState() const2369 v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetExecutionState()
2370 const {
2371 return v8::Utils::ToLocal(exec_state_);
2372 }
2373
GetEventData() const2374 v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetEventData() const {
2375 return v8::Utils::ToLocal(event_data_);
2376 }
2377
GetEventContext() const2378 v8::Local<v8::Context> NativeDebugDelegate::EventDetails::GetEventContext()
2379 const {
2380 return GetDebugEventContext(exec_state_->GetIsolate());
2381 }
2382
GetCallbackData() const2383 v8::Local<v8::Value> NativeDebugDelegate::EventDetails::GetCallbackData()
2384 const {
2385 return v8::Utils::ToLocal(callback_data_);
2386 }
2387
GetIsolate() const2388 v8::Isolate* NativeDebugDelegate::EventDetails::GetIsolate() const {
2389 return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate());
2390 }
2391
ProcessDebugEvent(v8::DebugEvent event,Handle<JSObject> event_data,Handle<JSObject> exec_state)2392 void NativeDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
2393 Handle<JSObject> event_data,
2394 Handle<JSObject> exec_state) {
2395 EventDetails event_details(event, exec_state, event_data, data_);
2396 Isolate* isolate = isolate_;
2397 callback_(event_details);
2398 CHECK(!isolate->has_scheduled_exception());
2399 }
2400
~NoSideEffectScope()2401 NoSideEffectScope::~NoSideEffectScope() {
2402 if (isolate_->needs_side_effect_check() &&
2403 isolate_->debug()->side_effect_check_failed_) {
2404 DCHECK(isolate_->has_pending_exception());
2405 DCHECK_EQ(isolate_->heap()->termination_exception(),
2406 isolate_->pending_exception());
2407 // Convert the termination exception into a regular exception.
2408 isolate_->CancelTerminateExecution();
2409 isolate_->Throw(*isolate_->factory()->NewEvalError(
2410 MessageTemplate::kNoSideEffectDebugEvaluate));
2411 }
2412 isolate_->set_needs_side_effect_check(old_needs_side_effect_check_);
2413 isolate_->debug()->UpdateHookOnFunctionCall();
2414 isolate_->debug()->side_effect_check_failed_ = false;
2415 }
2416
2417 } // namespace internal
2418 } // namespace v8
2419