debug.cc 82.9 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
#include "src/api-inl.h"
11
#include "src/arguments.h"
12
#include "src/assembler-inl.h"
13
#include "src/base/platform/mutex.h"
14
#include "src/bootstrapper.h"
15
#include "src/builtins/builtins.h"
16 17 18
#include "src/code-stubs.h"
#include "src/compilation-cache.h"
#include "src/compiler.h"
19
#include "src/debug/debug-evaluate.h"
20
#include "src/debug/liveedit.h"
21 22
#include "src/deoptimizer.h"
#include "src/execution.h"
23
#include "src/frames-inl.h"
24
#include "src/global-handles.h"
yangguo's avatar
yangguo committed
25
#include "src/globals.h"
26
#include "src/interpreter/bytecode-array-accessor.h"
27
#include "src/interpreter/bytecode-array-iterator.h"
28
#include "src/interpreter/interpreter.h"
29
#include "src/isolate-inl.h"
30
#include "src/log.h"
31
#include "src/messages.h"
32
#include "src/objects/api-callbacks-inl.h"
33
#include "src/objects/debug-objects-inl.h"
34
#include "src/objects/js-generator-inl.h"
35
#include "src/objects/js-promise-inl.h"
36
#include "src/snapshot/natives.h"
37
#include "src/snapshot/snapshot.h"
38
#include "src/wasm/wasm-objects-inl.h"
39

40 41
namespace v8 {
namespace internal {
42

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker {
 public:
  TemporaryObjectsTracker() = default;
  ~TemporaryObjectsTracker() = default;

  void AllocationEvent(Address addr, int) override { objects_.insert(addr); }

  void MoveEvent(Address from, Address to, int) override {
    if (from == to) return;
    base::LockGuard<base::Mutex> guard(&mutex_);
    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);
  }

65 66 67 68 69 70 71 72 73 74
  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();
75 76 77 78 79 80 81 82
  }

 private:
  std::unordered_set<Address> objects_;
  base::Mutex mutex_;
  DISALLOW_COPY_AND_ASSIGN(TemporaryObjectsTracker);
};

83
Debug::Debug(Isolate* isolate)
84
    : is_active_(false),
85
      hook_on_function_call_(false),
86 87
      is_suppressed_(false),
      break_disabled_(false),
88
      break_points_active_(true),
89 90
      break_on_exception_(false),
      break_on_uncaught_exception_(false),
91
      side_effect_check_failed_(false),
92
      debug_info_list_(nullptr),
93
      feature_tracker_(isolate),
94
      isolate_(isolate) {
95
  ThreadInit();
96 97
}

98 99
Debug::~Debug() { DCHECK_NULL(debug_delegate_); }

100 101
BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
                                       JavaScriptFrame* frame) {
102 103 104
  if (debug_info->CanBreakAtEntry()) {
    return BreakLocation(Debug::kBreakAtEntryPosition, DEBUG_BREAK_AT_ENTRY);
  }
105
  auto summary = FrameSummary::GetTop(frame).AsJavaScript();
106
  int offset = summary.code_offset();
107
  Handle<AbstractCode> abstract_code = summary.abstract_code();
108 109 110
  BreakIterator it(debug_info);
  it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
  return it.GetBreakLocation();
111
}
112

113 114 115
void BreakLocation::AllAtCurrentStatement(
    Handle<DebugInfo> debug_info, JavaScriptFrame* frame,
    std::vector<BreakLocation>* result_out) {
116
  DCHECK(!debug_info->CanBreakAtEntry());
117
  auto summary = FrameSummary::GetTop(frame).AsJavaScript();
118 119 120 121 122
  int offset = summary.code_offset();
  Handle<AbstractCode> abstract_code = summary.abstract_code();
  if (abstract_code->IsCode()) offset = offset - 1;
  int statement_position;
  {
123 124 125
    BreakIterator it(debug_info);
    it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
    statement_position = it.statement_position();
126
  }
127 128
  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
    if (it.statement_position() == statement_position) {
129
      result_out->push_back(it.GetBreakLocation());
130 131 132 133
    }
  }
}

134 135 136 137 138 139 140 141 142 143 144 145
JSGeneratorObject* BreakLocation::GetGeneratorObjectForSuspendedFrame(
    JavaScriptFrame* frame) const {
  DCHECK(IsSuspend());
  DCHECK_GE(generator_obj_reg_index_, 0);

  Object* generator_obj =
      InterpretedFrame::cast(frame)->ReadInterpreterRegister(
          generator_obj_reg_index_);

  return JSGeneratorObject::cast(generator_obj);
}

146 147 148 149 150 151 152
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());
153
  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
154
    // Check if this break point is closer that what was previously found.
155 156 157
    if (it.code_offset() <= offset && offset - it.code_offset() < distance) {
      closest_break = it.break_index();
      distance = offset - it.code_offset();
158 159 160 161 162
      // Check whether we can't get any closer.
      if (distance == 0) break;
    }
  }
  return closest_break;
163 164
}

165 166
bool BreakLocation::HasBreakPoint(Isolate* isolate,
                                  Handle<DebugInfo> debug_info) const {
167
  // First check whether there is a break point with the same source position.
168
  if (!debug_info->HasBreakPoint(isolate, position_)) return false;
169 170 171 172 173 174 175 176 177 178 179 180
  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_;
  }
181
}
182

183 184 185 186 187 188 189 190
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;
191 192 193

    // Externally, suspend breaks should look like normal breaks.
    case DEBUG_BREAK_SLOT_AT_SUSPEND:
194 195 196 197 198
    default:
      return debug::kCommonBreakLocation;
  }
}

199
BreakIterator::BreakIterator(Handle<DebugInfo> debug_info)
200 201 202 203
    : debug_info_(debug_info),
      break_index_(-1),
      source_position_iterator_(
          debug_info->DebugBytecodeArray()->SourcePositionTable()) {
204
  position_ = debug_info->shared()->StartPosition();
205
  statement_position_ = position_;
206 207 208
  // There is at least one break location.
  DCHECK(!Done());
  Next();
209
}
210

211
int BreakIterator::BreakIndexFromPosition(int source_position) {
212 213 214
  int distance = kMaxInt;
  int closest_break = break_index();
  while (!Done()) {
215
    int next_position = position();
216 217 218 219 220 221 222 223 224 225 226 227
    if (source_position <= next_position &&
        next_position - source_position < distance) {
      closest_break = break_index();
      distance = next_position - source_position;
      // Check whether we can't get any closer.
      if (distance == 0) break;
    }
    Next();
  }
  return closest_break;
}

228
void BreakIterator::Next() {
229 230 231 232 233 234 235
  DisallowHeapAllocation no_gc;
  DCHECK(!Done());
  bool first = break_index_ == -1;
  while (!Done()) {
    if (!first) source_position_iterator_.Advance();
    first = false;
    if (Done()) return;
236
    position_ = source_position_iterator_.source_position().ScriptOffset();
237 238 239
    if (source_position_iterator_.is_statement()) {
      statement_position_ = position_;
    }
240 241
    DCHECK_LE(0, position_);
    DCHECK_LE(0, statement_position_);
242

243
    DebugBreakType type = GetDebugBreakType();
244
    if (type != NOT_DEBUG_BREAK) break;
245
  }
246
  break_index_++;
247 248
}

249
DebugBreakType BreakIterator::GetDebugBreakType() {
250
  BytecodeArray* bytecode_array = debug_info_->OriginalBytecodeArray();
251
  interpreter::Bytecode bytecode =
252
      interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
253

254 255 256 257 258 259
  // Make sure we read the actual bytecode, not a prefix scaling bytecode.
  if (interpreter::Bytecodes::IsPrefixScalingBytecode(bytecode)) {
    bytecode = interpreter::Bytecodes::FromByte(
        bytecode_array->get(code_offset() + 1));
  }

260 261 262 263
  if (bytecode == interpreter::Bytecode::kDebugger) {
    return DEBUGGER_STATEMENT;
  } else if (bytecode == interpreter::Bytecode::kReturn) {
    return DEBUG_BREAK_SLOT_AT_RETURN;
264 265
  } else if (bytecode == interpreter::Bytecode::kSuspendGenerator) {
    return DEBUG_BREAK_SLOT_AT_SUSPEND;
266
  } else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) {
267 268 269 270 271 272 273 274
    return DEBUG_BREAK_SLOT_AT_CALL;
  } else if (source_position_iterator_.is_statement()) {
    return DEBUG_BREAK_SLOT;
  } else {
    return NOT_DEBUG_BREAK;
  }
}

275 276
void BreakIterator::SkipToPosition(int position) {
  BreakIterator it(debug_info_);
277
  SkipTo(it.BreakIndexFromPosition(position));
278 279
}

280
void BreakIterator::SetDebugBreak() {
281 282 283 284 285 286 287 288 289 290 291 292 293
  DebugBreakType debug_break_type = GetDebugBreakType();
  if (debug_break_type == DEBUGGER_STATEMENT) return;
  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
  BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
  interpreter::Bytecode bytecode =
      interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
  if (interpreter::Bytecodes::IsDebugBreak(bytecode)) return;
  interpreter::Bytecode debugbreak =
      interpreter::Bytecodes::GetDebugBreak(bytecode);
  bytecode_array->set(code_offset(),
                      interpreter::Bytecodes::ToByte(debugbreak));
}

294
void BreakIterator::ClearDebugBreak() {
295 296 297 298 299 300 301 302
  DebugBreakType debug_break_type = GetDebugBreakType();
  if (debug_break_type == DEBUGGER_STATEMENT) return;
  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
  BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
  BytecodeArray* original = debug_info_->OriginalBytecodeArray();
  bytecode_array->set(code_offset(), original->get(code_offset()));
}

