debug.cc 109 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5 6
#include "src/debug/debug.h"

7
#include <memory>
8
#include <unordered_set>
9

10 11
#include "src/api/api-inl.h"
#include "src/api/api-natives.h"
12
#include "src/base/platform/mutex.h"
13
#include "src/builtins/builtins.h"
14 15 16
#include "src/codegen/assembler-inl.h"
#include "src/codegen/compilation-cache.h"
#include "src/codegen/compiler.h"
17
#include "src/common/assert-scope.h"
18
#include "src/common/globals.h"
19
#include "src/common/message-template.h"
20
#include "src/debug/debug-evaluate.h"
21
#include "src/debug/liveedit.h"
22
#include "src/deoptimizer/deoptimizer.h"
23 24 25 26
#include "src/execution/arguments.h"
#include "src/execution/execution.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
27
#include "src/execution/v8threads.h"
28
#include "src/handles/global-handles-inl.h"
29
#include "src/heap/heap-inl.h"  // For NextDebuggingId.
30
#include "src/init/bootstrapper.h"
31
#include "src/interpreter/bytecode-array-iterator.h"
32
#include "src/interpreter/interpreter.h"
33
#include "src/logging/counters.h"
34
#include "src/logging/runtime-call-stats-scope.h"
35
#include "src/objects/api-callbacks-inl.h"
36
#include "src/objects/debug-objects-inl.h"
37
#include "src/objects/js-generator-inl.h"
38
#include "src/objects/js-promise-inl.h"
39
#include "src/objects/slots.h"
40
#include "src/snapshot/embedded/embedded-data.h"
41
#include "src/snapshot/snapshot.h"
42 43

#if V8_ENABLE_WEBASSEMBLY
44
#include "src/wasm/wasm-debug.h"
45
#include "src/wasm/wasm-objects-inl.h"
46
#endif  // V8_ENABLE_WEBASSEMBLY
47

48 49
namespace v8 {
namespace internal {
50

51 52 53
class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker {
 public:
  TemporaryObjectsTracker() = default;
54
  ~TemporaryObjectsTracker() override = default;
55 56
  TemporaryObjectsTracker(const TemporaryObjectsTracker&) = delete;
  TemporaryObjectsTracker& operator=(const TemporaryObjectsTracker&) = delete;
57

58 59 60 61
  void AllocationEvent(Address addr, int) override {
    if (disabled) return;
    objects_.insert(addr);
  }
62 63 64

  void MoveEvent(Address from, Address to, int) override {
    if (from == to) return;
65
    base::MutexGuard guard(&mutex_);
66 67 68 69 70 71 72 73 74 75 76 77
    auto it = objects_.find(from);
    if (it == objects_.end()) {
      // If temporary object was collected we can get MoveEvent which moves
      // existing non temporary object to the address where we had temporary
      // object. So we should mark new address as non temporary.
      objects_.erase(to);
      return;
    }
    objects_.erase(it);
    objects_.insert(to);
  }

78 79 80 81 82 83 84 85 86 87
  bool HasObject(Handle<HeapObject> obj) const {
    if (obj->IsJSObject() &&
        Handle<JSObject>::cast(obj)->GetEmbedderFieldCount()) {
      // Embedder may store any pointers using embedder fields and implements
      // non trivial logic, e.g. create wrappers lazily and store pointer to
      // native object inside embedder field. We should consider all objects
      // with embedder fields as non temporary.
      return false;
    }
    return objects_.find(obj->address()) != objects_.end();
88 89
  }

90 91
  bool disabled = false;

92 93 94 95 96
 private:
  std::unordered_set<Address> objects_;
  base::Mutex mutex_;
};

97
Debug::Debug(Isolate* isolate)
98
    : is_active_(false),
99
      hook_on_function_call_(false),
100 101
      is_suppressed_(false),
      break_disabled_(false),
102
      break_points_active_(true),
103 104
      break_on_exception_(false),
      break_on_uncaught_exception_(false),
105
      side_effect_check_failed_(false),
106
      debug_info_list_(nullptr),
107
      feature_tracker_(isolate),
108
      isolate_(isolate) {
109
  ThreadInit();
110 111
}

112 113
Debug::~Debug() { DCHECK_NULL(debug_delegate_); }

114 115
BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
                                       JavaScriptFrame* frame) {
116 117 118
  if (debug_info->CanBreakAtEntry()) {
    return BreakLocation(Debug::kBreakAtEntryPosition, DEBUG_BREAK_AT_ENTRY);
  }
119
  auto summary = FrameSummary::GetTop(frame).AsJavaScript();
120
  int offset = summary.code_offset();
121
  Handle<AbstractCode> abstract_code = summary.abstract_code();
122 123 124
  BreakIterator it(debug_info);
  it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
  return it.GetBreakLocation();
125
}
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
MaybeHandle<FixedArray> Debug::CheckBreakPointsForLocations(
    Handle<DebugInfo> debug_info, std::vector<BreakLocation>& break_locations,
    bool* has_break_points) {
  Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(
      debug_info->GetBreakPointCount(isolate_));
  int break_points_hit_count = 0;
  bool has_break_points_at_all = false;
  for (size_t i = 0; i < break_locations.size(); i++) {
    bool location_has_break_points;
    MaybeHandle<FixedArray> check_result = CheckBreakPoints(
        debug_info, &break_locations[i], &location_has_break_points);
    has_break_points_at_all |= location_has_break_points;
    if (!check_result.is_null()) {
      Handle<FixedArray> break_points_current_hit =
          check_result.ToHandleChecked();
      int num_objects = break_points_current_hit->length();
      for (int j = 0; j < num_objects; ++j) {
        break_points_hit->set(break_points_hit_count++,
                              break_points_current_hit->get(j));
      }
    }
  }
  *has_break_points = has_break_points_at_all;
  if (break_points_hit_count == 0) return {};

  break_points_hit->Shrink(isolate_, break_points_hit_count);
  return break_points_hit;
}

156 157 158
void BreakLocation::AllAtCurrentStatement(
    Handle<DebugInfo> debug_info, JavaScriptFrame* frame,
    std::vector<BreakLocation>* result_out) {
159
  DCHECK(!debug_info->CanBreakAtEntry());
160
  auto summary = FrameSummary::GetTop(frame).AsJavaScript();
161 162 163 164 165
  int offset = summary.code_offset();
  Handle<AbstractCode> abstract_code = summary.abstract_code();
  if (abstract_code->IsCode()) offset = offset - 1;
  int statement_position;
  {
166 167 168
    BreakIterator it(debug_info);
    it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
    statement_position = it.statement_position();
169
  }
170 171
  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
    if (it.statement_position() == statement_position) {
172
      result_out->push_back(it.GetBreakLocation());
173 174 175 176
    }
  }
}

177
JSGeneratorObject BreakLocation::GetGeneratorObjectForSuspendedFrame(
178 179 180 181
    JavaScriptFrame* frame) const {
  DCHECK(IsSuspend());
  DCHECK_GE(generator_obj_reg_index_, 0);

182
  Object generator_obj = UnoptimizedFrame::cast(frame)->ReadInterpreterRegister(
183
      generator_obj_reg_index_);
184 185 186 187

  return JSGeneratorObject::cast(generator_obj);
}

188 189 190 191 192 193 194
int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,
                                            Handle<AbstractCode> abstract_code,
                                            int offset) {
  // Run through all break points to locate the one closest to the address.
  int closest_break = 0;
  int distance = kMaxInt;
  DCHECK(0 <= offset && offset < abstract_code->Size());
195
  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
196
    // Check if this break point is closer that what was previously found.
197 198 199
    if (it.code_offset() <= offset && offset - it.code_offset() < distance) {
      closest_break = it.break_index();
      distance = offset - it.code_offset();
200 201 202 203 204
      // Check whether we can't get any closer.
      if (distance == 0) break;
    }
  }
  return closest_break;
205 206
}

207 208
bool BreakLocation::HasBreakPoint(Isolate* isolate,
                                  Handle<DebugInfo> debug_info) const {
209
  // First check whether there is a break point with the same source position.
210
  if (!debug_info->HasBreakPoint(isolate, position_)) return false;
211 212 213 214 215 216 217 218 219 220 221 222
  if (debug_info->CanBreakAtEntry()) {
    DCHECK_EQ(Debug::kBreakAtEntryPosition, position_);
    return debug_info->BreakAtEntry();
  } else {
    // Then check whether a break point at that source position would have
    // the same code offset. Otherwise it's just a break location that we can
    // step to, but not actually a location where we can put a break point.
    DCHECK(abstract_code_->IsBytecodeArray());
    BreakIterator it(debug_info);
    it.SkipToPosition(position_);
    return it.code_offset() == code_offset_;
  }
223
}
224

225 226 227 228 229 230 231 232
debug::BreakLocationType BreakLocation::type() const {
  switch (type_) {
    case DEBUGGER_STATEMENT:
      return debug::kDebuggerStatementBreakLocation;
    case DEBUG_BREAK_SLOT_AT_CALL:
      return debug::kCallBreakLocation;
    case DEBUG_BREAK_SLOT_AT_RETURN:
      return debug::kReturnBreakLocation;
233 234 235

    // Externally, suspend breaks should look like normal breaks.
    case DEBUG_BREAK_SLOT_AT_SUSPEND:
236 237 238 239 240
    default:
      return debug::kCommonBreakLocation;
  }
}

241
BreakIterator::BreakIterator(Handle<DebugInfo> debug_info)
242 243 244
    : debug_info_(debug_info),
      break_index_(-1),
      source_position_iterator_(
245 246
          debug_info->DebugBytecodeArray().SourcePositionTable()) {
  position_ = debug_info->shared().StartPosition();
247
  statement_position_ = position_;
248 249 250
  // There is at least one break location.
  DCHECK(!Done());
  Next();
251
}
252

253
int BreakIterator::BreakIndexFromPosition(int source_position) {
254 255 256 257 258 259 260
  for (; !Done(); Next()) {
    if (source_position <= position()) {
      int first_break = break_index();
      for (; !Done(); Next()) {
        if (source_position == position()) return break_index();
      }
      return first_break;
261 262
    }
  }
263
  return break_index();
264 265
}

266
void BreakIterator::Next() {
267
  DisallowGarbageCollection no_gc;
268 269 270 271 272 273
  DCHECK(!Done());
  bool first = break_index_ == -1;
  while (!Done()) {
    if (!first) source_position_iterator_.Advance();
    first = false;
    if (Done()) return;
274
    position_ = source_position_iterator_.source_position().ScriptOffset();
275 276 277
    if (source_position_iterator_.is_statement()) {
      statement_position_ = position_;
    }
278 279
    DCHECK_LE(0, position_);
    DCHECK_LE(0, statement_position_);
280

281
    DebugBreakType type = GetDebugBreakType();
282
    if (type != NOT_DEBUG_BREAK) break;
283
  }
284
  break_index_++;
285 286
}

287
DebugBreakType BreakIterator::GetDebugBreakType() {
288
  BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
289
  interpreter::Bytecode bytecode =
290
      interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset()));
291

292 293
  // Make sure we read the actual bytecode, not a prefix scaling bytecode.
  if (interpreter::Bytecodes::IsPrefixScalingBytecode(bytecode)) {
294 295
    bytecode =
        interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset() + 1));
296 297
  }

298 299 300 301
  if (bytecode == interpreter::Bytecode::kDebugger) {
    return DEBUGGER_STATEMENT;
  } else if (bytecode == interpreter::Bytecode::kReturn) {
    return DEBUG_BREAK_SLOT_AT_RETURN;
302 303
  } else if (bytecode == interpreter::Bytecode::kSuspendGenerator) {
    return DEBUG_BREAK_SLOT_AT_SUSPEND;
304
  } else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) {
305 306 307 308 309 310 311 312
    return DEBUG_BREAK_SLOT_AT_CALL;
  } else if (source_position_iterator_.is_statement()) {
    return DEBUG_BREAK_SLOT;
  } else {
    return NOT_DEBUG_BREAK;
  }
}

313 314
void BreakIterator::SkipToPosition(int position) {
  BreakIterator it(debug_info_);
315
  SkipTo(it.BreakIndexFromPosition(position));
316 317
}

318
void BreakIterator::SetDebugBreak() {
319 320
  DebugBreakType debug_break_type = GetDebugBreakType();
  if (debug_break_type == DEBUGGER_STATEMENT) return;
321
  HandleScope scope(isolate());
322
  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
323 324
  Handle<BytecodeArray> bytecode_array(debug_info_->DebugBytecodeArray(),
                                       isolate());
325
  interpreter::BytecodeArrayIterator(bytecode_array, code_offset())
326
      .ApplyDebugBreak();
327 328
}

329
void BreakIterator::ClearDebugBreak() {
330 331 332
  DebugBreakType debug_break_type = GetDebugBreakType();
  if (debug_break_type == DEBUGGER_STATEMENT) return;
  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
333 334
  BytecodeArray bytecode_array = debug_info_->DebugBytecodeArray();
  BytecodeArray original = debug_info_->OriginalBytecodeArray();
335
  bytecode_array.set(code_offset(), original.get(code_offset()));
336 337
}

338
BreakLocation BreakIterator::GetBreakLocation() {
339
  Handle<AbstractCode> code(
340
      AbstractCode::cast(debug_info_->DebugBytecodeArray()), isolate());
341 342
  DebugBreakType type = GetDebugBreakType();
  int generator_object_reg_index = -1;
343
  int generator_suspend_id = -1;
344 345 346 347 348 349
  if (type == DEBUG_BREAK_SLOT_AT_SUSPEND) {
    // For suspend break, we'll need the generator object to be able to step
    // over the suspend as if it didn't return. We get the interpreter register
    // index that holds the generator object by reading it directly off the
    // bytecode array, and we'll read the actual generator object off the
    // interpreter stack frame in GetGeneratorObjectForSuspendedFrame.
350
    BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
351
    interpreter::BytecodeArrayIterator iterator(
352
        handle(bytecode_array, isolate()), code_offset());
353

354
    DCHECK_EQ(iterator.current_bytecode(),
355
              interpreter::Bytecode::kSuspendGenerator);
356
    interpreter::Register generator_obj_reg = iterator.GetRegisterOperand(0);
357
    generator_object_reg_index = generator_obj_reg.index();
358 359 360 361

    // Also memorize the suspend ID, to be able to decide whether
    // we are paused on the implicit initial yield later.
    generator_suspend_id = iterator.GetUnsignedImmediateOperand(3);
362 363
  }
  return BreakLocation(code, type, code_offset(), position_,
364
                       generator_object_reg_index, generator_suspend_id);
365 366
}

367
Isolate* BreakIterator::isolate() { return debug_info_->GetIsolate(); }
368

