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