303
BreakLocation BreakIterator::GetBreakLocation() {
304
  Handle<AbstractCode> code(
305
      AbstractCode::cast(debug_info_->DebugBytecodeArray()), isolate());
306 307 308 309 310 311 312 313 314
  DebugBreakType type = GetDebugBreakType();
  int generator_object_reg_index = -1;
  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.
    BytecodeArray* bytecode_array = debug_info_->OriginalBytecodeArray();
315
    interpreter::BytecodeArrayAccessor accessor(
316
        handle(bytecode_array, isolate()), code_offset());
317 318 319 320 321 322 323 324

    DCHECK_EQ(accessor.current_bytecode(),
              interpreter::Bytecode::kSuspendGenerator);
    interpreter::Register generator_obj_reg = accessor.GetRegisterOperand(0);
    generator_object_reg_index = generator_obj_reg.index();
  }
  return BreakLocation(code, type, code_offset(), position_,
                       generator_object_reg_index);
325 326
}

327
Isolate* BreakIterator::isolate() { return debug_info_->GetIsolate(); }
328

329 330 331 332 333 334 335 336 337
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;
}


338 339
// Threading support.
void Debug::ThreadInit() {
340
  thread_local_.break_frame_id_ = StackFrame::NO_ID;
341
  thread_local_.last_step_action_ = StepNone;
yangguo's avatar
yangguo committed
342
  thread_local_.last_statement_position_ = kNoSourcePosition;
343
  thread_local_.last_frame_count_ = -1;
344 345
  thread_local_.fast_forward_to_return_ = false;
  thread_local_.ignore_step_into_function_ = Smi::kZero;
346
  thread_local_.target_frame_count_ = -1;
347
  thread_local_.return_value_ = Smi::kZero;
348
  thread_local_.last_breakpoint_id_ = 0;
349
  clear_suspended_generator();
350
  thread_local_.restart_fp_ = kNullAddress;
351 352
  base::Relaxed_Store(&thread_local_.current_debug_scope_,
                      static_cast<base::AtomicWord>(0));
353
  thread_local_.break_on_next_function_call_ = false;
354
  UpdateHookOnFunctionCall();
355 356 357 358
}


char* Debug::ArchiveDebug(char* storage) {
359 360
  MemCopy(storage, reinterpret_cast<char*>(&thread_local_),
          ArchiveSpacePerThread());
361 362 363 364
  return storage + ArchiveSpacePerThread();
}

char* Debug::RestoreDebug(char* storage) {
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
  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) {
    // Reset the previous step action for this thread.
    PrepareStep(thread_local_.last_step_action_);
  }

380 381 382
  return storage + ArchiveSpacePerThread();
}

383
int Debug::ArchiveSpacePerThread() { return sizeof(ThreadLocal); }
384

385
void Debug::Iterate(RootVisitor* v) {
386 387 388 389 390
  v->VisitRootPointer(Root::kDebug, nullptr, &thread_local_.return_value_);
  v->VisitRootPointer(Root::kDebug, nullptr,
                      &thread_local_.suspended_generator_);
  v->VisitRootPointer(Root::kDebug, nullptr,
                      &thread_local_.ignore_step_into_function_);
391 392
}

393 394
DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo* debug_info)
    : next_(nullptr) {
395
  // Globalize the request debug info object and make it weak.
396
  GlobalHandles* global_handles = isolate->global_handles();
397
  debug_info_ = global_handles->Create(debug_info).location();
398 399 400
}


401
DebugInfoListNode::~DebugInfoListNode() {
dcarney's avatar
dcarney committed
402 403 404
  if (debug_info_ == nullptr) return;
  GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_));
  debug_info_ = nullptr;
405 406
}

407
void Debug::Unload() {
408
  ClearAllBreakPoints();
409
  ClearStepping();
410
  RemoveAllCoverageInfos();
411
  ClearAllDebuggerHints();
412
  debug_delegate_ = nullptr;
413 414
}

415
void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) {
416 417
  // Initialize LiveEdit.
  LiveEdit::InitializeThreadLocal(this);
418 419

  // Just continue if breaks are disabled or debugger cannot be loaded.
420
  if (break_disabled()) return;
421

422
  // Enter the debugger.
423
  DebugScope debug_scope(this);
424
  DisableBreak no_recursive_break(this);
425

426
  // Return if we fail to retrieve debug info.
427
  Handle<SharedFunctionInfo> shared(break_target->shared(), isolate_);
428
  if (!EnsureBreakInfo(shared)) return;
429 430
  PrepareFunctionForDebugExecution(shared);

431
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
432

433 434
  // Find the break location where execution has stopped.
  BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
435 436

  // Find actual break points, if any, and trigger debug break event.
437 438
  MaybeHandle<FixedArray> break_points_hit =
      CheckBreakPoints(debug_info, &location);
439
  if (!break_points_hit.is_null() || break_on_next_function_call()) {
440 441 442
    // Clear all current stepping setup.
    ClearStepping();
    // Notify the debug event listeners.
443 444 445
    OnDebugBreak(!break_points_hit.is_null()
                     ? break_points_hit.ToHandleChecked()
                     : isolate_->factory()->empty_fixed_array());
446
    return;
447 448
  }

449 450 451 452 453 454 455 456
  // Debug break at function entry, do not worry about stepping.
  if (location.IsDebugBreakAtEntry()) {
    DCHECK(debug_info->BreakAtEntry());
    return;
  }

  DCHECK_NOT_NULL(frame);

457 458
  // No break point. Check for stepping.
  StepAction step_action = last_step_action();
459 460 461
  int current_frame_count = CurrentFrameCount();
  int target_frame_count = thread_local_.target_frame_count_;
  int last_frame_count = thread_local_.last_frame_count_;
462

463 464 465
  // StepOut at not return position was requested and return break locations
  // were flooded with one shots.
  if (thread_local_.fast_forward_to_return_) {
466
    DCHECK(location.IsReturnOrSuspend());
467 468 469 470 471 472 473
    // We have to ignore recursive calls to function.
    if (current_frame_count > target_frame_count) return;
    ClearStepping();
    PrepareStep(StepOut);
    return;
  }

474
  bool step_break = false;
475 476 477 478
  switch (step_action) {
    case StepNone:
      return;
    case StepOut:
479 480
      // Step out should not break in a deeper frame than target frame.
      if (current_frame_count > target_frame_count) return;
481
      step_break = true;
482 483
      break;
    case StepNext:
484 485
      // Step next should not break in a deeper frame than target frame.
      if (current_frame_count > target_frame_count) return;
486
      V8_FALLTHROUGH;
487
    case StepIn: {
488 489 490 491 492 493 494 495 496
      // Special case "next" and "in" for generators that are about to suspend.
      if (location.IsSuspend()) {
        DCHECK(!has_suspended_generator());
        thread_local_.suspended_generator_ =
            location.GetGeneratorObjectForSuspendedFrame(frame);
        ClearStepping();
        return;
      }

497
      FrameSummary summary = FrameSummary::GetTop(frame);
498 499
      step_break = step_break || location.IsReturn() ||
                   current_frame_count != last_frame_count ||
500 501
                   thread_local_.last_statement_position_ !=
                       summary.SourceStatementPosition();
502
      break;
503
    }
504 505
  }

506 507 508 509
  // Clear all current stepping setup.
  ClearStepping();

  if (step_break) {
510
    // Notify the debug event listeners.
511
    OnDebugBreak(isolate_->factory()->empty_fixed_array());
512 513 514
  } else {
    // Re-prepare to continue.
    PrepareStep(step_action);
515 516 517 518
  }
}


519
// Find break point objects for this location, if any, and evaluate them.
520 521 522 523 524
// Return an array of break point objects that evaluated true, or an empty
// handle if none evaluated true.
MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info,
                                                BreakLocation* location,
                                                bool* has_break_points) {
525
  bool has_break_points_to_check =
526
      break_points_active_ && location->HasBreakPoint(isolate_, debug_info);
527
  if (has_break_points) *has_break_points = has_break_points_to_check;
528
  if (!has_break_points_to_check) return {};
529

530
  return Debug::GetHitBreakPoints(debug_info, location->position());
531 532 533
}


534
bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
535
  HandleScope scope(isolate_);
536 537 538 539 540
  // 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.
541 542 543
  FrameSummary summary = FrameSummary::GetTop(frame);
  DCHECK(!summary.IsWasm());
  Handle<JSFunction> function = summary.AsJavaScript().function();
544
  if (!function->shared()->HasBreakInfo()) return false;
545
  Handle<DebugInfo> debug_info(function->shared()->GetDebugInfo(), isolate_);
546 547
  // Enter the debugger.
  DebugScope debug_scope(this);
548
  std::vector<BreakLocation> break_locations;
549
  BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations);
550
  bool has_break_points_at_all = false;
551
  for (size_t i = 0; i < break_locations.size(); i++) {
552
    bool has_break_points;
553
    MaybeHandle<FixedArray> check_result =
554
        CheckBreakPoints(debug_info, &break_locations[i], &has_break_points);
555
    has_break_points_at_all |= has_break_points;
556
    if (has_break_points && !check_result.is_null()) return false;
557 558
  }
  return has_break_points_at_all;
559 560
}

561
// Check whether a single break point object is triggered.
562 563
bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
                            bool is_break_at_entry) {
564
  HandleScope scope(isolate_);
565

566
  if (!break_point->condition()->length()) return true;
567
  Handle<String> condition(break_point->condition(), isolate_);
568
  MaybeHandle<Object> maybe_result;
569
  Handle<Object> result;
570 571 572 573 574 575 576 577 578 579 580 581 582 583

  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)) {
584 585 586
    if (isolate_->has_pending_exception()) {
      isolate_->clear_pending_exception();
    }
587 588
    return false;
  }
589
  return result->BooleanValue(isolate_);
590 591
}