369 370 371 372 373 374 375 376
void DebugFeatureTracker::Track(DebugFeatureTracker::Feature feature) {
  uint32_t mask = 1 << feature;
  // Only count one sample per feature and isolate.
  if (bitfield_ & mask) return;
  isolate_->counters()->debug_feature_usage()->AddSample(feature);
  bitfield_ |= mask;
}

377 378
// Threading support.
void Debug::ThreadInit() {
379
  thread_local_.break_frame_id_ = StackFrameId::NO_ID;
380
  thread_local_.last_step_action_ = StepNone;
yangguo's avatar
yangguo committed
381
  thread_local_.last_statement_position_ = kNoSourcePosition;
382
  thread_local_.last_frame_count_ = -1;
383
  thread_local_.fast_forward_to_return_ = false;
384
  thread_local_.ignore_step_into_function_ = Smi::zero();
385
  thread_local_.target_frame_count_ = -1;
386
  thread_local_.return_value_ = Smi::zero();
387
  thread_local_.last_breakpoint_id_ = 0;
388
  clear_suspended_generator();
389 390
  base::Relaxed_Store(&thread_local_.current_debug_scope_,
                      static_cast<base::AtomicWord>(0));
391
  thread_local_.break_on_next_function_call_ = false;
392
  UpdateHookOnFunctionCall();
393 394 395
}

char* Debug::ArchiveDebug(char* storage) {
396 397
  MemCopy(storage, reinterpret_cast<char*>(&thread_local_),
          ArchiveSpacePerThread());
398 399 400 401
  return storage + ArchiveSpacePerThread();
}

char* Debug::RestoreDebug(char* storage) {
402 403 404 405 406 407 408 409 410 411 412
  MemCopy(reinterpret_cast<char*>(&thread_local_), storage,
          ArchiveSpacePerThread());

  // Enter the debugger.
  DebugScope debug_scope(this);

  // Clear any one-shot breakpoints that may have been set by the other
  // thread, and reapply breakpoints for this thread.
  ClearOneShot();

  if (thread_local_.last_step_action_ != StepNone) {
413 414 415 416 417 418 419 420 421 422 423 424
    int current_frame_count = CurrentFrameCount();
    int target_frame_count = thread_local_.target_frame_count_;
    DCHECK(current_frame_count >= target_frame_count);
    StackTraceFrameIterator frames_it(isolate_);
    while (current_frame_count > target_frame_count) {
      current_frame_count -= frames_it.FrameFunctionCount();
      frames_it.Advance();
    }
    DCHECK(current_frame_count == target_frame_count);
    // Set frame to what it was at Step break
    thread_local_.break_frame_id_ = frames_it.frame()->id();

425 426 427 428
    // Reset the previous step action for this thread.
    PrepareStep(thread_local_.last_step_action_);
  }

429 430 431
  return storage + ArchiveSpacePerThread();
}

432
int Debug::ArchiveSpacePerThread() { return sizeof(ThreadLocal); }
433

434 435 436 437 438 439 440 441 442 443
void Debug::Iterate(RootVisitor* v) { Iterate(v, &thread_local_); }

char* Debug::Iterate(RootVisitor* v, char* thread_storage) {
  ThreadLocal* thread_local_data =
      reinterpret_cast<ThreadLocal*>(thread_storage);
  Iterate(v, thread_local_data);
  return thread_storage + ArchiveSpacePerThread();
}

void Debug::Iterate(RootVisitor* v, ThreadLocal* thread_local_data) {
444
  v->VisitRootPointer(Root::kDebug, nullptr,
445
                      FullObjectSlot(&thread_local_data->return_value_));
446
  v->VisitRootPointer(Root::kDebug, nullptr,
447
                      FullObjectSlot(&thread_local_data->suspended_generator_));
448 449
  v->VisitRootPointer(
      Root::kDebug, nullptr,
450
      FullObjectSlot(&thread_local_data->ignore_step_into_function_));
451 452
}

453
DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo debug_info)
454
    : next_(nullptr) {
455
  // Globalize the request debug info object and make it weak.
456
  GlobalHandles* global_handles = isolate->global_handles();
457
  debug_info_ = global_handles->Create(debug_info).location();
458 459
}

460
DebugInfoListNode::~DebugInfoListNode() {
dcarney's avatar
dcarney committed
461
  if (debug_info_ == nullptr) return;
462
  GlobalHandles::Destroy(debug_info_);
dcarney's avatar
dcarney committed
463
  debug_info_ = nullptr;
464 465
}

466
void Debug::Unload() {
467
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
468
  ClearAllBreakPoints();
469
  ClearStepping();
470
  RemoveAllCoverageInfos();
471
  ClearAllDebuggerHints();
472
  ClearGlobalPromiseStack();
473
  debug_delegate_ = nullptr;
474 475
}

476 477 478 479 480 481 482 483 484 485 486 487
void Debug::OnInstrumentationBreak() {
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
  if (!debug_delegate_) return;
  DCHECK(in_debug_scope());
  HandleScope scope(isolate_);
  DisableBreak no_recursive_break(this);

  Handle<Context> native_context(isolate_->native_context());
  debug_delegate_->BreakOnInstrumentation(v8::Utils::ToLocal(native_context),
                                          kInstrumentationId);
}

488
void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) {
489
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
490
  // Just continue if breaks are disabled or debugger cannot be loaded.
491
  if (break_disabled()) return;
492

493
  // Enter the debugger.
494
  DebugScope debug_scope(this);
495
  DisableBreak no_recursive_break(this);
496

497
  // Return if we fail to retrieve debug info.
498
  Handle<SharedFunctionInfo> shared(break_target->shared(), isolate_);
499
  if (!EnsureBreakInfo(shared)) return;
500 501
  PrepareFunctionForDebugExecution(shared);

502
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
503

504 505
  // Find the break location where execution has stopped.
  BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
506 507 508
  const bool hitInstrumentationBreak =
      IsBreakOnInstrumentation(debug_info, location);
  if (hitInstrumentationBreak) {
509 510
    OnInstrumentationBreak();
  }
511 512

  // Find actual break points, if any, and trigger debug break event.
513
  bool has_break_points;
514
  MaybeHandle<FixedArray> break_points_hit =
515
      CheckBreakPoints(debug_info, &location, &has_break_points);
516
  if (!break_points_hit.is_null() || break_on_next_function_call()) {
517
    StepAction lastStepAction = last_step_action();
518 519 520
    // Clear all current stepping setup.
    ClearStepping();
    // Notify the debug event listeners.
521 522
    OnDebugBreak(!break_points_hit.is_null()
                     ? break_points_hit.ToHandleChecked()
523 524
                     : isolate_->factory()->empty_fixed_array(),
                 lastStepAction);
525
    return;
526 527
  }

528 529 530 531 532 533 534 535
  // Debug break at function entry, do not worry about stepping.
  if (location.IsDebugBreakAtEntry()) {
    DCHECK(debug_info->BreakAtEntry());
    return;
  }

  DCHECK_NOT_NULL(frame);

536 537
  // No break point. Check for stepping.
  StepAction step_action = last_step_action();
538 539 540
  int current_frame_count = CurrentFrameCount();
  int target_frame_count = thread_local_.target_frame_count_;
  int last_frame_count = thread_local_.last_frame_count_;
541

542 543 544
  // StepOut at not return position was requested and return break locations
  // were flooded with one shots.
  if (thread_local_.fast_forward_to_return_) {
545 546 547
    // We might hit an instrumentation breakpoint before running into a
    // return/suspend location.
    DCHECK(location.IsReturnOrSuspend() || hitInstrumentationBreak);
548 549 550 551 552 553 554
    // We have to ignore recursive calls to function.
    if (current_frame_count > target_frame_count) return;
    ClearStepping();
    PrepareStep(StepOut);
    return;
  }

555
  bool step_break = false;
556 557 558 559
  switch (step_action) {
    case StepNone:
      return;
    case StepOut:
560
      // StepOut should not break in a deeper frame than target frame.
561
      if (current_frame_count > target_frame_count) return;
562
      step_break = true;
563
      break;
564 565
    case StepOver:
      // StepOver should not break in a deeper frame than target frame.
566
      if (current_frame_count > target_frame_count) return;
567
      V8_FALLTHROUGH;
568 569
    case StepInto: {
      // Special case StepInto and StepOver for generators that are about to
570 571 572 573 574 575
      // suspend, in which case we go into "generator stepping" mode. The
      // exception here is the initial implicit yield in generators (which
      // always has a suspend ID of 0), where we return to the caller first,
      // instead of triggering "generator stepping" mode straight away.
      if (location.IsSuspend() && (!IsGeneratorFunction(shared->kind()) ||
                                   location.generator_suspend_id() > 0)) {
576 577 578 579 580 581 582
        DCHECK(!has_suspended_generator());
        thread_local_.suspended_generator_ =
            location.GetGeneratorObjectForSuspendedFrame(frame);
        ClearStepping();
        return;
      }

583
      FrameSummary summary = FrameSummary::GetTop(frame);
584 585
      step_break = step_break || location.IsReturn() ||
                   current_frame_count != last_frame_count ||
586 587
                   thread_local_.last_statement_position_ !=
                       summary.SourceStatementPosition();
588
      break;
589
    }
590 591
  }

592
  StepAction lastStepAction = last_step_action();
593 594 595 596
  // Clear all current stepping setup.
  ClearStepping();

  if (step_break) {
597
    // Notify the debug event listeners.
598
    OnDebugBreak(isolate_->factory()->empty_fixed_array(), lastStepAction);
599 600 601
  } else {
    // Re-prepare to continue.
    PrepareStep(step_action);
602 603 604
  }
}

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
bool Debug::IsBreakOnInstrumentation(Handle<DebugInfo> debug_info,
                                     const BreakLocation& location) {
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
  bool has_break_points_to_check =
      break_points_active_ && location.HasBreakPoint(isolate_, debug_info);
  if (!has_break_points_to_check) return {};

  Handle<Object> break_points =
      debug_info->GetBreakPoints(isolate_, location.position());
  DCHECK(!break_points->IsUndefined(isolate_));
  if (!break_points->IsFixedArray()) {
    const Handle<BreakPoint> break_point =
        Handle<BreakPoint>::cast(break_points);
    return break_point->id() == kInstrumentationId;
  }

  Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
  for (int i = 0; i < array->length(); ++i) {
    const Handle<BreakPoint> break_point =
        Handle<BreakPoint>::cast(handle(array->get(i), isolate_));
    if (break_point->id() == kInstrumentationId) {
      return true;
    }
  }
  return false;
}

632
// Find break point objects for this location, if any, and evaluate them.
633 634
// Return an array of break point objects that evaluated true, or an empty
// handle if none evaluated true.
635 636
// has_break_points will be true, if there is any (non-instrumentation)
// breakpoint.
637 638 639
MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info,
                                                BreakLocation* location,
                                                bool* has_break_points) {
640
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
641
  bool has_break_points_to_check =
642
      break_points_active_ && location->HasBreakPoint(isolate_, debug_info);
643 644 645 646
  if (!has_break_points_to_check) {
    *has_break_points = false;
    return {};
  }
647

648 649
  return Debug::GetHitBreakPoints(debug_info, location->position(),
                                  has_break_points);
650 651
}

652
bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
653 654 655 656 657
  // A break location is considered muted if break locations on the current
  // statement have at least one break point, and all of these break points
  // evaluate to false. Aside from not triggering a debug break event at the
  // break location, we also do not trigger one for debugger statements, nor
  // an exception event on exception at this location.
658 659 660 661 662 663 664 665 666 667 668
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
  HandleScope scope(isolate_);
  bool has_break_points;
  MaybeHandle<FixedArray> checked =
      GetHitBreakpointsAtCurrentStatement(frame, &has_break_points);
  return has_break_points && checked.is_null();
}

MaybeHandle<FixedArray> Debug::GetHitBreakpointsAtCurrentStatement(
    JavaScriptFrame* frame, bool* has_break_points) {
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
669 670
  FrameSummary summary = FrameSummary::GetTop(frame);
  Handle<JSFunction> function = summary.AsJavaScript().function();
671 672 673 674
  if (!function->shared().HasBreakInfo()) {
    *has_break_points = false;
    return {};
  }
675
  Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(), isolate_);
676 677
  // Enter the debugger.
  DebugScope debug_scope(this);
678
  std::vector<BreakLocation> break_locations;
679
  BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations);
680 681
  return CheckBreakPointsForLocations(debug_info, break_locations,
                                      has_break_points);
682 683
}

684
// Check whether a single break point object is triggered.
685 686
bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
                            bool is_break_at_entry) {
687
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
688
  HandleScope scope(isolate_);
689

690 691 692 693 694
  // Instrumentation breakpoints are handled separately.
  if (break_point->id() == kInstrumentationId) {
    return false;
  }

695
  if (!break_point->condition().length()) return true;
696
  Handle<String> condition(break_point->condition(), isolate_);
697
  MaybeHandle<Object> maybe_result;
698
  Handle<Object> result;
699 700 701 702 703 704 705 706 707 708 709 710 711 712

  if (is_break_at_entry) {
    maybe_result = DebugEvaluate::WithTopmostArguments(isolate_, condition);
  } else {
    // Since we call CheckBreakpoint only for deoptimized frame on top of stack,
    // we can use 0 as index of inlined frame.
    const int inlined_jsframe_index = 0;
    const bool throw_on_side_effect = false;
    maybe_result =
        DebugEvaluate::Local(isolate_, break_frame_id(), inlined_jsframe_index,
                             condition, throw_on_side_effect);
  }

  if (!maybe_result.ToHandle(&result)) {
713 714 715
    if (isolate_->has_pending_exception()) {
      isolate_->clear_pending_exception();
    }
716 717
    return false;
  }
718
  return result->BooleanValue(isolate_);
719 720
}

721
bool Debug::SetBreakpoint(Handle<SharedFunctionInfo> shared,
722
                          Handle<BreakPoint> break_point,
723
                          int* source_position) {
724
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
725
  HandleScope scope(isolate_);
726

727
  // Make sure the function is compiled and has set up the debug info.
728
  if (!EnsureBreakInfo(shared)) return false;
729 730
  PrepareFunctionForDebugExecution(shared);

731
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
732
  // Source positions starts with zero.
733
  DCHECK_LE(0, *source_position);
734 735

  // Find the break point and change it.
736
  *source_position = FindBreakablePosition(debug_info, *source_position);
737
  DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
738
  // At least one active break point now.
739
  DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
740

741 742
  ClearBreakPoints(debug_info);
  ApplyBreakPoints(debug_info);
743

744 745
  feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
  return true;
746 747
}

