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