592
bool Debug::SetBreakPoint(Handle<JSFunction> function,
593
                          Handle<BreakPoint> break_point,
594
                          int* source_position) {
595
  HandleScope scope(isolate_);
596

597
  // Make sure the function is compiled and has set up the debug info.
598
  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
599
  if (!EnsureBreakInfo(shared)) return false;
600 601
  PrepareFunctionForDebugExecution(shared);

602
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
603
  // Source positions starts with zero.
604
  DCHECK_LE(0, *source_position);
605 606

  // Find the break point and change it.
607
  *source_position = FindBreakablePosition(debug_info, *source_position);
608
  DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
609
  // At least one active break point now.
610
  DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
611

612 613
  ClearBreakPoints(debug_info);
  ApplyBreakPoints(debug_info);
614

615 616
  feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
  return true;
617 618
}

619
bool Debug::SetBreakPointForScript(Handle<Script> script,
620 621 622 623 624
                                   Handle<String> condition,
                                   int* source_position, int* id) {
  *id = ++thread_local_.last_breakpoint_id_;
  Handle<BreakPoint> break_point =
      isolate_->factory()->NewBreakPoint(*id, condition);
625
  if (script->type() == Script::TYPE_WASM) {
626 627 628 629
    Handle<WasmModuleObject> module_object(
        WasmModuleObject::cast(script->wasm_module_object()), isolate_);
    return WasmModuleObject::SetBreakPoint(module_object, source_position,
                                           break_point);
630
  }
631

632 633
  HandleScope scope(isolate_);

634
  // Obtain shared function info for the function.
635 636
  Handle<Object> result =
      FindSharedFunctionInfoInScript(script, *source_position);
637
  if (result->IsUndefined(isolate_)) return false;
638 639

  // Make sure the function has set up the debug info.
640
  Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
641
  if (!EnsureBreakInfo(shared)) return false;
642
  PrepareFunctionForDebugExecution(shared);
643 644 645

  // Find position within function. The script position might be before the
  // source position of the first function.
646 647
  if (shared->StartPosition() > *source_position) {
    *source_position = shared->StartPosition();
648 649
  }

650
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
651

652 653 654 655 656 657 658
  // Find breakable position returns first breakable position after
  // *source_position, it can return 0 if no break location is found after
  // *source_position.
  int breakable_position = FindBreakablePosition(debug_info, *source_position);
  if (breakable_position < *source_position) return false;
  *source_position = breakable_position;

659
  DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
660
  // At least one active break point now.
661
  DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
662 663 664

  ClearBreakPoints(debug_info);
  ApplyBreakPoints(debug_info);
665

666
  feature_tracker()->Track(DebugFeatureTracker::kBreakPoint);
667 668
  return true;
}
669

670
int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
671
                                 int source_position) {
672 673 674
  if (debug_info->CanBreakAtEntry()) {
    return kBreakAtEntryPosition;
  } else {
675
    DCHECK(debug_info->HasInstrumentedBytecodeArray());
676 677 678 679
    BreakIterator it(debug_info);
    it.SkipToPosition(source_position);
    return it.position();
  }
680
}
681

682 683
void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
  DisallowHeapAllocation no_gc;
684 685 686
  if (debug_info->CanBreakAtEntry()) {
    debug_info->SetBreakAtEntry();
  } else {
687
    if (!debug_info->HasInstrumentedBytecodeArray()) return;
688 689 690 691
    FixedArray* break_points = debug_info->break_points();
    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));
692
      if (info->GetBreakPointCount(isolate_) == 0) continue;
693
      DCHECK(debug_info->HasInstrumentedBytecodeArray());
694 695 696 697
      BreakIterator it(debug_info);
      it.SkipToPosition(info->source_position());
      it.SetDebugBreak();
    }
698
  }
699
  debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints);
700 701
}

702
void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
703 704 705 706 707
  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.
708 709
    if (!debug_info->HasInstrumentedBytecodeArray() ||
        !debug_info->HasBreakInfo()) {
710 711
      return;
    }
712

713 714 715 716
    DisallowHeapAllocation no_gc;
    for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
      it.ClearDebugBreak();
    }
717 718
  }
}
719

720
void Debug::ClearBreakPoint(Handle<BreakPoint> break_point) {
721
  HandleScope scope(isolate_);
722

723
  for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
724
       node = node->next()) {
725
    if (!node->debug_info()->HasBreakInfo()) continue;
726 727
    Handle<Object> result = DebugInfo::FindBreakPointInfo(
        isolate_, node->debug_info(), break_point);
728 729
    if (result->IsUndefined(isolate_)) continue;
    Handle<DebugInfo> debug_info = node->debug_info();
730
    if (DebugInfo::ClearBreakPoint(isolate_, debug_info, break_point)) {
731
      ClearBreakPoints(debug_info);
732
      if (debug_info->GetBreakPointCount(isolate_) == 0) {
733
        RemoveBreakInfoAndMaybeFree(debug_info);
734 735
      } else {
        ApplyBreakPoints(debug_info);
736 737 738 739 740 741
      }
      return;
    }
  }
}

742 743 744 745 746 747 748 749 750 751 752
int Debug::GetFunctionDebuggingId(Handle<JSFunction> function) {
  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;
}

753 754
bool Debug::SetBreakpointForFunction(Handle<JSFunction> function,
                                     Handle<String> condition, int* id) {
755 756 757
  *id = ++thread_local_.last_breakpoint_id_;
  Handle<BreakPoint> breakpoint =
      isolate_->factory()->NewBreakPoint(*id, condition);
758 759
  int source_position = 0;
  return SetBreakPoint(function, breakpoint, &source_position);
760 761 762 763 764 765 766 767
}

void Debug::RemoveBreakpoint(int id) {
  Handle<BreakPoint> breakpoint = isolate_->factory()->NewBreakPoint(
      id, isolate_->factory()->empty_string());
  ClearBreakPoint(breakpoint);
}

768
// Clear out all the debug break code.
769
void Debug::ClearAllBreakPoints() {
770 771
  ClearAllDebugInfos([=](Handle<DebugInfo> info) {
    ClearBreakPoints(info);
772
    info->ClearBreakInfo(isolate_);
773
  });
774 775
}

776 777
void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
                             bool returns_only) {
778
  if (IsBlackboxed(shared)) return;
779
  // Make sure the function is compiled and has set up the debug info.
780
  if (!EnsureBreakInfo(shared)) return;
781 782
  PrepareFunctionForDebugExecution(shared);

783
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
784
  // Flood the function with break points.
785
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
786
  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
787
    if (returns_only && !it.GetBreakLocation().IsReturnOrSuspend()) continue;
788
    it.SetDebugBreak();
789 790 791 792 793 794 795 796 797 798 799 800
  }
}

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


801 802 803 804 805 806 807 808
bool Debug::IsBreakOnException(ExceptionBreakType type) {
  if (type == BreakUncaughtException) {
    return break_on_uncaught_exception_;
  } else {
    return break_on_exception_;
  }
}

809 810
MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<DebugInfo> debug_info,
                                                 int position) {
811
  Handle<Object> break_points = debug_info->GetBreakPoints(isolate_, position);
812
  bool is_break_at_entry = debug_info->BreakAtEntry();
813 814
  DCHECK(!break_points->IsUndefined(isolate_));
  if (!break_points->IsFixedArray()) {
815 816 817 818
    if (!CheckBreakPoint(Handle<BreakPoint>::cast(break_points),
                         is_break_at_entry)) {
      return {};
    }
819
    Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1);
820
    break_points_hit->set(0, *break_points);
821 822 823
    return break_points_hit;
  }

824
  Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
825 826 827 828 829
  int num_objects = array->length();
  Handle<FixedArray> break_points_hit =
      isolate_->factory()->NewFixedArray(num_objects);
  int break_points_hit_count = 0;
  for (int i = 0; i < num_objects; ++i) {
830
    Handle<Object> break_point(array->get(i), isolate_);
831 832
    if (CheckBreakPoint(Handle<BreakPoint>::cast(break_point),
                        is_break_at_entry)) {
833
      break_points_hit->set(break_points_hit_count++, *break_point);
834 835 836
    }
  }
  if (break_points_hit_count == 0) return {};
837
  break_points_hit->Shrink(isolate_, break_points_hit_count);
838 839
  return break_points_hit;
}
840

841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
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();
}

857
void Debug::PrepareStepIn(Handle<JSFunction> function) {
858
  CHECK(last_step_action() >= StepIn || break_on_next_function_call());
859
  if (ignore_events()) return;
860
  if (in_debug_scope()) return;
861
  if (break_disabled()) return;
862
  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
863 864 865
  if (IsBlackboxed(shared)) return;
  if (*function == thread_local_.ignore_step_into_function_) return;
  thread_local_.ignore_step_into_function_ = Smi::kZero;
866
  FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
867 868
}

869
void Debug::PrepareStepInSuspendedGenerator() {
870
  CHECK(has_suspended_generator());
871
  if (ignore_events()) return;
872
  if (in_debug_scope()) return;
873
  if (break_disabled()) return;
874
  thread_local_.last_step_action_ = StepIn;
875
  UpdateHookOnFunctionCall();
876
  Handle<JSFunction> function(
877 878
      JSGeneratorObject::cast(thread_local_.suspended_generator_)->function(),
      isolate_);
879
  FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
880 881
  clear_suspended_generator();
}
882