748
bool Debug::SetBreakPointForScript(Handle<Script> script,
749 750
                                   Handle<String> condition,
                                   int* source_position, int* id) {
751
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
752 753 754
  *id = ++thread_local_.last_breakpoint_id_;
  Handle<BreakPoint> break_point =
      isolate_->factory()->NewBreakPoint(*id, condition);
755
#if V8_ENABLE_WEBASSEMBLY
756
  if (script->type() == Script::TYPE_WASM) {
757
    RecordWasmScriptWithBreakpoints(script);
758
    return WasmScript::SetBreakPoint(script, source_position, break_point);
759
  }
760
#endif  //  V8_ENABLE_WEBASSEMBLY
761

762 763
  HandleScope scope(isolate_);

764 765
  // Obtain shared function info for the innermost function containing this
  // position.
766
  Handle<Object> result =
767
      FindInnermostContainingFunctionInfo(script, *source_position);
768
  if (result->IsUndefined(isolate_)) return false;
769

770
  auto shared = Handle<SharedFunctionInfo>::cast(result);
771 772 773 774 775 776 777 778 779
  if (!EnsureBreakInfo(shared)) return false;
  PrepareFunctionForDebugExecution(shared);

  // Find the nested shared function info that is closest to the position within
  // the containing function.
  shared = FindClosestSharedFunctionInfoFromPosition(*source_position, script,
                                                     shared);

  // Set the breakpoint in the function.
780
  return SetBreakpoint(shared, break_point, source_position);
781
}
782

783
int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
784
                                 int source_position) {
785
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
786 787 788
  if (debug_info->CanBreakAtEntry()) {
    return kBreakAtEntryPosition;
  } else {
789
    DCHECK(debug_info->HasInstrumentedBytecodeArray());
790 791 792 793
    BreakIterator it(debug_info);
    it.SkipToPosition(source_position);
    return it.position();
  }
794
}
795

796
void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
797
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
798
  DisallowGarbageCollection no_gc;
799 800 801
  if (debug_info->CanBreakAtEntry()) {
    debug_info->SetBreakAtEntry();
  } else {
802
    if (!debug_info->HasInstrumentedBytecodeArray()) return;
803
    FixedArray break_points = debug_info->break_points();
804 805 806 807
    for (int i = 0; i < break_points.length(); i++) {
      if (break_points.get(i).IsUndefined(isolate_)) continue;
      BreakPointInfo info = BreakPointInfo::cast(break_points.get(i));
      if (info.GetBreakPointCount(isolate_) == 0) continue;
808
      DCHECK(debug_info->HasInstrumentedBytecodeArray());
809
      BreakIterator it(debug_info);
810
      it.SkipToPosition(info.source_position());
811 812
      it.SetDebugBreak();
    }
813
  }
814
  debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints);
815 816
}

817
void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
818
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
819 820 821 822 823
  if (debug_info->CanBreakAtEntry()) {
    debug_info->ClearBreakAtEntry();
  } else {
    // If we attempt to clear breakpoints but none exist, simply return. This
    // can happen e.g. CoverageInfos exist but no breakpoints are set.
824 825
    if (!debug_info->HasInstrumentedBytecodeArray() ||
        !debug_info->HasBreakInfo()) {
826 827
      return;
    }
828

829
    DisallowGarbageCollection no_gc;
830 831 832
    for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
      it.ClearDebugBreak();
    }
833 834
  }
}
835

836
void Debug::ClearBreakPoint(Handle<BreakPoint> break_point) {
837
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
838
  HandleScope scope(isolate_);
839

840
  for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
841
       node = node->next()) {
842
    if (!node->debug_info()->HasBreakInfo()) continue;
843 844
    Handle<Object> result = DebugInfo::FindBreakPointInfo(
        isolate_, node->debug_info(), break_point);
845 846
    if (result->IsUndefined(isolate_)) continue;
    Handle<DebugInfo> debug_info = node->debug_info();
847
    if (DebugInfo::ClearBreakPoint(isolate_, debug_info, break_point)) {
848
      ClearBreakPoints(debug_info);
849
      if (debug_info->GetBreakPointCount(isolate_) == 0) {
850
        RemoveBreakInfoAndMaybeFree(debug_info);
851 852
      } else {
        ApplyBreakPoints(debug_info);
853 854 855 856 857 858
      }
      return;
    }
  }
}

859
int Debug::GetFunctionDebuggingId(Handle<JSFunction> function) {
860
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
861 862 863 864 865 866 867 868 869 870
  Handle<SharedFunctionInfo> shared = handle(function->shared(), isolate_);
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
  int id = debug_info->debugging_id();
  if (id == DebugInfo::kNoDebuggingId) {
    id = isolate_->heap()->NextDebuggingId();
    debug_info->set_debugging_id(id);
  }
  return id;
}

871
bool Debug::SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
872 873
                                     Handle<String> condition, int* id,
                                     BreakPointKind kind) {
874
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
875 876 877 878 879
  if (kind == kInstrumentation) {
    *id = kInstrumentationId;
  } else {
    *id = ++thread_local_.last_breakpoint_id_;
  }
880 881
  Handle<BreakPoint> breakpoint =
      isolate_->factory()->NewBreakPoint(*id, condition);
882
  int source_position = 0;
883
#if V8_ENABLE_WEBASSEMBLY
884 885 886 887 888 889 890 891 892 893
  // Handle wasm function.
  if (shared->HasWasmExportedFunctionData()) {
    int func_index = shared->wasm_exported_function_data().function_index();
    Handle<WasmInstanceObject> wasm_instance(
        shared->wasm_exported_function_data().instance(), isolate_);
    Handle<Script> script(Script::cast(wasm_instance->module_object().script()),
                          isolate_);
    return WasmScript::SetBreakPointOnFirstBreakableForFunction(
        script, func_index, breakpoint);
  }
894
#endif  // V8_ENABLE_WEBASSEMBLY
895
  return SetBreakpoint(shared, breakpoint, &source_position);
896 897 898
}

void Debug::RemoveBreakpoint(int id) {
899
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
900 901 902 903 904
  Handle<BreakPoint> breakpoint = isolate_->factory()->NewBreakPoint(
      id, isolate_->factory()->empty_string());
  ClearBreakPoint(breakpoint);
}

905
#if V8_ENABLE_WEBASSEMBLY
906 907
void Debug::SetInstrumentationBreakpointForWasmScript(Handle<Script> script,
                                                      int* id) {
908 909
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
  DCHECK_EQ(Script::TYPE_WASM, script->type());
910 911
  *id = kInstrumentationId;

912 913 914
  Handle<BreakPoint> break_point = isolate_->factory()->NewBreakPoint(
      *id, isolate_->factory()->empty_string());
  RecordWasmScriptWithBreakpoints(script);
915
  WasmScript::SetInstrumentationBreakpoint(script, break_point);
916 917
}

918
void Debug::RemoveBreakpointForWasmScript(Handle<Script> script, int id) {
919
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
920 921 922 923 924
  if (script->type() == Script::TYPE_WASM) {
    WasmScript::ClearBreakPointById(script, id);
  }
}

925
void Debug::RecordWasmScriptWithBreakpoints(Handle<Script> script) {
926
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
927
  if (wasm_scripts_with_break_points_.is_null()) {
928
    Handle<WeakArrayList> new_list = isolate_->factory()->NewWeakArrayList(4);
929
    wasm_scripts_with_break_points_ =
930 931 932
        isolate_->global_handles()->Create(*new_list);
  }
  {
933
    DisallowGarbageCollection no_gc;
934
    for (int idx = wasm_scripts_with_break_points_->length() - 1; idx >= 0;
935 936
         --idx) {
      HeapObject wasm_script;
937
      if (wasm_scripts_with_break_points_->Get(idx).GetHeapObject(
938 939 940 941 942 943 944
              &wasm_script) &&
          wasm_script == *script) {
        return;
      }
    }
  }
  Handle<WeakArrayList> new_list = WeakArrayList::Append(
945 946
      isolate_, wasm_scripts_with_break_points_, MaybeObjectHandle{script});
  if (*new_list != *wasm_scripts_with_break_points_) {
947
    isolate_->global_handles()->Destroy(
948 949
        wasm_scripts_with_break_points_.location());
    wasm_scripts_with_break_points_ =
950 951 952
        isolate_->global_handles()->Create(*new_list);
  }
}
953
#endif  // V8_ENABLE_WEBASSEMBLY
954

955
// Clear out all the debug break code.
956
void Debug::ClearAllBreakPoints() {
957
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
958 959
  ClearAllDebugInfos([=](Handle<DebugInfo> info) {
    ClearBreakPoints(info);
960
    info->ClearBreakInfo(isolate_);
961
  });
962
#if V8_ENABLE_WEBASSEMBLY
963
  // Clear all wasm breakpoints.
964
  if (!wasm_scripts_with_break_points_.is_null()) {
965
    DisallowGarbageCollection no_gc;
966
    for (int idx = wasm_scripts_with_break_points_->length() - 1; idx >= 0;
967 968
         --idx) {
      HeapObject raw_wasm_script;
969
      if (wasm_scripts_with_break_points_->Get(idx).GetHeapObject(
970
              &raw_wasm_script)) {
971 972 973 974
        Script wasm_script = Script::cast(raw_wasm_script);
        WasmScript::ClearAllBreakpoints(wasm_script);
        wasm_script.wasm_native_module()->GetDebugInfo()->RemoveIsolate(
            isolate_);
975 976
      }
    }
977
    wasm_scripts_with_break_points_ = Handle<WeakArrayList>{};
978
  }
979
#endif  // V8_ENABLE_WEBASSEMBLY
980 981
}

982 983
void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
                             bool returns_only) {
984
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
985
  if (IsBlackboxed(shared)) return;
986
  // Make sure the function is compiled and has set up the debug info.
987
  if (!EnsureBreakInfo(shared)) return;
988 989
  PrepareFunctionForDebugExecution(shared);

990
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
991
  // Flood the function with break points.
992
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
993
  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
994
    if (returns_only && !it.GetBreakLocation().IsReturnOrSuspend()) continue;
995
    it.SetDebugBreak();
996 997 998 999 1000 1001 1002 1003 1004 1005 1006
  }
}

void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
  if (type == BreakUncaughtException) {
    break_on_uncaught_exception_ = enable;
  } else {
    break_on_exception_ = enable;
  }
}

1007 1008 1009 1010 1011 1012 1013 1014
bool Debug::IsBreakOnException(ExceptionBreakType type) {
  if (type == BreakUncaughtException) {
    return break_on_uncaught_exception_;
  } else {
    return break_on_exception_;
  }
}

1015
MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<DebugInfo> debug_info,
1016 1017
                                                 int position,
                                                 bool* has_break_points) {
1018
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1019
  Handle<Object> break_points = debug_info->GetBreakPoints(isolate_, position);
1020
  bool is_break_at_entry = debug_info->BreakAtEntry();
1021 1022
  DCHECK(!break_points->IsUndefined(isolate_));
  if (!break_points->IsFixedArray()) {
1023 1024 1025 1026
    const Handle<BreakPoint> break_point =
        Handle<BreakPoint>::cast(break_points);
    *has_break_points = break_point->id() != kInstrumentationId;
    if (!CheckBreakPoint(break_point, is_break_at_entry)) {
1027 1028
      return {};
    }
1029
    Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1);
1030
    break_points_hit->set(0, *break_points);
1031 1032 1033
    return break_points_hit;
  }

1034
  Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
1035 1036 1037 1038
  int num_objects = array->length();
  Handle<FixedArray> break_points_hit =
      isolate_->factory()->NewFixedArray(num_objects);
  int break_points_hit_count = 0;
1039
  *has_break_points = false;
1040
  for (int i = 0; i < num_objects; ++i) {
1041 1042 1043 1044
    Handle<BreakPoint> break_point =
        Handle<BreakPoint>::cast(handle(array->get(i), isolate_));
    *has_break_points |= break_point->id() != kInstrumentationId;
    if (CheckBreakPoint(break_point, is_break_at_entry)) {
1045
      break_points_hit->set(break_points_hit_count++, *break_point);
1046 1047 1048
    }
  }
  if (break_points_hit_count == 0) return {};
1049
  break_points_hit->Shrink(isolate_, break_points_hit_count);
1050 1051
  return break_points_hit;
}
1052

1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
void Debug::SetBreakOnNextFunctionCall() {
  // This method forces V8 to break on next function call regardless current
  // last_step_action_. If any break happens between SetBreakOnNextFunctionCall
  // and ClearBreakOnNextFunctionCall, we will clear this flag and stepping. If
  // break does not happen, e.g. all called functions are blackboxed or no
  // function is called, then we will clear this flag and let stepping continue
  // its normal business.
  thread_local_.break_on_next_function_call_ = true;
  UpdateHookOnFunctionCall();
}

void Debug::ClearBreakOnNextFunctionCall() {
  thread_local_.break_on_next_function_call_ = false;
  UpdateHookOnFunctionCall();
}

1069
void Debug::PrepareStepIn(Handle<JSFunction> function) {
1070
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1071
  CHECK(last_step_action() >= StepInto || break_on_next_function_call());
1072
  if (ignore_events()) return;
1073
  if (in_debug_scope()) return;
1074
  if (break_disabled()) return;
1075
  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
1076 1077
  if (IsBlackboxed(shared)) return;
  if (*function == thread_local_.ignore_step_into_function_) return;
1078
  thread_local_.ignore_step_into_function_ = Smi::zero();
1079
  FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
1080 1081
}

1082
void Debug::PrepareStepInSuspendedGenerator() {
1083
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1084
  CHECK(has_suspended_generator());
1085
  if (ignore_events()) return;
1086
  if (in_debug_scope()) return;
1087
  if (break_disabled()) return;
1088
  thread_local_.last_step_action_ = StepInto;
1089
  UpdateHookOnFunctionCall();
1090
  Handle<JSFunction> function(
1091
      JSGeneratorObject::cast(thread_local_.suspended_generator_).function(),
1092
      isolate_);
1093
  FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
1094 1095
  clear_suspended_generator();
}
1096

