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