883 884
void Debug::PrepareStepOnThrow() {
  if (last_step_action() == StepNone) return;
885
  if (ignore_events()) return;
886
  if (in_debug_scope()) return;
887
  if (break_disabled()) return;
888 889 890

  ClearOneShot();

891 892
  int current_frame_count = CurrentFrameCount();

893 894 895 896
  // Iterate through the JavaScript stack looking for handlers.
  JavaScriptFrameIterator it(isolate_);
  while (!it.done()) {
    JavaScriptFrame* frame = it.frame();
897
    if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break;
898
    std::vector<SharedFunctionInfo*> infos;
899
    frame->GetFunctions(&infos);
900
    current_frame_count -= infos.size();
901 902 903
    it.Advance();
  }

904 905 906 907 908 909 910 911 912 913 914
  // 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());
    if (last_step_action() == StepIn) {
      // Deoptimize frame to ensure calls are checked for step-in.
      Deoptimizer::DeoptimizeFunction(frame->function());
915
    }
916
    std::vector<FrameSummary> summaries;
917
    frame->Summarize(&summaries);
918 919
    for (size_t i = summaries.size(); i != 0; i--, current_frame_count--) {
      const FrameSummary& summary = summaries[i - 1];
920 921 922 923
      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.
924 925
        if (summaries.size() > 1) {
          Handle<AbstractCode> code = summary.AsJavaScript().abstract_code();
926
          CHECK_EQ(AbstractCode::INTERPRETED_FUNCTION, code->kind());
927
          HandlerTable table(code->GetBytecodeArray());
928
          int code_offset = summary.code_offset();
929
          HandlerTable::CatchPrediction prediction;
930
          int index = table.LookupRange(code_offset, nullptr, &prediction);
931 932 933 934 935
          if (index > 0) found_handler = true;
        } else {
          found_handler = true;
        }
      }
936

937 938 939 940 941 942 943 944
      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.
        if ((last_step_action() == StepNext || last_step_action() == StepOut) &&
            current_frame_count > thread_local_.target_frame_count_) {
          continue;
        }
        Handle<SharedFunctionInfo> info(
945
            summary.AsJavaScript().function()->shared(), isolate_);
946
        if (IsBlackboxed(info)) continue;
947 948 949 950
        FloodWithOneShot(info);
        return;
      }
    }
951 952 953 954
  }
}


955
void Debug::PrepareStep(StepAction step_action) {
956
  HandleScope scope(isolate_);
957

958
  DCHECK(in_debug_scope());
959 960 961 962 963

  // 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.
964 965 966 967
  StackFrame::Id frame_id = break_frame_id();
  // If there is no JavaScript stack don't do anything.
  if (frame_id == StackFrame::NO_ID) return;

968 969
  feature_tracker()->Track(DebugFeatureTracker::kStepping);

970 971
  thread_local_.last_step_action_ = step_action;

972 973 974
  StackTraceFrameIterator frames_it(isolate_, frame_id);
  StandardFrame* frame = frames_it.frame();

975 976 977 978 979 980
  // Handle stepping in wasm functions via the wasm interpreter.
  if (frame->is_wasm()) {
    // If the top frame is compiled, we cannot step.
    if (frame->is_wasm_compiled()) return;
    WasmInterpreterEntryFrame* wasm_frame =
        WasmInterpreterEntryFrame::cast(frame);
981
    wasm_frame->debug_info()->PrepareStep(step_action);
982 983 984 985
    return;
  }

  JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
986
  DCHECK(js_frame->function()->IsJSFunction());
987 988

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

995
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
996

997
  BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame);
998

999 1000 1001
  // Any step at a return is a step-out, and a step-out at a suspend behaves
  // like a return.
  if (location.IsReturn() || (location.IsSuspend() && step_action == StepOut)) {
1002 1003 1004 1005 1006 1007 1008 1009
    // 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;
    thread_local_.last_step_action_ = StepIn;
  }
1010 1011

  // We need to schedule DebugOnFunction call callback
1012 1013
  UpdateHookOnFunctionCall();

1014 1015
  // A step-next in blackboxed function is a step-out.
  if (step_action == StepNext && IsBlackboxed(shared)) step_action = StepOut;
1016 1017

  thread_local_.last_statement_position_ =
1018
      summary.abstract_code()->SourceStatementPosition(summary.code_offset());
1019 1020
  int current_frame_count = CurrentFrameCount();
  thread_local_.last_frame_count_ = current_frame_count;
1021 1022
  // No longer perform the current async step.
  clear_suspended_generator();
1023

1024 1025 1026 1027
  switch (step_action) {
    case StepNone:
      UNREACHABLE();
      break;
1028 1029 1030 1031
    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;
1032
      if (!location.IsReturnOrSuspend() && !IsBlackboxed(shared)) {
1033 1034 1035 1036 1037 1038 1039
        // 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;
      }
1040 1041 1042 1043
      // 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()) {
1044
        // TODO(clemensh): Implement stepping out from JS to wasm.
1045 1046 1047 1048 1049
        if (frames_it.frame()->is_wasm()) continue;
        JavaScriptFrame* frame = JavaScriptFrame::cast(frames_it.frame());
        if (last_step_action() == StepIn) {
          // Deoptimize frame to ensure calls are checked for step-in.
          Deoptimizer::DeoptimizeFunction(frame->function());
1050
        }
1051
        HandleScope scope(isolate_);
1052
        std::vector<Handle<SharedFunctionInfo>> infos;
1053
        frame->GetFunctions(&infos);
1054 1055 1056
        for (; !infos.empty(); current_frame_count--) {
          Handle<SharedFunctionInfo> info = infos.back();
          infos.pop_back();
1057 1058 1059 1060 1061
          if (in_current_frame) {
            // We want to skip out, so skip the current frame.
            in_current_frame = false;
            continue;
          }
1062
          if (IsBlackboxed(info)) continue;
1063 1064 1065
          FloodWithOneShot(info);
          thread_local_.target_frame_count_ = current_frame_count;
          return;
1066
        }
1067 1068
      }
      break;
1069
    }
1070
    case StepNext:
1071
      thread_local_.target_frame_count_ = current_frame_count;
1072
      V8_FALLTHROUGH;
1073
    case StepIn:
1074
      // TODO(clemensh): Implement stepping from JS into wasm.
1075
      FloodWithOneShot(shared);
1076
      break;
1077 1078 1079 1080 1081
  }
}

// Simple function for returning the source positions for active break points.
Handle<Object> Debug::GetSourceBreakLocations(
1082
    Isolate* isolate, Handle<SharedFunctionInfo> shared) {
1083
  if (!shared->HasBreakInfo()) {
1084
    return isolate->factory()->undefined_value();
1085
  }
1086

1087
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate);
1088
  if (debug_info->GetBreakPointCount(isolate) == 0) {
1089
    return isolate->factory()->undefined_value();
1090
  }
1091 1092
  Handle<FixedArray> locations = isolate->factory()->NewFixedArray(
      debug_info->GetBreakPointCount(isolate));
1093
  int count = 0;
1094
  for (int i = 0; i < debug_info->break_points()->length(); ++i) {
1095
    if (!debug_info->break_points()->get(i)->IsUndefined(isolate)) {
1096 1097
      BreakPointInfo* break_point_info =
          BreakPointInfo::cast(debug_info->break_points()->get(i));
1098
      int break_points = break_point_info->GetBreakPointCount(isolate);
1099
      if (break_points == 0) continue;
1100 1101 1102
      for (int j = 0; j < break_points; ++j) {
        locations->set(count++,
                       Smi::FromInt(break_point_info->source_position()));
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
      }
    }
  }
  return locations;
}

void Debug::ClearStepping() {
  // Clear the various stepping setup.
  ClearOneShot();

1113
  thread_local_.last_step_action_ = StepNone;
yangguo's avatar
yangguo committed
1114
  thread_local_.last_statement_position_ = kNoSourcePosition;
1115 1116
  thread_local_.ignore_step_into_function_ = Smi::kZero;
  thread_local_.fast_forward_to_return_ = false;
1117 1118
  thread_local_.last_frame_count_ = -1;
  thread_local_.target_frame_count_ = -1;
1119
  thread_local_.break_on_next_function_call_ = false;
1120
  UpdateHookOnFunctionCall();
1121 1122
}

1123

1124 1125 1126 1127 1128
// 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() {
  // The current implementation just runs through all the breakpoints. When the
1129
  // last break point for a function is removed that function is automatically
1130
  // removed from the list.
1131
  for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
1132
       node = node->next()) {
1133 1134 1135
    Handle<DebugInfo> debug_info = node->debug_info();
    ClearBreakPoints(debug_info);
    ApplyBreakPoints(debug_info);
1136 1137 1138
  }
}

1139 1140 1141 1142
class RedirectActiveFunctions : public ThreadVisitor {
 public:
  explicit RedirectActiveFunctions(SharedFunctionInfo* shared)
      : shared_(shared) {
1143
    DCHECK(shared->HasBytecodeArray());
1144 1145
  }

1146 1147 1148 1149
  void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
    for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
      JavaScriptFrame* frame = it.frame();
      JSFunction* function = frame->function();
1150 1151
      if (!frame->is_interpreted()) continue;
      if (function->shared() != shared_) continue;
1152 1153 1154 1155
      InterpretedFrame* interpreted_frame =
          reinterpret_cast<InterpretedFrame*>(frame);
      BytecodeArray* debug_copy = shared_->GetDebugInfo()->DebugBytecodeArray();
      interpreted_frame->PatchBytecodeArray(debug_copy);
1156
    }
1157 1158 1159
  }

 private:
1160 1161
  SharedFunctionInfo* shared_;
  DisallowHeapAllocation no_gc_;
1162 1163
};

1164 1165 1166
void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
  // Deoptimize all code compiled from this shared function info including
  // inlining.
1167
  isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock);
1168

1169 1170 1171 1172
  // Make sure we abort incremental marking.
  isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
                                      GarbageCollectionReason::kDebugger);

1173 1174 1175 1176 1177 1178
  bool found_something = false;
  Code::OptimizedCodeIterator iterator(isolate_);
  while (Code* code = iterator.Next()) {
    if (code->Inlines(*shared)) {
      code->set_marked_for_deoptimization(true);
      found_something = true;
1179
    }
1180
  }
1181

1182 1183 1184 1185 1186 1187
  if (found_something) {
    // Only go through with the deoptimization if something was found.
    Deoptimizer::DeoptimizeMarkedCode(isolate_);
  }
}