1097
void Debug::PrepareStepOnThrow() {
1098
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1099
  if (last_step_action() == StepNone) return;
1100
  if (ignore_events()) return;
1101
  if (in_debug_scope()) return;
1102
  if (break_disabled()) return;
1103 1104 1105

  ClearOneShot();

1106 1107
  int current_frame_count = CurrentFrameCount();

1108 1109 1110 1111
  // Iterate through the JavaScript stack looking for handlers.
  JavaScriptFrameIterator it(isolate_);
  while (!it.done()) {
    JavaScriptFrame* frame = it.frame();
1112
    if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break;
1113
    std::vector<SharedFunctionInfo> infos;
1114
    frame->GetFunctions(&infos);
1115
    current_frame_count -= infos.size();
1116 1117 1118
    it.Advance();
  }

1119 1120 1121 1122 1123 1124 1125 1126
  // No handler found. Nothing to instrument.
  if (it.done()) return;

  bool found_handler = false;
  // Iterate frames, including inlined frames. First, find the handler frame.
  // Then skip to the frame we want to break in, then instrument for stepping.
  for (; !it.done(); it.Advance()) {
    JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
1127
    if (last_step_action() == StepInto) {
1128 1129
      // Deoptimize frame to ensure calls are checked for step-in.
      Deoptimizer::DeoptimizeFunction(frame->function());
1130
    }
1131
    std::vector<FrameSummary> summaries;
1132
    frame->Summarize(&summaries);
1133 1134
    for (size_t i = summaries.size(); i != 0; i--, current_frame_count--) {
      const FrameSummary& summary = summaries[i - 1];
1135 1136 1137 1138
      if (!found_handler) {
        // We have yet to find the handler. If the frame inlines multiple
        // functions, we have to check each one for the handler.
        // If it only contains one function, we already found the handler.
1139 1140
        if (summaries.size() > 1) {
          Handle<AbstractCode> code = summary.AsJavaScript().abstract_code();
1141
          CHECK_EQ(CodeKind::INTERPRETED_FUNCTION, code->kind());
1142
          HandlerTable table(code->GetBytecodeArray());
1143
          int code_offset = summary.code_offset();
1144
          HandlerTable::CatchPrediction prediction;
1145
          int index = table.LookupRange(code_offset, nullptr, &prediction);
1146 1147 1148 1149 1150
          if (index > 0) found_handler = true;
        } else {
          found_handler = true;
        }
      }
1151

1152 1153 1154
      if (found_handler) {
        // We found the handler. If we are stepping next or out, we need to
        // iterate until we found the suitable target frame to break in.
1155
        if ((last_step_action() == StepOver || last_step_action() == StepOut) &&
1156 1157 1158 1159
            current_frame_count > thread_local_.target_frame_count_) {
          continue;
        }
        Handle<SharedFunctionInfo> info(
1160
            summary.AsJavaScript().function()->shared(), isolate_);
1161
        if (IsBlackboxed(info)) continue;
1162 1163 1164 1165
        FloodWithOneShot(info);
        return;
      }
    }
1166 1167 1168
  }
}

1169
void Debug::PrepareStep(StepAction step_action) {
1170
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1171
  HandleScope scope(isolate_);
1172

1173
  DCHECK(in_debug_scope());
1174 1175 1176 1177 1178

  // Get the frame where the execution has stopped and skip the debug frame if
  // any. The debug frame will only be present if execution was stopped due to
  // hitting a break point. In other situations (e.g. unhandled exception) the
  // debug frame is not present.
1179
  StackFrameId frame_id = break_frame_id();
1180
  // If there is no JavaScript stack don't do anything.
1181
  if (frame_id == StackFrameId::NO_ID) return;
1182

1183 1184
  feature_tracker()->Track(DebugFeatureTracker::kStepping);

1185 1186
  thread_local_.last_step_action_ = step_action;

1187
  StackTraceFrameIterator frames_it(isolate_, frame_id);
1188
  CommonFrame* frame = frames_it.frame();
1189

1190 1191 1192
  BreakLocation location = BreakLocation::Invalid();
  Handle<SharedFunctionInfo> shared;
  int current_frame_count = CurrentFrameCount();
1193

1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
  if (frame->is_java_script()) {
    JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
    DCHECK(js_frame->function().IsJSFunction());

    // Get the debug info (create it if it does not exist).
    auto summary = FrameSummary::GetTop(frame).AsJavaScript();
    Handle<JSFunction> function(summary.function());
    shared = Handle<SharedFunctionInfo>(function->shared(), isolate_);
    if (!EnsureBreakInfo(shared)) return;
    PrepareFunctionForDebugExecution(shared);

1205
    // PrepareFunctionForDebugExecution can invalidate Baseline frames
1206
    js_frame = JavaScriptFrame::cast(frames_it.Reframe());
1207

1208
    Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1209 1210 1211 1212 1213
    location = BreakLocation::FromFrame(debug_info, js_frame);

    // Any step at a return is a step-out, and a step-out at a suspend behaves
    // like a return.
    if (location.IsReturn() ||
1214 1215 1216
        (location.IsSuspend() &&
         (step_action == StepOut || (IsGeneratorFunction(shared->kind()) &&
                                     location.generator_suspend_id() == 0)))) {
1217 1218 1219 1220 1221 1222
      // On StepOut we'll ignore our further calls to current function in
      // PrepareStepIn callback.
      if (last_step_action() == StepOut) {
        thread_local_.ignore_step_into_function_ = *function;
      }
      step_action = StepOut;
1223
      thread_local_.last_step_action_ = StepInto;
1224
    }
1225

1226 1227
    // We need to schedule DebugOnFunction call callback
    UpdateHookOnFunctionCall();
1228

1229
    // A step-next in blackboxed function is a step-out.
1230
    if (step_action == StepOver && IsBlackboxed(shared)) step_action = StepOut;
1231

1232 1233 1234 1235 1236
    thread_local_.last_statement_position_ =
        summary.abstract_code()->SourceStatementPosition(summary.code_offset());
    thread_local_.last_frame_count_ = current_frame_count;
    // No longer perform the current async step.
    clear_suspended_generator();
1237
#if V8_ENABLE_WEBASSEMBLY
1238
  } else if (frame->is_wasm() && step_action != StepOut) {
1239
    // Handle stepping in wasm.
1240
    WasmFrame* wasm_frame = WasmFrame::cast(frame);
1241 1242 1243 1244
    auto* debug_info = wasm_frame->native_module()->GetDebugInfo();
    if (debug_info->PrepareStep(wasm_frame)) {
      UpdateHookOnFunctionCall();
      return;
1245
    }
1246 1247 1248
    // If the wasm code is not debuggable or will return after this step
    // (indicated by {PrepareStep} returning false), then step out of that frame
    // instead.
1249 1250
    step_action = StepOut;
    UpdateHookOnFunctionCall();
1251
#endif  // V8_ENABLE_WEBASSEMBLY
1252
  }
1253

1254 1255 1256
  switch (step_action) {
    case StepNone:
      UNREACHABLE();
1257 1258 1259 1260
    case StepOut: {
      // Clear last position info. For stepping out it does not matter.
      thread_local_.last_statement_position_ = kNoSourcePosition;
      thread_local_.last_frame_count_ = -1;
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
      if (!shared.is_null()) {
        if (!location.IsReturnOrSuspend() && !IsBlackboxed(shared)) {
          // At not return position we flood return positions with one shots and
          // will repeat StepOut automatically at next break.
          thread_local_.target_frame_count_ = current_frame_count;
          thread_local_.fast_forward_to_return_ = true;
          FloodWithOneShot(shared, true);
          return;
        }
        if (IsAsyncFunction(shared->kind())) {
          // Stepping out of an async function whose implicit promise is awaited
          // by some other async function, should resume the latter. The return
          // value here is either a JSPromise or a JSGeneratorObject (for the
          // initial yield of async generators).
          Handle<JSReceiver> return_value(
              JSReceiver::cast(thread_local_.return_value_), isolate_);
          Handle<Object> awaited_by = JSReceiver::GetDataProperty(
1278 1279
              isolate_, return_value,
              isolate_->factory()->promise_awaited_by_symbol());
1280 1281 1282 1283 1284 1285 1286
          if (awaited_by->IsJSGeneratorObject()) {
            DCHECK(!has_suspended_generator());
            thread_local_.suspended_generator_ = *awaited_by;
            ClearStepping();
            return;
          }
        }
1287
      }
1288 1289 1290 1291
      // Skip the current frame, find the first frame we want to step out to
      // and deoptimize every frame along the way.
      bool in_current_frame = true;
      for (; !frames_it.done(); frames_it.Advance()) {
1292
#if V8_ENABLE_WEBASSEMBLY
1293
        if (frames_it.frame()->is_wasm()) {
1294 1295 1296 1297 1298 1299 1300 1301 1302
          if (in_current_frame) {
            in_current_frame = false;
            continue;
          }
          // Handle stepping out into Wasm.
          WasmFrame* wasm_frame = WasmFrame::cast(frames_it.frame());
          auto* debug_info = wasm_frame->native_module()->GetDebugInfo();
          debug_info->PrepareStepOutTo(wasm_frame);
          return;
1303
        }
1304
#endif  // V8_ENABLE_WEBASSEMBLY
1305
        JavaScriptFrame* js_frame = JavaScriptFrame::cast(frames_it.frame());
1306
        if (last_step_action() == StepInto) {
1307
          // Deoptimize frame to ensure calls are checked for step-in.
1308
          Deoptimizer::DeoptimizeFunction(js_frame->function());
1309
        }
1310
        HandleScope inner_scope(isolate_);
1311
        std::vector<Handle<SharedFunctionInfo>> infos;
1312
        js_frame->GetFunctions(&infos);
1313 1314 1315
        for (; !infos.empty(); current_frame_count--) {
          Handle<SharedFunctionInfo> info = infos.back();
          infos.pop_back();
1316
          if (in_current_frame) {
1317
            // We want to step out, so skip the current frame.
1318 1319 1320
            in_current_frame = false;
            continue;
          }
1321
          if (IsBlackboxed(info)) continue;
1322 1323 1324
          FloodWithOneShot(info);
          thread_local_.target_frame_count_ = current_frame_count;
          return;
1325
        }
1326 1327
      }
      break;
1328
    }
1329
    case StepOver:
1330
      thread_local_.target_frame_count_ = current_frame_count;
1331
      V8_FALLTHROUGH;
1332
    case StepInto:
1333
      FloodWithOneShot(shared);
1334
      break;
1335 1336 1337 1338 1339
  }
}

// Simple function for returning the source positions for active break points.
Handle<Object> Debug::GetSourceBreakLocations(
1340
    Isolate* isolate, Handle<SharedFunctionInfo> shared) {
1341
  RCS_SCOPE(isolate, RuntimeCallCounterId::kDebugger);
1342
  if (!shared->HasBreakInfo()) {
1343
    return isolate->factory()->undefined_value();
1344
  }
1345

1346
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate);
1347
  if (debug_info->GetBreakPointCount(isolate) == 0) {
1348
    return isolate->factory()->undefined_value();
1349
  }
1350 1351
  Handle<FixedArray> locations = isolate->factory()->NewFixedArray(
      debug_info->GetBreakPointCount(isolate));
1352
  int count = 0;
1353 1354
  for (int i = 0; i < debug_info->break_points().length(); ++i) {
    if (!debug_info->break_points().get(i).IsUndefined(isolate)) {
1355
      BreakPointInfo break_point_info =
1356 1357
          BreakPointInfo::cast(debug_info->break_points().get(i));
      int break_points = break_point_info.GetBreakPointCount(isolate);
1358
      if (break_points == 0) continue;
1359 1360
      for (int j = 0; j < break_points; ++j) {
        locations->set(count++,
1361
                       Smi::FromInt(break_point_info.source_position()));
1362 1363 1364 1365 1366 1367 1368
      }
    }
  }
  return locations;
}

void Debug::ClearStepping() {
1369
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1370 1371 1372
  // Clear the various stepping setup.
  ClearOneShot();

1373
  thread_local_.last_step_action_ = StepNone;
yangguo's avatar
yangguo committed
1374
  thread_local_.last_statement_position_ = kNoSourcePosition;
1375
  thread_local_.ignore_step_into_function_ = Smi::zero();
1376
  thread_local_.fast_forward_to_return_ = false;
1377 1378
  thread_local_.last_frame_count_ = -1;
  thread_local_.target_frame_count_ = -1;
1379
  thread_local_.break_on_next_function_call_ = false;
1380
  UpdateHookOnFunctionCall();
1381 1382 1383 1384 1385 1386
}

// Clears all the one-shot break points that are currently set. Normally this
// function is called each time a break point is hit as one shot break points
// are used to support stepping.
void Debug::ClearOneShot() {
1387
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1388
  // The current implementation just runs through all the breakpoints. When the
1389
  // last break point for a function is removed that function is automatically
1390
  // removed from the list.
1391
  for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
1392
       node = node->next()) {
1393 1394 1395
    Handle<DebugInfo> debug_info = node->debug_info();
    ClearBreakPoints(debug_info);
    ApplyBreakPoints(debug_info);
1396 1397 1398
  }
}

1399 1400 1401 1402 1403 1404 1405 1406
namespace {
class DiscardBaselineCodeVisitor : public ThreadVisitor {
 public:
  explicit DiscardBaselineCodeVisitor(SharedFunctionInfo shared)
      : shared_(shared) {}
  DiscardBaselineCodeVisitor() : shared_(SharedFunctionInfo()) {}

  void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
1407
    DisallowGarbageCollection diallow_gc;
1408 1409
    bool deopt_all = shared_ == SharedFunctionInfo();
    for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
1410
      if (!deopt_all && it.frame()->function().shared() != shared_) continue;
1411 1412
      if (it.frame()->type() == StackFrame::BASELINE) {
        BaselineFrame* frame = BaselineFrame::cast(it.frame());
1413
        int bytecode_offset = frame->GetBytecodeOffset();
1414
        Address* pc_addr = frame->pc_address();
1415
        Address advance = BUILTIN_CODE(isolate, InterpreterEnterAtNextBytecode)
1416 1417
                              ->InstructionStart();
        PointerAuthentication::ReplacePC(pc_addr, advance, kSystemPointerSize);
1418 1419
        InterpretedFrame::cast(it.Reframe())
            ->PatchBytecodeOffset(bytecode_offset);
1420 1421 1422 1423 1424 1425 1426
      } else if (it.frame()->type() == StackFrame::INTERPRETED) {
        // Check if the PC is a baseline entry trampoline. If it is, replace it
        // with the corresponding interpreter entry trampoline.
        // This is the case if a baseline function was inlined into a function
        // we deoptimized in the debugger and are stepping into it.
        JavaScriptFrame* frame = it.frame();
        Address pc = frame->pc();
1427
        Builtin builtin = OffHeapInstructionStream::TryLookupCode(isolate, pc);
1428 1429
        if (builtin == Builtin::kBaselineOrInterpreterEnterAtBytecode ||
            builtin == Builtin::kBaselineOrInterpreterEnterAtNextBytecode) {
1430
          Address* pc_addr = frame->pc_address();
1431 1432 1433 1434
          Builtin advance =
              builtin == Builtin::kBaselineOrInterpreterEnterAtBytecode
                  ? Builtin::kInterpreterEnterAtBytecode
                  : Builtin::kInterpreterEnterAtNextBytecode;
1435
          Address advance_pc =
1436
              isolate->builtins()->code(advance).InstructionStart();
1437 1438 1439
          PointerAuthentication::ReplacePC(pc_addr, advance_pc,
                                           kSystemPointerSize);
        }
1440 1441 1442 1443 1444 1445 1446 1447 1448 1449
      }
    }
  }

 private:
  SharedFunctionInfo shared_;
};
}  // namespace

