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 #include <unordered_set>
9
10 #include "src/api/api-inl.h"
11 #include "src/api/api-natives.h"
12 #include "src/base/platform/mutex.h"
13 #include "src/builtins/builtins.h"
14 #include "src/codegen/assembler-inl.h"
15 #include "src/codegen/compilation-cache.h"
16 #include "src/codegen/compiler.h"
17 #include "src/common/assert-scope.h"
18 #include "src/common/globals.h"
19 #include "src/common/message-template.h"
20 #include "src/debug/debug-evaluate.h"
21 #include "src/debug/liveedit.h"
22 #include "src/deoptimizer/deoptimizer.h"
23 #include "src/execution/arguments.h"
24 #include "src/execution/execution.h"
25 #include "src/execution/frames-inl.h"
26 #include "src/execution/isolate-inl.h"
27 #include "src/execution/v8threads.h"
28 #include "src/handles/global-handles-inl.h"
29 #include "src/heap/heap-inl.h" // For NextDebuggingId.
30 #include "src/init/bootstrapper.h"
31 #include "src/interpreter/bytecode-array-iterator.h"
32 #include "src/interpreter/interpreter.h"
33 #include "src/logging/counters.h"
34 #include "src/logging/runtime-call-stats-scope.h"
35 #include "src/objects/api-callbacks-inl.h"
36 #include "src/objects/debug-objects-inl.h"
37 #include "src/objects/js-generator-inl.h"
38 #include "src/objects/js-promise-inl.h"
39 #include "src/objects/slots.h"
40 #include "src/snapshot/embedded/embedded-data.h"
41 #include "src/snapshot/snapshot.h"
42
43 #if V8_ENABLE_WEBASSEMBLY
44 #include "src/wasm/wasm-debug.h"
45 #include "src/wasm/wasm-objects-inl.h"
46 #endif // V8_ENABLE_WEBASSEMBLY
47
48 namespace v8 {
49 namespace internal {
50
51 class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker {
52 public:
53 TemporaryObjectsTracker() = default;
54 ~TemporaryObjectsTracker() override = default;
55 TemporaryObjectsTracker(const TemporaryObjectsTracker&) = delete;
56 TemporaryObjectsTracker& operator=(const TemporaryObjectsTracker&) = delete;
57
AllocationEvent(Address addr,int)58 void AllocationEvent(Address addr, int) override {
59 if (disabled) return;
60 objects_.insert(addr);
61 }
62
MoveEvent(Address from,Address to,int)63 void MoveEvent(Address from, Address to, int) override {
64 if (from == to) return;
65 base::MutexGuard guard(&mutex_);
66 auto it = objects_.find(from);
67 if (it == objects_.end()) {
68 // If temporary object was collected we can get MoveEvent which moves
69 // existing non temporary object to the address where we had temporary
70 // object. So we should mark new address as non temporary.
71 objects_.erase(to);
72 return;
73 }
74 objects_.erase(it);
75 objects_.insert(to);
76 }
77
HasObject(Handle<HeapObject> obj) const78 bool HasObject(Handle<HeapObject> obj) const {
79 if (obj->IsJSObject() &&
80 Handle<JSObject>::cast(obj)->GetEmbedderFieldCount()) {
81 // Embedder may store any pointers using embedder fields and implements
82 // non trivial logic, e.g. create wrappers lazily and store pointer to
83 // native object inside embedder field. We should consider all objects
84 // with embedder fields as non temporary.
85 return false;
86 }
87 return objects_.find(obj->address()) != objects_.end();
88 }
89
90 bool disabled = false;
91
92 private:
93 std::unordered_set<Address> objects_;
94 base::Mutex mutex_;
95 };
96
Debug(Isolate * isolate)97 Debug::Debug(Isolate* isolate)
98 : is_active_(false),
99 hook_on_function_call_(false),
100 is_suppressed_(false),
101 break_disabled_(false),
102 break_points_active_(true),
103 break_on_exception_(false),
104 break_on_uncaught_exception_(false),
105 side_effect_check_failed_(false),
106 debug_info_list_(nullptr),
107 feature_tracker_(isolate),
108 isolate_(isolate) {
109 ThreadInit();
110 }
111
~Debug()112 Debug::~Debug() { DCHECK_NULL(debug_delegate_); }
113
FromFrame(Handle<DebugInfo> debug_info,JavaScriptFrame * frame)114 BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
115 JavaScriptFrame* frame) {
116 if (debug_info->CanBreakAtEntry()) {
117 return BreakLocation(Debug::kBreakAtEntryPosition, DEBUG_BREAK_AT_ENTRY);
118 }
119 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
120 int offset = summary.code_offset();
121 Handle<AbstractCode> abstract_code = summary.abstract_code();
122 BreakIterator it(debug_info);
123 it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
124 return it.GetBreakLocation();
125 }
126
CheckBreakPointsForLocations(Handle<DebugInfo> debug_info,std::vector<BreakLocation> & break_locations,bool * has_break_points)127 MaybeHandle<FixedArray> Debug::CheckBreakPointsForLocations(
128 Handle<DebugInfo> debug_info, std::vector<BreakLocation>& break_locations,
129 bool* has_break_points) {
130 Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(
131 debug_info->GetBreakPointCount(isolate_));
132 int break_points_hit_count = 0;
133 bool has_break_points_at_all = false;
134 for (size_t i = 0; i < break_locations.size(); i++) {
135 bool location_has_break_points;
136 MaybeHandle<FixedArray> check_result = CheckBreakPoints(
137 debug_info, &break_locations[i], &location_has_break_points);
138 has_break_points_at_all |= location_has_break_points;
139 if (!check_result.is_null()) {
140 Handle<FixedArray> break_points_current_hit =
141 check_result.ToHandleChecked();
142 int num_objects = break_points_current_hit->length();
143 for (int j = 0; j < num_objects; ++j) {
144 break_points_hit->set(break_points_hit_count++,
145 break_points_current_hit->get(j));
146 }
147 }
148 }
149 *has_break_points = has_break_points_at_all;
150 if (break_points_hit_count == 0) return {};
151
152 break_points_hit->Shrink(isolate_, break_points_hit_count);
153 return break_points_hit;
154 }
155
AllAtCurrentStatement(Handle<DebugInfo> debug_info,JavaScriptFrame * frame,std::vector<BreakLocation> * result_out)156 void BreakLocation::AllAtCurrentStatement(
157 Handle<DebugInfo> debug_info, JavaScriptFrame* frame,
158 std::vector<BreakLocation>* result_out) {
159 DCHECK(!debug_info->CanBreakAtEntry());
160 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
161 int offset = summary.code_offset();
162 Handle<AbstractCode> abstract_code = summary.abstract_code();
163 if (abstract_code->IsCode()) offset = offset - 1;
164 int statement_position;
165 {
166 BreakIterator it(debug_info);
167 it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
168 statement_position = it.statement_position();
169 }
170 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
171 if (it.statement_position() == statement_position) {
172 result_out->push_back(it.GetBreakLocation());
173 }
174 }
175 }
176
GetGeneratorObjectForSuspendedFrame(JavaScriptFrame * frame) const177 JSGeneratorObject BreakLocation::GetGeneratorObjectForSuspendedFrame(
178 JavaScriptFrame* frame) const {
179 DCHECK(IsSuspend());
180 DCHECK_GE(generator_obj_reg_index_, 0);
181
182 Object generator_obj = UnoptimizedFrame::cast(frame)->ReadInterpreterRegister(
183 generator_obj_reg_index_);
184
185 return JSGeneratorObject::cast(generator_obj);
186 }
187
BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,Handle<AbstractCode> abstract_code,int offset)188 int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,
189 Handle<AbstractCode> abstract_code,
190 int offset) {
191 // Run through all break points to locate the one closest to the address.
192 int closest_break = 0;
193 int distance = kMaxInt;
194 DCHECK(0 <= offset && offset < abstract_code->Size());
195 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
196 // Check if this break point is closer that what was previously found.
197 if (it.code_offset() <= offset && offset - it.code_offset() < distance) {
198 closest_break = it.break_index();
199 distance = offset - it.code_offset();
200 // Check whether we can't get any closer.
201 if (distance == 0) break;
202 }
203 }
204 return closest_break;
205 }
206
HasBreakPoint(Isolate * isolate,Handle<DebugInfo> debug_info) const207 bool BreakLocation::HasBreakPoint(Isolate* isolate,
208 Handle<DebugInfo> debug_info) const {
209 // First check whether there is a break point with the same source position.
210 if (!debug_info->HasBreakPoint(isolate, position_)) return false;
211 if (debug_info->CanBreakAtEntry()) {
212 DCHECK_EQ(Debug::kBreakAtEntryPosition, position_);
213 return debug_info->BreakAtEntry();
214 } else {
215 // Then check whether a break point at that source position would have
216 // the same code offset. Otherwise it's just a break location that we can
217 // step to, but not actually a location where we can put a break point.
218 DCHECK(abstract_code_->IsBytecodeArray());
219 BreakIterator it(debug_info);
220 it.SkipToPosition(position_);
221 return it.code_offset() == code_offset_;
222 }
223 }
224
type() const225 debug::BreakLocationType BreakLocation::type() const {
226 switch (type_) {
227 case DEBUGGER_STATEMENT:
228 return debug::kDebuggerStatementBreakLocation;
229 case DEBUG_BREAK_SLOT_AT_CALL:
230 return debug::kCallBreakLocation;
231 case DEBUG_BREAK_SLOT_AT_RETURN:
232 return debug::kReturnBreakLocation;
233
234 // Externally, suspend breaks should look like normal breaks.
235 case DEBUG_BREAK_SLOT_AT_SUSPEND:
236 default:
237 return debug::kCommonBreakLocation;
238 }
239 }
240
BreakIterator(Handle<DebugInfo> debug_info)241 BreakIterator::BreakIterator(Handle<DebugInfo> debug_info)
242 : debug_info_(debug_info),
243 break_index_(-1),
244 source_position_iterator_(
245 debug_info->DebugBytecodeArray().SourcePositionTable()) {
246 position_ = debug_info->shared().StartPosition();
247 statement_position_ = position_;
248 // There is at least one break location.
249 DCHECK(!Done());
250 Next();
251 }
252
BreakIndexFromPosition(int source_position)253 int BreakIterator::BreakIndexFromPosition(int source_position) {
254 for (; !Done(); Next()) {
255 if (source_position <= position()) {
256 int first_break = break_index();
257 for (; !Done(); Next()) {
258 if (source_position == position()) return break_index();
259 }
260 return first_break;
261 }
262 }
263 return break_index();
264 }
265
Next()266 void BreakIterator::Next() {
267 DisallowGarbageCollection no_gc;
268 DCHECK(!Done());
269 bool first = break_index_ == -1;
270 while (!Done()) {
271 if (!first) source_position_iterator_.Advance();
272 first = false;
273 if (Done()) return;
274 position_ = source_position_iterator_.source_position().ScriptOffset();
275 if (source_position_iterator_.is_statement()) {
276 statement_position_ = position_;
277 }
278 DCHECK_LE(0, position_);
279 DCHECK_LE(0, statement_position_);
280
281 DebugBreakType type = GetDebugBreakType();
282 if (type != NOT_DEBUG_BREAK) break;
283 }
284 break_index_++;
285 }
286
GetDebugBreakType()287 DebugBreakType BreakIterator::GetDebugBreakType() {
288 BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
289 interpreter::Bytecode bytecode =
290 interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset()));
291
292 // Make sure we read the actual bytecode, not a prefix scaling bytecode.
293 if (interpreter::Bytecodes::IsPrefixScalingBytecode(bytecode)) {
294 bytecode =
295 interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset() + 1));
296 }
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::kSuspendGenerator) {
303 return DEBUG_BREAK_SLOT_AT_SUSPEND;
304 } else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) {
305 return DEBUG_BREAK_SLOT_AT_CALL;
306 } else if (source_position_iterator_.is_statement()) {
307 return DEBUG_BREAK_SLOT;
308 } else {
309 return NOT_DEBUG_BREAK;
310 }
311 }
312
SkipToPosition(int position)313 void BreakIterator::SkipToPosition(int position) {
314 BreakIterator it(debug_info_);
315 SkipTo(it.BreakIndexFromPosition(position));
316 }
317
SetDebugBreak()318 void BreakIterator::SetDebugBreak() {
319 DebugBreakType debug_break_type = GetDebugBreakType();
320 if (debug_break_type == DEBUGGER_STATEMENT) return;
321 HandleScope scope(isolate());
322 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
323 Handle<BytecodeArray> bytecode_array(debug_info_->DebugBytecodeArray(),
324 isolate());
325 interpreter::BytecodeArrayIterator(bytecode_array, code_offset())
326 .ApplyDebugBreak();
327 }
328
ClearDebugBreak()329 void BreakIterator::ClearDebugBreak() {
330 DebugBreakType debug_break_type = GetDebugBreakType();
331 if (debug_break_type == DEBUGGER_STATEMENT) return;
332 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
333 BytecodeArray bytecode_array = debug_info_->DebugBytecodeArray();
334 BytecodeArray original = debug_info_->OriginalBytecodeArray();
335 bytecode_array.set(code_offset(), original.get(code_offset()));
336 }
337
GetBreakLocation()338 BreakLocation BreakIterator::GetBreakLocation() {
339 Handle<AbstractCode> code(
340 AbstractCode::cast(debug_info_->DebugBytecodeArray()), isolate());
341 DebugBreakType type = GetDebugBreakType();
342 int generator_object_reg_index = -1;
343 int generator_suspend_id = -1;
344 if (type == DEBUG_BREAK_SLOT_AT_SUSPEND) {
345 // For suspend break, we'll need the generator object to be able to step
346 // over the suspend as if it didn't return. We get the interpreter register
347 // index that holds the generator object by reading it directly off the
348 // bytecode array, and we'll read the actual generator object off the
349 // interpreter stack frame in GetGeneratorObjectForSuspendedFrame.
350 BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
351 interpreter::BytecodeArrayIterator iterator(
352 handle(bytecode_array, isolate()), code_offset());
353
354 DCHECK_EQ(iterator.current_bytecode(),
355 interpreter::Bytecode::kSuspendGenerator);
356 interpreter::Register generator_obj_reg = iterator.GetRegisterOperand(0);
357 generator_object_reg_index = generator_obj_reg.index();
358
359 // Also memorize the suspend ID, to be able to decide whether
360 // we are paused on the implicit initial yield later.
361 generator_suspend_id = iterator.GetUnsignedImmediateOperand(3);
362 }
363 return BreakLocation(code, type, code_offset(), position_,
364 generator_object_reg_index, generator_suspend_id);
365 }
366
isolate()367 Isolate* BreakIterator::isolate() { return debug_info_->GetIsolate(); }
368
Track(DebugFeatureTracker::Feature feature)369 void DebugFeatureTracker::Track(DebugFeatureTracker::Feature feature) {
370 uint32_t mask = 1 << feature;
371 // Only count one sample per feature and isolate.
372 if (bitfield_ & mask) return;
373 isolate_->counters()->debug_feature_usage()->AddSample(feature);
374 bitfield_ |= mask;
375 }
376
377 // Threading support.
ThreadInit()378 void Debug::ThreadInit() {
379 thread_local_.break_frame_id_ = StackFrameId::NO_ID;
380 thread_local_.last_step_action_ = StepNone;
381 thread_local_.last_statement_position_ = kNoSourcePosition;
382 thread_local_.last_frame_count_ = -1;
383 thread_local_.fast_forward_to_return_ = false;
384 thread_local_.ignore_step_into_function_ = Smi::zero();
385 thread_local_.target_frame_count_ = -1;
386 thread_local_.return_value_ = Smi::zero();
387 thread_local_.last_breakpoint_id_ = 0;
388 clear_suspended_generator();
389 base::Relaxed_Store(&thread_local_.current_debug_scope_,
390 static_cast<base::AtomicWord>(0));
391 thread_local_.break_on_next_function_call_ = false;
392 UpdateHookOnFunctionCall();
393 thread_local_.promise_stack_ = Smi::zero();
394 }
395
ArchiveDebug(char * storage)396 char* Debug::ArchiveDebug(char* storage) {
397 MemCopy(storage, reinterpret_cast<char*>(&thread_local_),
398 ArchiveSpacePerThread());
399 return storage + ArchiveSpacePerThread();
400 }
401
RestoreDebug(char * storage)402 char* Debug::RestoreDebug(char* storage) {
403 MemCopy(reinterpret_cast<char*>(&thread_local_), storage,
404 ArchiveSpacePerThread());
405
406 // Enter the debugger.
407 DebugScope debug_scope(this);
408
409 // Clear any one-shot breakpoints that may have been set by the other
410 // thread, and reapply breakpoints for this thread.
411 ClearOneShot();
412
413 if (thread_local_.last_step_action_ != StepNone) {
414 int current_frame_count = CurrentFrameCount();
415 int target_frame_count = thread_local_.target_frame_count_;
416 DCHECK(current_frame_count >= target_frame_count);
417 StackTraceFrameIterator frames_it(isolate_);
418 while (current_frame_count > target_frame_count) {
419 current_frame_count -= frames_it.FrameFunctionCount();
420 frames_it.Advance();
421 }
422 DCHECK(current_frame_count == target_frame_count);
423 // Set frame to what it was at Step break
424 thread_local_.break_frame_id_ = frames_it.frame()->id();
425
426 // Reset the previous step action for this thread.
427 PrepareStep(thread_local_.last_step_action_);
428 }
429
430 return storage + ArchiveSpacePerThread();
431 }
432
ArchiveSpacePerThread()433 int Debug::ArchiveSpacePerThread() { return sizeof(ThreadLocal); }
434
Iterate(RootVisitor * v)435 void Debug::Iterate(RootVisitor* v) { Iterate(v, &thread_local_); }
436
Iterate(RootVisitor * v,char * thread_storage)437 char* Debug::Iterate(RootVisitor* v, char* thread_storage) {
438 ThreadLocal* thread_local_data =
439 reinterpret_cast<ThreadLocal*>(thread_storage);
440 Iterate(v, thread_local_data);
441 return thread_storage + ArchiveSpacePerThread();
442 }
443
Iterate(RootVisitor * v,ThreadLocal * thread_local_data)444 void Debug::Iterate(RootVisitor* v, ThreadLocal* thread_local_data) {
445 v->VisitRootPointer(Root::kDebug, nullptr,
446 FullObjectSlot(&thread_local_data->return_value_));
447 v->VisitRootPointer(Root::kDebug, nullptr,
448 FullObjectSlot(&thread_local_data->suspended_generator_));
449 v->VisitRootPointer(
450 Root::kDebug, nullptr,
451 FullObjectSlot(&thread_local_data->ignore_step_into_function_));
452 v->VisitRootPointer(Root::kDebug, nullptr,
453 FullObjectSlot(&thread_local_data->promise_stack_));
454 }
455
DebugInfoListNode(Isolate * isolate,DebugInfo debug_info)456 DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo debug_info)
457 : next_(nullptr) {
458 // Globalize the request debug info object and make it weak.
459 GlobalHandles* global_handles = isolate->global_handles();
460 debug_info_ = global_handles->Create(debug_info).location();
461 }
462
~DebugInfoListNode()463 DebugInfoListNode::~DebugInfoListNode() {
464 if (debug_info_ == nullptr) return;
465 GlobalHandles::Destroy(debug_info_);
466 debug_info_ = nullptr;
467 }
468
Unload()469 void Debug::Unload() {
470 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
471 ClearAllBreakPoints();
472 ClearStepping();
473 RemoveAllCoverageInfos();
474 ClearAllDebuggerHints();
475 debug_delegate_ = nullptr;
476 }
477
OnInstrumentationBreak()478 void Debug::OnInstrumentationBreak() {
479 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
480 if (!debug_delegate_) return;
481 DCHECK(in_debug_scope());
482 HandleScope scope(isolate_);
483 DisableBreak no_recursive_break(this);
484
485 Handle<Context> native_context(isolate_->native_context());
486 debug_delegate_->BreakOnInstrumentation(v8::Utils::ToLocal(native_context),
487 kInstrumentationId);
488 }
489
Break(JavaScriptFrame * frame,Handle<JSFunction> break_target)490 void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) {
491 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
492 // Just continue if breaks are disabled or debugger cannot be loaded.
493 if (break_disabled()) return;
494
495 // Enter the debugger.
496 DebugScope debug_scope(this);
497 DisableBreak no_recursive_break(this);
498
499 // Return if we fail to retrieve debug info.
500 Handle<SharedFunctionInfo> shared(break_target->shared(), isolate_);
501 if (!EnsureBreakInfo(shared)) return;
502 PrepareFunctionForDebugExecution(shared);
503
504 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
505
506 // Find the break location where execution has stopped.
507 BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
508 const bool hitInstrumentationBreak =
509 IsBreakOnInstrumentation(debug_info, location);
510 if (hitInstrumentationBreak) {
511 OnInstrumentationBreak();
512 }
513
514 // Find actual break points, if any, and trigger debug break event.
515 bool has_break_points;
516 MaybeHandle<FixedArray> break_points_hit =
517 CheckBreakPoints(debug_info, &location, &has_break_points);
518 if (!break_points_hit.is_null() || break_on_next_function_call()) {
519 StepAction lastStepAction = last_step_action();
520 // Clear all current stepping setup.
521 ClearStepping();
522 // Notify the debug event listeners.
523 OnDebugBreak(!break_points_hit.is_null()
524 ? break_points_hit.ToHandleChecked()
525 : isolate_->factory()->empty_fixed_array(),
526 lastStepAction);
527 return;
528 }
529
530 // Debug break at function entry, do not worry about stepping.
531 if (location.IsDebugBreakAtEntry()) {
532 DCHECK(debug_info->BreakAtEntry());
533 return;
534 }
535
536 DCHECK_NOT_NULL(frame);
537
538 // No break point. Check for stepping.
539 StepAction step_action = last_step_action();
540 int current_frame_count = CurrentFrameCount();
541 int target_frame_count = thread_local_.target_frame_count_;
542 int last_frame_count = thread_local_.last_frame_count_;
543
544 // StepOut at not return position was requested and return break locations
545 // were flooded with one shots.
546 if (thread_local_.fast_forward_to_return_) {
547 // We might hit an instrumentation breakpoint before running into a
548 // return/suspend location.
549 DCHECK(location.IsReturnOrSuspend() || hitInstrumentationBreak);
550 // We have to ignore recursive calls to function.
551 if (current_frame_count > target_frame_count) return;
552 ClearStepping();
553 PrepareStep(StepOut);
554 return;
555 }
556
557 bool step_break = false;
558 switch (step_action) {
559 case StepNone:
560 return;
561 case StepOut:
562 // StepOut should not break in a deeper frame than target frame.
563 if (current_frame_count > target_frame_count) return;
564 step_break = true;
565 break;
566 case StepOver:
567 // StepOver should not break in a deeper frame than target frame.
568 if (current_frame_count > target_frame_count) return;
569 V8_FALLTHROUGH;
570 case StepInto: {
571 // Special case StepInto and StepOver for generators that are about to
572 // suspend, in which case we go into "generator stepping" mode. The
573 // exception here is the initial implicit yield in generators (which
574 // always has a suspend ID of 0), where we return to the caller first,
575 // instead of triggering "generator stepping" mode straight away.
576 if (location.IsSuspend() && (!IsGeneratorFunction(shared->kind()) ||
577 location.generator_suspend_id() > 0)) {
578 DCHECK(!has_suspended_generator());
579 thread_local_.suspended_generator_ =
580 location.GetGeneratorObjectForSuspendedFrame(frame);
581 ClearStepping();
582 return;
583 }
584
585 FrameSummary summary = FrameSummary::GetTop(frame);
586 step_break = step_break || location.IsReturn() ||
587 current_frame_count != last_frame_count ||
588 thread_local_.last_statement_position_ !=
589 summary.SourceStatementPosition();
590 break;
591 }
592 }
593
594 StepAction lastStepAction = last_step_action();
595 // Clear all current stepping setup.
596 ClearStepping();
597
598 if (step_break) {
599 // Notify the debug event listeners.
600 OnDebugBreak(isolate_->factory()->empty_fixed_array(), lastStepAction);
601 } else {
602 // Re-prepare to continue.
603 PrepareStep(step_action);
604 }
605 }
606
IsBreakOnInstrumentation(Handle<DebugInfo> debug_info,const BreakLocation & location)607 bool Debug::IsBreakOnInstrumentation(Handle<DebugInfo> debug_info,
608 const BreakLocation& location) {
609 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
610 bool has_break_points_to_check =
611 break_points_active_ && location.HasBreakPoint(isolate_, debug_info);
612 if (!has_break_points_to_check) return {};
613
614 Handle<Object> break_points =
615 debug_info->GetBreakPoints(isolate_, location.position());
616 DCHECK(!break_points->IsUndefined(isolate_));
617 if (!break_points->IsFixedArray()) {
618 const Handle<BreakPoint> break_point =
619 Handle<BreakPoint>::cast(break_points);
620 return break_point->id() == kInstrumentationId;
621 }
622
623 Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
624 for (int i = 0; i < array->length(); ++i) {
625 const Handle<BreakPoint> break_point =
626 Handle<BreakPoint>::cast(handle(array->get(i), isolate_));
627 if (break_point->id() == kInstrumentationId) {
628 return true;
629 }
630 }
631 return false;
632 }
633
634 // Find break point objects for this location, if any, and evaluate them.
635 // Return an array of break point objects that evaluated true, or an empty
636 // handle if none evaluated true.
637 // has_break_points will be true, if there is any (non-instrumentation)
638 // breakpoint.
CheckBreakPoints(Handle<DebugInfo> debug_info,BreakLocation * location,bool * has_break_points)639 MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info,
640 BreakLocation* location,
641 bool* has_break_points) {
642 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
643 bool has_break_points_to_check =
644 break_points_active_ && location->HasBreakPoint(isolate_, debug_info);
645 if (!has_break_points_to_check) {
646 *has_break_points = false;
647 return {};
648 }
649
650 return Debug::GetHitBreakPoints(debug_info, location->position(),
651 has_break_points);
652 }
653
IsMutedAtCurrentLocation(JavaScriptFrame * frame)654 bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
655 // A break location is considered muted if break locations on the current
656 // statement have at least one break point, and all of these break points
657 // evaluate to false. Aside from not triggering a debug break event at the
658 // break location, we also do not trigger one for debugger statements, nor
659 // an exception event on exception at this location.
660 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
661 HandleScope scope(isolate_);
662 bool has_break_points;
663 MaybeHandle<FixedArray> checked =
664 GetHitBreakpointsAtCurrentStatement(frame, &has_break_points);
665 return has_break_points && checked.is_null();
666 }
667
GetHitBreakpointsAtCurrentStatement(JavaScriptFrame * frame,bool * has_break_points)668 MaybeHandle<FixedArray> Debug::GetHitBreakpointsAtCurrentStatement(
669 JavaScriptFrame* frame, bool* has_break_points) {
670 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
671 FrameSummary summary = FrameSummary::GetTop(frame);
672 Handle<JSFunction> function = summary.AsJavaScript().function();
673 if (!function->shared().HasBreakInfo()) {
674 *has_break_points = false;
675 return {};
676 }
677 Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(), isolate_);
678 // Enter the debugger.
679 DebugScope debug_scope(this);
680 std::vector<BreakLocation> break_locations;
681 BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations);
682 return CheckBreakPointsForLocations(debug_info, break_locations,
683 has_break_points);
684 }
685
686 // Check whether a single break point object is triggered.
CheckBreakPoint(Handle<BreakPoint> break_point,bool is_break_at_entry)687 bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
688 bool is_break_at_entry) {
689 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
690 HandleScope scope(isolate_);
691
692 // Instrumentation breakpoints are handled separately.
693 if (break_point->id() == kInstrumentationId) {
694 return false;
695 }
696
697 if (!break_point->condition().length()) return true;
698 Handle<String> condition(break_point->condition(), isolate_);
699 MaybeHandle<Object> maybe_result;
700 Handle<Object> result;
701
702 if (is_break_at_entry) {
703 maybe_result = DebugEvaluate::WithTopmostArguments(isolate_, condition);
704 } else {
705 // Since we call CheckBreakpoint only for deoptimized frame on top of stack,
706 // we can use 0 as index of inlined frame.
707 const int inlined_jsframe_index = 0;
708 const bool throw_on_side_effect = false;
709 maybe_result =
710 DebugEvaluate::Local(isolate_, break_frame_id(), inlined_jsframe_index,
711 condition, throw_on_side_effect);
712 }
713
714 if (!maybe_result.ToHandle(&result)) {
715 if (isolate_->has_pending_exception()) {
716 isolate_->clear_pending_exception();
717 }
718 return false;
719 }
720 return result->BooleanValue(isolate_);
721 }
722
SetBreakpoint(Handle<SharedFunctionInfo> shared,Handle<BreakPoint> break_point,int * source_position)723 bool Debug::SetBreakpoint(Handle<SharedFunctionInfo> shared,
724 Handle<BreakPoint> break_point,
725 int* source_position) {
726 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
727 HandleScope scope(isolate_);
728
729 // Make sure the function is compiled and has set up the debug info.
730 if (!EnsureBreakInfo(shared)) return false;
731 PrepareFunctionForDebugExecution(shared);
732
733 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
734 // Source positions starts with zero.
735 DCHECK_LE(0, *source_position);
736
737 // Find the break point and change it.
738 *source_position = FindBreakablePosition(debug_info, *source_position);
739 DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
740 // At least one active break point now.
741 DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
742
743 ClearBreakPoints(debug_info);
744 ApplyBreakPoints(debug_info);
745
746 feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
747 return true;
748 }
749
SetBreakPointForScript(Handle<Script> script,Handle<String> condition,int * source_position,int * id)750 bool Debug::SetBreakPointForScript(Handle<Script> script,
751 Handle<String> condition,
752 int* source_position, int* id) {
753 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
754 *id = ++thread_local_.last_breakpoint_id_;
755 Handle<BreakPoint> break_point =
756 isolate_->factory()->NewBreakPoint(*id, condition);
757 #if V8_ENABLE_WEBASSEMBLY
758 if (script->type() == Script::TYPE_WASM) {
759 RecordWasmScriptWithBreakpoints(script);
760 return WasmScript::SetBreakPoint(script, source_position, break_point);
761 }
762 #endif // V8_ENABLE_WEBASSEMBLY
763
764 HandleScope scope(isolate_);
765
766 // Obtain shared function info for the innermost function containing this
767 // position.
768 Handle<Object> result =
769 FindInnermostContainingFunctionInfo(script, *source_position);
770 if (result->IsUndefined(isolate_)) return false;
771
772 auto shared = Handle<SharedFunctionInfo>::cast(result);
773 if (!EnsureBreakInfo(shared)) return false;
774 PrepareFunctionForDebugExecution(shared);
775
776 // Find the nested shared function info that is closest to the position within
777 // the containing function.
778 shared = FindClosestSharedFunctionInfoFromPosition(*source_position, script,
779 shared);
780
781 // Set the breakpoint in the function.
782 return SetBreakpoint(shared, break_point, source_position);
783 }
784
FindBreakablePosition(Handle<DebugInfo> debug_info,int source_position)785 int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
786 int source_position) {
787 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
788 if (debug_info->CanBreakAtEntry()) {
789 return kBreakAtEntryPosition;
790 } else {
791 DCHECK(debug_info->HasInstrumentedBytecodeArray());
792 BreakIterator it(debug_info);
793 it.SkipToPosition(source_position);
794 return it.position();
795 }
796 }
797
ApplyBreakPoints(Handle<DebugInfo> debug_info)798 void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
799 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
800 DisallowGarbageCollection no_gc;
801 if (debug_info->CanBreakAtEntry()) {
802 debug_info->SetBreakAtEntry();
803 } else {
804 if (!debug_info->HasInstrumentedBytecodeArray()) return;
805 FixedArray break_points = debug_info->break_points();
806 for (int i = 0; i < break_points.length(); i++) {
807 if (break_points.get(i).IsUndefined(isolate_)) continue;
808 BreakPointInfo info = BreakPointInfo::cast(break_points.get(i));
809 if (info.GetBreakPointCount(isolate_) == 0) continue;
810 DCHECK(debug_info->HasInstrumentedBytecodeArray());
811 BreakIterator it(debug_info);
812 it.SkipToPosition(info.source_position());
813 it.SetDebugBreak();
814 }
815 }
816 debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints);
817 }
818
ClearBreakPoints(Handle<DebugInfo> debug_info)819 void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
820 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
821 if (debug_info->CanBreakAtEntry()) {
822 debug_info->ClearBreakAtEntry();
823 } else {
824 // If we attempt to clear breakpoints but none exist, simply return. This
825 // can happen e.g. CoverageInfos exist but no breakpoints are set.
826 if (!debug_info->HasInstrumentedBytecodeArray() ||
827 !debug_info->HasBreakInfo()) {
828 return;
829 }
830
831 DisallowGarbageCollection no_gc;
832 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
833 it.ClearDebugBreak();
834 }
835 }
836 }
837
ClearBreakPoint(Handle<BreakPoint> break_point)838 void Debug::ClearBreakPoint(Handle<BreakPoint> break_point) {
839 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
840 HandleScope scope(isolate_);
841
842 for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
843 node = node->next()) {
844 if (!node->debug_info()->HasBreakInfo()) continue;
845 Handle<Object> result = DebugInfo::FindBreakPointInfo(
846 isolate_, node->debug_info(), break_point);
847 if (result->IsUndefined(isolate_)) continue;
848 Handle<DebugInfo> debug_info = node->debug_info();
849 if (DebugInfo::ClearBreakPoint(isolate_, debug_info, break_point)) {
850 ClearBreakPoints(debug_info);
851 if (debug_info->GetBreakPointCount(isolate_) == 0) {
852 RemoveBreakInfoAndMaybeFree(debug_info);
853 } else {
854 ApplyBreakPoints(debug_info);
855 }
856 return;
857 }
858 }
859 }
860
GetFunctionDebuggingId(Handle<JSFunction> function)861 int Debug::GetFunctionDebuggingId(Handle<JSFunction> function) {
862 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
863 Handle<SharedFunctionInfo> shared = handle(function->shared(), isolate_);
864 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
865 int id = debug_info->debugging_id();
866 if (id == DebugInfo::kNoDebuggingId) {
867 id = isolate_->heap()->NextDebuggingId();
868 debug_info->set_debugging_id(id);
869 }
870 return id;
871 }
872
SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,Handle<String> condition,int * id,BreakPointKind kind)873 bool Debug::SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
874 Handle<String> condition, int* id,
875 BreakPointKind kind) {
876 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
877 if (kind == kInstrumentation) {
878 *id = kInstrumentationId;
879 } else {
880 *id = ++thread_local_.last_breakpoint_id_;
881 }
882 Handle<BreakPoint> breakpoint =
883 isolate_->factory()->NewBreakPoint(*id, condition);
884 int source_position = 0;
885 #if V8_ENABLE_WEBASSEMBLY
886 // Handle wasm function.
887 if (shared->HasWasmExportedFunctionData()) {
888 int func_index = shared->wasm_exported_function_data().function_index();
889 Handle<WasmInstanceObject> wasm_instance(
890 shared->wasm_exported_function_data().instance(), isolate_);
891 Handle<Script> script(Script::cast(wasm_instance->module_object().script()),
892 isolate_);
893 return WasmScript::SetBreakPointOnFirstBreakableForFunction(
894 script, func_index, breakpoint);
895 }
896 #endif // V8_ENABLE_WEBASSEMBLY
897 return SetBreakpoint(shared, breakpoint, &source_position);
898 }
899
RemoveBreakpoint(int id)900 void Debug::RemoveBreakpoint(int id) {
901 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
902 Handle<BreakPoint> breakpoint = isolate_->factory()->NewBreakPoint(
903 id, isolate_->factory()->empty_string());
904 ClearBreakPoint(breakpoint);
905 }
906
907 #if V8_ENABLE_WEBASSEMBLY
SetInstrumentationBreakpointForWasmScript(Handle<Script> script,int * id)908 void Debug::SetInstrumentationBreakpointForWasmScript(Handle<Script> script,
909 int* id) {
910 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
911 DCHECK_EQ(Script::TYPE_WASM, script->type());
912 *id = kInstrumentationId;
913
914 Handle<BreakPoint> break_point = isolate_->factory()->NewBreakPoint(
915 *id, isolate_->factory()->empty_string());
916 RecordWasmScriptWithBreakpoints(script);
917 WasmScript::SetInstrumentationBreakpoint(script, break_point);
918 }
919
RemoveBreakpointForWasmScript(Handle<Script> script,int id)920 void Debug::RemoveBreakpointForWasmScript(Handle<Script> script, int id) {
921 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
922 if (script->type() == Script::TYPE_WASM) {
923 WasmScript::ClearBreakPointById(script, id);
924 }
925 }
926
RecordWasmScriptWithBreakpoints(Handle<Script> script)927 void Debug::RecordWasmScriptWithBreakpoints(Handle<Script> script) {
928 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
929 if (wasm_scripts_with_break_points_.is_null()) {
930 Handle<WeakArrayList> new_list = isolate_->factory()->NewWeakArrayList(4);
931 wasm_scripts_with_break_points_ =
932 isolate_->global_handles()->Create(*new_list);
933 }
934 {
935 DisallowGarbageCollection no_gc;
936 for (int idx = wasm_scripts_with_break_points_->length() - 1; idx >= 0;
937 --idx) {
938 HeapObject wasm_script;
939 if (wasm_scripts_with_break_points_->Get(idx).GetHeapObject(
940 &wasm_script) &&
941 wasm_script == *script) {
942 return;
943 }
944 }
945 }
946 Handle<WeakArrayList> new_list = WeakArrayList::Append(
947 isolate_, wasm_scripts_with_break_points_, MaybeObjectHandle{script});
948 if (*new_list != *wasm_scripts_with_break_points_) {
949 isolate_->global_handles()->Destroy(
950 wasm_scripts_with_break_points_.location());
951 wasm_scripts_with_break_points_ =
952 isolate_->global_handles()->Create(*new_list);
953 }
954 }
955 #endif // V8_ENABLE_WEBASSEMBLY
956
957 // Clear out all the debug break code.
ClearAllBreakPoints()958 void Debug::ClearAllBreakPoints() {
959 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
960 ClearAllDebugInfos([=](Handle<DebugInfo> info) {
961 ClearBreakPoints(info);
962 info->ClearBreakInfo(isolate_);
963 });
964 #if V8_ENABLE_WEBASSEMBLY
965 // Clear all wasm breakpoints.
966 if (!wasm_scripts_with_break_points_.is_null()) {
967 DisallowGarbageCollection no_gc;
968 for (int idx = wasm_scripts_with_break_points_->length() - 1; idx >= 0;
969 --idx) {
970 HeapObject raw_wasm_script;
971 if (wasm_scripts_with_break_points_->Get(idx).GetHeapObject(
972 &raw_wasm_script)) {
973 Script wasm_script = Script::cast(raw_wasm_script);
974 WasmScript::ClearAllBreakpoints(wasm_script);
975 wasm_script.wasm_native_module()->GetDebugInfo()->RemoveIsolate(
976 isolate_);
977 }
978 }
979 wasm_scripts_with_break_points_ = Handle<WeakArrayList>{};
980 }
981 #endif // V8_ENABLE_WEBASSEMBLY
982 }
983
FloodWithOneShot(Handle<SharedFunctionInfo> shared,bool returns_only)984 void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
985 bool returns_only) {
986 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
987 if (IsBlackboxed(shared)) return;
988 // Make sure the function is compiled and has set up the debug info.
989 if (!EnsureBreakInfo(shared)) return;
990 PrepareFunctionForDebugExecution(shared);
991
992 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
993 // Flood the function with break points.
994 DCHECK(debug_info->HasInstrumentedBytecodeArray());
995 for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
996 if (returns_only && !it.GetBreakLocation().IsReturnOrSuspend()) continue;
997 it.SetDebugBreak();
998 }
999 }
1000
ChangeBreakOnException(ExceptionBreakType type,bool enable)1001 void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
1002 if (type == BreakUncaughtException) {
1003 break_on_uncaught_exception_ = enable;
1004 } else {
1005 break_on_exception_ = enable;
1006 }
1007 }
1008
IsBreakOnException(ExceptionBreakType type)1009 bool Debug::IsBreakOnException(ExceptionBreakType type) {
1010 if (type == BreakUncaughtException) {
1011 return break_on_uncaught_exception_;
1012 } else {
1013 return break_on_exception_;
1014 }
1015 }
1016
GetHitBreakPoints(Handle<DebugInfo> debug_info,int position,bool * has_break_points)1017 MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<DebugInfo> debug_info,
1018 int position,
1019 bool* has_break_points) {
1020 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1021 Handle<Object> break_points = debug_info->GetBreakPoints(isolate_, position);
1022 bool is_break_at_entry = debug_info->BreakAtEntry();
1023 DCHECK(!break_points->IsUndefined(isolate_));
1024 if (!break_points->IsFixedArray()) {
1025 const Handle<BreakPoint> break_point =
1026 Handle<BreakPoint>::cast(break_points);
1027 *has_break_points = break_point->id() != kInstrumentationId;
1028 if (!CheckBreakPoint(break_point, is_break_at_entry)) {
1029 return {};
1030 }
1031 Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1);
1032 break_points_hit->set(0, *break_points);
1033 return break_points_hit;
1034 }
1035
1036 Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
1037 int num_objects = array->length();
1038 Handle<FixedArray> break_points_hit =
1039 isolate_->factory()->NewFixedArray(num_objects);
1040 int break_points_hit_count = 0;
1041 *has_break_points = false;
1042 for (int i = 0; i < num_objects; ++i) {
1043 Handle<BreakPoint> break_point =
1044 Handle<BreakPoint>::cast(handle(array->get(i), isolate_));
1045 *has_break_points |= break_point->id() != kInstrumentationId;
1046 if (CheckBreakPoint(break_point, is_break_at_entry)) {
1047 break_points_hit->set(break_points_hit_count++, *break_point);
1048 }
1049 }
1050 if (break_points_hit_count == 0) return {};
1051 break_points_hit->Shrink(isolate_, break_points_hit_count);
1052 return break_points_hit;
1053 }
1054
SetBreakOnNextFunctionCall()1055 void Debug::SetBreakOnNextFunctionCall() {
1056 // This method forces V8 to break on next function call regardless current
1057 // last_step_action_. If any break happens between SetBreakOnNextFunctionCall
1058 // and ClearBreakOnNextFunctionCall, we will clear this flag and stepping. If
1059 // break does not happen, e.g. all called functions are blackboxed or no
1060 // function is called, then we will clear this flag and let stepping continue
1061 // its normal business.
1062 thread_local_.break_on_next_function_call_ = true;
1063 UpdateHookOnFunctionCall();
1064 }
1065
ClearBreakOnNextFunctionCall()1066 void Debug::ClearBreakOnNextFunctionCall() {
1067 thread_local_.break_on_next_function_call_ = false;
1068 UpdateHookOnFunctionCall();
1069 }
1070
PrepareStepIn(Handle<JSFunction> function)1071 void Debug::PrepareStepIn(Handle<JSFunction> function) {
1072 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1073 CHECK(last_step_action() >= StepInto || break_on_next_function_call());
1074 if (ignore_events()) return;
1075 if (in_debug_scope()) return;
1076 if (break_disabled()) return;
1077 Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
1078 if (IsBlackboxed(shared)) return;
1079 if (*function == thread_local_.ignore_step_into_function_) return;
1080 thread_local_.ignore_step_into_function_ = Smi::zero();
1081 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
1082 }
1083
PrepareStepInSuspendedGenerator()1084 void Debug::PrepareStepInSuspendedGenerator() {
1085 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1086 CHECK(has_suspended_generator());
1087 if (ignore_events()) return;
1088 if (in_debug_scope()) return;
1089 if (break_disabled()) return;
1090 thread_local_.last_step_action_ = StepInto;
1091 UpdateHookOnFunctionCall();
1092 Handle<JSFunction> function(
1093 JSGeneratorObject::cast(thread_local_.suspended_generator_).function(),
1094 isolate_);
1095 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
1096 clear_suspended_generator();
1097 }
1098
PrepareStepOnThrow()1099 void Debug::PrepareStepOnThrow() {
1100 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1101 if (last_step_action() == StepNone) return;
1102 if (ignore_events()) return;
1103 if (in_debug_scope()) return;
1104 if (break_disabled()) return;
1105
1106 ClearOneShot();
1107
1108 int current_frame_count = CurrentFrameCount();
1109
1110 // Iterate through the JavaScript stack looking for handlers.
1111 JavaScriptFrameIterator it(isolate_);
1112 while (!it.done()) {
1113 JavaScriptFrame* frame = it.frame();
1114 if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break;
1115 std::vector<SharedFunctionInfo> infos;
1116 frame->GetFunctions(&infos);
1117 current_frame_count -= infos.size();
1118 it.Advance();
1119 }
1120
1121 // No handler found. Nothing to instrument.
1122 if (it.done()) return;
1123
1124 bool found_handler = false;
1125 // Iterate frames, including inlined frames. First, find the handler frame.
1126 // Then skip to the frame we want to break in, then instrument for stepping.
1127 for (; !it.done(); it.Advance()) {
1128 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
1129 if (last_step_action() == StepInto) {
1130 // Deoptimize frame to ensure calls are checked for step-in.
1131 Deoptimizer::DeoptimizeFunction(frame->function());
1132 }
1133 std::vector<FrameSummary> summaries;
1134 frame->Summarize(&summaries);
1135 for (size_t i = summaries.size(); i != 0; i--, current_frame_count--) {
1136 const FrameSummary& summary = summaries[i - 1];
1137 if (!found_handler) {
1138 // We have yet to find the handler. If the frame inlines multiple
1139 // functions, we have to check each one for the handler.
1140 // If it only contains one function, we already found the handler.
1141 if (summaries.size() > 1) {
1142 Handle<AbstractCode> code = summary.AsJavaScript().abstract_code();
1143 CHECK_EQ(CodeKind::INTERPRETED_FUNCTION, code->kind());
1144 HandlerTable table(code->GetBytecodeArray());
1145 int code_offset = summary.code_offset();
1146 HandlerTable::CatchPrediction prediction;
1147 int index = table.LookupRange(code_offset, nullptr, &prediction);
1148 if (index > 0) found_handler = true;
1149 } else {
1150 found_handler = true;
1151 }
1152 }
1153
1154 if (found_handler) {
1155 // We found the handler. If we are stepping next or out, we need to
1156 // iterate until we found the suitable target frame to break in.
1157 if ((last_step_action() == StepOver || last_step_action() == StepOut) &&
1158 current_frame_count > thread_local_.target_frame_count_) {
1159 continue;
1160 }
1161 Handle<SharedFunctionInfo> info(
1162 summary.AsJavaScript().function()->shared(), isolate_);
1163 if (IsBlackboxed(info)) continue;
1164 FloodWithOneShot(info);
1165 return;
1166 }
1167 }
1168 }
1169 }
1170
PrepareStep(StepAction step_action)1171 void Debug::PrepareStep(StepAction step_action) {
1172 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1173 HandleScope scope(isolate_);
1174
1175 DCHECK(in_debug_scope());
1176
1177 // Get the frame where the execution has stopped and skip the debug frame if
1178 // any. The debug frame will only be present if execution was stopped due to
1179 // hitting a break point. In other situations (e.g. unhandled exception) the
1180 // debug frame is not present.
1181 StackFrameId frame_id = break_frame_id();
1182 // If there is no JavaScript stack don't do anything.
1183 if (frame_id == StackFrameId::NO_ID) return;
1184
1185 feature_tracker()->Track(DebugFeatureTracker::kStepping);
1186
1187 thread_local_.last_step_action_ = step_action;
1188
1189 StackTraceFrameIterator frames_it(isolate_, frame_id);
1190 CommonFrame* frame = frames_it.frame();
1191
1192 BreakLocation location = BreakLocation::Invalid();
1193 Handle<SharedFunctionInfo> shared;
1194 int current_frame_count = CurrentFrameCount();
1195
1196 if (frame->is_java_script()) {
1197 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
1198 DCHECK(js_frame->function().IsJSFunction());
1199
1200 // Get the debug info (create it if it does not exist).
1201 auto summary = FrameSummary::GetTop(frame).AsJavaScript();
1202 Handle<JSFunction> function(summary.function());
1203 shared = Handle<SharedFunctionInfo>(function->shared(), isolate_);
1204 if (!EnsureBreakInfo(shared)) return;
1205 PrepareFunctionForDebugExecution(shared);
1206
1207 // PrepareFunctionForDebugExecution can invalidate Baseline frames
1208 js_frame = JavaScriptFrame::cast(frames_it.Reframe());
1209
1210 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1211 location = BreakLocation::FromFrame(debug_info, js_frame);
1212
1213 // Any step at a return is a step-out, and a step-out at a suspend behaves
1214 // like a return.
1215 if (location.IsReturn() ||
1216 (location.IsSuspend() &&
1217 (step_action == StepOut || (IsGeneratorFunction(shared->kind()) &&
1218 location.generator_suspend_id() == 0)))) {
1219 // On StepOut we'll ignore our further calls to current function in
1220 // PrepareStepIn callback.
1221 if (last_step_action() == StepOut) {
1222 thread_local_.ignore_step_into_function_ = *function;
1223 }
1224 step_action = StepOut;
1225 thread_local_.last_step_action_ = StepInto;
1226 }
1227
1228 // We need to schedule DebugOnFunction call callback
1229 UpdateHookOnFunctionCall();
1230
1231 // A step-next in blackboxed function is a step-out.
1232 if (step_action == StepOver && IsBlackboxed(shared)) step_action = StepOut;
1233
1234 thread_local_.last_statement_position_ =
1235 summary.abstract_code()->SourceStatementPosition(summary.code_offset());
1236 thread_local_.last_frame_count_ = current_frame_count;
1237 // No longer perform the current async step.
1238 clear_suspended_generator();
1239 #if V8_ENABLE_WEBASSEMBLY
1240 } else if (frame->is_wasm() && step_action != StepOut) {
1241 // Handle stepping in wasm.
1242 WasmFrame* wasm_frame = WasmFrame::cast(frame);
1243 auto* debug_info = wasm_frame->native_module()->GetDebugInfo();
1244 if (debug_info->PrepareStep(wasm_frame)) {
1245 UpdateHookOnFunctionCall();
1246 return;
1247 }
1248 // If the wasm code is not debuggable or will return after this step
1249 // (indicated by {PrepareStep} returning false), then step out of that frame
1250 // instead.
1251 step_action = StepOut;
1252 UpdateHookOnFunctionCall();
1253 #endif // V8_ENABLE_WEBASSEMBLY
1254 }
1255
1256 switch (step_action) {
1257 case StepNone:
1258 UNREACHABLE();
1259 case StepOut: {
1260 // Clear last position info. For stepping out it does not matter.
1261 thread_local_.last_statement_position_ = kNoSourcePosition;
1262 thread_local_.last_frame_count_ = -1;
1263 if (!shared.is_null()) {
1264 if (!location.IsReturnOrSuspend() && !IsBlackboxed(shared)) {
1265 // At not return position we flood return positions with one shots and
1266 // will repeat StepOut automatically at next break.
1267 thread_local_.target_frame_count_ = current_frame_count;
1268 thread_local_.fast_forward_to_return_ = true;
1269 FloodWithOneShot(shared, true);
1270 return;
1271 }
1272 if (IsAsyncFunction(shared->kind())) {
1273 // Stepping out of an async function whose implicit promise is awaited
1274 // by some other async function, should resume the latter. The return
1275 // value here is either a JSPromise or a JSGeneratorObject (for the
1276 // initial yield of async generators).
1277 Handle<JSReceiver> return_value(
1278 JSReceiver::cast(thread_local_.return_value_), isolate_);
1279 Handle<Object> awaited_by = JSReceiver::GetDataProperty(
1280 isolate_, return_value,
1281 isolate_->factory()->promise_awaited_by_symbol());
1282 if (awaited_by->IsJSGeneratorObject()) {
1283 DCHECK(!has_suspended_generator());
1284 thread_local_.suspended_generator_ = *awaited_by;
1285 ClearStepping();
1286 return;
1287 }
1288 }
1289 }
1290 // Skip the current frame, find the first frame we want to step out to
1291 // and deoptimize every frame along the way.
1292 bool in_current_frame = true;
1293 for (; !frames_it.done(); frames_it.Advance()) {
1294 #if V8_ENABLE_WEBASSEMBLY
1295 if (frames_it.frame()->is_wasm()) {
1296 if (in_current_frame) {
1297 in_current_frame = false;
1298 continue;
1299 }
1300 // Handle stepping out into Wasm.
1301 WasmFrame* wasm_frame = WasmFrame::cast(frames_it.frame());
1302 auto* debug_info = wasm_frame->native_module()->GetDebugInfo();
1303 debug_info->PrepareStepOutTo(wasm_frame);
1304 return;
1305 }
1306 #endif // V8_ENABLE_WEBASSEMBLY
1307 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frames_it.frame());
1308 if (last_step_action() == StepInto) {
1309 // Deoptimize frame to ensure calls are checked for step-in.
1310 Deoptimizer::DeoptimizeFunction(js_frame->function());
1311 }
1312 HandleScope inner_scope(isolate_);
1313 std::vector<Handle<SharedFunctionInfo>> infos;
1314 js_frame->GetFunctions(&infos);
1315 for (; !infos.empty(); current_frame_count--) {
1316 Handle<SharedFunctionInfo> info = infos.back();
1317 infos.pop_back();
1318 if (in_current_frame) {
1319 // We want to step out, so skip the current frame.
1320 in_current_frame = false;
1321 continue;
1322 }
1323 if (IsBlackboxed(info)) continue;
1324 FloodWithOneShot(info);
1325 thread_local_.target_frame_count_ = current_frame_count;
1326 return;
1327 }
1328 }
1329 break;
1330 }
1331 case StepOver:
1332 thread_local_.target_frame_count_ = current_frame_count;
1333 V8_FALLTHROUGH;
1334 case StepInto:
1335 FloodWithOneShot(shared);
1336 break;
1337 }
1338 }
1339
1340 // Simple function for returning the source positions for active break points.
GetSourceBreakLocations(Isolate * isolate,Handle<SharedFunctionInfo> shared)1341 Handle<Object> Debug::GetSourceBreakLocations(
1342 Isolate* isolate, Handle<SharedFunctionInfo> shared) {
1343 RCS_SCOPE(isolate, RuntimeCallCounterId::kDebugger);
1344 if (!shared->HasBreakInfo()) {
1345 return isolate->factory()->undefined_value();
1346 }
1347
1348 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate);
1349 if (debug_info->GetBreakPointCount(isolate) == 0) {
1350 return isolate->factory()->undefined_value();
1351 }
1352 Handle<FixedArray> locations = isolate->factory()->NewFixedArray(
1353 debug_info->GetBreakPointCount(isolate));
1354 int count = 0;
1355 for (int i = 0; i < debug_info->break_points().length(); ++i) {
1356 if (!debug_info->break_points().get(i).IsUndefined(isolate)) {
1357 BreakPointInfo break_point_info =
1358 BreakPointInfo::cast(debug_info->break_points().get(i));
1359 int break_points = break_point_info.GetBreakPointCount(isolate);
1360 if (break_points == 0) continue;
1361 for (int j = 0; j < break_points; ++j) {
1362 locations->set(count++,
1363 Smi::FromInt(break_point_info.source_position()));
1364 }
1365 }
1366 }
1367 return locations;
1368 }
1369
ClearStepping()1370 void Debug::ClearStepping() {
1371 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1372 // Clear the various stepping setup.
1373 ClearOneShot();
1374
1375 thread_local_.last_step_action_ = StepNone;
1376 thread_local_.last_statement_position_ = kNoSourcePosition;
1377 thread_local_.ignore_step_into_function_ = Smi::zero();
1378 thread_local_.fast_forward_to_return_ = false;
1379 thread_local_.last_frame_count_ = -1;
1380 thread_local_.target_frame_count_ = -1;
1381 thread_local_.break_on_next_function_call_ = false;
1382 UpdateHookOnFunctionCall();
1383 }
1384
1385 // Clears all the one-shot break points that are currently set. Normally this
1386 // function is called each time a break point is hit as one shot break points
1387 // are used to support stepping.
ClearOneShot()1388 void Debug::ClearOneShot() {
1389 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1390 // The current implementation just runs through all the breakpoints. When the
1391 // last break point for a function is removed that function is automatically
1392 // removed from the list.
1393 for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
1394 node = node->next()) {
1395 Handle<DebugInfo> debug_info = node->debug_info();
1396 ClearBreakPoints(debug_info);
1397 ApplyBreakPoints(debug_info);
1398 }
1399 }
1400
1401 namespace {
1402 class DiscardBaselineCodeVisitor : public ThreadVisitor {
1403 public:
DiscardBaselineCodeVisitor(SharedFunctionInfo shared)1404 explicit DiscardBaselineCodeVisitor(SharedFunctionInfo shared)
1405 : shared_(shared) {}
DiscardBaselineCodeVisitor()1406 DiscardBaselineCodeVisitor() : shared_(SharedFunctionInfo()) {}
1407
VisitThread(Isolate * isolate,ThreadLocalTop * top)1408 void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
1409 DisallowGarbageCollection diallow_gc;
1410 bool deopt_all = shared_ == SharedFunctionInfo();
1411 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
1412 if (!deopt_all && it.frame()->function().shared() != shared_) continue;
1413 if (it.frame()->type() == StackFrame::BASELINE) {
1414 BaselineFrame* frame = BaselineFrame::cast(it.frame());
1415 int bytecode_offset = frame->GetBytecodeOffset();
1416 Address* pc_addr = frame->pc_address();
1417 Address advance = BUILTIN_CODE(isolate, InterpreterEnterAtNextBytecode)
1418 ->InstructionStart();
1419 PointerAuthentication::ReplacePC(pc_addr, advance, kSystemPointerSize);
1420 InterpretedFrame::cast(it.Reframe())
1421 ->PatchBytecodeOffset(bytecode_offset);
1422 } else if (it.frame()->type() == StackFrame::INTERPRETED) {
1423 // Check if the PC is a baseline entry trampoline. If it is, replace it
1424 // with the corresponding interpreter entry trampoline.
1425 // This is the case if a baseline function was inlined into a function
1426 // we deoptimized in the debugger and are stepping into it.
1427 JavaScriptFrame* frame = it.frame();
1428 Address pc = frame->pc();
1429 Builtin builtin = OffHeapInstructionStream::TryLookupCode(isolate, pc);
1430 if (builtin == Builtin::kBaselineOrInterpreterEnterAtBytecode ||
1431 builtin == Builtin::kBaselineOrInterpreterEnterAtNextBytecode) {
1432 Address* pc_addr = frame->pc_address();
1433 Builtin advance =
1434 builtin == Builtin::kBaselineOrInterpreterEnterAtBytecode
1435 ? Builtin::kInterpreterEnterAtBytecode
1436 : Builtin::kInterpreterEnterAtNextBytecode;
1437 Address advance_pc =
1438 isolate->builtins()->code(advance).InstructionStart();
1439 PointerAuthentication::ReplacePC(pc_addr, advance_pc,
1440 kSystemPointerSize);
1441 }
1442 }
1443 }
1444 }
1445
1446 private:
1447 SharedFunctionInfo shared_;
1448 };
1449 } // namespace
1450
DiscardBaselineCode(SharedFunctionInfo shared)1451 void Debug::DiscardBaselineCode(SharedFunctionInfo shared) {
1452 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1453 DCHECK(shared.HasBaselineCode());
1454 Isolate* isolate = shared.GetIsolate();
1455 DiscardBaselineCodeVisitor visitor(shared);
1456 visitor.VisitThread(isolate, isolate->thread_local_top());
1457 isolate->thread_manager()->IterateArchivedThreads(&visitor);
1458 // TODO(v8:11429): Avoid this heap walk somehow.
1459 HeapObjectIterator iterator(isolate->heap());
1460 auto trampoline = BUILTIN_CODE(isolate, InterpreterEntryTrampoline);
1461 shared.FlushBaselineCode();
1462 for (HeapObject obj = iterator.Next(); !obj.is_null();
1463 obj = iterator.Next()) {
1464 if (obj.IsJSFunction()) {
1465 JSFunction fun = JSFunction::cast(obj);
1466 if (fun.shared() == shared && fun.ActiveTierIsBaseline()) {
1467 fun.set_code(*trampoline);
1468 }
1469 }
1470 }
1471 }
1472
DiscardAllBaselineCode()1473 void Debug::DiscardAllBaselineCode() {
1474 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1475 DiscardBaselineCodeVisitor visitor;
1476 visitor.VisitThread(isolate_, isolate_->thread_local_top());
1477 HeapObjectIterator iterator(isolate_->heap());
1478 auto trampoline = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline);
1479 isolate_->thread_manager()->IterateArchivedThreads(&visitor);
1480 for (HeapObject obj = iterator.Next(); !obj.is_null();
1481 obj = iterator.Next()) {
1482 if (obj.IsJSFunction()) {
1483 JSFunction fun = JSFunction::cast(obj);
1484 if (fun.ActiveTierIsBaseline()) {
1485 fun.set_code(*trampoline);
1486 }
1487 } else if (obj.IsSharedFunctionInfo()) {
1488 SharedFunctionInfo shared = SharedFunctionInfo::cast(obj);
1489 if (shared.HasBaselineCode()) {
1490 shared.FlushBaselineCode();
1491 }
1492 }
1493 }
1494 }
1495
DeoptimizeFunction(Handle<SharedFunctionInfo> shared)1496 void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
1497 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1498 // Deoptimize all code compiled from this shared function info including
1499 // inlining.
1500 isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock);
1501
1502 if (shared->HasBaselineCode()) {
1503 DiscardBaselineCode(*shared);
1504 }
1505
1506 bool found_something = false;
1507 Code::OptimizedCodeIterator iterator(isolate_);
1508 do {
1509 Code code = iterator.Next();
1510 if (code.is_null()) break;
1511 if (code.Inlines(*shared)) {
1512 code.set_marked_for_deoptimization(true);
1513 found_something = true;
1514 }
1515 } while (true);
1516
1517 if (found_something) {
1518 // Only go through with the deoptimization if something was found.
1519 Deoptimizer::DeoptimizeMarkedCode(isolate_);
1520 }
1521 }
1522
PrepareFunctionForDebugExecution(Handle<SharedFunctionInfo> shared)1523 void Debug::PrepareFunctionForDebugExecution(
1524 Handle<SharedFunctionInfo> shared) {
1525 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1526 // To prepare bytecode for debugging, we already need to have the debug
1527 // info (containing the debug copy) upfront, but since we do not recompile,
1528 // preparing for break points cannot fail.
1529 DCHECK(shared->is_compiled());
1530 DCHECK(shared->HasDebugInfo());
1531 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1532 if (debug_info->flags(kRelaxedLoad) & DebugInfo::kPreparedForDebugExecution) {
1533 return;
1534 }
1535
1536 // Have to discard baseline code before installing debug bytecode, since the
1537 // bytecode array field on the baseline code object is immutable.
1538 if (debug_info->CanBreakAtEntry()) {
1539 // Deopt everything in case the function is inlined anywhere.
1540 Deoptimizer::DeoptimizeAll(isolate_);
1541 DiscardAllBaselineCode();
1542 } else {
1543 DeoptimizeFunction(shared);
1544 }
1545
1546 if (shared->HasBytecodeArray()) {
1547 DCHECK(!shared->HasBaselineCode());
1548 SharedFunctionInfo::InstallDebugBytecode(shared, isolate_);
1549 }
1550
1551 if (debug_info->CanBreakAtEntry()) {
1552 InstallDebugBreakTrampoline();
1553 } else {
1554 // Update PCs on the stack to point to recompiled code.
1555 RedirectActiveFunctions redirect_visitor(
1556 *shared, RedirectActiveFunctions::Mode::kUseDebugBytecode);
1557 redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
1558 isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
1559 }
1560
1561 debug_info->set_flags(
1562 debug_info->flags(kRelaxedLoad) | DebugInfo::kPreparedForDebugExecution,
1563 kRelaxedStore);
1564 }
1565
InstallDebugBreakTrampoline()1566 void Debug::InstallDebugBreakTrampoline() {
1567 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1568 // Check the list of debug infos whether the debug break trampoline needs to
1569 // be installed. If that's the case, iterate the heap for functions to rewire
1570 // to the trampoline.
1571 HandleScope scope(isolate_);
1572 // If there is a breakpoint at function entry, we need to install trampoline.
1573 bool needs_to_use_trampoline = false;
1574 // If there we break at entry to an api callback, we need to clear ICs.
1575 bool needs_to_clear_ic = false;
1576 for (DebugInfoListNode* current = debug_info_list_; current != nullptr;
1577 current = current->next()) {
1578 if (current->debug_info()->CanBreakAtEntry()) {
1579 needs_to_use_trampoline = true;
1580 if (current->debug_info()->shared().IsApiFunction()) {
1581 needs_to_clear_ic = true;
1582 break;
1583 }
1584 }
1585 }
1586
1587 if (!needs_to_use_trampoline) return;
1588
1589 Handle<CodeT> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline);
1590 std::vector<Handle<JSFunction>> needs_compile;
1591 {
1592 HeapObjectIterator iterator(isolate_->heap());
1593 for (HeapObject obj = iterator.Next(); !obj.is_null();
1594 obj = iterator.Next()) {
1595 if (needs_to_clear_ic && obj.IsFeedbackVector()) {
1596 FeedbackVector::cast(obj).ClearSlots(isolate_);
1597 continue;
1598 } else if (obj.IsJSFunction()) {
1599 JSFunction fun = JSFunction::cast(obj);
1600 SharedFunctionInfo shared = fun.shared();
1601 if (!shared.HasDebugInfo()) continue;
1602 if (!shared.GetDebugInfo().CanBreakAtEntry()) continue;
1603 if (!fun.is_compiled()) {
1604 needs_compile.push_back(handle(fun, isolate_));
1605 } else {
1606 fun.set_code(*trampoline);
1607 }
1608 }
1609 }
1610 }
1611
1612 // By overwriting the function code with DebugBreakTrampoline, which tailcalls
1613 // to shared code, we bypass CompileLazy. Perform CompileLazy here instead.
1614 for (Handle<JSFunction> fun : needs_compile) {
1615 IsCompiledScope is_compiled_scope;
1616 Compiler::Compile(isolate_, fun, Compiler::CLEAR_EXCEPTION,
1617 &is_compiled_scope);
1618 DCHECK(is_compiled_scope.is_compiled());
1619 fun->set_code(*trampoline);
1620 }
1621 }
1622
1623 namespace {
1624 template <typename Iterator>
GetBreakablePositions(Iterator * it,int start_position,int end_position,std::vector<BreakLocation> * locations)1625 void GetBreakablePositions(Iterator* it, int start_position, int end_position,
1626 std::vector<BreakLocation>* locations) {
1627 while (!it->Done()) {
1628 if (it->position() >= start_position && it->position() < end_position) {
1629 locations->push_back(it->GetBreakLocation());
1630 }
1631 it->Next();
1632 }
1633 }
1634
FindBreakablePositions(Handle<DebugInfo> debug_info,int start_position,int end_position,std::vector<BreakLocation> * locations)1635 void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
1636 int end_position,
1637 std::vector<BreakLocation>* locations) {
1638 DCHECK(debug_info->HasInstrumentedBytecodeArray());
1639 BreakIterator it(debug_info);
1640 GetBreakablePositions(&it, start_position, end_position, locations);
1641 }
1642
CompileTopLevel(Isolate * isolate,Handle<Script> script)1643 bool CompileTopLevel(Isolate* isolate, Handle<Script> script) {
1644 UnoptimizedCompileState compile_state;
1645 ReusableUnoptimizedCompileState reusable_state(isolate);
1646 UnoptimizedCompileFlags flags =
1647 UnoptimizedCompileFlags::ForScriptCompile(isolate, *script);
1648 ParseInfo parse_info(isolate, flags, &compile_state, &reusable_state);
1649 IsCompiledScope is_compiled_scope;
1650 const MaybeHandle<SharedFunctionInfo> maybe_result =
1651 Compiler::CompileToplevel(&parse_info, script, isolate,
1652 &is_compiled_scope);
1653 if (maybe_result.is_null()) {
1654 if (isolate->has_pending_exception()) {
1655 isolate->clear_pending_exception();
1656 }
1657 return false;
1658 }
1659 return true;
1660 }
1661 } // namespace
1662
GetPossibleBreakpoints(Handle<Script> script,int start_position,int end_position,bool restrict_to_function,std::vector<BreakLocation> * locations)1663 bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
1664 int end_position, bool restrict_to_function,
1665 std::vector<BreakLocation>* locations) {
1666 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1667 if (restrict_to_function) {
1668 Handle<Object> result =
1669 FindInnermostContainingFunctionInfo(script, start_position);
1670 if (result->IsUndefined(isolate_)) return false;
1671
1672 // Make sure the function has set up the debug info.
1673 Handle<SharedFunctionInfo> shared =
1674 Handle<SharedFunctionInfo>::cast(result);
1675 if (!EnsureBreakInfo(shared)) return false;
1676 PrepareFunctionForDebugExecution(shared);
1677
1678 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1679 FindBreakablePositions(debug_info, start_position, end_position, locations);
1680 return true;
1681 }
1682
1683 HandleScope scope(isolate_);
1684 std::vector<Handle<SharedFunctionInfo>> candidates;
1685 if (!FindSharedFunctionInfosIntersectingRange(script, start_position,
1686 end_position, &candidates)) {
1687 return false;
1688 }
1689 for (const auto& candidate : candidates) {
1690 CHECK(candidate->HasBreakInfo());
1691 Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
1692 FindBreakablePositions(debug_info, start_position, end_position, locations);
1693 }
1694 return true;
1695 }
1696
1697 class SharedFunctionInfoFinder {
1698 public:
SharedFunctionInfoFinder(int target_position)1699 explicit SharedFunctionInfoFinder(int target_position)
1700 : current_start_position_(kNoSourcePosition),
1701 target_position_(target_position) {}
1702
NewCandidate(SharedFunctionInfo shared,JSFunction closure=JSFunction ())1703 void NewCandidate(SharedFunctionInfo shared,
1704 JSFunction closure = JSFunction()) {
1705 if (!shared.IsSubjectToDebugging()) return;
1706 int start_position = shared.function_token_position();
1707 if (start_position == kNoSourcePosition) {
1708 start_position = shared.StartPosition();
1709 }
1710
1711 if (start_position > target_position_) return;
1712 if (target_position_ >= shared.EndPosition()) {
1713 // The SharedFunctionInfo::EndPosition() is generally exclusive, but there
1714 // are assumptions in various places in the debugger that for script level
1715 // (toplevel function) there's an end position that is technically outside
1716 // the script. It might be worth revisiting the overall design here at
1717 // some point in the future.
1718 if (!shared.is_toplevel() || target_position_ > shared.EndPosition()) {
1719 return;
1720 }
1721 }
1722
1723 if (!current_candidate_.is_null()) {
1724 if (current_start_position_ == start_position &&
1725 shared.EndPosition() == current_candidate_.EndPosition()) {
1726 // If we already have a matching closure, do not throw it away.
1727 if (!current_candidate_closure_.is_null() && closure.is_null()) return;
1728 // If a top-level function contains only one function
1729 // declaration the source for the top-level and the function
1730 // is the same. In that case prefer the non top-level function.
1731 if (!current_candidate_.is_toplevel() && shared.is_toplevel()) return;
1732 } else if (start_position < current_start_position_ ||
1733 current_candidate_.EndPosition() < shared.EndPosition()) {
1734 return;
1735 }
1736 }
1737
1738 current_start_position_ = start_position;
1739 current_candidate_ = shared;
1740 current_candidate_closure_ = closure;
1741 }
1742
Result()1743 SharedFunctionInfo Result() { return current_candidate_; }
1744
ResultClosure()1745 JSFunction ResultClosure() { return current_candidate_closure_; }
1746
1747 private:
1748 SharedFunctionInfo current_candidate_;
1749 JSFunction current_candidate_closure_;
1750 int current_start_position_;
1751 int target_position_;
1752 DISALLOW_GARBAGE_COLLECTION(no_gc_)
1753 };
1754
1755 namespace {
FindSharedFunctionInfoCandidate(int position,Handle<Script> script,Isolate * isolate)1756 SharedFunctionInfo FindSharedFunctionInfoCandidate(int position,
1757 Handle<Script> script,
1758 Isolate* isolate) {
1759 SharedFunctionInfoFinder finder(position);
1760 SharedFunctionInfo::ScriptIterator iterator(isolate, *script);
1761 for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
1762 info = iterator.Next()) {
1763 finder.NewCandidate(info);
1764 }
1765 return finder.Result();
1766 }
1767 } // namespace
1768
FindClosestSharedFunctionInfoFromPosition(int position,Handle<Script> script,Handle<SharedFunctionInfo> outer_shared)1769 Handle<SharedFunctionInfo> Debug::FindClosestSharedFunctionInfoFromPosition(
1770 int position, Handle<Script> script,
1771 Handle<SharedFunctionInfo> outer_shared) {
1772 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1773 CHECK(outer_shared->HasBreakInfo());
1774 int closest_position = FindBreakablePosition(
1775 Handle<DebugInfo>(outer_shared->GetDebugInfo(), isolate_), position);
1776 Handle<SharedFunctionInfo> closest_candidate = outer_shared;
1777 if (closest_position == position) return outer_shared;
1778
1779 const int start_position = outer_shared->StartPosition();
1780 const int end_position = outer_shared->EndPosition();
1781 if (start_position == end_position) return outer_shared;
1782
1783 if (closest_position == 0) closest_position = end_position;
1784 std::vector<Handle<SharedFunctionInfo>> candidates;
1785 // Find all shared function infos of functions that are intersecting from
1786 // the requested position until the end of the enclosing function.
1787 if (!FindSharedFunctionInfosIntersectingRange(
1788 script, position, closest_position, &candidates)) {
1789 return outer_shared;
1790 }
1791
1792 for (auto candidate : candidates) {
1793 CHECK(candidate->HasBreakInfo());
1794 Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
1795 const int candidate_position = FindBreakablePosition(debug_info, position);
1796 if (candidate_position >= position &&
1797 candidate_position < closest_position) {
1798 closest_position = candidate_position;
1799 closest_candidate = candidate;
1800 }
1801 if (closest_position == position) break;
1802 }
1803 return closest_candidate;
1804 }
1805
FindSharedFunctionInfosIntersectingRange(Handle<Script> script,int start_position,int end_position,std::vector<Handle<SharedFunctionInfo>> * intersecting_shared)1806 bool Debug::FindSharedFunctionInfosIntersectingRange(
1807 Handle<Script> script, int start_position, int end_position,
1808 std::vector<Handle<SharedFunctionInfo>>* intersecting_shared) {
1809 bool candidateSubsumesRange = false;
1810 bool triedTopLevelCompile = false;
1811
1812 while (true) {
1813 std::vector<Handle<SharedFunctionInfo>> candidates;
1814 std::vector<IsCompiledScope> compiled_scopes;
1815 {
1816 DisallowGarbageCollection no_gc;
1817 SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
1818 for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
1819 info = iterator.Next()) {
1820 if (info.EndPosition() < start_position ||
1821 info.StartPosition() >= end_position) {
1822 continue;
1823 }
1824 candidateSubsumesRange |= info.StartPosition() <= start_position &&
1825 info.EndPosition() >= end_position;
1826 if (!info.IsSubjectToDebugging()) continue;
1827 if (!info.is_compiled() && !info.allows_lazy_compilation()) continue;
1828 candidates.push_back(i::handle(info, isolate_));
1829 }
1830 }
1831
1832 if (!triedTopLevelCompile && !candidateSubsumesRange &&
1833 script->shared_function_info_count() > 0) {
1834 DCHECK_LE(script->shared_function_info_count(),
1835 script->shared_function_infos().length());
1836 MaybeObject maybeToplevel = script->shared_function_infos().Get(0);
1837 HeapObject heap_object;
1838 const bool topLevelInfoExists =
1839 maybeToplevel->GetHeapObject(&heap_object) &&
1840 !heap_object.IsUndefined();
1841 if (!topLevelInfoExists) {
1842 triedTopLevelCompile = true;
1843 const bool success = CompileTopLevel(isolate_, script);
1844 if (!success) return false;
1845 continue;
1846 }
1847 }
1848
1849 bool was_compiled = false;
1850 for (const auto& candidate : candidates) {
1851 IsCompiledScope is_compiled_scope(candidate->is_compiled_scope(isolate_));
1852 if (!is_compiled_scope.is_compiled()) {
1853 // Code that cannot be compiled lazily are internal and not debuggable.
1854 DCHECK(candidate->allows_lazy_compilation());
1855 if (!Compiler::Compile(isolate_, candidate, Compiler::CLEAR_EXCEPTION,
1856 &is_compiled_scope)) {
1857 return false;
1858 } else {
1859 was_compiled = true;
1860 }
1861 }
1862 DCHECK(is_compiled_scope.is_compiled());
1863 compiled_scopes.push_back(is_compiled_scope);
1864 if (!EnsureBreakInfo(candidate)) return false;
1865 PrepareFunctionForDebugExecution(candidate);
1866 }
1867 if (was_compiled) continue;
1868 *intersecting_shared = std::move(candidates);
1869 return true;
1870 }
1871 UNREACHABLE();
1872 }
1873
1874 // We need to find a SFI for a literal that may not yet have been compiled yet,
1875 // and there may not be a JSFunction referencing it. Find the SFI closest to
1876 // the given position, compile it to reveal possible inner SFIs and repeat.
1877 // While we are at this, also ensure code with debug break slots so that we do
1878 // not have to compile a SFI without JSFunction, which is paifu for those that
1879 // cannot be compiled without context (need to find outer compilable SFI etc.)
FindInnermostContainingFunctionInfo(Handle<Script> script,int position)1880 Handle<Object> Debug::FindInnermostContainingFunctionInfo(Handle<Script> script,
1881 int position) {
1882 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1883 for (int iteration = 0;; iteration++) {
1884 // Go through all shared function infos associated with this script to
1885 // find the innermost function containing this position.
1886 // If there is no shared function info for this script at all, there is
1887 // no point in looking for it by walking the heap.
1888
1889 SharedFunctionInfo shared;
1890 IsCompiledScope is_compiled_scope;
1891 {
1892 shared = FindSharedFunctionInfoCandidate(position, script, isolate_);
1893 if (shared.is_null()) {
1894 if (iteration > 0) break;
1895 // It might be that the shared function info is not available as the
1896 // top level functions are removed due to the GC. Try to recompile
1897 // the top level functions.
1898 const bool success = CompileTopLevel(isolate_, script);
1899 if (!success) break;
1900 continue;
1901 }
1902 // We found it if it's already compiled.
1903 is_compiled_scope = shared.is_compiled_scope(isolate_);
1904 if (is_compiled_scope.is_compiled()) {
1905 Handle<SharedFunctionInfo> shared_handle(shared, isolate_);
1906 // If the iteration count is larger than 1, we had to compile the outer
1907 // function in order to create this shared function info. So there can
1908 // be no JSFunction referencing it. We can anticipate creating a debug
1909 // info while bypassing PrepareFunctionForDebugExecution.
1910 if (iteration > 1) {
1911 CreateBreakInfo(shared_handle);
1912 }
1913 return shared_handle;
1914 }
1915 }
1916 // If not, compile to reveal inner functions.
1917 HandleScope scope(isolate_);
1918 // Code that cannot be compiled lazily are internal and not debuggable.
1919 DCHECK(shared.allows_lazy_compilation());
1920 if (!Compiler::Compile(isolate_, handle(shared, isolate_),
1921 Compiler::CLEAR_EXCEPTION, &is_compiled_scope)) {
1922 break;
1923 }
1924 }
1925 return isolate_->factory()->undefined_value();
1926 }
1927
1928 // Ensures the debug information is present for shared.
EnsureBreakInfo(Handle<SharedFunctionInfo> shared)1929 bool Debug::EnsureBreakInfo(Handle<SharedFunctionInfo> shared) {
1930 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1931 // Return if we already have the break info for shared.
1932 if (shared->HasBreakInfo()) return true;
1933 if (!shared->IsSubjectToDebugging() && !CanBreakAtEntry(shared)) {
1934 return false;
1935 }
1936 IsCompiledScope is_compiled_scope = shared->is_compiled_scope(isolate_);
1937 if (!is_compiled_scope.is_compiled() &&
1938 !Compiler::Compile(isolate_, shared, Compiler::CLEAR_EXCEPTION,
1939 &is_compiled_scope, CreateSourcePositions::kYes)) {
1940 return false;
1941 }
1942 CreateBreakInfo(shared);
1943 return true;
1944 }
1945
CreateBreakInfo(Handle<SharedFunctionInfo> shared)1946 void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) {
1947 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1948 HandleScope scope(isolate_);
1949 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1950
1951 // Initialize with break information.
1952
1953 DCHECK(!debug_info->HasBreakInfo());
1954
1955 Factory* factory = isolate_->factory();
1956 Handle<FixedArray> break_points(
1957 factory->NewFixedArray(DebugInfo::kEstimatedNofBreakPointsInFunction));
1958
1959 int flags = debug_info->flags(kRelaxedLoad);
1960 flags |= DebugInfo::kHasBreakInfo;
1961 if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry;
1962 debug_info->set_flags(flags, kRelaxedStore);
1963 debug_info->set_break_points(*break_points);
1964
1965 SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared);
1966 }
1967
GetOrCreateDebugInfo(Handle<SharedFunctionInfo> shared)1968 Handle<DebugInfo> Debug::GetOrCreateDebugInfo(
1969 Handle<SharedFunctionInfo> shared) {
1970 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1971 if (shared->HasDebugInfo()) return handle(shared->GetDebugInfo(), isolate_);
1972
1973 // Create debug info and add it to the list.
1974 Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
1975 DebugInfoListNode* node = new DebugInfoListNode(isolate_, *debug_info);
1976 node->set_next(debug_info_list_);
1977 debug_info_list_ = node;
1978
1979 return debug_info;
1980 }
1981
InstallCoverageInfo(Handle<SharedFunctionInfo> shared,Handle<CoverageInfo> coverage_info)1982 void Debug::InstallCoverageInfo(Handle<SharedFunctionInfo> shared,
1983 Handle<CoverageInfo> coverage_info) {
1984 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1985 DCHECK(!coverage_info.is_null());
1986
1987 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1988
1989 DCHECK(!debug_info->HasCoverageInfo());
1990
1991 debug_info->set_flags(
1992 debug_info->flags(kRelaxedLoad) | DebugInfo::kHasCoverageInfo,
1993 kRelaxedStore);
1994 debug_info->set_coverage_info(*coverage_info);
1995 }
1996
RemoveAllCoverageInfos()1997 void Debug::RemoveAllCoverageInfos() {
1998 ClearAllDebugInfos(
1999 [=](Handle<DebugInfo> info) { info->ClearCoverageInfo(isolate_); });
2000 }
2001
ClearAllDebuggerHints()2002 void Debug::ClearAllDebuggerHints() {
2003 ClearAllDebugInfos(
2004 [=](Handle<DebugInfo> info) { info->set_debugger_hints(0); });
2005 }
2006
FindDebugInfo(Handle<DebugInfo> debug_info,DebugInfoListNode ** prev,DebugInfoListNode ** curr)2007 void Debug::FindDebugInfo(Handle<DebugInfo> debug_info,
2008 DebugInfoListNode** prev, DebugInfoListNode** curr) {
2009 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2010 HandleScope scope(isolate_);
2011 *prev = nullptr;
2012 *curr = debug_info_list_;
2013 while (*curr != nullptr) {
2014 if ((*curr)->debug_info().is_identical_to(debug_info)) return;
2015 *prev = *curr;
2016 *curr = (*curr)->next();
2017 }
2018
2019 UNREACHABLE();
2020 }
2021
ClearAllDebugInfos(const DebugInfoClearFunction & clear_function)2022 void Debug::ClearAllDebugInfos(const DebugInfoClearFunction& clear_function) {
2023 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2024 DebugInfoListNode* prev = nullptr;
2025 DebugInfoListNode* current = debug_info_list_;
2026 while (current != nullptr) {
2027 DebugInfoListNode* next = current->next();
2028 Handle<DebugInfo> debug_info = current->debug_info();
2029 clear_function(debug_info);
2030 if (debug_info->IsEmpty()) {
2031 FreeDebugInfoListNode(prev, current);
2032 current = next;
2033 } else {
2034 prev = current;
2035 current = next;
2036 }
2037 }
2038 }
2039
RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info)2040 void Debug::RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info) {
2041 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2042 debug_info->ClearBreakInfo(isolate_);
2043 if (debug_info->IsEmpty()) {
2044 DebugInfoListNode* prev;
2045 DebugInfoListNode* node;
2046 FindDebugInfo(debug_info, &prev, &node);
2047 FreeDebugInfoListNode(prev, node);
2048 }
2049 }
2050
FreeDebugInfoListNode(DebugInfoListNode * prev,DebugInfoListNode * node)2051 void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev,
2052 DebugInfoListNode* node) {
2053 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2054 DCHECK(node->debug_info()->IsEmpty());
2055
2056 // Unlink from list. If prev is nullptr we are looking at the first element.
2057 if (prev == nullptr) {
2058 debug_info_list_ = node->next();
2059 } else {
2060 prev->set_next(node->next());
2061 }
2062
2063 // Pack script back into the
2064 // SFI::script_or_debug_info field.
2065 Handle<DebugInfo> debug_info(node->debug_info());
2066 debug_info->shared().set_script_or_debug_info(debug_info->script(),
2067 kReleaseStore);
2068
2069 delete node;
2070 }
2071
IsBreakAtReturn(JavaScriptFrame * frame)2072 bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
2073 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2074 HandleScope scope(isolate_);
2075
2076 // Get the executing function in which the debug break occurred.
2077 Handle<SharedFunctionInfo> shared(frame->function().shared(), isolate_);
2078
2079 // With no debug info there are no break points, so we can't be at a return.
2080 if (!shared->HasBreakInfo()) return false;
2081
2082 DCHECK(!frame->is_optimized());
2083 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
2084 BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
2085 return location.IsReturn();
2086 }
2087
GetLoadedScripts()2088 Handle<FixedArray> Debug::GetLoadedScripts() {
2089 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2090 isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
2091 GarbageCollectionReason::kDebugger);
2092 Factory* factory = isolate_->factory();
2093 if (!factory->script_list()->IsWeakArrayList()) {
2094 return factory->empty_fixed_array();
2095 }
2096 Handle<WeakArrayList> array =
2097 Handle<WeakArrayList>::cast(factory->script_list());
2098 Handle<FixedArray> results = factory->NewFixedArray(array->length());
2099 int length = 0;
2100 {
2101 Script::Iterator iterator(isolate_);
2102 for (Script script = iterator.Next(); !script.is_null();
2103 script = iterator.Next()) {
2104 if (script.HasValidSource()) results->set(length++, script);
2105 }
2106 }
2107 return FixedArray::ShrinkOrEmpty(isolate_, results, length);
2108 }
2109
OnThrow(Handle<Object> exception)2110 base::Optional<Object> Debug::OnThrow(Handle<Object> exception) {
2111 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2112 if (in_debug_scope() || ignore_events()) return {};
2113 // Temporarily clear any scheduled_exception to allow evaluating
2114 // JavaScript from the debug event handler.
2115 HandleScope scope(isolate_);
2116 Handle<Object> scheduled_exception;
2117 if (isolate_->has_scheduled_exception()) {
2118 scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
2119 isolate_->clear_scheduled_exception();
2120 }
2121 Handle<Object> maybe_promise = isolate_->GetPromiseOnStackOnThrow();
2122 OnException(exception, maybe_promise,
2123 maybe_promise->IsJSPromise() ? v8::debug::kPromiseRejection
2124 : v8::debug::kException);
2125 if (!scheduled_exception.is_null()) {
2126 isolate_->set_scheduled_exception(*scheduled_exception);
2127 }
2128 PrepareStepOnThrow();
2129 // If the OnException handler requested termination, then indicated this to
2130 // our caller Isolate::Throw so it can deal with it immediatelly instead of
2131 // throwing the original exception.
2132 if (isolate_->stack_guard()->CheckTerminateExecution()) {
2133 isolate_->stack_guard()->ClearTerminateExecution();
2134 return isolate_->TerminateExecution();
2135 }
2136 return {};
2137 }
2138
OnPromiseReject(Handle<Object> promise,Handle<Object> value)2139 void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
2140 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2141 if (in_debug_scope() || ignore_events()) return;
2142 HandleScope scope(isolate_);
2143 // Check whether the promise has been marked as having triggered a message.
2144 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
2145 if (!promise->IsJSObject() ||
2146 JSReceiver::GetDataProperty(isolate_, Handle<JSObject>::cast(promise),
2147 key)
2148 ->IsUndefined(isolate_)) {
2149 OnException(value, promise, v8::debug::kPromiseRejection);
2150 }
2151 }
2152
IsExceptionBlackboxed(bool uncaught)2153 bool Debug::IsExceptionBlackboxed(bool uncaught) {
2154 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2155 // Uncaught exception is blackboxed if all current frames are blackboxed,
2156 // caught exception if top frame is blackboxed.
2157 StackTraceFrameIterator it(isolate_);
2158 #if V8_ENABLE_WEBASSEMBLY
2159 while (!it.done() && it.is_wasm()) it.Advance();
2160 #endif // V8_ENABLE_WEBASSEMBLY
2161 bool is_top_frame_blackboxed =
2162 !it.done() ? IsFrameBlackboxed(it.javascript_frame()) : true;
2163 if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
2164 return AllFramesOnStackAreBlackboxed();
2165 }
2166
IsFrameBlackboxed(JavaScriptFrame * frame)2167 bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
2168 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2169 HandleScope scope(isolate_);
2170 std::vector<Handle<SharedFunctionInfo>> infos;
2171 frame->GetFunctions(&infos);
2172 for (const auto& info : infos) {
2173 if (!IsBlackboxed(info)) return false;
2174 }
2175 return true;
2176 }
2177
OnException(Handle<Object> exception,Handle<Object> promise,v8::debug::ExceptionType exception_type)2178 void Debug::OnException(Handle<Object> exception, Handle<Object> promise,
2179 v8::debug::ExceptionType exception_type) {
2180 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2181 // Do not trigger exception event on stack overflow. We cannot perform
2182 // anything useful for debugging in that situation.
2183 StackLimitCheck stack_limit_check(isolate_);
2184 if (stack_limit_check.JsHasOverflowed()) return;
2185
2186 // Return if the event has nowhere to go.
2187 if (!debug_delegate_) return;
2188
2189 // Return if we are not interested in exception events.
2190 if (!break_on_exception_ && !break_on_uncaught_exception_) return;
2191
2192 Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
2193
2194 bool uncaught = catch_type == Isolate::NOT_CAUGHT;
2195 if (promise->IsJSObject()) {
2196 Handle<JSObject> jsobject = Handle<JSObject>::cast(promise);
2197 // Mark the promise as already having triggered a message.
2198 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
2199 Object::SetProperty(isolate_, jsobject, key, key, StoreOrigin::kMaybeKeyed,
2200 Just(ShouldThrow::kThrowOnError))
2201 .Assert();
2202 // Check whether the promise reject is considered an uncaught exception.
2203 if (jsobject->IsJSPromise()) {
2204 Handle<JSPromise> jspromise = Handle<JSPromise>::cast(jsobject);
2205
2206 // Ignore the exception if the promise was marked as silent
2207 if (jspromise->is_silent()) return;
2208
2209 uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
2210 } else {
2211 uncaught = true;
2212 }
2213 }
2214
2215 // Return if the exception is caught and we only care about uncaught
2216 // exceptions.
2217 if (!uncaught && !break_on_exception_) {
2218 DCHECK(break_on_uncaught_exception_);
2219 return;
2220 }
2221
2222 {
2223 JavaScriptFrameIterator it(isolate_);
2224 // Check whether the top frame is blackboxed or the break location is muted.
2225 if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) ||
2226 IsExceptionBlackboxed(uncaught))) {
2227 return;
2228 }
2229 if (it.done()) return; // Do not trigger an event with an empty stack.
2230 }
2231
2232 DebugScope debug_scope(this);
2233 HandleScope scope(isolate_);
2234 DisableBreak no_recursive_break(this);
2235
2236 {
2237 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
2238 Handle<Context> native_context(isolate_->native_context());
2239 debug_delegate_->ExceptionThrown(
2240 v8::Utils::ToLocal(native_context), v8::Utils::ToLocal(exception),
2241 v8::Utils::ToLocal(promise), uncaught, exception_type);
2242 }
2243 }
2244
OnDebugBreak(Handle<FixedArray> break_points_hit,StepAction lastStepAction,v8::debug::BreakReasons break_reasons)2245 void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit,
2246 StepAction lastStepAction,
2247 v8::debug::BreakReasons break_reasons) {
2248 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2249 DCHECK(!break_points_hit.is_null());
2250 // The caller provided for DebugScope.
2251 AssertDebugContext();
2252 // Bail out if there is no listener for this event
2253 if (ignore_events()) return;
2254
2255 #ifdef DEBUG
2256 PrintBreakLocation();
2257 #endif // DEBUG
2258
2259 if (!debug_delegate_) return;
2260 DCHECK(in_debug_scope());
2261 HandleScope scope(isolate_);
2262 DisableBreak no_recursive_break(this);
2263
2264 if ((lastStepAction == StepAction::StepOver ||
2265 lastStepAction == StepAction::StepInto) &&
2266 ShouldBeSkipped()) {
2267 PrepareStep(lastStepAction);
2268 return;
2269 }
2270
2271 std::vector<int> inspector_break_points_hit;
2272 // This array contains breakpoints installed using JS debug API.
2273 for (int i = 0; i < break_points_hit->length(); ++i) {
2274 BreakPoint break_point = BreakPoint::cast(break_points_hit->get(i));
2275 inspector_break_points_hit.push_back(break_point.id());
2276 }
2277 {
2278 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
2279 Handle<Context> native_context(isolate_->native_context());
2280 if (lastStepAction != StepAction::StepNone)
2281 break_reasons.Add(debug::BreakReason::kStep);
2282 debug_delegate_->BreakProgramRequested(v8::Utils::ToLocal(native_context),
2283 inspector_break_points_hit,
2284 break_reasons);
2285 }
2286 }
2287
2288 namespace {
GetDebugLocation(Handle<Script> script,int source_position)2289 debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
2290 Script::PositionInfo info;
2291 Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
2292 // V8 provides ScriptCompiler::CompileFunction method which takes
2293 // expression and compile it as anonymous function like (function() ..
2294 // expression ..). To produce correct locations for stmts inside of this
2295 // expression V8 compile this function with negative offset. Instead of stmt
2296 // position blackboxing use function start position which is negative in
2297 // described case.
2298 return debug::Location(std::max(info.line, 0), std::max(info.column, 0));
2299 }
2300 } // namespace
2301
IsBlackboxed(Handle<SharedFunctionInfo> shared)2302 bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
2303 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2304 if (!debug_delegate_) return !shared->IsSubjectToDebugging();
2305 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
2306 if (!debug_info->computed_debug_is_blackboxed()) {
2307 bool is_blackboxed =
2308 !shared->IsSubjectToDebugging() || !shared->script().IsScript();
2309 if (!is_blackboxed) {
2310 SuppressDebug while_processing(this);
2311 HandleScope handle_scope(isolate_);
2312 PostponeInterruptsScope no_interrupts(isolate_);
2313 DisableBreak no_recursive_break(this);
2314 DCHECK(shared->script().IsScript());
2315 Handle<Script> script(Script::cast(shared->script()), isolate_);
2316 DCHECK(script->IsUserJavaScript());
2317 debug::Location start = GetDebugLocation(script, shared->StartPosition());
2318 debug::Location end = GetDebugLocation(script, shared->EndPosition());
2319 {
2320 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
2321 is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
2322 ToApiHandle<debug::Script>(script), start, end);
2323 }
2324 }
2325 debug_info->set_debug_is_blackboxed(is_blackboxed);
2326 debug_info->set_computed_debug_is_blackboxed(true);
2327 }
2328 return debug_info->debug_is_blackboxed();
2329 }
2330
ShouldBeSkipped()2331 bool Debug::ShouldBeSkipped() {
2332 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2333 SuppressDebug while_processing(this);
2334 PostponeInterruptsScope no_interrupts(isolate_);
2335 DisableBreak no_recursive_break(this);
2336
2337 StackTraceFrameIterator iterator(isolate_);
2338 FrameSummary summary = iterator.GetTopValidFrame();
2339 Handle<Object> script_obj = summary.script();
2340 if (!script_obj->IsScript()) return false;
2341
2342 Handle<Script> script = Handle<Script>::cast(script_obj);
2343 summary.EnsureSourcePositionsAvailable();
2344 int source_position = summary.SourcePosition();
2345 int line = Script::GetLineNumber(script, source_position);
2346 int column = Script::GetColumnNumber(script, source_position);
2347
2348 {
2349 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
2350 return debug_delegate_->ShouldBeSkipped(ToApiHandle<debug::Script>(script),
2351 line, column);
2352 }
2353 }
2354
AllFramesOnStackAreBlackboxed()2355 bool Debug::AllFramesOnStackAreBlackboxed() {
2356 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2357 HandleScope scope(isolate_);
2358 for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
2359 if (!it.is_javascript()) continue;
2360 if (!IsFrameBlackboxed(it.javascript_frame())) return false;
2361 }
2362 return true;
2363 }
2364
CanBreakAtEntry(Handle<SharedFunctionInfo> shared)2365 bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
2366 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2367 // Allow break at entry for builtin functions.
2368 if (shared->native() || shared->IsApiFunction()) {
2369 // Functions that are subject to debugging can have regular breakpoints.
2370 DCHECK(!shared->IsSubjectToDebugging());
2371 return true;
2372 }
2373 return false;
2374 }
2375
SetScriptSource(Handle<Script> script,Handle<String> source,bool preview,debug::LiveEditResult * result)2376 bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
2377 bool preview, debug::LiveEditResult* result) {
2378 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2379 DebugScope debug_scope(this);
2380 feature_tracker()->Track(DebugFeatureTracker::kLiveEdit);
2381 running_live_edit_ = true;
2382 LiveEdit::PatchScript(isolate_, script, source, preview, result);
2383 running_live_edit_ = false;
2384 return result->status == debug::LiveEditResult::OK;
2385 }
2386
OnCompileError(Handle<Script> script)2387 void Debug::OnCompileError(Handle<Script> script) {
2388 ProcessCompileEvent(true, script);
2389 }
2390
OnAfterCompile(Handle<Script> script)2391 void Debug::OnAfterCompile(Handle<Script> script) {
2392 ProcessCompileEvent(false, script);
2393 }
2394
ProcessCompileEvent(bool has_compile_error,Handle<Script> script)2395 void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
2396 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2397 // Ignore temporary scripts.
2398 if (script->id() == Script::kTemporaryScriptId) return;
2399 // TODO(kozyatinskiy): teach devtools to work with liveedit scripts better
2400 // first and then remove this fast return.
2401 if (running_live_edit_) return;
2402 // Attach the correct debug id to the script. The debug id is used by the
2403 // inspector to filter scripts by native context.
2404 script->set_context_data(isolate_->native_context()->debug_context_id());
2405 if (ignore_events()) return;
2406 #if V8_ENABLE_WEBASSEMBLY
2407 if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) {
2408 return;
2409 }
2410 #else
2411 if (!script->IsUserJavaScript()) return;
2412 #endif // V8_ENABLE_WEBASSEMBLY
2413 if (!debug_delegate_) return;
2414 SuppressDebug while_processing(this);
2415 DebugScope debug_scope(this);
2416 HandleScope scope(isolate_);
2417 DisableBreak no_recursive_break(this);
2418 AllowJavascriptExecution allow_script(isolate_);
2419 {
2420 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
2421 debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
2422 running_live_edit_, has_compile_error);
2423 }
2424 }
2425
CurrentFrameCount()2426 int Debug::CurrentFrameCount() {
2427 StackTraceFrameIterator it(isolate_);
2428 if (break_frame_id() != StackFrameId::NO_ID) {
2429 // Skip to break frame.
2430 DCHECK(in_debug_scope());
2431 while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance();
2432 }
2433 int counter = 0;
2434 for (; !it.done(); it.Advance()) {
2435 counter += it.FrameFunctionCount();
2436 }
2437 return counter;
2438 }
2439
SetDebugDelegate(debug::DebugDelegate * delegate)2440 void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
2441 debug_delegate_ = delegate;
2442 UpdateState();
2443 }
2444
UpdateState()2445 void Debug::UpdateState() {
2446 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2447 bool is_active = debug_delegate_ != nullptr;
2448 if (is_active == is_active_) return;
2449 if (is_active) {
2450 // Note that the debug context could have already been loaded to
2451 // bootstrap test cases.
2452 isolate_->compilation_cache()->DisableScriptAndEval();
2453 isolate_->CollectSourcePositionsForAllBytecodeArrays();
2454 is_active = true;
2455 feature_tracker()->Track(DebugFeatureTracker::kActive);
2456 } else {
2457 isolate_->compilation_cache()->EnableScriptAndEval();
2458 Unload();
2459 }
2460 is_active_ = is_active;
2461 isolate_->PromiseHookStateUpdated();
2462 }
2463
UpdateHookOnFunctionCall()2464 void Debug::UpdateHookOnFunctionCall() {
2465 STATIC_ASSERT(LastStepAction == StepInto);
2466 hook_on_function_call_ =
2467 thread_local_.last_step_action_ == StepInto ||
2468 isolate_->debug_execution_mode() == DebugInfo::kSideEffects ||
2469 thread_local_.break_on_next_function_call_;
2470 }
2471
HandleDebugBreak(IgnoreBreakMode ignore_break_mode,v8::debug::BreakReasons break_reasons)2472 void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode,
2473 v8::debug::BreakReasons break_reasons) {
2474 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2475 // Ignore debug break during bootstrapping.
2476 if (isolate_->bootstrapper()->IsActive()) return;
2477 // Just continue if breaks are disabled.
2478 if (break_disabled()) return;
2479 // Ignore debug break if debugger is not active.
2480 if (!is_active()) return;
2481
2482 StackLimitCheck check(isolate_);
2483 if (check.HasOverflowed()) return;
2484
2485 HandleScope scope(isolate_);
2486 MaybeHandle<FixedArray> break_points;
2487 {
2488 JavaScriptFrameIterator it(isolate_);
2489 DCHECK(!it.done());
2490 Object fun = it.frame()->function();
2491 if (fun.IsJSFunction()) {
2492 Handle<JSFunction> function(JSFunction::cast(fun), isolate_);
2493 // Don't stop in builtin and blackboxed functions.
2494 Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
2495 bool ignore_break = ignore_break_mode == kIgnoreIfTopFrameBlackboxed
2496 ? IsBlackboxed(shared)
2497 : AllFramesOnStackAreBlackboxed();
2498 if (ignore_break) return;
2499 if (function->shared().HasBreakInfo()) {
2500 Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(),
2501 isolate_);
2502 // Enter the debugger.
2503 DebugScope debug_scope(this);
2504
2505 std::vector<BreakLocation> break_locations;
2506 BreakLocation::AllAtCurrentStatement(debug_info, it.frame(),
2507 &break_locations);
2508
2509 for (size_t i = 0; i < break_locations.size(); i++) {
2510 if (IsBreakOnInstrumentation(debug_info, break_locations[i])) {
2511 OnInstrumentationBreak();
2512 break;
2513 }
2514 }
2515
2516 bool has_break_points;
2517 break_points = CheckBreakPointsForLocations(debug_info, break_locations,
2518 &has_break_points);
2519 bool is_muted = has_break_points && break_points.is_null();
2520 // If we get to this point, a break was triggered because e.g. of a
2521 // debugger statement, an assert, .. . However, we do not stop if this
2522 // position "is muted", which happens if a conditional breakpoint at
2523 // this point evaluates to false.
2524 if (is_muted) return;
2525 }
2526 }
2527 }
2528
2529 StepAction lastStepAction = last_step_action();
2530
2531 // Clear stepping to avoid duplicate breaks.
2532 ClearStepping();
2533
2534 DebugScope debug_scope(this);
2535 OnDebugBreak(break_points.is_null() ? isolate_->factory()->empty_fixed_array()
2536 : break_points.ToHandleChecked(),
2537 lastStepAction, break_reasons);
2538 }
2539
2540 #ifdef DEBUG
PrintBreakLocation()2541 void Debug::PrintBreakLocation() {
2542 if (!FLAG_print_break_location) return;
2543 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2544 HandleScope scope(isolate_);
2545 StackTraceFrameIterator iterator(isolate_);
2546 if (iterator.done()) return;
2547 CommonFrame* frame = iterator.frame();
2548 std::vector<FrameSummary> frames;
2549 frame->Summarize(&frames);
2550 int inlined_frame_index = static_cast<int>(frames.size() - 1);
2551 FrameInspector inspector(frame, inlined_frame_index, isolate_);
2552 int source_position = inspector.GetSourcePosition();
2553 Handle<Object> script_obj = inspector.GetScript();
2554 PrintF("[debug] break in function '");
2555 inspector.GetFunctionName()->PrintOn(stdout);
2556 PrintF("'.\n");
2557 if (script_obj->IsScript()) {
2558 Handle<Script> script = Handle<Script>::cast(script_obj);
2559 Handle<String> source(String::cast(script->source()), isolate_);
2560 Script::InitLineEnds(isolate_, script);
2561 int line =
2562 Script::GetLineNumber(script, source_position) - script->line_offset();
2563 int column = Script::GetColumnNumber(script, source_position) -
2564 (line == 0 ? script->column_offset() : 0);
2565 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()),
2566 isolate_);
2567 int line_start = line == 0 ? 0 : Smi::ToInt(line_ends->get(line - 1)) + 1;
2568 int line_end = Smi::ToInt(line_ends->get(line));
2569 DisallowGarbageCollection no_gc;
2570 String::FlatContent content = source->GetFlatContent(no_gc);
2571 if (content.IsOneByte()) {
2572 PrintF("[debug] %.*s\n", line_end - line_start,
2573 content.ToOneByteVector().begin() + line_start);
2574 PrintF("[debug] ");
2575 for (int i = 0; i < column; i++) PrintF(" ");
2576 PrintF("^\n");
2577 } else {
2578 PrintF("[debug] at line %d column %d\n", line, column);
2579 }
2580 }
2581 }
2582 #endif // DEBUG
2583
DebugScope(Debug * debug)2584 DebugScope::DebugScope(Debug* debug)
2585 : debug_(debug),
2586 prev_(reinterpret_cast<DebugScope*>(
2587 base::Relaxed_Load(&debug->thread_local_.current_debug_scope_))),
2588 no_interrupts_(debug_->isolate_) {
2589 // Link recursive debugger entry.
2590 base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
2591 reinterpret_cast<base::AtomicWord>(this));
2592 // Store the previous frame id and return value.
2593 break_frame_id_ = debug_->break_frame_id();
2594
2595 // Create the new break info. If there is no proper frames there is no break
2596 // frame id.
2597 StackTraceFrameIterator it(isolate());
2598 bool has_frames = !it.done();
2599 debug_->thread_local_.break_frame_id_ =
2600 has_frames ? it.frame()->id() : StackFrameId::NO_ID;
2601
2602 debug_->UpdateState();
2603 }
2604
set_terminate_on_resume()2605 void DebugScope::set_terminate_on_resume() { terminate_on_resume_ = true; }
2606
~DebugScope()2607 DebugScope::~DebugScope() {
2608 // Terminate on resume must have been handled by retrieving it, if this is
2609 // the outer scope.
2610 if (terminate_on_resume_) {
2611 if (!prev_) {
2612 debug_->isolate_->stack_guard()->RequestTerminateExecution();
2613 } else {
2614 prev_->set_terminate_on_resume();
2615 }
2616 }
2617 // Leaving this debugger entry.
2618 base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
2619 reinterpret_cast<base::AtomicWord>(prev_));
2620
2621 // Restore to the previous break state.
2622 debug_->thread_local_.break_frame_id_ = break_frame_id_;
2623
2624 debug_->UpdateState();
2625 }
2626
ReturnValueScope(Debug * debug)2627 ReturnValueScope::ReturnValueScope(Debug* debug) : debug_(debug) {
2628 return_value_ = debug_->return_value_handle();
2629 }
2630
~ReturnValueScope()2631 ReturnValueScope::~ReturnValueScope() {
2632 debug_->set_return_value(*return_value_);
2633 }
2634
UpdateDebugInfosForExecutionMode()2635 void Debug::UpdateDebugInfosForExecutionMode() {
2636 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2637 // Walk all debug infos and update their execution mode if it is different
2638 // from the isolate execution mode.
2639 DebugInfoListNode* current = debug_info_list_;
2640 while (current != nullptr) {
2641 Handle<DebugInfo> debug_info = current->debug_info();
2642 if (debug_info->HasInstrumentedBytecodeArray() &&
2643 debug_info->DebugExecutionMode() != isolate_->debug_execution_mode()) {
2644 DCHECK(debug_info->shared().HasBytecodeArray());
2645 if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) {
2646 ClearSideEffectChecks(debug_info);
2647 ApplyBreakPoints(debug_info);
2648 } else {
2649 ClearBreakPoints(debug_info);
2650 ApplySideEffectChecks(debug_info);
2651 }
2652 }
2653 current = current->next();
2654 }
2655 }
2656
SetTerminateOnResume()2657 void Debug::SetTerminateOnResume() {
2658 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2659 DebugScope* scope = reinterpret_cast<DebugScope*>(
2660 base::Acquire_Load(&thread_local_.current_debug_scope_));
2661 CHECK_NOT_NULL(scope);
2662 scope->set_terminate_on_resume();
2663 }
2664
StartSideEffectCheckMode()2665 void Debug::StartSideEffectCheckMode() {
2666 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2667 DCHECK(isolate_->debug_execution_mode() != DebugInfo::kSideEffects);
2668 isolate_->set_debug_execution_mode(DebugInfo::kSideEffects);
2669 UpdateHookOnFunctionCall();
2670 side_effect_check_failed_ = false;
2671
2672 DCHECK(!temporary_objects_);
2673 temporary_objects_.reset(new TemporaryObjectsTracker());
2674 isolate_->heap()->AddHeapObjectAllocationTracker(temporary_objects_.get());
2675 Handle<FixedArray> array(isolate_->native_context()->regexp_last_match_info(),
2676 isolate_);
2677 regexp_match_info_ =
2678 Handle<RegExpMatchInfo>::cast(isolate_->factory()->CopyFixedArray(array));
2679
2680 // Update debug infos to have correct execution mode.
2681 UpdateDebugInfosForExecutionMode();
2682 }
2683
StopSideEffectCheckMode()2684 void Debug::StopSideEffectCheckMode() {
2685 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2686 DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects);
2687 if (side_effect_check_failed_) {
2688 DCHECK(isolate_->has_pending_exception());
2689 DCHECK_EQ(ReadOnlyRoots(isolate_).termination_exception(),
2690 isolate_->pending_exception());
2691 // Convert the termination exception into a regular exception.
2692 isolate_->CancelTerminateExecution();
2693 isolate_->Throw(*isolate_->factory()->NewEvalError(
2694 MessageTemplate::kNoSideEffectDebugEvaluate));
2695 }
2696 isolate_->set_debug_execution_mode(DebugInfo::kBreakpoints);
2697 UpdateHookOnFunctionCall();
2698 side_effect_check_failed_ = false;
2699
2700 DCHECK(temporary_objects_);
2701 isolate_->heap()->RemoveHeapObjectAllocationTracker(temporary_objects_.get());
2702 temporary_objects_.reset();
2703 isolate_->native_context()->set_regexp_last_match_info(*regexp_match_info_);
2704 regexp_match_info_ = Handle<RegExpMatchInfo>::null();
2705
2706 // Update debug infos to have correct execution mode.
2707 UpdateDebugInfosForExecutionMode();
2708 }
2709
ApplySideEffectChecks(Handle<DebugInfo> debug_info)2710 void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) {
2711 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2712 DCHECK(debug_info->HasInstrumentedBytecodeArray());
2713 Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
2714 isolate_);
2715 DebugEvaluate::ApplySideEffectChecks(debug_bytecode);
2716 debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects);
2717 }
2718
ClearSideEffectChecks(Handle<DebugInfo> debug_info)2719 void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
2720 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2721 DCHECK(debug_info->HasInstrumentedBytecodeArray());
2722 Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
2723 isolate_);
2724 Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray(), isolate_);
2725 for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done();
2726 it.Advance()) {
2727 // Restore from original. This may copy only the scaling prefix, which is
2728 // correct, since we patch scaling prefixes to debug breaks if exists.
2729 debug_bytecode->set(it.current_offset(),
2730 original->get(it.current_offset()));
2731 }
2732 }
2733
PerformSideEffectCheck(Handle<JSFunction> function,Handle<Object> receiver)2734 bool Debug::PerformSideEffectCheck(Handle<JSFunction> function,
2735 Handle<Object> receiver) {
2736 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2737 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2738 DisallowJavascriptExecution no_js(isolate_);
2739 IsCompiledScope is_compiled_scope(
2740 function->shared().is_compiled_scope(isolate_));
2741 if (!function->is_compiled() &&
2742 !Compiler::Compile(isolate_, function, Compiler::KEEP_EXCEPTION,
2743 &is_compiled_scope)) {
2744 return false;
2745 }
2746 DCHECK(is_compiled_scope.is_compiled());
2747 Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
2748 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
2749 DebugInfo::SideEffectState side_effect_state =
2750 debug_info->GetSideEffectState(isolate_);
2751 switch (side_effect_state) {
2752 case DebugInfo::kHasSideEffects:
2753 if (FLAG_trace_side_effect_free_debug_evaluate) {
2754 PrintF("[debug-evaluate] Function %s failed side effect check.\n",
2755 function->shared().DebugNameCStr().get());
2756 }
2757 side_effect_check_failed_ = true;
2758 // Throw an uncatchable termination exception.
2759 isolate_->TerminateExecution();
2760 return false;
2761 case DebugInfo::kRequiresRuntimeChecks: {
2762 if (!shared->HasBytecodeArray()) {
2763 return PerformSideEffectCheckForObject(receiver);
2764 }
2765 // If function has bytecode array then prepare function for debug
2766 // execution to perform runtime side effect checks.
2767 DCHECK(shared->is_compiled());
2768 PrepareFunctionForDebugExecution(shared);
2769 ApplySideEffectChecks(debug_info);
2770 return true;
2771 }
2772 case DebugInfo::kHasNoSideEffect:
2773 return true;
2774 case DebugInfo::kNotComputed:
2775 default:
2776 UNREACHABLE();
2777 }
2778 }
2779
return_value_handle()2780 Handle<Object> Debug::return_value_handle() {
2781 return handle(thread_local_.return_value_, isolate_);
2782 }
2783
PerformSideEffectCheckForCallback(Handle<Object> callback_info,Handle<Object> receiver,Debug::AccessorKind accessor_kind)2784 bool Debug::PerformSideEffectCheckForCallback(
2785 Handle<Object> callback_info, Handle<Object> receiver,
2786 Debug::AccessorKind accessor_kind) {
2787 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2788 DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo());
2789 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2790 if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() &&
2791 i::CallHandlerInfo::cast(*callback_info).NextCallHasNoSideEffect()) {
2792 return true;
2793 }
2794 // TODO(7515): always pass a valid callback info object.
2795 if (!callback_info.is_null()) {
2796 if (callback_info->IsAccessorInfo()) {
2797 // List of allowlisted internal accessors can be found in accessors.h.
2798 AccessorInfo info = AccessorInfo::cast(*callback_info);
2799 DCHECK_NE(kNotAccessor, accessor_kind);
2800 switch (accessor_kind == kSetter ? info.setter_side_effect_type()
2801 : info.getter_side_effect_type()) {
2802 case SideEffectType::kHasNoSideEffect:
2803 // We do not support setter accessors with no side effects, since
2804 // calling set accessors go through a store bytecode. Store bytecodes
2805 // are considered to cause side effects (to non-temporary objects).
2806 DCHECK_NE(kSetter, accessor_kind);
2807 return true;
2808 case SideEffectType::kHasSideEffectToReceiver:
2809 DCHECK(!receiver.is_null());
2810 if (PerformSideEffectCheckForObject(receiver)) return true;
2811 isolate_->OptionalRescheduleException(false);
2812 return false;
2813 case SideEffectType::kHasSideEffect:
2814 break;
2815 }
2816 if (FLAG_trace_side_effect_free_debug_evaluate) {
2817 PrintF("[debug-evaluate] API Callback '");
2818 info.name().ShortPrint();
2819 PrintF("' may cause side effect.\n");
2820 }
2821 } else if (callback_info->IsInterceptorInfo()) {
2822 InterceptorInfo info = InterceptorInfo::cast(*callback_info);
2823 if (info.has_no_side_effect()) return true;
2824 if (FLAG_trace_side_effect_free_debug_evaluate) {
2825 PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
2826 }
2827 } else if (callback_info->IsCallHandlerInfo()) {
2828 CallHandlerInfo info = CallHandlerInfo::cast(*callback_info);
2829 if (info.IsSideEffectFreeCallHandlerInfo()) return true;
2830 if (FLAG_trace_side_effect_free_debug_evaluate) {
2831 PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
2832 }
2833 }
2834 }
2835 side_effect_check_failed_ = true;
2836 // Throw an uncatchable termination exception.
2837 isolate_->TerminateExecution();
2838 isolate_->OptionalRescheduleException(false);
2839 return false;
2840 }
2841
PerformSideEffectCheckAtBytecode(InterpretedFrame * frame)2842 bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
2843 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2844 using interpreter::Bytecode;
2845
2846 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2847 SharedFunctionInfo shared = frame->function().shared();
2848 BytecodeArray bytecode_array = shared.GetBytecodeArray(isolate_);
2849 int offset = frame->GetBytecodeOffset();
2850 interpreter::BytecodeArrayIterator bytecode_iterator(
2851 handle(bytecode_array, isolate_), offset);
2852
2853 Bytecode bytecode = bytecode_iterator.current_bytecode();
2854 if (interpreter::Bytecodes::IsCallRuntime(bytecode)) {
2855 auto id = (bytecode == Bytecode::kInvokeIntrinsic)
2856 ? bytecode_iterator.GetIntrinsicIdOperand(0)
2857 : bytecode_iterator.GetRuntimeIdOperand(0);
2858 if (DebugEvaluate::IsSideEffectFreeIntrinsic(id)) {
2859 return true;
2860 }
2861 side_effect_check_failed_ = true;
2862 // Throw an uncatchable termination exception.
2863 isolate_->TerminateExecution();
2864 return false;
2865 }
2866 interpreter::Register reg;
2867 switch (bytecode) {
2868 case Bytecode::kStaCurrentContextSlot:
2869 reg = interpreter::Register::current_context();
2870 break;
2871 default:
2872 reg = bytecode_iterator.GetRegisterOperand(0);
2873 break;
2874 }
2875 Handle<Object> object =
2876 handle(frame->ReadInterpreterRegister(reg.index()), isolate_);
2877 return PerformSideEffectCheckForObject(object);
2878 }
2879
PerformSideEffectCheckForObject(Handle<Object> object)2880 bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
2881 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2882 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2883
2884 // We expect no side-effects for primitives.
2885 if (object->IsNumber()) return true;
2886 if (object->IsName()) return true;
2887
2888 if (temporary_objects_->HasObject(Handle<HeapObject>::cast(object))) {
2889 return true;
2890 }
2891
2892 if (FLAG_trace_side_effect_free_debug_evaluate) {
2893 PrintF("[debug-evaluate] failed runtime side effect check.\n");
2894 }
2895 side_effect_check_failed_ = true;
2896 // Throw an uncatchable termination exception.
2897 isolate_->TerminateExecution();
2898 return false;
2899 }
2900
SetTemporaryObjectTrackingDisabled(bool disabled)2901 void Debug::SetTemporaryObjectTrackingDisabled(bool disabled) {
2902 if (temporary_objects_) {
2903 temporary_objects_->disabled = disabled;
2904 }
2905 }
2906
GetTemporaryObjectTrackingDisabled() const2907 bool Debug::GetTemporaryObjectTrackingDisabled() const {
2908 if (temporary_objects_) {
2909 return temporary_objects_->disabled;
2910 }
2911 return false;
2912 }
2913
2914 } // namespace internal
2915 } // namespace v8
2916