1188 1189
void Debug::PrepareFunctionForDebugExecution(
    Handle<SharedFunctionInfo> shared) {
1190 1191 1192 1193 1194 1195
  // 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);
1196 1197 1198
  if (debug_info->flags() & DebugInfo::kPreparedForDebugExecution) return;

  // Make a copy of the bytecode array if available.
1199
  Handle<Object> maybe_original_bytecode_array =
1200 1201
      isolate_->factory()->undefined_value();
  if (shared->HasBytecodeArray()) {
1202 1203 1204 1205 1206 1207
    Handle<BytecodeArray> original_bytecode_array =
        handle(shared->GetBytecodeArray(), isolate_);
    Handle<BytecodeArray> debug_bytecode_array =
        isolate_->factory()->CopyBytecodeArray(original_bytecode_array);
    shared->SetDebugBytecodeArray(*debug_bytecode_array);
    maybe_original_bytecode_array = original_bytecode_array;
1208
  }
1209
  debug_info->set_original_bytecode_array(*maybe_original_bytecode_array);
1210

1211 1212 1213
  if (debug_info->CanBreakAtEntry()) {
    // Deopt everything in case the function is inlined anywhere.
    Deoptimizer::DeoptimizeAll(isolate_);
1214
    InstallDebugBreakTrampoline();
1215 1216 1217 1218 1219 1220 1221
  } else {
    DeoptimizeFunction(shared);
    // Update PCs on the stack to point to recompiled code.
    RedirectActiveFunctions redirect_visitor(*shared);
    redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
    isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
  }
1222
  debug_info->set_flags(debug_info->flags() |
1223
                        DebugInfo::kPreparedForDebugExecution);
1224 1225
}

1226 1227 1228 1229 1230
void Debug::InstallDebugBreakTrampoline() {
  // 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_);
1231 1232 1233 1234
  // 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;
1235 1236 1237
  for (DebugInfoListNode* current = debug_info_list_; current != nullptr;
       current = current->next()) {
    if (current->debug_info()->CanBreakAtEntry()) {
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
      needs_to_use_trampoline = true;
      if (current->debug_info()->shared()->IsApiFunction()) {
        needs_to_clear_ic = true;
        break;
      }
    }
  }

  if (!needs_to_use_trampoline) return;

  Handle<Code> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline);
  std::vector<Handle<JSFunction>> needs_compile;
  {
    HeapIterator iterator(isolate_->heap());
    while (HeapObject* obj = iterator.next()) {
      if (needs_to_clear_ic && obj->IsFeedbackVector()) {
        FeedbackVector::cast(obj)->ClearSlots(isolate_);
        continue;
      } else if (obj->IsJSFunction()) {
1257 1258 1259 1260 1261
        JSFunction* fun = JSFunction::cast(obj);
        SharedFunctionInfo* shared = fun->shared();
        if (!shared->HasDebugInfo()) continue;
        if (!shared->GetDebugInfo()->CanBreakAtEntry()) continue;
        if (!fun->is_compiled()) {
1262
          needs_compile.push_back(handle(fun, isolate_));
1263 1264 1265 1266 1267 1268 1269 1270
        } else {
          fun->set_code(*trampoline);
        }
      }
    }
  }
  // By overwriting the function code with DebugBreakTrampoline, which tailcalls
  // to shared code, we bypass CompileLazy. Perform CompileLazy here instead.
1271
  for (Handle<JSFunction> fun : needs_compile) {
1272 1273 1274 1275 1276
    Compiler::Compile(fun, Compiler::CLEAR_EXCEPTION);
    fun->set_code(*trampoline);
  }
}

1277 1278 1279
namespace {
template <typename Iterator>
void GetBreakablePositions(Iterator* it, int start_position, int end_position,
1280
                           std::vector<BreakLocation>* locations) {
1281 1282 1283 1284
  while (!it->Done()) {
    if (it->position() >= start_position && it->position() < end_position) {
      locations->push_back(it->GetBreakLocation());
    }
1285 1286 1287 1288 1289
    it->Next();
  }
}

void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
1290 1291
                            int end_position,
                            std::vector<BreakLocation>* locations) {
1292
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
1293 1294
  BreakIterator it(debug_info);
  GetBreakablePositions(&it, start_position, end_position, locations);
1295 1296 1297 1298
}
}  // namespace

bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
1299
                                   int end_position, bool restrict_to_function,
1300
                                   std::vector<BreakLocation>* locations) {
1301 1302 1303 1304 1305 1306 1307 1308
  if (restrict_to_function) {
    Handle<Object> result =
        FindSharedFunctionInfoInScript(script, start_position);
    if (result->IsUndefined(isolate_)) return false;

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

1312
    Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1313
    FindBreakablePositions(debug_info, start_position, end_position, locations);
1314 1315 1316
    return true;
  }

1317 1318
  while (true) {
    HandleScope scope(isolate_);
1319
    std::vector<Handle<SharedFunctionInfo>> candidates;
1320
    SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
1321 1322
    for (SharedFunctionInfo* info = iterator.Next(); info != nullptr;
         info = iterator.Next()) {
1323 1324
      if (info->EndPosition() < start_position ||
          info->StartPosition() >= end_position) {
1325
        continue;
1326
      }
1327
      if (!info->IsSubjectToDebugging()) continue;
1328
      if (!info->is_compiled() && !info->allows_lazy_compilation()) continue;
1329
      candidates.push_back(i::handle(info, isolate_));
1330 1331 1332
    }

    bool was_compiled = false;
1333
    for (const auto& candidate : candidates) {
1334
      // Code that cannot be compiled lazily are internal and not debuggable.
1335 1336 1337
      DCHECK(candidate->allows_lazy_compilation());
      if (!candidate->is_compiled()) {
        if (!Compiler::Compile(candidate, Compiler::CLEAR_EXCEPTION)) {
1338 1339 1340 1341 1342
          return false;
        } else {
          was_compiled = true;
        }
      }
1343
      if (!EnsureBreakInfo(candidate)) return false;
1344
      PrepareFunctionForDebugExecution(candidate);
1345 1346 1347
    }
    if (was_compiled) continue;

1348 1349
    for (const auto& candidate : candidates) {
      CHECK(candidate->HasBreakInfo());
1350
      Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
1351
      FindBreakablePositions(debug_info, start_position, end_position,
1352
                             locations);
1353 1354 1355 1356 1357 1358
    }
    return true;
  }
  UNREACHABLE();
}

1359 1360 1361
class SharedFunctionInfoFinder {
 public:
  explicit SharedFunctionInfoFinder(int target_position)
1362 1363
      : current_candidate_(nullptr),
        current_candidate_closure_(nullptr),
yangguo's avatar
yangguo committed
1364
        current_start_position_(kNoSourcePosition),
1365 1366
        target_position_(target_position) {}

1367
  void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = nullptr) {
1368
    if (!shared->IsSubjectToDebugging()) return;
1369
    int start_position = shared->function_token_position();
yangguo's avatar
yangguo committed
1370
    if (start_position == kNoSourcePosition) {
1371
      start_position = shared->StartPosition();
1372
    }
1373

1374
    if (start_position > target_position_) return;
1375
    if (target_position_ > shared->EndPosition()) return;
1376

1377
    if (current_candidate_ != nullptr) {
1378
      if (current_start_position_ == start_position &&
1379
          shared->EndPosition() == current_candidate_->EndPosition()) {
1380
        // If we already have a matching closure, do not throw it away.
1381
        if (current_candidate_closure_ != nullptr && closure == nullptr) return;
1382 1383 1384
        // 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.
1385
        if (!current_candidate_->is_toplevel() && shared->is_toplevel()) return;
1386
      } else if (start_position < current_start_position_ ||
1387
                 current_candidate_->EndPosition() < shared->EndPosition()) {
1388 1389 1390
        return;
      }
    }
1391

1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
    current_start_position_ = start_position;
    current_candidate_ = shared;
    current_candidate_closure_ = closure;
  }

  SharedFunctionInfo* Result() { return current_candidate_; }

  JSFunction* ResultClosure() { return current_candidate_closure_; }

 private:
  SharedFunctionInfo* current_candidate_;
  JSFunction* current_candidate_closure_;
  int current_start_position_;
  int target_position_;
  DisallowHeapAllocation no_gc_;
};
1408

1409

1410 1411 1412 1413 1414 1415
// 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.)
1416 1417
Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
                                                     int position) {
1418
  for (int iteration = 0;; iteration++) {
1419 1420
    // Go through all shared function infos associated with this script to
    // find the inner most function containing this position.
1421 1422
    // If there is no shared function info for this script at all, there is
    // no point in looking for it by walking the heap.
1423 1424 1425 1426

    SharedFunctionInfo* shared;
    {
      SharedFunctionInfoFinder finder(position);
1427
      SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
1428 1429 1430
      for (SharedFunctionInfo* info = iterator.Next(); info != nullptr;
           info = iterator.Next()) {
        finder.NewCandidate(info);
1431 1432
      }
      shared = finder.Result();
1433
      if (shared == nullptr) break;
1434 1435
      // We found it if it's already compiled.
      if (shared->is_compiled()) {
1436
        Handle<SharedFunctionInfo> shared_handle(shared, isolate_);
1437 1438 1439
        // 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
1440
        // info while bypassing PrepareFunctionForDebugExecution.
1441 1442
        if (iteration > 1) {
          AllowHeapAllocation allow_before_return;
1443
          CreateBreakInfo(shared_handle);
1444 1445 1446
        }
        return shared_handle;
      }
1447
    }
1448
    // If not, compile to reveal inner functions.
1449
    HandleScope scope(isolate_);
1450 1451
    // Code that cannot be compiled lazily are internal and not debuggable.
    DCHECK(shared->allows_lazy_compilation());
1452 1453
    if (!Compiler::Compile(handle(shared, isolate_), Compiler::CLEAR_EXCEPTION))
      break;
1454 1455
  }
  return isolate_->factory()->undefined_value();
1456 1457 1458
}