void Debug::DiscardBaselineCode(SharedFunctionInfo shared) {
1450
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1451
  DCHECK(shared.HasBaselineCode());
1452 1453 1454 1455 1456 1457
  Isolate* isolate = shared.GetIsolate();
  DiscardBaselineCodeVisitor visitor(shared);
  visitor.VisitThread(isolate, isolate->thread_local_top());
  isolate->thread_manager()->IterateArchivedThreads(&visitor);
  // TODO(v8:11429): Avoid this heap walk somehow.
  HeapObjectIterator iterator(isolate->heap());
1458
  auto trampoline = BUILTIN_CODE(isolate, InterpreterEntryTrampoline);
1459
  shared.FlushBaselineCode();
1460 1461 1462 1463
  for (HeapObject obj = iterator.Next(); !obj.is_null();
       obj = iterator.Next()) {
    if (obj.IsJSFunction()) {
      JSFunction fun = JSFunction::cast(obj);
1464
      if (fun.shared() == shared && fun.ActiveTierIsBaseline()) {
1465 1466 1467 1468 1469 1470 1471
        fun.set_code(*trampoline);
      }
    }
  }
}

void Debug::DiscardAllBaselineCode() {
1472
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1473 1474 1475
  DiscardBaselineCodeVisitor visitor;
  visitor.VisitThread(isolate_, isolate_->thread_local_top());
  HeapObjectIterator iterator(isolate_->heap());
1476
  auto trampoline = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline);
1477 1478 1479 1480 1481
  isolate_->thread_manager()->IterateArchivedThreads(&visitor);
  for (HeapObject obj = iterator.Next(); !obj.is_null();
       obj = iterator.Next()) {
    if (obj.IsJSFunction()) {
      JSFunction fun = JSFunction::cast(obj);
1482
      if (fun.ActiveTierIsBaseline()) {
1483 1484
        fun.set_code(*trampoline);
      }
1485 1486 1487 1488 1489
    } else if (obj.IsSharedFunctionInfo()) {
      SharedFunctionInfo shared = SharedFunctionInfo::cast(obj);
      if (shared.HasBaselineCode()) {
        shared.FlushBaselineCode();
      }
1490 1491 1492 1493
    }
  }
}

1494
void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
1495
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1496 1497
  // Deoptimize all code compiled from this shared function info including
  // inlining.
1498
  isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock);
1499

1500
  if (shared->HasBaselineCode()) {
1501
    DiscardBaselineCode(*shared);
1502 1503
  }

1504 1505
  bool found_something = false;
  Code::OptimizedCodeIterator iterator(isolate_);
1506 1507 1508
  do {
    Code code = iterator.Next();
    if (code.is_null()) break;
1509 1510
    if (code.Inlines(*shared)) {
      code.set_marked_for_deoptimization(true);
1511
      found_something = true;
1512
    }
1513
  } while (true);
1514

1515 1516 1517 1518 1519 1520
  if (found_something) {
    // Only go through with the deoptimization if something was found.
    Deoptimizer::DeoptimizeMarkedCode(isolate_);
  }
}

1521 1522
void Debug::PrepareFunctionForDebugExecution(
    Handle<SharedFunctionInfo> shared) {
1523
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1524 1525 1526 1527 1528 1529
  // To prepare bytecode for debugging, we already need to have the debug
  // info (containing the debug copy) upfront, but since we do not recompile,
  // preparing for break points cannot fail.
  DCHECK(shared->is_compiled());
  DCHECK(shared->HasDebugInfo());
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1530
  if (debug_info->flags(kRelaxedLoad) & DebugInfo::kPreparedForDebugExecution) {
1531
    return;
1532
  }
1533

1534 1535
  // Have to discard baseline code before installing debug bytecode, since the
  // bytecode array field on the baseline code object is immutable.
1536 1537 1538
  if (debug_info->CanBreakAtEntry()) {
    // Deopt everything in case the function is inlined anywhere.
    Deoptimizer::DeoptimizeAll(isolate_);
1539
    DiscardAllBaselineCode();
1540 1541
  } else {
    DeoptimizeFunction(shared);
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551
  }

  if (shared->HasBytecodeArray()) {
    DCHECK(!shared->HasBaselineCode());
    SharedFunctionInfo::InstallDebugBytecode(shared, isolate_);
  }

  if (debug_info->CanBreakAtEntry()) {
    InstallDebugBreakTrampoline();
  } else {
1552
    // Update PCs on the stack to point to recompiled code.
1553 1554
    RedirectActiveFunctions redirect_visitor(
        *shared, RedirectActiveFunctions::Mode::kUseDebugBytecode);
1555 1556 1557
    redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
    isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
  }
1558

1559 1560 1561
  debug_info->set_flags(
      debug_info->flags(kRelaxedLoad) | DebugInfo::kPreparedForDebugExecution,
      kRelaxedStore);
1562 1563
}

1564
void Debug::InstallDebugBreakTrampoline() {
1565
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1566 1567 1568 1569
  // Check the list of debug infos whether the debug break trampoline needs to
  // be installed. If that's the case, iterate the heap for functions to rewire
  // to the trampoline.
  HandleScope scope(isolate_);
1570 1571 1572 1573
  // If there is a breakpoint at function entry, we need to install trampoline.
  bool needs_to_use_trampoline = false;
  // If there we break at entry to an api callback, we need to clear ICs.
  bool needs_to_clear_ic = false;
1574 1575 1576
  for (DebugInfoListNode* current = debug_info_list_; current != nullptr;
       current = current->next()) {
    if (current->debug_info()->CanBreakAtEntry()) {
1577
      needs_to_use_trampoline = true;
1578
      if (current->debug_info()->shared().IsApiFunction()) {
1579 1580 1581 1582 1583 1584 1585 1586
        needs_to_clear_ic = true;
        break;
      }
    }
  }

  if (!needs_to_use_trampoline) return;

1587
  Handle<CodeT> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline);
1588 1589
  std::vector<Handle<JSFunction>> needs_compile;
  {
1590
    HeapObjectIterator iterator(isolate_->heap());
1591 1592
    for (HeapObject obj = iterator.Next(); !obj.is_null();
         obj = iterator.Next()) {
1593 1594
      if (needs_to_clear_ic && obj.IsFeedbackVector()) {
        FeedbackVector::cast(obj).ClearSlots(isolate_);
1595
        continue;
1596
      } else if (obj.IsJSFunction()) {
1597
        JSFunction fun = JSFunction::cast(obj);
1598 1599 1600 1601
        SharedFunctionInfo shared = fun.shared();
        if (!shared.HasDebugInfo()) continue;
        if (!shared.GetDebugInfo().CanBreakAtEntry()) continue;
        if (!fun.is_compiled()) {
1602
          needs_compile.push_back(handle(fun, isolate_));
1603
        } else {
1604
          fun.set_code(*trampoline);
1605 1606 1607 1608
        }
      }
    }
  }
1609

1610 1611
  // By overwriting the function code with DebugBreakTrampoline, which tailcalls
  // to shared code, we bypass CompileLazy. Perform CompileLazy here instead.
1612
  for (Handle<JSFunction> fun : needs_compile) {
1613
    IsCompiledScope is_compiled_scope;
1614 1615
    Compiler::Compile(isolate_, fun, Compiler::CLEAR_EXCEPTION,
                      &is_compiled_scope);
1616
    DCHECK(is_compiled_scope.is_compiled());
1617 1618 1619 1620
    fun->set_code(*trampoline);
  }
}

1621 1622 1623
namespace {
template <typename Iterator>
void GetBreakablePositions(Iterator* it, int start_position, int end_position,
1624
                           std::vector<BreakLocation>* locations) {
1625 1626 1627 1628
  while (!it->Done()) {
    if (it->position() >= start_position && it->position() < end_position) {
      locations->push_back(it->GetBreakLocation());
    }
1629 1630 1631 1632 1633
    it->Next();
  }
}

void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
1634 1635
                            int end_position,
                            std::vector<BreakLocation>* locations) {
1636
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
1637 1638
  BreakIterator it(debug_info);
  GetBreakablePositions(&it, start_position, end_position, locations);
1639
}
1640

1641
bool CompileTopLevel(Isolate* isolate, Handle<Script> script) {
1642 1643
  UnoptimizedCompileState compile_state;
  ReusableUnoptimizedCompileState reusable_state(isolate);
1644 1645
  UnoptimizedCompileFlags flags =
      UnoptimizedCompileFlags::ForScriptCompile(isolate, *script);
1646
  ParseInfo parse_info(isolate, flags, &compile_state, &reusable_state);
1647
  IsCompiledScope is_compiled_scope;
1648 1649 1650 1651 1652 1653 1654 1655 1656 1657
  const MaybeHandle<SharedFunctionInfo> maybe_result =
      Compiler::CompileToplevel(&parse_info, script, isolate,
                                &is_compiled_scope);
  if (maybe_result.is_null()) {
    if (isolate->has_pending_exception()) {
      isolate->clear_pending_exception();
    }
    return false;
  }
  return true;
1658
}
1659 1660 1661
}  // namespace

bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
1662
                                   int end_position, bool restrict_to_function,
1663
                                   std::vector<BreakLocation>* locations) {
1664
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1665 1666
  if (restrict_to_function) {
    Handle<Object> result =
1667
        FindInnermostContainingFunctionInfo(script, start_position);
1668 1669 1670 1671 1672
    if (result->IsUndefined(isolate_)) return false;

    // Make sure the function has set up the debug info.
    Handle<SharedFunctionInfo> shared =
        Handle<SharedFunctionInfo>::cast(result);
1673
    if (!EnsureBreakInfo(shared)) return false;
1674
    PrepareFunctionForDebugExecution(shared);
1675

1676
    Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1677
    FindBreakablePositions(debug_info, start_position, end_position, locations);
1678 1679 1680
    return true;
  }

1681 1682 1683 1684 1685
  HandleScope scope(isolate_);
  std::vector<Handle<SharedFunctionInfo>> candidates;
  if (!FindSharedFunctionInfosIntersectingRange(script, start_position,
                                                end_position, &candidates)) {
    return false;
1686
  }
1687 1688 1689 1690 1691 1692
  for (const auto& candidate : candidates) {
    CHECK(candidate->HasBreakInfo());
    Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
    FindBreakablePositions(debug_info, start_position, end_position, locations);
  }
  return true;
1693 1694
}

1695 1696 1697
class SharedFunctionInfoFinder {
 public:
  explicit SharedFunctionInfoFinder(int target_position)
1698
      : current_start_position_(kNoSourcePosition),
1699 1700
        target_position_(target_position) {}

1701 1702
  void NewCandidate(SharedFunctionInfo shared,
                    JSFunction closure = JSFunction()) {
1703 1704
    if (!shared.IsSubjectToDebugging()) return;
    int start_position = shared.function_token_position();
yangguo's avatar
yangguo committed
1705
    if (start_position == kNoSourcePosition) {
1706
      start_position = shared.StartPosition();
1707
    }
1708

1709
    if (start_position > target_position_) return;
1710 1711 1712 1713 1714 1715 1716 1717 1718 1719
    if (target_position_ >= shared.EndPosition()) {
      // The SharedFunctionInfo::EndPosition() is generally exclusive, but there
      // are assumptions in various places in the debugger that for script level
      // (toplevel function) there's an end position that is technically outside
      // the script. It might be worth revisiting the overall design here at
      // some point in the future.
      if (!shared.is_toplevel() || target_position_ > shared.EndPosition()) {
        return;
      }
    }
1720

1721
    if (!current_candidate_.is_null()) {
1722
      if (current_start_position_ == start_position &&
1723
          shared.EndPosition() == current_candidate_.EndPosition()) {
1724
        // If we already have a matching closure, do not throw it away.
1725
        if (!current_candidate_closure_.is_null() && closure.is_null()) return;
1726 1727 1728
        // If a top-level function contains only one function
        // declaration the source for the top-level and the function
        // is the same. In that case prefer the non top-level function.
1729
        if (!current_candidate_.is_toplevel() && shared.is_toplevel()) return;
1730
      } else if (start_position < current_start_position_ ||
1731
                 current_candidate_.EndPosition() < shared.EndPosition()) {
1732 1733 1734
        return;
      }
    }
1735

1736 1737 1738 1739 1740
    current_start_position_ = start_position;
    current_candidate_ = shared;
    current_candidate_closure_ = closure;
  }

1741
  SharedFunctionInfo Result() { return current_candidate_; }
1742

1743
  JSFunction ResultClosure() { return current_candidate_closure_; }
1744 1745

 private:
1746
  SharedFunctionInfo current_candidate_;
1747
  JSFunction current_candidate_closure_;
1748 1749
  int current_start_position_;
  int target_position_;
1750
  DISALLOW_GARBAGE_COLLECTION(no_gc_)
1751
};
1752

1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
namespace {
SharedFunctionInfo FindSharedFunctionInfoCandidate(int position,
                                                   Handle<Script> script,
                                                   Isolate* isolate) {
  SharedFunctionInfoFinder finder(position);
  SharedFunctionInfo::ScriptIterator iterator(isolate, *script);
  for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
       info = iterator.Next()) {
    finder.NewCandidate(info);
  }
  return finder.Result();
}
}  // namespace
1766

1767 1768 1769
Handle<SharedFunctionInfo> Debug::FindClosestSharedFunctionInfoFromPosition(
    int position, Handle<Script> script,
    Handle<SharedFunctionInfo> outer_shared) {
1770
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830
  CHECK(outer_shared->HasBreakInfo());
  int closest_position = FindBreakablePosition(
      Handle<DebugInfo>(outer_shared->GetDebugInfo(), isolate_), position);
  Handle<SharedFunctionInfo> closest_candidate = outer_shared;
  if (closest_position == position) return outer_shared;

  const int start_position = outer_shared->StartPosition();
  const int end_position = outer_shared->EndPosition();
  if (start_position == end_position) return outer_shared;

  if (closest_position == 0) closest_position = end_position;
  std::vector<Handle<SharedFunctionInfo>> candidates;
  // Find all shared function infos of functions that are intersecting from
  // the requested position until the end of the enclosing function.
  if (!FindSharedFunctionInfosIntersectingRange(
          script, position, closest_position, &candidates)) {
    return outer_shared;
  }

  for (auto candidate : candidates) {
    CHECK(candidate->HasBreakInfo());
    Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
    const int candidate_position = FindBreakablePosition(debug_info, position);
    if (candidate_position >= position &&
        candidate_position < closest_position) {
      closest_position = candidate_position;
      closest_candidate = candidate;
    }
    if (closest_position == position) break;
  }
  return closest_candidate;
}