1459
// Ensures the debug information is present for shared.
1460 1461 1462
bool Debug::EnsureBreakInfo(Handle<SharedFunctionInfo> shared) {
  // Return if we already have the break info for shared.
  if (shared->HasBreakInfo()) return true;
1463 1464 1465
  if (!shared->IsSubjectToDebugging() && !CanBreakAtEntry(shared)) {
    return false;
  }
1466 1467
  if (!shared->is_compiled() &&
      !Compiler::Compile(shared, Compiler::CLEAR_EXCEPTION)) {
1468 1469
    return false;
  }
1470
  if (shared->GetCode() ==
1471 1472 1473
      isolate_->builtins()->builtin(Builtins::kDeserializeLazy)) {
    Snapshot::EnsureBuiltinIsDeserialized(isolate_, shared);
  }
1474
  CreateBreakInfo(shared);
1475 1476 1477
  return true;
}

1478 1479 1480
void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) {
  HandleScope scope(isolate_);
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1481

1482
  // Initialize with break information.
1483

1484 1485 1486 1487 1488 1489
  DCHECK(!debug_info->HasBreakInfo());

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

1490 1491 1492 1493
  int flags = debug_info->flags();
  flags |= DebugInfo::kHasBreakInfo;
  if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry;
  debug_info->set_flags(flags);
1494 1495 1496 1497 1498
  debug_info->set_break_points(*break_points);
}

Handle<DebugInfo> Debug::GetOrCreateDebugInfo(
    Handle<SharedFunctionInfo> shared) {
1499
  if (shared->HasDebugInfo()) return handle(shared->GetDebugInfo(), isolate_);
1500 1501 1502

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

1507 1508
  return debug_info;
}
1509

1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
void Debug::InstallCoverageInfo(Handle<SharedFunctionInfo> shared,
                                Handle<CoverageInfo> coverage_info) {
  DCHECK(!coverage_info.is_null());

  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);

  DCHECK(!debug_info->HasCoverageInfo());

  debug_info->set_flags(debug_info->flags() | DebugInfo::kHasCoverageInfo);
  debug_info->set_coverage_info(*coverage_info);
}

void Debug::RemoveAllCoverageInfos() {
1523 1524 1525 1526 1527 1528 1529
  ClearAllDebugInfos(
      [=](Handle<DebugInfo> info) { info->ClearCoverageInfo(isolate_); });
}

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

1532 1533
void Debug::FindDebugInfo(Handle<DebugInfo> debug_info,
                          DebugInfoListNode** prev, DebugInfoListNode** curr) {
1534
  HandleScope scope(isolate_);
1535 1536 1537 1538 1539 1540
  *prev = nullptr;
  *curr = debug_info_list_;
  while (*curr != nullptr) {
    if ((*curr)->debug_info().is_identical_to(debug_info)) return;
    *prev = *curr;
    *curr = (*curr)->next();
1541
  }
1542 1543 1544 1545

  UNREACHABLE();
}

1546
void Debug::ClearAllDebugInfos(const DebugInfoClearFunction& clear_function) {
1547 1548 1549 1550 1551
  DebugInfoListNode* prev = nullptr;
  DebugInfoListNode* current = debug_info_list_;
  while (current != nullptr) {
    DebugInfoListNode* next = current->next();
    Handle<DebugInfo> debug_info = current->debug_info();
1552 1553
    clear_function(debug_info);
    if (debug_info->IsEmpty()) {
1554 1555 1556 1557 1558 1559 1560 1561 1562
      FreeDebugInfoListNode(prev, current);
      current = next;
    } else {
      prev = current;
      current = next;
    }
  }
}

1563
void Debug::RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info) {
1564 1565
  debug_info->ClearBreakInfo(isolate_);
  if (debug_info->IsEmpty()) {
1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
    DebugInfoListNode* prev;
    DebugInfoListNode* node;
    FindDebugInfo(debug_info, &prev, &node);
    FreeDebugInfoListNode(prev, node);
  }
}

void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev,
                                  DebugInfoListNode* node) {
  DCHECK(node->debug_info()->IsEmpty());

1577
  // Unlink from list. If prev is nullptr we are looking at the first element.
1578 1579 1580 1581 1582 1583
  if (prev == nullptr) {
    debug_info_list_ = node->next();
  } else {
    prev->set_next(node->next());
  }

1584 1585
  // Pack script back into the
  // SFI::script_or_debug_info field.
1586
  Handle<DebugInfo> debug_info(node->debug_info());
1587
  debug_info->shared()->set_script_or_debug_info(debug_info->script());
1588 1589 1590 1591

  delete node;
}

1592
bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
1593
  HandleScope scope(isolate_);
1594

1595
  // Get the executing function in which the debug break occurred.
1596
  Handle<SharedFunctionInfo> shared(frame->function()->shared(), isolate_);
1597

1598
  // With no debug info there are no break points, so we can't be at a return.
1599
  if (!shared->HasBreakInfo()) return false;
1600 1601

  DCHECK(!frame->is_optimized());
1602
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1603
  BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
1604
  return location.IsReturn();
1605 1606
}

1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624
void Debug::ScheduleFrameRestart(StackFrame* frame) {
  // Set a target FP for the FrameDropperTrampoline builtin to drop to once
  // we return from the debugger.
  DCHECK(frame->is_java_script());
  // Only reschedule to a frame further below a frame we already scheduled for.
  if (frame->fp() <= thread_local_.restart_fp_) return;
  // If the frame is optimized, trigger a deopt and jump into the
  // FrameDropperTrampoline in the deoptimizer.
  thread_local_.restart_fp_ = frame->fp();

  // Reset break frame ID to the frame below the restarted frame.
  StackTraceFrameIterator it(isolate_);
  thread_local_.break_frame_id_ = StackFrame::NO_ID;
  for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
    if (it.frame()->fp() > thread_local_.restart_fp_) {
      thread_local_.break_frame_id_ = it.frame()->id();
      return;
    }
1625
  }
1626 1627
}

1628
Handle<FixedArray> Debug::GetLoadedScripts() {
1629 1630
  isolate_->heap()->CollectAllGarbage(Heap::kFinalizeIncrementalMarkingMask,
                                      GarbageCollectionReason::kDebugger);
1631
  Factory* factory = isolate_->factory();
1632
  if (!factory->script_list()->IsWeakArrayList()) {
1633 1634
    return factory->empty_fixed_array();
  }
1635 1636 1637
  Handle<WeakArrayList> array =
      Handle<WeakArrayList>::cast(factory->script_list());
  Handle<FixedArray> results = factory->NewFixedArray(array->length());
1638
  int length = 0;
1639 1640 1641
  {
    Script::Iterator iterator(isolate_);
    Script* script;
1642
    while ((script = iterator.Next()) != nullptr) {
1643
      if (script->HasValidSource()) results->set(length++, script);
1644 1645
    }
  }
1646
  return FixedArray::ShrinkOrEmpty(isolate_, results, length);
1647 1648
}

1649
void Debug::OnThrow(Handle<Object> exception) {
1650
  if (in_debug_scope() || ignore_events()) return;
1651 1652
  // Temporarily clear any scheduled_exception to allow evaluating
  // JavaScript from the debug event handler.
1653
  HandleScope scope(isolate_);
1654 1655 1656 1657 1658
  Handle<Object> scheduled_exception;
  if (isolate_->has_scheduled_exception()) {
    scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
    isolate_->clear_scheduled_exception();
  }
1659
  OnException(exception, isolate_->GetPromiseOnStackOnThrow());
1660 1661 1662
  if (!scheduled_exception.is_null()) {
    isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
  }
1663
  PrepareStepOnThrow();
1664 1665
}

1666
void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
1667
  if (in_debug_scope() || ignore_events()) return;
1668
  HandleScope scope(isolate_);
1669 1670
  // Check whether the promise has been marked as having triggered a message.
  Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
1671 1672 1673
  if (!promise->IsJSObject() ||
      JSReceiver::GetDataProperty(Handle<JSObject>::cast(promise), key)
          ->IsUndefined(isolate_)) {
1674
    OnException(value, promise);
1675 1676 1677
  }
}

1678 1679 1680
bool Debug::IsExceptionBlackboxed(bool uncaught) {
  // Uncaught exception is blackboxed if all current frames are blackboxed,
  // caught exception if top frame is blackboxed.
1681 1682 1683 1684
  StackTraceFrameIterator it(isolate_);
  while (!it.done() && it.is_wasm()) it.Advance();
  bool is_top_frame_blackboxed =
      !it.done() ? IsFrameBlackboxed(it.javascript_frame()) : true;
1685
  if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
1686
  return AllFramesOnStackAreBlackboxed();
1687 1688 1689 1690
}

bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
  HandleScope scope(isolate_);
1691
  std::vector<Handle<SharedFunctionInfo>> infos;
1692
  frame->GetFunctions(&infos);
1693
  for (const auto& info : infos) {
1694
    if (!IsBlackboxed(info)) return false;
1695
  }
1696 1697 1698
  return true;
}

1699
void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
1700
  // TODO(kozyatinskiy): regress-662674.js test fails on arm without this.
1701 1702
  if (!AllowJavascriptExecution::IsAllowed(isolate_)) return;

1703
  Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
1704 1705 1706 1707

  // Don't notify listener of exceptions that are internal to a desugaring.
  if (catch_type == Isolate::CAUGHT_BY_DESUGARING) return;

1708
  bool uncaught = catch_type == Isolate::NOT_CAUGHT;