bool Debug::FindSharedFunctionInfosIntersectingRange(
    Handle<Script> script, int start_position, int end_position,
    std::vector<Handle<SharedFunctionInfo>>* intersecting_shared) {
  bool candidateSubsumesRange = false;
  bool triedTopLevelCompile = false;

  while (true) {
    std::vector<Handle<SharedFunctionInfo>> candidates;
    std::vector<IsCompiledScope> compiled_scopes;
    {
      DisallowGarbageCollection no_gc;
      SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
      for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
           info = iterator.Next()) {
        if (info.EndPosition() < start_position ||
            info.StartPosition() >= end_position) {
          continue;
        }
        candidateSubsumesRange |= info.StartPosition() <= start_position &&
                                  info.EndPosition() >= end_position;
        if (!info.IsSubjectToDebugging()) continue;
        if (!info.is_compiled() && !info.allows_lazy_compilation()) continue;
        candidates.push_back(i::handle(info, isolate_));
      }
    }

    if (!triedTopLevelCompile && !candidateSubsumesRange &&
1831 1832 1833
        script->shared_function_info_count() > 0) {
      DCHECK_LE(script->shared_function_info_count(),
                script->shared_function_infos().length());
1834 1835 1836 1837 1838 1839 1840
      MaybeObject maybeToplevel = script->shared_function_infos().Get(0);
      HeapObject heap_object;
      const bool topLevelInfoExists =
          maybeToplevel->GetHeapObject(&heap_object) &&
          !heap_object.IsUndefined();
      if (!topLevelInfoExists) {
        triedTopLevelCompile = true;
1841 1842
        const bool success = CompileTopLevel(isolate_, script);
        if (!success) return false;
1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
        continue;
      }
    }

    bool was_compiled = false;
    for (const auto& candidate : candidates) {
      IsCompiledScope is_compiled_scope(candidate->is_compiled_scope(isolate_));
      if (!is_compiled_scope.is_compiled()) {
        // Code that cannot be compiled lazily are internal and not debuggable.
        DCHECK(candidate->allows_lazy_compilation());
        if (!Compiler::Compile(isolate_, candidate, Compiler::CLEAR_EXCEPTION,
                               &is_compiled_scope)) {
          return false;
        } else {
          was_compiled = true;
        }
      }
      DCHECK(is_compiled_scope.is_compiled());
      compiled_scopes.push_back(is_compiled_scope);
      if (!EnsureBreakInfo(candidate)) return false;
      PrepareFunctionForDebugExecution(candidate);
    }
    if (was_compiled) continue;
    *intersecting_shared = std::move(candidates);
    return true;
  }
  UNREACHABLE();
}

1872 1873 1874 1875 1876 1877
// We need to find a SFI for a literal that may not yet have been compiled yet,
// and there may not be a JSFunction referencing it. Find the SFI closest to
// the given position, compile it to reveal possible inner SFIs and repeat.
// While we are at this, also ensure code with debug break slots so that we do
// not have to compile a SFI without JSFunction, which is paifu for those that
// cannot be compiled without context (need to find outer compilable SFI etc.)
1878 1879
Handle<Object> Debug::FindInnermostContainingFunctionInfo(Handle<Script> script,
                                                          int position) {
1880
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1881
  for (int iteration = 0;; iteration++) {
1882
    // Go through all shared function infos associated with this script to
1883
    // find the innermost function containing this position.
1884 1885
    // If there is no shared function info for this script at all, there is
    // no point in looking for it by walking the heap.
1886

1887
    SharedFunctionInfo shared;
1888
    IsCompiledScope is_compiled_scope;
1889
    {
1890
      shared = FindSharedFunctionInfoCandidate(position, script, isolate_);
1891 1892
      if (shared.is_null()) {
        if (iteration > 0) break;
1893 1894 1895
        // It might be that the shared function info is not available as the
        // top level functions are removed due to the GC. Try to recompile
        // the top level functions.
1896 1897
        const bool success = CompileTopLevel(isolate_, script);
        if (!success) break;
1898
        continue;
1899
      }
1900
      // We found it if it's already compiled.
1901
      is_compiled_scope = shared.is_compiled_scope(isolate_);
1902
      if (is_compiled_scope.is_compiled()) {
1903
        Handle<SharedFunctionInfo> shared_handle(shared, isolate_);
1904 1905 1906
        // If the iteration count is larger than 1, we had to compile the outer
        // function in order to create this shared function info. So there can
        // be no JSFunction referencing it. We can anticipate creating a debug
1907
        // info while bypassing PrepareFunctionForDebugExecution.
1908
        if (iteration > 1) {
1909
          CreateBreakInfo(shared_handle);
1910 1911 1912
        }
        return shared_handle;
      }
1913
    }
1914
    // If not, compile to reveal inner functions.
1915
    HandleScope scope(isolate_);
1916
    // Code that cannot be compiled lazily are internal and not debuggable.
1917
    DCHECK(shared.allows_lazy_compilation());
1918 1919
    if (!Compiler::Compile(isolate_, handle(shared, isolate_),
                           Compiler::CLEAR_EXCEPTION, &is_compiled_scope)) {
1920
      break;
1921
    }
1922 1923
  }
  return isolate_->factory()->undefined_value();
1924 1925
}

1926
// Ensures the debug information is present for shared.
1927
bool Debug::EnsureBreakInfo(Handle<SharedFunctionInfo> shared) {
1928
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1929 1930
  // Return if we already have the break info for shared.
  if (shared->HasBreakInfo()) return true;
1931 1932 1933
  if (!shared->IsSubjectToDebugging() && !CanBreakAtEntry(shared)) {
    return false;
  }
1934
  IsCompiledScope is_compiled_scope = shared->is_compiled_scope(isolate_);
1935
  if (!is_compiled_scope.is_compiled() &&
1936
      !Compiler::Compile(isolate_, shared, Compiler::CLEAR_EXCEPTION,
1937
                         &is_compiled_scope, CreateSourcePositions::kYes)) {
1938 1939
    return false;
  }
1940
  CreateBreakInfo(shared);
1941 1942 1943
  return true;
}

1944
void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) {
1945
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1946 1947
  HandleScope scope(isolate_);
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1948

1949
  // Initialize with break information.
1950

1951 1952 1953 1954 1955 1956
  DCHECK(!debug_info->HasBreakInfo());

  Factory* factory = isolate_->factory();
  Handle<FixedArray> break_points(
      factory->NewFixedArray(DebugInfo::kEstimatedNofBreakPointsInFunction));

1957
  int flags = debug_info->flags(kRelaxedLoad);
1958 1959
  flags |= DebugInfo::kHasBreakInfo;
  if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry;
1960
  debug_info->set_flags(flags, kRelaxedStore);
1961
  debug_info->set_break_points(*break_points);
1962 1963

  SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared);
1964 1965 1966 1967
}

Handle<DebugInfo> Debug::GetOrCreateDebugInfo(
    Handle<SharedFunctionInfo> shared) {
1968
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1969
  if (shared->HasDebugInfo()) return handle(shared->GetDebugInfo(), isolate_);
1970 1971 1972

  // Create debug info and add it to the list.
  Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
1973
  DebugInfoListNode* node = new DebugInfoListNode(isolate_, *debug_info);
1974 1975 1976
  node->set_next(debug_info_list_);
  debug_info_list_ = node;

1977 1978
  return debug_info;
}
1979

1980 1981
void Debug::InstallCoverageInfo(Handle<SharedFunctionInfo> shared,
                                Handle<CoverageInfo> coverage_info) {
1982
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
1983 1984 1985 1986 1987 1988
  DCHECK(!coverage_info.is_null());

  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);

  DCHECK(!debug_info->HasCoverageInfo());

1989 1990 1991
  debug_info->set_flags(
      debug_info->flags(kRelaxedLoad) | DebugInfo::kHasCoverageInfo,
      kRelaxedStore);
1992 1993 1994 1995
  debug_info->set_coverage_info(*coverage_info);
}

void Debug::RemoveAllCoverageInfos() {
1996 1997 1998 1999 2000 2001 2002
  ClearAllDebugInfos(
      [=](Handle<DebugInfo> info) { info->ClearCoverageInfo(isolate_); });
}

void Debug::ClearAllDebuggerHints() {
  ClearAllDebugInfos(
      [=](Handle<DebugInfo> info) { info->set_debugger_hints(0); });
2003 2004
}

2005 2006
void Debug::FindDebugInfo(Handle<DebugInfo> debug_info,
                          DebugInfoListNode** prev, DebugInfoListNode** curr) {
2007
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2008
  HandleScope scope(isolate_);
2009 2010 2011 2012 2013 2014
  *prev = nullptr;
  *curr = debug_info_list_;
  while (*curr != nullptr) {
    if ((*curr)->debug_info().is_identical_to(debug_info)) return;
    *prev = *curr;
    *curr = (*curr)->next();
2015
  }
2016 2017 2018 2019

  UNREACHABLE();
}

2020
void Debug::ClearAllDebugInfos(const DebugInfoClearFunction& clear_function) {
2021
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2022 2023 2024 2025 2026
  DebugInfoListNode* prev = nullptr;
  DebugInfoListNode* current = debug_info_list_;
  while (current != nullptr) {
    DebugInfoListNode* next = current->next();
    Handle<DebugInfo> debug_info = current->debug_info();
2027 2028
    clear_function(debug_info);
    if (debug_info->IsEmpty()) {
2029 2030 2031 2032 2033 2034 2035 2036 2037
      FreeDebugInfoListNode(prev, current);
      current = next;
    } else {
      prev = current;
      current = next;
    }
  }
}

2038
void Debug::RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info) {
2039
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2040 2041
  debug_info->ClearBreakInfo(isolate_);
  if (debug_info->IsEmpty()) {
2042 2043 2044 2045 2046 2047 2048 2049 2050
    DebugInfoListNode* prev;
    DebugInfoListNode* node;
    FindDebugInfo(debug_info, &prev, &node);
    FreeDebugInfoListNode(prev, node);
  }
}

void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev,
                                  DebugInfoListNode* node) {
2051
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2052 2053
  DCHECK(node->debug_info()->IsEmpty());

2054
  // Unlink from list. If prev is nullptr we are looking at the first element.
2055 2056 2057 2058 2059 2060
  if (prev == nullptr) {
    debug_info_list_ = node->next();
  } else {
    prev->set_next(node->next());
  }

2061 2062
  // Pack script back into the
  // SFI::script_or_debug_info field.
2063
  Handle<DebugInfo> debug_info(node->debug_info());
2064 2065
  debug_info->shared().set_script_or_debug_info(debug_info->script(),
                                                kReleaseStore);
2066 2067 2068 2069

  delete node;
}

2070 2071 2072 2073 2074
void Debug::ClearGlobalPromiseStack() {
  while (isolate_->PopPromise()) {
  }
}

2075
bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
2076
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2077
  HandleScope scope(isolate_);
2078

2079
  // Get the executing function in which the debug break occurred.
2080
  Handle<SharedFunctionInfo> shared(frame->function().shared(), isolate_);
2081

2082
  // With no debug info there are no break points, so we can't be at a return.
2083
  if (!shared->HasBreakInfo()) return false;
2084 2085

  DCHECK(!frame->is_optimized());
2086
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
2087
  BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
2088
  return location.IsReturn();
2089 2090
}

2091
Handle<FixedArray> Debug::GetLoadedScripts() {
2092
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2093
  isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
2094
                                      GarbageCollectionReason::kDebugger);
2095
  Factory* factory = isolate_->factory();
2096
  if (!factory->script_list()->IsWeakArrayList()) {
2097 2098
    return factory->empty_fixed_array();
  }
2099 2100 2101
  Handle<WeakArrayList> array =
      Handle<WeakArrayList>::cast(factory->script_list());
  Handle<FixedArray> results = factory->NewFixedArray(array->length());
2102
  int length = 0;
2103 2104
  {
    Script::Iterator iterator(isolate_);
2105 2106
    for (Script script = iterator.Next(); !script.is_null();
         script = iterator.Next()) {
2107
      if (script.HasValidSource()) results->set(length++, script);
2108 2109
    }
  }
2110
  return FixedArray::ShrinkOrEmpty(isolate_, results, length);
2111 2112
}

2113
base::Optional<Object> Debug::OnThrow(Handle<Object> exception) {
2114
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2115
  if (in_debug_scope() || ignore_events()) return {};
2116 2117
  // Temporarily clear any scheduled_exception to allow evaluating
  // JavaScript from the debug event handler.
2118
  HandleScope scope(isolate_);
2119 2120 2121 2122 2123
  Handle<Object> scheduled_exception;
  if (isolate_->has_scheduled_exception()) {
    scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
    isolate_->clear_scheduled_exception();
  }
2124 2125 2126 2127
  Handle<Object> maybe_promise = isolate_->GetPromiseOnStackOnThrow();
  OnException(exception, maybe_promise,
              maybe_promise->IsJSPromise() ? v8::debug::kPromiseRejection
                                           : v8::debug::kException);
2128
  if (!scheduled_exception.is_null()) {
2129
    isolate_->set_scheduled_exception(*scheduled_exception);
2130
  }
2131
  PrepareStepOnThrow();
2132 2133 2134 2135 2136 2137 2138 2139
  // If the OnException handler requested termination, then indicated this to
  // our caller Isolate::Throw so it can deal with it immediatelly instead of
  // throwing the original exception.
  if (isolate_->stack_guard()->CheckTerminateExecution()) {
    isolate_->stack_guard()->ClearTerminateExecution();
    return isolate_->TerminateExecution();
  }
  return {};
2140 2141
}

2142
void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
2143
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2144
  if (in_debug_scope() || ignore_events()) return;
2145
  HandleScope scope(isolate_);
2146 2147
  // Check whether the promise has been marked as having triggered a message.
  Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
2148
  if (!promise->IsJSObject() ||
2149 2150
      JSReceiver::GetDataProperty(isolate_, Handle<JSObject>::cast(promise),
                                  key)
2151
          ->IsUndefined(isolate_)) {
2152
    OnException(value, promise, v8::debug::kPromiseRejection);
2153 2154 2155
  }
}

2156
bool Debug::IsExceptionBlackboxed(bool uncaught) {
2157
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2158 2159
  // Uncaught exception is blackboxed if all current frames are blackboxed,
  // caught exception if top frame is blackboxed.
2160
  StackTraceFrameIterator it(isolate_);
2161
#if V8_ENABLE_WEBASSEMBLY
2162
  while (!it.done() && it.is_wasm()) it.Advance();
2163
#endif  // V8_ENABLE_WEBASSEMBLY
2164 2165
  bool is_top_frame_blackboxed =
      !it.done() ? IsFrameBlackboxed(it.javascript_frame()) : true;
2166
  if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
2167
  return AllFramesOnStackAreBlackboxed();
2168 2169 2170
}

bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
2171
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2172
  HandleScope scope(isolate_);
2173
  std::vector<Handle<SharedFunctionInfo>> infos;
2174
  frame->GetFunctions(&infos);
2175
  for (const auto& info : infos) {
2176
    if (!IsBlackboxed(info)) return false;
2177
  }