1709
  if (promise->IsJSObject()) {
1710 1711 1712
    Handle<JSObject> jspromise = Handle<JSObject>::cast(promise);
    // Mark the promise as already having triggered a message.
    Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
1713 1714
    JSObject::SetProperty(isolate_, jspromise, key, key, LanguageMode::kStrict)
        .Assert();
1715
    // Check whether the promise reject is considered an uncaught exception.
1716
    uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
1717
  }
1718 1719 1720

  if (!debug_delegate_) return;

1721 1722 1723
  // Bail out if exception breaks are not active
  if (uncaught) {
    // Uncaught exceptions are reported by either flags.
1724
    if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
1725 1726
  } else {
    // Caught exceptions are reported is activated.
1727
    if (!break_on_exception_) return;
1728 1729
  }

1730 1731
  {
    JavaScriptFrameIterator it(isolate_);
1732
    // Check whether the top frame is blackboxed or the break location is muted.
1733 1734
    if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) ||
                       IsExceptionBlackboxed(uncaught))) {
1735 1736
      return;
    }
1737
    if (it.done()) return;  // Do not trigger an event with an empty stack.
1738 1739
  }

1740
  DebugScope debug_scope(this);
1741 1742
  HandleScope scope(isolate_);
  DisableBreak no_recursive_break(this);
1743

1744 1745 1746 1747
  Handle<Context> native_context(isolate_->native_context());
  debug_delegate_->ExceptionThrown(v8::Utils::ToLocal(native_context),
                                   v8::Utils::ToLocal(exception),
                                   v8::Utils::ToLocal(promise), uncaught);
1748 1749
}

1750 1751
void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit) {
  DCHECK(!break_points_hit.is_null());
1752 1753
  // The caller provided for DebugScope.
  AssertDebugContext();
1754
  // Bail out if there is no listener for this event
1755
  if (ignore_events()) return;
1756

1757 1758 1759 1760
#ifdef DEBUG
  PrintBreakLocation();
#endif  // DEBUG

1761
  if (!debug_delegate_) return;
1762
  DCHECK(in_debug_scope());
1763
  HandleScope scope(isolate_);
1764 1765
  DisableBreak no_recursive_break(this);

1766 1767 1768 1769
  std::vector<int> inspector_break_points_hit;
  int inspector_break_points_count = 0;
  // This array contains breakpoints installed using JS debug API.
  for (int i = 0; i < break_points_hit->length(); ++i) {
1770 1771 1772
    BreakPoint* break_point = BreakPoint::cast(break_points_hit->get(i));
    inspector_break_points_hit.push_back(break_point->id());
    ++inspector_break_points_count;
1773 1774
  }

1775 1776 1777
  Handle<Context> native_context(isolate_->native_context());
  debug_delegate_->BreakProgramRequested(v8::Utils::ToLocal(native_context),
                                         inspector_break_points_hit);
1778 1779
}

1780 1781 1782 1783
namespace {
debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
  Script::PositionInfo info;
  Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
1784 1785 1786 1787 1788 1789 1790
  // V8 provides ScriptCompiler::CompileFunctionInContext method which takes
  // 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));
1791 1792 1793 1794
}
}  // namespace

bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
1795
  if (!debug_delegate_) return !shared->IsSubjectToDebugging();
1796 1797
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
  if (!debug_info->computed_debug_is_blackboxed()) {
1798 1799 1800
    bool is_blackboxed =
        !shared->IsSubjectToDebugging() || !shared->script()->IsScript();
    if (!is_blackboxed) {
1801
      SuppressDebug while_processing(this);
1802
      HandleScope handle_scope(isolate_);
1803 1804
      PostponeInterruptsScope no_interrupts(isolate_);
      DisableBreak no_recursive_break(this);
1805
      DCHECK(shared->script()->IsScript());
1806
      Handle<Script> script(Script::cast(shared->script()), isolate_);
1807
      DCHECK(script->IsUserJavaScript());
1808 1809
      debug::Location start = GetDebugLocation(script, shared->StartPosition());
      debug::Location end = GetDebugLocation(script, shared->EndPosition());
1810 1811
      is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
          ToApiHandle<debug::Script>(script), start, end);
1812
    }
1813 1814
    debug_info->set_debug_is_blackboxed(is_blackboxed);
    debug_info->set_computed_debug_is_blackboxed(true);
1815
  }
1816
  return debug_info->debug_is_blackboxed();
1817 1818
}

1819 1820 1821 1822 1823 1824 1825 1826
bool Debug::AllFramesOnStackAreBlackboxed() {
  HandleScope scope(isolate_);
  for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
    if (!IsFrameBlackboxed(it.javascript_frame())) return false;
  }
  return true;
}

1827 1828
bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
  // Allow break at entry for builtin functions.
1829
  if (shared->native() || shared->IsApiFunction()) {
1830 1831 1832 1833 1834 1835 1836
    // Functions that are subject to debugging can have regular breakpoints.
    DCHECK(!shared->IsSubjectToDebugging());
    return true;
  }
  return false;
}

1837
bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
1838
                            bool preview, debug::LiveEditResult* result) {
1839
  DebugScope debug_scope(this);
1840
  running_live_edit_ = true;
1841
  LiveEdit::PatchScript(isolate_, script, source, preview, result);
1842
  running_live_edit_ = false;
1843
  return result->status == debug::LiveEditResult::OK;
1844 1845
}

1846
void Debug::OnCompileError(Handle<Script> script) {
1847
  ProcessCompileEvent(true, script);
1848 1849 1850
}

void Debug::OnAfterCompile(Handle<Script> script) {
1851
  ProcessCompileEvent(false, script);
1852 1853
}

1854
void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
1855 1856 1857
  // TODO(kozyatinskiy): teach devtools to work with liveedit scripts better
  // first and then remove this fast return.
  if (running_live_edit_) return;
1858 1859
  // Attach the correct debug id to the script. The debug id is used by the
  // inspector to filter scripts by native context.
1860
  script->set_context_data(isolate_->native_context()->debug_context_id());
1861
  if (ignore_events()) return;
1862 1863 1864
  if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) {
    return;
  }
1865
  if (!debug_delegate_) return;
1866
  SuppressDebug while_processing(this);
1867
  DebugScope debug_scope(this);
1868
  HandleScope scope(isolate_);
1869
  DisableBreak no_recursive_break(this);
1870
  AllowJavascriptExecution allow_script(isolate_);
1871
  debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
1872
                                  running_live_edit_, has_compile_error);
1873 1874
}

1875 1876 1877 1878 1879 1880 1881 1882 1883 1884
int Debug::CurrentFrameCount() {
  StackTraceFrameIterator it(isolate_);
  if (break_frame_id() != StackFrame::NO_ID) {
    // Skip to break frame.
    DCHECK(in_debug_scope());
    while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance();
  }
  int counter = 0;
  while (!it.done()) {
    if (it.frame()->is_optimized()) {
1885
      std::vector<SharedFunctionInfo*> infos;
1886
      OptimizedFrame::cast(it.frame())->GetFunctions(&infos);
1887
      counter += infos.size();
1888 1889 1890 1891 1892 1893 1894 1895
    } else {
      counter++;
    }
    it.Advance();
  }
  return counter;
}

1896
void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
1897
  debug_delegate_ = delegate;
1898 1899 1900
  UpdateState();
}

1901
void Debug::UpdateState() {
1902
  bool is_active = debug_delegate_ != nullptr;
1903 1904
  if (is_active == is_active_) return;
  if (is_active) {
1905 1906
    // Note that the debug context could have already been loaded to
    // bootstrap test cases.
1907
    isolate_->compilation_cache()->Disable();
1908 1909 1910
    is_active = true;
    feature_tracker()->Track(DebugFeatureTracker::kActive);
  } else {
1911
    isolate_->compilation_cache()->Enable();
1912
    Unload();
1913
  }
1914
  is_active_ = is_active;
1915 1916 1917
  if (is_active && isolate_->IsPromiseHookProtectorIntact()) {
    isolate_->InvalidatePromiseHookProtector();
  }
1918 1919
}

1920
void Debug::UpdateHookOnFunctionCall() {
1921
  STATIC_ASSERT(LastStepAction == StepIn);
1922 1923
  hook_on_function_call_ =
      thread_local_.last_step_action_ == StepIn ||
1924 1925
      isolate_->debug_execution_mode() == DebugInfo::kSideEffects ||
      thread_local_.break_on_next_function_call_;
1926 1927
}

1928
void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode) {
1929 1930
  // Initialize LiveEdit.
  LiveEdit::InitializeThreadLocal(this);
1931 1932 1933
  // Ignore debug break during bootstrapping.
  if (isolate_->bootstrapper()->IsActive()) return;
  // Just continue if breaks are disabled.
1934
  if (break_disabled()) return;
1935 1936 1937 1938 1939 1940 1941
  // Ignore debug break if debugger is not active.
  if (!is_active()) return;

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

  { JavaScriptFrameIterator it(isolate_);
1942
    DCHECK(!it.done());
1943 1944
    Object* fun = it.frame()->function();
    if (fun && fun->IsJSFunction()) {
1945
      HandleScope scope(isolate_);
1946
      Handle<JSFunction> function(JSFunction::cast(fun), isolate_);
1947
      // Don't stop in builtin and blackboxed functions.
1948
      Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
1949 1950 1951
      bool ignore_break = ignore_break_mode == kIgnoreIfTopFrameBlackboxed
                              ? IsBlackboxed(shared)
                              : AllFramesOnStackAreBlackboxed();
1952
      if (ignore_break) return;
1953 1954
      // Don't stop if the break location is muted.
      if (IsMutedAtCurrentLocation(it.frame())) return;
1955 1956 1957
    }
  }

1958 1959 1960
  // Clear stepping to avoid duplicate breaks.
  ClearStepping();

1961
  HandleScope scope(isolate_);
1962
  DebugScope debug_scope(this);
1963

1964
  OnDebugBreak(isolate_->factory()->empty_fixed_array());
1965 1966
}

1967 1968 1969 1970
#ifdef DEBUG
void Debug::PrintBreakLocation() {
  if (!FLAG_print_break_location) return;
  HandleScope scope(isolate_);
1971
  StackTraceFrameIterator iterator(isolate_);
1972
  if (iterator.done()) return;
1973
  StandardFrame* frame = iterator.frame();
1974
  FrameSummary summary = FrameSummary::GetTop(frame);
1975 1976
  int source_position = summary.SourcePosition();
  Handle<Object> script_obj = summary.script();
1977
  PrintF("[debug] break in function '");
1978
  summary.FunctionName()->PrintOn(stdout);
1979 1980 1981
  PrintF("'.\n");
  if (script_obj->IsScript()) {
    Handle<Script> script = Handle<Script>::cast(script_obj);
1982
    Handle<String> source(String::cast(script->source()), isolate_);
1983
    Script::InitLineEnds(script);
1984 1985 1986 1987
    int line =
        Script::GetLineNumber(script, source_position) - script->line_offset();
    int column = Script::GetColumnNumber(script, source_position) -
                 (line == 0 ? script->column_offset() : 0);
1988 1989
    Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()),
                                 isolate_);
jgruber's avatar
jgruber committed
1990 1991
    int line_start = line == 0 ? 0 : Smi::ToInt(line_ends->get(line - 1)) + 1;
    int line_end = Smi::ToInt(line_ends->get(line));
1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005
    DisallowHeapAllocation no_gc;
    String::FlatContent content = source->GetFlatContent();
    if (content.IsOneByte()) {
      PrintF("[debug] %.*s\n", line_end - line_start,
             content.ToOneByteVector().start() + line_start);
      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
2006

2007 2008
DebugScope::DebugScope(Debug* debug)
    : debug_(debug),
Yang Guo's avatar
Yang Guo committed
2009 2010
      prev_(reinterpret_cast<DebugScope*>(
          base::Relaxed_Load(&debug->thread_local_.current_debug_scope_))),
2011
      no_interrupts_(debug_->isolate_) {
2012
  // Link recursive debugger entry.
2013 2014
  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
                      reinterpret_cast<base::AtomicWord>(this));
2015

Yang Guo's avatar
Yang Guo committed
2016
  // Store the previous frame id and return value.
2017
  break_frame_id_ = debug_->break_frame_id();
2018

2019 2020 2021 2022 2023 2024
  // 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_ =
      has_frames ? it.frame()->id() : StackFrame::NO_ID;
2025

2026
  debug_->UpdateState();
2027 2028 2029
}


2030
DebugScope::~DebugScope() {
2031
  // Leaving this debugger entry.
2032 2033
  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
                      reinterpret_cast<base::AtomicWord>(prev_));
2034 2035 2036

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

2038
  debug_->UpdateState();
2039 2040
}

2041 2042 2043 2044 2045 2046 2047 2048
ReturnValueScope::ReturnValueScope(Debug* debug) : debug_(debug) {
  return_value_ = debug_->return_value_handle();
}

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

2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069
void Debug::UpdateDebugInfosForExecutionMode() {
  // 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()) {
      DCHECK(debug_info->shared()->HasBytecodeArray());
      if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) {
        ClearSideEffectChecks(debug_info);
        ApplyBreakPoints(debug_info);
      } else {
        ClearBreakPoints(debug_info);
        ApplySideEffectChecks(debug_info);
      }
    }
    current = current->next();
  }
}

2070 2071 2072 2073 2074 2075 2076 2077 2078
void Debug::StartSideEffectCheckMode() {
  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());
2079 2080
  Handle<FixedArray> array(isolate_->native_context()->regexp_last_match_info(),
                           isolate_);
2081 2082
  regexp_match_info_ =
      Handle<RegExpMatchInfo>::cast(isolate_->factory()->CopyFixedArray(array));
2083 2084 2085

  // Update debug infos to have correct execution mode.
  UpdateDebugInfosForExecutionMode();
2086 2087 2088 2089 2090 2091
}

void Debug::StopSideEffectCheckMode() {
  DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects);
  if (side_effect_check_failed_) {
    DCHECK(isolate_->has_pending_exception());
2092
    DCHECK_EQ(ReadOnlyRoots(isolate_).termination_exception(),
2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105
              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();
2106 2107
  isolate_->native_context()->set_regexp_last_match_info(*regexp_match_info_);
  regexp_match_info_ = Handle<RegExpMatchInfo>::null();
2108 2109 2110

  // Update debug infos to have correct execution mode.
  UpdateDebugInfosForExecutionMode();
2111 2112 2113
}

void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) {
2114
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
2115 2116
  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
                                       isolate_);
2117 2118 2119 2120 2121
  DebugEvaluate::ApplySideEffectChecks(debug_bytecode);
  debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects);
}

void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
2122
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
2123 2124 2125
  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
                                       isolate_);
  Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray(), isolate_);
2126 2127 2128 2129 2130 2131 2132
  for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done();
       it.Advance()) {
    debug_bytecode->set(it.current_offset(),
                        original->get(it.current_offset()));
  }
}

2133 2134
bool Debug::PerformSideEffectCheck(Handle<JSFunction> function,
                                   Handle<Object> receiver) {
2135
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2136
  DisallowJavascriptExecution no_js(isolate_);
2137 2138 2139 2140
  if (!function->is_compiled() &&
      !Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
    return false;
  }
2141 2142 2143 2144
  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
  DebugInfo::SideEffectState side_effect_state =
      debug_info->GetSideEffectState(isolate_);
2145
  switch (side_effect_state) {
2146
    case DebugInfo::kHasSideEffects:
2147 2148 2149 2150 2151 2152 2153 2154
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] Function %s failed side effect check.\n",
               function->shared()->DebugName()->ToCString().get());
      }
      side_effect_check_failed_ = true;
      // Throw an uncatchable termination exception.
      isolate_->TerminateExecution();
      return false;
2155
    case DebugInfo::kRequiresRuntimeChecks: {
2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
      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());
      if (shared->GetCode() ==
          isolate_->builtins()->builtin(Builtins::kDeserializeLazy)) {
        Snapshot::EnsureBuiltinIsDeserialized(isolate_, shared);
      }
      PrepareFunctionForDebugExecution(shared);
2167
      ApplySideEffectChecks(debug_info);
2168
      return true;
2169
    }
2170
    case DebugInfo::kHasNoSideEffect:
2171
      return true;
2172
    case DebugInfo::kNotComputed:
2173 2174
      UNREACHABLE();
      return false;
2175
  }
2176 2177
  UNREACHABLE();
  return false;
2178 2179
}

2180 2181 2182 2183
Handle<Object> Debug::return_value_handle() {
  return handle(thread_local_.return_value_, isolate_);
}

2184 2185 2186 2187
bool Debug::PerformSideEffectCheckForCallback(
    Handle<Object> callback_info, Handle<Object> receiver,
    Debug::AccessorKind accessor_kind) {
  DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo());
2188
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2189
  if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() &&
2190
      i::CallHandlerInfo::cast(*callback_info)->NextCallHasNoSideEffect()) {
2191 2192
    return true;
  }
2193
  // TODO(7515): always pass a valid callback info object.
2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232
  if (!callback_info.is_null()) {
    if (callback_info->IsAccessorInfo()) {
      // List of whitelisted internal accessors can be found in accessors.h.
      AccessorInfo* info = AccessorInfo::cast(*callback_info);
      DCHECK_NE(kNotAccessor, accessor_kind);
      switch (accessor_kind == kSetter ? info->setter_side_effect_type()
                                       : info->getter_side_effect_type()) {
        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 '");
        info->name()->ShortPrint();
        PrintF("' may cause side effect.\n");
      }
    } else if (callback_info->IsInterceptorInfo()) {
      InterceptorInfo* info = InterceptorInfo::cast(*callback_info);
      if (info->has_no_side_effect()) return true;
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
      }
    } else if (callback_info->IsCallHandlerInfo()) {
      CallHandlerInfo* info = CallHandlerInfo::cast(*callback_info);
      if (info->IsSideEffectFreeCallHandlerInfo()) return true;
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
      }
    }
2233
  }
2234 2235 2236 2237 2238 2239 2240
  side_effect_check_failed_ = true;
  // Throw an uncatchable termination exception.
  isolate_->TerminateExecution();
  isolate_->OptionalRescheduleException(false);
  return false;
}

2241 2242 2243
bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
  using interpreter::Bytecode;

2244
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2245
  SharedFunctionInfo* shared = frame->function()->shared();
2246
  BytecodeArray* bytecode_array = shared->GetBytecodeArray();
2247
  int offset = frame->GetBytecodeOffset();
2248 2249
  interpreter::BytecodeArrayAccessor bytecode_accessor(
      handle(bytecode_array, isolate_), offset);
2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262

  Bytecode bytecode = bytecode_accessor.current_bytecode();
  interpreter::Register reg;
  switch (bytecode) {
    case Bytecode::kStaCurrentContextSlot:
      reg = interpreter::Register::current_context();
      break;
    default:
      reg = bytecode_accessor.GetRegisterOperand(0);
      break;
  }
  Handle<Object> object =
      handle(frame->ReadInterpreterRegister(reg.index()), isolate_);
2263 2264 2265 2266 2267 2268
  return PerformSideEffectCheckForObject(object);
}

bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);

2269 2270 2271 2272 2273 2274
  // 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;
2275
  }
2276

2277
  if (FLAG_trace_side_effect_free_debug_evaluate) {
2278
    PrintF("[debug-evaluate] failed runtime side effect check.\n");
2279 2280 2281 2282 2283 2284
  }
  side_effect_check_failed_ = true;
  // Throw an uncatchable termination exception.
  isolate_->TerminateExecution();
  return false;
}
2285 2286
}  // namespace internal
}  // namespace v8