2178 2179 2180
  return true;
}

2181 2182
void Debug::OnException(Handle<Object> exception, Handle<Object> promise,
                        v8::debug::ExceptionType exception_type) {
2183
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
  // Do not trigger exception event on stack overflow. We cannot perform
  // anything useful for debugging in that situation.
  StackLimitCheck stack_limit_check(isolate_);
  if (stack_limit_check.JsHasOverflowed()) return;

  // Return if the event has nowhere to go.
  if (!debug_delegate_) return;

  // Return if we are not interested in exception events.
  if (!break_on_exception_ && !break_on_uncaught_exception_) return;

2195
  Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
2196

2197
  bool uncaught = catch_type == Isolate::NOT_CAUGHT;
2198
  if (promise->IsJSObject()) {
2199
    Handle<JSObject> jsobject = Handle<JSObject>::cast(promise);
2200 2201
    // Mark the promise as already having triggered a message.
    Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
2202
    Object::SetProperty(isolate_, jsobject, key, key, StoreOrigin::kMaybeKeyed,
2203
                        Just(ShouldThrow::kThrowOnError))
2204
        .Assert();
2205
    // Check whether the promise reject is considered an uncaught exception.
2206 2207 2208 2209 2210 2211 2212
    if (jsobject->IsJSPromise()) {
      Handle<JSPromise> jspromise = Handle<JSPromise>::cast(jsobject);

      // Ignore the exception if the promise was marked as silent
      if (jspromise->is_silent()) return;

      uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
2213 2214 2215
    } else {
      uncaught = true;
    }
2216
  }
2217

2218 2219 2220 2221 2222
  // Return if the exception is caught and we only care about uncaught
  // exceptions.
  if (!uncaught && !break_on_exception_) {
    DCHECK(break_on_uncaught_exception_);
    return;
2223 2224
  }

2225 2226
  {
    JavaScriptFrameIterator it(isolate_);
2227
    // Check whether the top frame is blackboxed or the break location is muted.
2228 2229
    if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) ||
                       IsExceptionBlackboxed(uncaught))) {
2230 2231
      return;
    }
2232
    if (it.done()) return;  // Do not trigger an event with an empty stack.
2233 2234
  }

2235
  DebugScope debug_scope(this);
2236 2237
  HandleScope scope(isolate_);
  DisableBreak no_recursive_break(this);
2238

2239 2240 2241 2242 2243 2244 2245
  {
    RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
    Handle<Context> native_context(isolate_->native_context());
    debug_delegate_->ExceptionThrown(
        v8::Utils::ToLocal(native_context), v8::Utils::ToLocal(exception),
        v8::Utils::ToLocal(promise), uncaught, exception_type);
  }
2246 2247
}

2248
void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit,
2249 2250
                         StepAction lastStepAction,
                         v8::debug::BreakReasons break_reasons) {
2251
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2252
  DCHECK(!break_points_hit.is_null());
2253 2254
  // The caller provided for DebugScope.
  AssertDebugContext();
2255
  // Bail out if there is no listener for this event
2256
  if (ignore_events()) return;
2257

2258 2259 2260 2261
#ifdef DEBUG
  PrintBreakLocation();
#endif  // DEBUG

2262
  if (!debug_delegate_) return;
2263
  DCHECK(in_debug_scope());
2264
  HandleScope scope(isolate_);
2265 2266
  DisableBreak no_recursive_break(this);

2267 2268
  if ((lastStepAction == StepAction::StepOver ||
       lastStepAction == StepAction::StepInto) &&
2269
      ShouldBeSkipped()) {
2270 2271 2272 2273
    PrepareStep(lastStepAction);
    return;
  }

2274 2275 2276
  std::vector<int> inspector_break_points_hit;
  // This array contains breakpoints installed using JS debug API.
  for (int i = 0; i < break_points_hit->length(); ++i) {
2277
    BreakPoint break_point = BreakPoint::cast(break_points_hit->get(i));
2278
    inspector_break_points_hit.push_back(break_point.id());
2279
  }
2280 2281 2282
  {
    RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
    Handle<Context> native_context(isolate_->native_context());
2283 2284 2285 2286 2287
    if (lastStepAction != StepAction::StepNone)
      break_reasons.Add(debug::BreakReason::kStep);
    debug_delegate_->BreakProgramRequested(v8::Utils::ToLocal(native_context),
                                           inspector_break_points_hit,
                                           break_reasons);
2288
  }
2289 2290
}

2291 2292 2293 2294
namespace {
debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
  Script::PositionInfo info;
  Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
2295
  // V8 provides ScriptCompiler::CompileFunction method which takes
2296 2297 2298 2299 2300 2301
  // expression and compile it as anonymous function like (function() ..
  // expression ..). To produce correct locations for stmts inside of this
  // expression V8 compile this function with negative offset. Instead of stmt
  // position blackboxing use function start position which is negative in
  // described case.
  return debug::Location(std::max(info.line, 0), std::max(info.column, 0));
2302 2303 2304 2305
}
}  // namespace

bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
2306
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2307
  if (!debug_delegate_) return !shared->IsSubjectToDebugging();
2308 2309
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
  if (!debug_info->computed_debug_is_blackboxed()) {
2310
    bool is_blackboxed =
2311
        !shared->IsSubjectToDebugging() || !shared->script().IsScript();
2312
    if (!is_blackboxed) {
2313
      SuppressDebug while_processing(this);
2314
      HandleScope handle_scope(isolate_);
2315 2316
      PostponeInterruptsScope no_interrupts(isolate_);
      DisableBreak no_recursive_break(this);
2317
      DCHECK(shared->script().IsScript());
2318
      Handle<Script> script(Script::cast(shared->script()), isolate_);
2319
      DCHECK(script->IsUserJavaScript());
2320 2321
      debug::Location start = GetDebugLocation(script, shared->StartPosition());
      debug::Location end = GetDebugLocation(script, shared->EndPosition());
2322 2323 2324 2325 2326
      {
        RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
        is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
            ToApiHandle<debug::Script>(script), start, end);
      }
2327
    }
2328 2329
    debug_info->set_debug_is_blackboxed(is_blackboxed);
    debug_info->set_computed_debug_is_blackboxed(true);
2330
  }
2331
  return debug_info->debug_is_blackboxed();
2332 2333
}

2334
bool Debug::ShouldBeSkipped() {
2335
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2336 2337 2338 2339 2340
  SuppressDebug while_processing(this);
  PostponeInterruptsScope no_interrupts(isolate_);
  DisableBreak no_recursive_break(this);

  StackTraceFrameIterator iterator(isolate_);
2341
  FrameSummary summary = iterator.GetTopValidFrame();
2342 2343 2344 2345 2346 2347 2348 2349 2350
  Handle<Object> script_obj = summary.script();
  if (!script_obj->IsScript()) return false;

  Handle<Script> script = Handle<Script>::cast(script_obj);
  summary.EnsureSourcePositionsAvailable();
  int source_position = summary.SourcePosition();
  int line = Script::GetLineNumber(script, source_position);
  int column = Script::GetColumnNumber(script, source_position);

2351 2352 2353 2354 2355
  {
    RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
    return debug_delegate_->ShouldBeSkipped(ToApiHandle<debug::Script>(script),
                                            line, column);
  }
2356 2357
}

2358
bool Debug::AllFramesOnStackAreBlackboxed() {
2359
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2360 2361
  HandleScope scope(isolate_);
  for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
2362
    if (!it.is_javascript()) continue;
2363 2364 2365 2366 2367
    if (!IsFrameBlackboxed(it.javascript_frame())) return false;
  }
  return true;
}

2368
bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
2369
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2370
  // Allow break at entry for builtin functions.
2371
  if (shared->native() || shared->IsApiFunction()) {
2372 2373 2374 2375 2376 2377 2378
    // Functions that are subject to debugging can have regular breakpoints.
    DCHECK(!shared->IsSubjectToDebugging());
    return true;
  }
  return false;
}

2379
bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
2380
                            bool preview, debug::LiveEditResult* result) {
2381
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2382
  DebugScope debug_scope(this);
2383
  feature_tracker()->Track(DebugFeatureTracker::kLiveEdit);
2384
  running_live_edit_ = true;
2385
  LiveEdit::PatchScript(isolate_, script, source, preview, result);
2386
  running_live_edit_ = false;
2387
  return result->status == debug::LiveEditResult::OK;
2388 2389
}

2390
void Debug::OnCompileError(Handle<Script> script) {
2391
  ProcessCompileEvent(true, script);
2392 2393 2394
}

void Debug::OnAfterCompile(Handle<Script> script) {
2395
  ProcessCompileEvent(false, script);
2396 2397
}

2398
void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
2399
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2400 2401
  // Ignore temporary scripts.
  if (script->id() == Script::kTemporaryScriptId) return;
2402 2403 2404
  // TODO(kozyatinskiy): teach devtools to work with liveedit scripts better
  // first and then remove this fast return.
  if (running_live_edit_) return;
2405 2406
  // Attach the correct debug id to the script. The debug id is used by the
  // inspector to filter scripts by native context.
2407
  script->set_context_data(isolate_->native_context()->debug_context_id());
2408
  if (ignore_events()) return;
2409
#if V8_ENABLE_WEBASSEMBLY
2410 2411 2412
  if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) {
    return;
  }
2413 2414 2415
#else
  if (!script->IsUserJavaScript()) return;
#endif  // V8_ENABLE_WEBASSEMBLY
2416
  if (!debug_delegate_) return;
2417
  SuppressDebug while_processing(this);
2418
  DebugScope debug_scope(this);
2419
  HandleScope scope(isolate_);
2420
  DisableBreak no_recursive_break(this);
2421
  AllowJavascriptExecution allow_script(isolate_);
2422 2423 2424 2425 2426
  {
    RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback);
    debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
                                    running_live_edit_, has_compile_error);
  }
2427 2428
}

2429 2430
int Debug::CurrentFrameCount() {
  StackTraceFrameIterator it(isolate_);
2431
  if (break_frame_id() != StackFrameId::NO_ID) {
2432 2433 2434 2435 2436
    // Skip to break frame.
    DCHECK(in_debug_scope());
    while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance();
  }
  int counter = 0;
2437 2438
  for (; !it.done(); it.Advance()) {
    counter += it.FrameFunctionCount();
2439 2440 2441 2442
  }
  return counter;
}

2443
void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
2444
  debug_delegate_ = delegate;
2445 2446 2447
  UpdateState();
}

2448
void Debug::UpdateState() {
2449
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2450
  bool is_active = debug_delegate_ != nullptr;
2451 2452
  if (is_active == is_active_) return;
  if (is_active) {
2453 2454
    // Note that the debug context could have already been loaded to
    // bootstrap test cases.
2455
    isolate_->compilation_cache()->DisableScriptAndEval();
2456
    isolate_->CollectSourcePositionsForAllBytecodeArrays();
2457 2458 2459
    is_active = true;
    feature_tracker()->Track(DebugFeatureTracker::kActive);
  } else {
2460
    isolate_->compilation_cache()->EnableScriptAndEval();
2461
    Unload();
2462
  }
2463
  is_active_ = is_active;
2464
  isolate_->PromiseHookStateUpdated();
2465 2466
}

2467
void Debug::UpdateHookOnFunctionCall() {
2468
  STATIC_ASSERT(LastStepAction == StepInto);
2469
  hook_on_function_call_ =
2470
      thread_local_.last_step_action_ == StepInto ||
2471 2472
      isolate_->debug_execution_mode() == DebugInfo::kSideEffects ||
      thread_local_.break_on_next_function_call_;
2473 2474
}

2475 2476
void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode,
                             v8::debug::BreakReasons break_reasons) {
2477
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2478 2479 2480
  // Ignore debug break during bootstrapping.
  if (isolate_->bootstrapper()->IsActive()) return;
  // Just continue if breaks are disabled.
2481
  if (break_disabled()) return;
2482 2483 2484 2485 2486 2487
  // Ignore debug break if debugger is not active.
  if (!is_active()) return;

  StackLimitCheck check(isolate_);
  if (check.HasOverflowed()) return;

2488 2489
  HandleScope scope(isolate_);
  MaybeHandle<FixedArray> break_points;
2490 2491
  {
    JavaScriptFrameIterator it(isolate_);
2492
    DCHECK(!it.done());
2493
    Object fun = it.frame()->function();
2494
    if (fun.IsJSFunction()) {
2495
      Handle<JSFunction> function(JSFunction::cast(fun), isolate_);
2496
      // Don't stop in builtin and blackboxed functions.
2497
      Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
2498 2499 2500
      bool ignore_break = ignore_break_mode == kIgnoreIfTopFrameBlackboxed
                              ? IsBlackboxed(shared)
                              : AllFramesOnStackAreBlackboxed();
2501
      if (ignore_break) return;
2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528
      if (function->shared().HasBreakInfo()) {
        Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(),
                                     isolate_);
        // Enter the debugger.
        DebugScope debug_scope(this);

        std::vector<BreakLocation> break_locations;
        BreakLocation::AllAtCurrentStatement(debug_info, it.frame(),
                                             &break_locations);

        for (size_t i = 0; i < break_locations.size(); i++) {
          if (IsBreakOnInstrumentation(debug_info, break_locations[i])) {
            OnInstrumentationBreak();
            break;
          }
        }

        bool has_break_points;
        break_points = CheckBreakPointsForLocations(debug_info, break_locations,
                                                    &has_break_points);
        bool is_muted = has_break_points && break_points.is_null();
        // If we get to this point, a break was triggered because e.g. of a
        // debugger statement, an assert, .. . However, we do not stop if this
        // position "is muted", which happens if a conditional breakpoint at
        // this point evaluates to false.
        if (is_muted) return;
      }
2529 2530 2531
    }
  }

2532 2533
  StepAction lastStepAction = last_step_action();

2534 2535 2536
  // Clear stepping to avoid duplicate breaks.
  ClearStepping();

2537
  DebugScope debug_scope(this);
2538 2539
  OnDebugBreak(break_points.is_null() ? isolate_->factory()->empty_fixed_array()
                                      : break_points.ToHandleChecked(),
2540
               lastStepAction, break_reasons);
2541 2542
}

2543 2544 2545
#ifdef DEBUG
void Debug::PrintBreakLocation() {
  if (!FLAG_print_break_location) return;
2546
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2547
  HandleScope scope(isolate_);
2548
  StackTraceFrameIterator iterator(isolate_);
2549
  if (iterator.done()) return;
2550
  CommonFrame* frame = iterator.frame();
2551 2552 2553 2554 2555 2556
  std::vector<FrameSummary> frames;
  frame->Summarize(&frames);
  int inlined_frame_index = static_cast<int>(frames.size() - 1);
  FrameInspector inspector(frame, inlined_frame_index, isolate_);
  int source_position = inspector.GetSourcePosition();
  Handle<Object> script_obj = inspector.GetScript();
2557
  PrintF("[debug] break in function '");
2558
  inspector.GetFunctionName()->PrintOn(stdout);
2559 2560 2561
  PrintF("'.\n");
  if (script_obj->IsScript()) {
    Handle<Script> script = Handle<Script>::cast(script_obj);
2562
    Handle<String> source(String::cast(script->source()), isolate_);
2563
    Script::InitLineEnds(isolate_, script);
2564 2565 2566 2567
    int line =
        Script::GetLineNumber(script, source_position) - script->line_offset();
    int column = Script::GetColumnNumber(script, source_position) -
                 (line == 0 ? script->column_offset() : 0);
2568 2569
    Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()),
                                 isolate_);
jgruber's avatar
jgruber committed
2570 2571
    int line_start = line == 0 ? 0 : Smi::ToInt(line_ends->get(line - 1)) + 1;
    int line_end = Smi::ToInt(line_ends->get(line));
2572
    DisallowGarbageCollection no_gc;
2573
    String::FlatContent content = source->GetFlatContent(no_gc);
2574 2575
    if (content.IsOneByte()) {
      PrintF("[debug] %.*s\n", line_end - line_start,
2576
             content.ToOneByteVector().begin() + line_start);
2577 2578 2579 2580 2581 2582 2583 2584 2585
      PrintF("[debug] ");
      for (int i = 0; i < column; i++) PrintF(" ");
      PrintF("^\n");
    } else {
      PrintF("[debug] at line %d column %d\n", line, column);
    }
  }
}
#endif  // DEBUG
2586

2587 2588
DebugScope::DebugScope(Debug* debug)
    : debug_(debug),
Yang Guo's avatar
Yang Guo committed
2589 2590
      prev_(reinterpret_cast<DebugScope*>(
          base::Relaxed_Load(&debug->thread_local_.current_debug_scope_))),
2591
      no_interrupts_(debug_->isolate_) {
2592
  // Link recursive debugger entry.
2593 2594
  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
                      reinterpret_cast<base::AtomicWord>(this));
Yang Guo's avatar
Yang Guo committed
2595
  // Store the previous frame id and return value.
2596
  break_frame_id_ = debug_->break_frame_id();
2597

2598 2599 2600 2601 2602
  // Create the new break info. If there is no proper frames there is no break
  // frame id.
  StackTraceFrameIterator it(isolate());
  bool has_frames = !it.done();
  debug_->thread_local_.break_frame_id_ =
2603
      has_frames ? it.frame()->id() : StackFrameId::NO_ID;
2604

2605
  debug_->UpdateState();
2606 2607
}

2608
void DebugScope::set_terminate_on_resume() { terminate_on_resume_ = true; }
2609

2610
DebugScope::~DebugScope() {
2611 2612 2613 2614 2615 2616 2617 2618 2619
  // Terminate on resume must have been handled by retrieving it, if this is
  // the outer scope.
  if (terminate_on_resume_) {
    if (!prev_) {
      debug_->isolate_->stack_guard()->RequestTerminateExecution();
    } else {
      prev_->set_terminate_on_resume();
    }
  }
2620
  // Leaving this debugger entry.
2621 2622
  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
                      reinterpret_cast<base::AtomicWord>(prev_));
2623 2624 2625

  // Restore to the previous break state.
  debug_->thread_local_.break_frame_id_ = break_frame_id_;
2626

2627
  debug_->UpdateState();
2628 2629
}

2630 2631 2632 2633 2634 2635 2636 2637
ReturnValueScope::ReturnValueScope(Debug* debug) : debug_(debug) {
  return_value_ = debug_->return_value_handle();
}

ReturnValueScope::~ReturnValueScope() {
  debug_->set_return_value(*return_value_);
}

2638
void Debug::UpdateDebugInfosForExecutionMode() {
2639
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2640 2641 2642 2643 2644 2645 2646
  // Walk all debug infos and update their execution mode if it is different
  // from the isolate execution mode.
  DebugInfoListNode* current = debug_info_list_;
  while (current != nullptr) {
    Handle<DebugInfo> debug_info = current->debug_info();
    if (debug_info->HasInstrumentedBytecodeArray() &&
        debug_info->DebugExecutionMode() != isolate_->debug_execution_mode()) {
2647
      DCHECK(debug_info->shared().HasBytecodeArray());
2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659
      if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) {
        ClearSideEffectChecks(debug_info);
        ApplyBreakPoints(debug_info);
      } else {
        ClearBreakPoints(debug_info);
        ApplySideEffectChecks(debug_info);
      }
    }
    current = current->next();
  }
}

2660
void Debug::SetTerminateOnResume() {
2661
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2662 2663 2664 2665 2666 2667
  DebugScope* scope = reinterpret_cast<DebugScope*>(
      base::Acquire_Load(&thread_local_.current_debug_scope_));
  CHECK_NOT_NULL(scope);
  scope->set_terminate_on_resume();
}

2668
void Debug::StartSideEffectCheckMode() {
2669
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2670 2671 2672 2673 2674 2675 2676 2677
  DCHECK(isolate_->debug_execution_mode() != DebugInfo::kSideEffects);
  isolate_->set_debug_execution_mode(DebugInfo::kSideEffects);
  UpdateHookOnFunctionCall();
  side_effect_check_failed_ = false;

  DCHECK(!temporary_objects_);
  temporary_objects_.reset(new TemporaryObjectsTracker());
  isolate_->heap()->AddHeapObjectAllocationTracker(temporary_objects_.get());
2678 2679
  Handle<FixedArray> array(isolate_->native_context()->regexp_last_match_info(),
                           isolate_);
2680 2681
  regexp_match_info_ =
      Handle<RegExpMatchInfo>::cast(isolate_->factory()->CopyFixedArray(array));
2682 2683 2684

  // Update debug infos to have correct execution mode.
  UpdateDebugInfosForExecutionMode();
2685 2686 2687
}

void Debug::StopSideEffectCheckMode() {
2688
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2689 2690 2691
  DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects);
  if (side_effect_check_failed_) {
    DCHECK(isolate_->has_pending_exception());
2692
    DCHECK_EQ(ReadOnlyRoots(isolate_).termination_exception(),
2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705
              isolate_->pending_exception());
    // Convert the termination exception into a regular exception.
    isolate_->CancelTerminateExecution();
    isolate_->Throw(*isolate_->factory()->NewEvalError(
        MessageTemplate::kNoSideEffectDebugEvaluate));
  }
  isolate_->set_debug_execution_mode(DebugInfo::kBreakpoints);
  UpdateHookOnFunctionCall();
  side_effect_check_failed_ = false;

  DCHECK(temporary_objects_);
  isolate_->heap()->RemoveHeapObjectAllocationTracker(temporary_objects_.get());
  temporary_objects_.reset();
2706 2707
  isolate_->native_context()->set_regexp_last_match_info(*regexp_match_info_);
  regexp_match_info_ = Handle<RegExpMatchInfo>::null();
2708 2709 2710

  // Update debug infos to have correct execution mode.
  UpdateDebugInfosForExecutionMode();
2711 2712 2713
}

void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) {
2714
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2715
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
2716 2717
  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
                                       isolate_);
2718 2719 2720 2721 2722
  DebugEvaluate::ApplySideEffectChecks(debug_bytecode);
  debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects);
}

void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
2723
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2724
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
2725 2726 2727
  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
                                       isolate_);
  Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray(), isolate_);
2728 2729
  for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done();
       it.Advance()) {
2730 2731
    // Restore from original. This may copy only the scaling prefix, which is
    // correct, since we patch scaling prefixes to debug breaks if exists.
2732 2733 2734 2735 2736
    debug_bytecode->set(it.current_offset(),
                        original->get(it.current_offset()));
  }
}

2737 2738
bool Debug::PerformSideEffectCheck(Handle<JSFunction> function,
                                   Handle<Object> receiver) {
2739
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2740
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2741
  DisallowJavascriptExecution no_js(isolate_);
2742 2743
  IsCompiledScope is_compiled_scope(
      function->shared().is_compiled_scope(isolate_));
2744
  if (!function->is_compiled() &&
2745
      !Compiler::Compile(isolate_, function, Compiler::KEEP_EXCEPTION,
2746
                         &is_compiled_scope)) {
2747 2748
    return false;
  }
2749
  DCHECK(is_compiled_scope.is_compiled());
2750 2751 2752 2753
  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
  DebugInfo::SideEffectState side_effect_state =
      debug_info->GetSideEffectState(isolate_);
2754
  switch (side_effect_state) {
2755
    case DebugInfo::kHasSideEffects:
2756 2757
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] Function %s failed side effect check.\n",
2758
               function->shared().DebugNameCStr().get());
2759 2760 2761 2762 2763
      }
      side_effect_check_failed_ = true;
      // Throw an uncatchable termination exception.
      isolate_->TerminateExecution();
      return false;
2764
    case DebugInfo::kRequiresRuntimeChecks: {
2765 2766 2767 2768 2769 2770 2771
      if (!shared->HasBytecodeArray()) {
        return PerformSideEffectCheckForObject(receiver);
      }
      // If function has bytecode array then prepare function for debug
      // execution to perform runtime side effect checks.
      DCHECK(shared->is_compiled());
      PrepareFunctionForDebugExecution(shared);
2772
      ApplySideEffectChecks(debug_info);
2773
      return true;
2774
    }
2775
    case DebugInfo::kHasNoSideEffect:
2776
      return true;
2777
    case DebugInfo::kNotComputed:
2778
    default:
2779
      UNREACHABLE();
2780
  }
2781 2782
}

2783 2784 2785 2786
Handle<Object> Debug::return_value_handle() {
  return handle(thread_local_.return_value_, isolate_);
}

2787 2788 2789
bool Debug::PerformSideEffectCheckForCallback(
    Handle<Object> callback_info, Handle<Object> receiver,
    Debug::AccessorKind accessor_kind) {
2790
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2791
  DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo());
2792
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2793
  if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() &&
2794
      i::CallHandlerInfo::cast(*callback_info).NextCallHasNoSideEffect()) {
2795 2796
    return true;
  }
2797
  // TODO(7515): always pass a valid callback info object.
2798 2799
  if (!callback_info.is_null()) {
    if (callback_info->IsAccessorInfo()) {
Dan Elphick's avatar
Dan Elphick committed
2800
      // List of allowlisted internal accessors can be found in accessors.h.
2801
      AccessorInfo info = AccessorInfo::cast(*callback_info);
2802
      DCHECK_NE(kNotAccessor, accessor_kind);
2803 2804
      switch (accessor_kind == kSetter ? info.setter_side_effect_type()
                                       : info.getter_side_effect_type()) {
2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820
        case SideEffectType::kHasNoSideEffect:
          // We do not support setter accessors with no side effects, since
          // calling set accessors go through a store bytecode. Store bytecodes
          // are considered to cause side effects (to non-temporary objects).
          DCHECK_NE(kSetter, accessor_kind);
          return true;
        case SideEffectType::kHasSideEffectToReceiver:
          DCHECK(!receiver.is_null());
          if (PerformSideEffectCheckForObject(receiver)) return true;
          isolate_->OptionalRescheduleException(false);
          return false;
        case SideEffectType::kHasSideEffect:
          break;
      }
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] API Callback '");
2821
        info.name().ShortPrint();
2822 2823 2824
        PrintF("' may cause side effect.\n");
      }
    } else if (callback_info->IsInterceptorInfo()) {
2825
      InterceptorInfo info = InterceptorInfo::cast(*callback_info);
2826
      if (info.has_no_side_effect()) return true;
2827 2828 2829 2830
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
      }
    } else if (callback_info->IsCallHandlerInfo()) {
2831
      CallHandlerInfo info = CallHandlerInfo::cast(*callback_info);
2832
      if (info.IsSideEffectFreeCallHandlerInfo()) return true;
2833 2834 2835 2836
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
      }
    }
2837
  }
2838 2839 2840 2841 2842 2843 2844
  side_effect_check_failed_ = true;
  // Throw an uncatchable termination exception.
  isolate_->TerminateExecution();
  isolate_->OptionalRescheduleException(false);
  return false;
}

2845
bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
2846
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2847 2848
  using interpreter::Bytecode;

2849
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2850
  SharedFunctionInfo shared = frame->function().shared();
2851
  BytecodeArray bytecode_array = shared.GetBytecodeArray(isolate_);
2852
  int offset = frame->GetBytecodeOffset();
2853
  interpreter::BytecodeArrayIterator bytecode_iterator(
2854
      handle(bytecode_array, isolate_), offset);
2855

2856
  Bytecode bytecode = bytecode_iterator.current_bytecode();
2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868
  if (interpreter::Bytecodes::IsCallRuntime(bytecode)) {
    auto id = (bytecode == Bytecode::kInvokeIntrinsic)
                  ? bytecode_iterator.GetIntrinsicIdOperand(0)
                  : bytecode_iterator.GetRuntimeIdOperand(0);
    if (DebugEvaluate::IsSideEffectFreeIntrinsic(id)) {
      return true;
    }
    side_effect_check_failed_ = true;
    // Throw an uncatchable termination exception.
    isolate_->TerminateExecution();
    return false;
  }
2869 2870 2871 2872 2873 2874
  interpreter::Register reg;
  switch (bytecode) {
    case Bytecode::kStaCurrentContextSlot:
      reg = interpreter::Register::current_context();
      break;
    default:
2875
      reg = bytecode_iterator.GetRegisterOperand(0);
2876 2877 2878 2879
      break;
  }
  Handle<Object> object =
      handle(frame->ReadInterpreterRegister(reg.index()), isolate_);
2880 2881 2882 2883
  return PerformSideEffectCheckForObject(object);
}

bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
2884
  RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
2885 2886
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);

2887 2888 2889 2890 2891 2892
  // We expect no side-effects for primitives.
  if (object->IsNumber()) return true;
  if (object->IsName()) return true;

  if (temporary_objects_->HasObject(Handle<HeapObject>::cast(object))) {
    return true;
2893
  }
2894

2895
  if (FLAG_trace_side_effect_free_debug_evaluate) {
2896
    PrintF("[debug-evaluate] failed runtime side effect check.\n");
2897 2898 2899 2900 2901 2902
  }
  side_effect_check_failed_ = true;
  // Throw an uncatchable termination exception.
  isolate_->TerminateExecution();
  return false;
}
2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916

void Debug::SetTemporaryObjectTrackingDisabled(bool disabled) {
  if (temporary_objects_) {
    temporary_objects_->disabled = disabled;
  }
}

bool Debug::GetTemporaryObjectTrackingDisabled() const {
  if (temporary_objects_) {
    return temporary_objects_->disabled;
  }
  return false;
}

2917 2918
}  // namespace internal
}  // namespace v8