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

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

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

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

43 44
namespace v8 {
namespace internal {
45

46 47 48
class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker {
 public:
  TemporaryObjectsTracker() = default;
49
  ~TemporaryObjectsTracker() override = default;
50 51 52 53 54

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

  void MoveEvent(Address from, Address to, int) override {
    if (from == to) return;
55
    base::MutexGuard guard(&mutex_);
56 57 58 59 60 61 62 63 64 65 66 67
    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);
  }

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

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

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

101 102
Debug::~Debug() { DCHECK_NULL(debug_delegate_); }

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

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

137
JSGeneratorObject BreakLocation::GetGeneratorObjectForSuspendedFrame(
138 139 140 141
    JavaScriptFrame* frame) const {
  DCHECK(IsSuspend());
  DCHECK_GE(generator_obj_reg_index_, 0);

142 143
  Object generator_obj = InterpretedFrame::cast(frame)->ReadInterpreterRegister(
      generator_obj_reg_index_);
144 145 146 147

  return JSGeneratorObject::cast(generator_obj);
}

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

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

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

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

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

213
int BreakIterator::BreakIndexFromPosition(int source_position) {
214 215 216
  int distance = kMaxInt;
  int closest_break = break_index();
  while (!Done()) {
217
    int next_position = position();
218 219 220 221 222 223 224 225 226 227 228 229
    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;
}

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

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

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

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

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

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

282
void BreakIterator::SetDebugBreak() {
283 284
  DebugBreakType debug_break_type = GetDebugBreakType();
  if (debug_break_type == DEBUGGER_STATEMENT) return;
285
  HandleScope scope(isolate());
286
  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
287 288 289 290
  Handle<BytecodeArray> bytecode_array(debug_info_->DebugBytecodeArray(),
                                       isolate());
  interpreter::BytecodeArrayAccessor(bytecode_array, code_offset())
      .ApplyDebugBreak();
291 292
}

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

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

    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);
324 325
}

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

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


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


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

char* Debug::RestoreDebug(char* storage) {
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
  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_);
  }

379 380 381
  return storage + ArchiveSpacePerThread();
}

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

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

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

401
DebugInfoListNode::~DebugInfoListNode() {
dcarney's avatar
dcarney committed
402
  if (debug_info_ == nullptr) return;
403
  GlobalHandles::Destroy(debug_info_);
dcarney's avatar
dcarney committed
404
  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 545
  if (!function->shared().HasBreakInfo()) return false;
  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<SharedFunctionInfo> shared,
593
                          Handle<BreakPoint> break_point,
594
                          int* source_position) {
595
  HandleScope scope(isolate_);
596

597
  // Make sure the function is compiled and has set up the debug info.
598
  if (!EnsureBreakInfo(shared)) return false;
599 600
  PrepareFunctionForDebugExecution(shared);

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

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

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

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

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

631 632
  HandleScope scope(isolate_);

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

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

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

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

651 652 653 654 655 656 657
  // 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;

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

  ClearBreakPoints(debug_info);
  ApplyBreakPoints(debug_info);
664

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

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

681 682
void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
  DisallowHeapAllocation no_gc;
683 684 685
  if (debug_info->CanBreakAtEntry()) {
    debug_info->SetBreakAtEntry();
  } else {
686
    if (!debug_info->HasInstrumentedBytecodeArray()) return;
687
    FixedArray break_points = debug_info->break_points();
688 689 690 691
    for (int i = 0; i < break_points.length(); i++) {
      if (break_points.get(i).IsUndefined(isolate_)) continue;
      BreakPointInfo info = BreakPointInfo::cast(break_points.get(i));
      if (info.GetBreakPointCount(isolate_) == 0) continue;
692
      DCHECK(debug_info->HasInstrumentedBytecodeArray());
693
      BreakIterator it(debug_info);
694
      it.SkipToPosition(info.source_position());
695 696
      it.SetDebugBreak();
    }
697
  }
698
  debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints);
699 700
}

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

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

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

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

741 742 743 744 745 746 747 748 749 750 751
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;
}

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

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

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

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

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

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


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

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

823
  Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
824 825 826 827 828
  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) {
829
    Handle<Object> break_point(array->get(i), isolate_);
830 831
    if (CheckBreakPoint(Handle<BreakPoint>::cast(break_point),
                        is_break_at_entry)) {
832
      break_points_hit->set(break_points_hit_count++, *break_point);
833 834 835
    }
  }
  if (break_points_hit_count == 0) return {};
836
  break_points_hit->Shrink(isolate_, break_points_hit_count);
837 838
  return break_points_hit;
}
839

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

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

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

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

  ClearOneShot();

890 891
  int current_frame_count = CurrentFrameCount();

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

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

936 937 938 939 940 941 942 943
      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(
944
            summary.AsJavaScript().function()->shared(), isolate_);
945
        if (IsBlackboxed(info)) continue;
946 947 948 949
        FloodWithOneShot(info);
        return;
      }
    }
950 951 952 953
  }
}


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

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

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

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

969 970
  thread_local_.last_step_action_ = step_action;

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

974 975 976 977 978 979
  // 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);
980
    wasm_frame->debug_info().PrepareStep(step_action);
981 982 983 984
    return;
  }

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

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

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

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

998 999 1000
  // 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)) {
1001 1002 1003 1004 1005 1006 1007 1008
    // 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;
  }
1009 1010

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

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

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

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

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

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

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

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

1121

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

1137 1138 1139
void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
  // Deoptimize all code compiled from this shared function info including
  // inlining.
1140
  isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock);
1141

1142 1143 1144
  // TODO(mlippautz): Try to remove this call.
  isolate_->heap()->PreciseCollectAllGarbage(
      Heap::kNoGCFlags, GarbageCollectionReason::kDebugger);
1145

1146 1147
  bool found_something = false;
  Code::OptimizedCodeIterator iterator(isolate_);
1148 1149 1150
  do {
    Code code = iterator.Next();
    if (code.is_null()) break;
1151 1152
    if (code.Inlines(*shared)) {
      code.set_marked_for_deoptimization(true);
1153
      found_something = true;
1154
    }
1155
  } while (true);
1156

1157 1158 1159 1160 1161 1162
  if (found_something) {
    // Only go through with the deoptimization if something was found.
    Deoptimizer::DeoptimizeMarkedCode(isolate_);
  }
}

1163 1164
void Debug::PrepareFunctionForDebugExecution(
    Handle<SharedFunctionInfo> shared) {
1165 1166 1167 1168 1169 1170
  // 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);
1171 1172 1173
  if (debug_info->flags() & DebugInfo::kPreparedForDebugExecution) return;

  // Make a copy of the bytecode array if available.
1174
  Handle<Object> maybe_original_bytecode_array =
1175 1176
      isolate_->factory()->undefined_value();
  if (shared->HasBytecodeArray()) {
1177 1178 1179 1180
    Handle<BytecodeArray> original_bytecode_array =
        handle(shared->GetBytecodeArray(), isolate_);
    Handle<BytecodeArray> debug_bytecode_array =
        isolate_->factory()->CopyBytecodeArray(original_bytecode_array);
1181
    debug_info->set_debug_bytecode_array(*debug_bytecode_array);
1182 1183
    shared->SetDebugBytecodeArray(*debug_bytecode_array);
    maybe_original_bytecode_array = original_bytecode_array;
1184
  }
1185
  debug_info->set_original_bytecode_array(*maybe_original_bytecode_array);
1186

1187 1188 1189
  if (debug_info->CanBreakAtEntry()) {
    // Deopt everything in case the function is inlined anywhere.
    Deoptimizer::DeoptimizeAll(isolate_);
1190
    InstallDebugBreakTrampoline();
1191 1192 1193
  } else {
    DeoptimizeFunction(shared);
    // Update PCs on the stack to point to recompiled code.
1194 1195
    RedirectActiveFunctions redirect_visitor(
        *shared, RedirectActiveFunctions::Mode::kUseDebugBytecode);
1196 1197 1198
    redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
    isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
  }
1199
  debug_info->set_flags(debug_info->flags() |
1200
                        DebugInfo::kPreparedForDebugExecution);
1201 1202
}

1203 1204 1205 1206 1207
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_);
1208 1209 1210 1211
  // 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;
1212 1213 1214
  for (DebugInfoListNode* current = debug_info_list_; current != nullptr;
       current = current->next()) {
    if (current->debug_info()->CanBreakAtEntry()) {
1215
      needs_to_use_trampoline = true;
1216
      if (current->debug_info()->shared().IsApiFunction()) {
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
        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;
1227
  std::vector<Handle<AccessorPair>> needs_instantiate;
1228
  {
1229
    HeapObjectIterator iterator(isolate_->heap());
1230 1231
    for (HeapObject obj = iterator.Next(); !obj.is_null();
         obj = iterator.Next()) {
1232 1233
      if (needs_to_clear_ic && obj.IsFeedbackVector()) {
        FeedbackVector::cast(obj).ClearSlots(isolate_);
1234
        continue;
1235
      } else if (obj.IsJSFunction()) {
1236
        JSFunction fun = JSFunction::cast(obj);
1237 1238 1239 1240
        SharedFunctionInfo shared = fun.shared();
        if (!shared.HasDebugInfo()) continue;
        if (!shared.GetDebugInfo().CanBreakAtEntry()) continue;
        if (!fun.is_compiled()) {
1241
          needs_compile.push_back(handle(fun, isolate_));
1242
        } else {
1243
          fun.set_code(*trampoline);
1244
        }
1245
      } else if (obj.IsAccessorPair()) {
1246
        AccessorPair accessor_pair = AccessorPair::cast(obj);
1247 1248
        if (accessor_pair.getter().IsFunctionTemplateInfo() ||
            accessor_pair.setter().IsFunctionTemplateInfo()) {
1249 1250
          needs_instantiate.push_back(handle(accessor_pair, isolate_));
        }
1251 1252 1253
      }
    }
  }
1254 1255 1256 1257

  // Forcibly instantiate all lazy accessor pairs to make sure that they
  // properly hit the debug break trampoline.
  for (Handle<AccessorPair> accessor_pair : needs_instantiate) {
1258
    if (accessor_pair->getter().IsFunctionTemplateInfo()) {
1259 1260 1261 1262 1263 1264 1265
      Handle<JSFunction> fun =
          ApiNatives::InstantiateFunction(
              handle(FunctionTemplateInfo::cast(accessor_pair->getter()),
                     isolate_))
              .ToHandleChecked();
      accessor_pair->set_getter(*fun);
    }
1266
    if (accessor_pair->setter().IsFunctionTemplateInfo()) {
1267 1268 1269 1270 1271 1272 1273 1274 1275
      Handle<JSFunction> fun =
          ApiNatives::InstantiateFunction(
              handle(FunctionTemplateInfo::cast(accessor_pair->setter()),
                     isolate_))
              .ToHandleChecked();
      accessor_pair->set_setter(*fun);
    }
  }

1276 1277
  // By overwriting the function code with DebugBreakTrampoline, which tailcalls
  // to shared code, we bypass CompileLazy. Perform CompileLazy here instead.
1278
  for (Handle<JSFunction> fun : needs_compile) {
1279 1280 1281
    IsCompiledScope is_compiled_scope;
    Compiler::Compile(fun, Compiler::CLEAR_EXCEPTION, &is_compiled_scope);
    DCHECK(is_compiled_scope.is_compiled());
1282 1283 1284 1285
    fun->set_code(*trampoline);
  }
}

1286 1287 1288
namespace {
template <typename Iterator>
void GetBreakablePositions(Iterator* it, int start_position, int end_position,
1289
                           std::vector<BreakLocation>* locations) {
1290 1291 1292 1293
  while (!it->Done()) {
    if (it->position() >= start_position && it->position() < end_position) {
      locations->push_back(it->GetBreakLocation());
    }
1294 1295 1296 1297 1298
    it->Next();
  }
}

void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
1299 1300
                            int end_position,
                            std::vector<BreakLocation>* locations) {
1301
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
1302 1303
  BreakIterator it(debug_info);
  GetBreakablePositions(&it, start_position, end_position, locations);
1304 1305 1306 1307
}
}  // namespace

bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
1308
                                   int end_position, bool restrict_to_function,
1309
                                   std::vector<BreakLocation>* locations) {
1310 1311 1312 1313 1314 1315 1316 1317
  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);
1318
    if (!EnsureBreakInfo(shared)) return false;
1319
    PrepareFunctionForDebugExecution(shared);
1320

1321
    Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1322
    FindBreakablePositions(debug_info, start_position, end_position, locations);
1323 1324 1325
    return true;
  }

1326 1327
  while (true) {
    HandleScope scope(isolate_);
1328
    std::vector<Handle<SharedFunctionInfo>> candidates;
1329
    std::vector<IsCompiledScope> compiled_scopes;
1330
    SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
1331
    for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
1332
         info = iterator.Next()) {
1333 1334
      if (info.EndPosition() < start_position ||
          info.StartPosition() >= end_position) {
1335
        continue;
1336
      }
1337 1338
      if (!info.IsSubjectToDebugging()) continue;
      if (!info.is_compiled() && !info.allows_lazy_compilation()) continue;
1339
      candidates.push_back(i::handle(info, isolate_));
1340 1341 1342
    }

    bool was_compiled = false;
1343
    for (const auto& candidate : candidates) {
1344 1345
      IsCompiledScope is_compiled_scope(candidate->is_compiled_scope());
      if (!is_compiled_scope.is_compiled()) {
1346 1347
        // Code that cannot be compiled lazily are internal and not debuggable.
        DCHECK(candidate->allows_lazy_compilation());
1348 1349
        if (!Compiler::Compile(candidate, Compiler::CLEAR_EXCEPTION,
                               &is_compiled_scope)) {
1350 1351 1352 1353 1354
          return false;
        } else {
          was_compiled = true;
        }
      }
1355 1356
      DCHECK(is_compiled_scope.is_compiled());
      compiled_scopes.push_back(is_compiled_scope);
1357
      if (!EnsureBreakInfo(candidate)) return false;
1358
      PrepareFunctionForDebugExecution(candidate);
1359 1360 1361
    }
    if (was_compiled) continue;

1362 1363
    for (const auto& candidate : candidates) {
      CHECK(candidate->HasBreakInfo());
1364
      Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
1365
      FindBreakablePositions(debug_info, start_position, end_position,
1366
                             locations);
1367 1368 1369 1370 1371 1372
    }
    return true;
  }
  UNREACHABLE();
}

1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
MaybeHandle<JSArray> Debug::GetPrivateFields(Handle<JSReceiver> receiver) {
  Factory* factory = isolate_->factory();

  Handle<FixedArray> internal_fields;
  ASSIGN_RETURN_ON_EXCEPTION(isolate_, internal_fields,
                             JSReceiver::GetPrivateEntries(isolate_, receiver),
                             JSArray);

  int nof_internal_fields = internal_fields->length();
  if (nof_internal_fields == 0) {
    return factory->NewJSArray(0);
  }

  return factory->NewJSArrayWithElements(internal_fields);
}

1389 1390 1391
class SharedFunctionInfoFinder {
 public:
  explicit SharedFunctionInfoFinder(int target_position)
1392
      : current_start_position_(kNoSourcePosition),
1393 1394
        target_position_(target_position) {}

1395 1396
  void NewCandidate(SharedFunctionInfo shared,
                    JSFunction closure = JSFunction()) {
1397 1398
    if (!shared.IsSubjectToDebugging()) return;
    int start_position = shared.function_token_position();
yangguo's avatar
yangguo committed
1399
    if (start_position == kNoSourcePosition) {
1400
      start_position = shared.StartPosition();
1401
    }
1402

1403
    if (start_position > target_position_) return;
1404
    if (target_position_ > shared.EndPosition()) return;
1405

1406
    if (!current_candidate_.is_null()) {
1407
      if (current_start_position_ == start_position &&
1408
          shared.EndPosition() == current_candidate_.EndPosition()) {
1409
        // If we already have a matching closure, do not throw it away.
1410
        if (!current_candidate_closure_.is_null() && closure.is_null()) return;
1411 1412 1413
        // 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.
1414
        if (!current_candidate_.is_toplevel() && shared.is_toplevel()) return;
1415
      } else if (start_position < current_start_position_ ||
1416
                 current_candidate_.EndPosition() < shared.EndPosition()) {
1417 1418 1419
        return;
      }
    }
1420

1421 1422 1423 1424 1425
    current_start_position_ = start_position;
    current_candidate_ = shared;
    current_candidate_closure_ = closure;
  }

1426
  SharedFunctionInfo Result() { return current_candidate_; }
1427

1428
  JSFunction ResultClosure() { return current_candidate_closure_; }
1429 1430

 private:
1431
  SharedFunctionInfo current_candidate_;
1432
  JSFunction current_candidate_closure_;
1433 1434 1435 1436
  int current_start_position_;
  int target_position_;
  DisallowHeapAllocation no_gc_;
};
1437

1438

1439 1440 1441 1442 1443 1444
// 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.)
1445 1446
Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
                                                     int position) {
1447
  for (int iteration = 0;; iteration++) {
1448 1449
    // Go through all shared function infos associated with this script to
    // find the inner most function containing this position.
1450 1451
    // If there is no shared function info for this script at all, there is
    // no point in looking for it by walking the heap.
1452

1453
    SharedFunctionInfo shared;
1454
    IsCompiledScope is_compiled_scope;
1455 1456
    {
      SharedFunctionInfoFinder finder(position);
1457
      SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
1458
      for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
1459 1460
           info = iterator.Next()) {
        finder.NewCandidate(info);
1461 1462
      }
      shared = finder.Result();
1463
      if (shared.is_null()) break;
1464
      // We found it if it's already compiled.
1465
      is_compiled_scope = shared.is_compiled_scope();
1466
      if (is_compiled_scope.is_compiled()) {
1467
        Handle<SharedFunctionInfo> shared_handle(shared, isolate_);
1468 1469 1470
        // 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
1471
        // info while bypassing PrepareFunctionForDebugExecution.
1472 1473
        if (iteration > 1) {
          AllowHeapAllocation allow_before_return;
1474
          CreateBreakInfo(shared_handle);
1475 1476 1477
        }
        return shared_handle;
      }
1478
    }
1479
    // If not, compile to reveal inner functions.
1480
    HandleScope scope(isolate_);
1481
    // Code that cannot be compiled lazily are internal and not debuggable.
1482
    DCHECK(shared.allows_lazy_compilation());
1483 1484
    if (!Compiler::Compile(handle(shared, isolate_), Compiler::CLEAR_EXCEPTION,
                           &is_compiled_scope)) {
1485
      break;
1486
    }
1487 1488
  }
  return isolate_->factory()->undefined_value();
1489 1490 1491
}


1492
// Ensures the debug information is present for shared.
1493 1494 1495
bool Debug::EnsureBreakInfo(Handle<SharedFunctionInfo> shared) {
  // Return if we already have the break info for shared.
  if (shared->HasBreakInfo()) return true;
1496 1497 1498
  if (!shared->IsSubjectToDebugging() && !CanBreakAtEntry(shared)) {
    return false;
  }
1499 1500 1501 1502
  IsCompiledScope is_compiled_scope = shared->is_compiled_scope();
  if (!is_compiled_scope.is_compiled() &&
      !Compiler::Compile(shared, Compiler::CLEAR_EXCEPTION,
                         &is_compiled_scope)) {
1503 1504
    return false;
  }
1505
  CreateBreakInfo(shared);
1506 1507 1508
  return true;
}

1509 1510 1511
void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) {
  HandleScope scope(isolate_);
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
1512

1513
  // Initialize with break information.
1514

1515 1516 1517 1518 1519 1520
  DCHECK(!debug_info->HasBreakInfo());

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

1521 1522 1523 1524
  int flags = debug_info->flags();
  flags |= DebugInfo::kHasBreakInfo;
  if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry;
  debug_info->set_flags(flags);
1525
  debug_info->set_break_points(*break_points);
1526 1527

  SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared);
1528 1529 1530 1531
}

Handle<DebugInfo> Debug::GetOrCreateDebugInfo(
    Handle<SharedFunctionInfo> shared) {
1532
  if (shared->HasDebugInfo()) return handle(shared->GetDebugInfo(), isolate_);
1533 1534 1535

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

1540 1541
  return debug_info;
}
1542

1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
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() {
1556 1557 1558 1559 1560 1561 1562
  ClearAllDebugInfos(
      [=](Handle<DebugInfo> info) { info->ClearCoverageInfo(isolate_); });
}

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

1565 1566
void Debug::FindDebugInfo(Handle<DebugInfo> debug_info,
                          DebugInfoListNode** prev, DebugInfoListNode** curr) {
1567
  HandleScope scope(isolate_);
1568 1569 1570 1571 1572 1573
  *prev = nullptr;
  *curr = debug_info_list_;
  while (*curr != nullptr) {
    if ((*curr)->debug_info().is_identical_to(debug_info)) return;
    *prev = *curr;
    *curr = (*curr)->next();
1574
  }
1575 1576 1577 1578

  UNREACHABLE();
}

1579
void Debug::ClearAllDebugInfos(const DebugInfoClearFunction& clear_function) {
1580 1581 1582 1583 1584
  DebugInfoListNode* prev = nullptr;
  DebugInfoListNode* current = debug_info_list_;
  while (current != nullptr) {
    DebugInfoListNode* next = current->next();
    Handle<DebugInfo> debug_info = current->debug_info();
1585 1586
    clear_function(debug_info);
    if (debug_info->IsEmpty()) {
1587 1588 1589 1590 1591 1592 1593 1594 1595
      FreeDebugInfoListNode(prev, current);
      current = next;
    } else {
      prev = current;
      current = next;
    }
  }
}

1596
void Debug::RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info) {
1597 1598
  debug_info->ClearBreakInfo(isolate_);
  if (debug_info->IsEmpty()) {
1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609
    DebugInfoListNode* prev;
    DebugInfoListNode* node;
    FindDebugInfo(debug_info, &prev, &node);
    FreeDebugInfoListNode(prev, node);
  }
}

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

1610
  // Unlink from list. If prev is nullptr we are looking at the first element.
1611 1612 1613 1614 1615 1616
  if (prev == nullptr) {
    debug_info_list_ = node->next();
  } else {
    prev->set_next(node->next());
  }

1617 1618
  // Pack script back into the
  // SFI::script_or_debug_info field.
1619
  Handle<DebugInfo> debug_info(node->debug_info());
1620
  debug_info->shared().set_script_or_debug_info(debug_info->script());
1621 1622 1623 1624

  delete node;
}

1625
bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
1626
  HandleScope scope(isolate_);
1627

1628
  // Get the executing function in which the debug break occurred.
1629
  Handle<SharedFunctionInfo> shared(frame->function().shared(), isolate_);
1630

1631
  // With no debug info there are no break points, so we can't be at a return.
1632
  if (!shared->HasBreakInfo()) return false;
1633 1634

  DCHECK(!frame->is_optimized());
1635
  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
1636
  BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
1637
  return location.IsReturn();
1638 1639
}

1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
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_);
1652
  thread_local_.break_frame_id_ = StackFrameId::NO_ID;
1653 1654 1655 1656 1657
  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;
    }
1658
  }
1659 1660
}

1661
Handle<FixedArray> Debug::GetLoadedScripts() {
1662
  isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
1663
                                      GarbageCollectionReason::kDebugger);
1664
  Factory* factory = isolate_->factory();
1665
  if (!factory->script_list()->IsWeakArrayList()) {
1666 1667
    return factory->empty_fixed_array();
  }
1668 1669 1670
  Handle<WeakArrayList> array =
      Handle<WeakArrayList>::cast(factory->script_list());
  Handle<FixedArray> results = factory->NewFixedArray(array->length());
1671
  int length = 0;
1672 1673
  {
    Script::Iterator iterator(isolate_);
1674 1675
    for (Script script = iterator.Next(); !script.is_null();
         script = iterator.Next()) {
1676
      if (script.HasValidSource()) results->set(length++, script);
1677 1678
    }
  }
1679
  return FixedArray::ShrinkOrEmpty(isolate_, results, length);
1680 1681
}

1682
void Debug::OnThrow(Handle<Object> exception) {
1683
  if (in_debug_scope() || ignore_events()) return;
1684 1685
  // Temporarily clear any scheduled_exception to allow evaluating
  // JavaScript from the debug event handler.
1686
  HandleScope scope(isolate_);
1687 1688 1689 1690 1691
  Handle<Object> scheduled_exception;
  if (isolate_->has_scheduled_exception()) {
    scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
    isolate_->clear_scheduled_exception();
  }
1692 1693 1694 1695
  Handle<Object> maybe_promise = isolate_->GetPromiseOnStackOnThrow();
  OnException(exception, maybe_promise,
              maybe_promise->IsJSPromise() ? v8::debug::kPromiseRejection
                                           : v8::debug::kException);
1696 1697 1698
  if (!scheduled_exception.is_null()) {
    isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
  }
1699
  PrepareStepOnThrow();
1700 1701
}

1702
void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
1703
  if (in_debug_scope() || ignore_events()) return;
1704
  HandleScope scope(isolate_);
1705 1706
  // Check whether the promise has been marked as having triggered a message.
  Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
1707 1708 1709
  if (!promise->IsJSObject() ||
      JSReceiver::GetDataProperty(Handle<JSObject>::cast(promise), key)
          ->IsUndefined(isolate_)) {
1710
    OnException(value, promise, v8::debug::kPromiseRejection);
1711 1712 1713
  }
}

1714 1715 1716
bool Debug::IsExceptionBlackboxed(bool uncaught) {
  // Uncaught exception is blackboxed if all current frames are blackboxed,
  // caught exception if top frame is blackboxed.
1717 1718 1719 1720
  StackTraceFrameIterator it(isolate_);
  while (!it.done() && it.is_wasm()) it.Advance();
  bool is_top_frame_blackboxed =
      !it.done() ? IsFrameBlackboxed(it.javascript_frame()) : true;
1721
  if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
1722
  return AllFramesOnStackAreBlackboxed();
1723 1724 1725 1726
}

bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
  HandleScope scope(isolate_);
1727
  std::vector<Handle<SharedFunctionInfo>> infos;
1728
  frame->GetFunctions(&infos);
1729
  for (const auto& info : infos) {
1730
    if (!IsBlackboxed(info)) return false;
1731
  }
1732 1733 1734
  return true;
}

1735 1736
void Debug::OnException(Handle<Object> exception, Handle<Object> promise,
                        v8::debug::ExceptionType exception_type) {
1737
  // TODO(kozyatinskiy): regress-662674.js test fails on arm without this.
1738 1739
  if (!AllowJavascriptExecution::IsAllowed(isolate_)) return;

1740
  Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
1741 1742 1743 1744

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

1745
  bool uncaught = catch_type == Isolate::NOT_CAUGHT;
1746
  if (promise->IsJSObject()) {
1747 1748 1749
    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();
1750
    Object::SetProperty(isolate_, jspromise, key, key, StoreOrigin::kMaybeKeyed,
1751
                        Just(ShouldThrow::kThrowOnError))
1752
        .Assert();
1753
    // Check whether the promise reject is considered an uncaught exception.
1754
    uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
1755
  }
1756 1757 1758

  if (!debug_delegate_) return;

1759 1760 1761
  // Bail out if exception breaks are not active
  if (uncaught) {
    // Uncaught exceptions are reported by either flags.
1762
    if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
1763 1764
  } else {
    // Caught exceptions are reported is activated.
1765
    if (!break_on_exception_) return;
1766 1767
  }

1768 1769
  {
    JavaScriptFrameIterator it(isolate_);
1770
    // Check whether the top frame is blackboxed or the break location is muted.
1771 1772
    if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) ||
                       IsExceptionBlackboxed(uncaught))) {
1773 1774
      return;
    }
1775
    if (it.done()) return;  // Do not trigger an event with an empty stack.
1776 1777
  }

1778
  DebugScope debug_scope(this);
1779 1780
  HandleScope scope(isolate_);
  DisableBreak no_recursive_break(this);
1781

1782
  Handle<Context> native_context(isolate_->native_context());
1783 1784 1785
  debug_delegate_->ExceptionThrown(
      v8::Utils::ToLocal(native_context), v8::Utils::ToLocal(exception),
      v8::Utils::ToLocal(promise), uncaught, exception_type);
1786 1787
}

1788 1789
void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit) {
  DCHECK(!break_points_hit.is_null());
1790 1791
  // The caller provided for DebugScope.
  AssertDebugContext();
1792
  // Bail out if there is no listener for this event
1793
  if (ignore_events()) return;
1794

1795 1796 1797 1798
#ifdef DEBUG
  PrintBreakLocation();
#endif  // DEBUG

1799
  if (!debug_delegate_) return;
1800
  DCHECK(in_debug_scope());
1801
  HandleScope scope(isolate_);
1802 1803
  DisableBreak no_recursive_break(this);

1804 1805 1806 1807
  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) {
1808
    BreakPoint break_point = BreakPoint::cast(break_points_hit->get(i));
1809
    inspector_break_points_hit.push_back(break_point.id());
1810
    ++inspector_break_points_count;
1811 1812
  }

1813 1814 1815
  Handle<Context> native_context(isolate_->native_context());
  debug_delegate_->BreakProgramRequested(v8::Utils::ToLocal(native_context),
                                         inspector_break_points_hit);
1816 1817
}

1818 1819 1820 1821
namespace {
debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
  Script::PositionInfo info;
  Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
1822 1823 1824 1825 1826 1827 1828
  // 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));
1829 1830 1831 1832
}
}  // namespace

bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
1833
  if (!debug_delegate_) return !shared->IsSubjectToDebugging();
1834 1835
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
  if (!debug_info->computed_debug_is_blackboxed()) {
1836
    bool is_blackboxed =
1837
        !shared->IsSubjectToDebugging() || !shared->script().IsScript();
1838
    if (!is_blackboxed) {
1839
      SuppressDebug while_processing(this);
1840
      HandleScope handle_scope(isolate_);
1841 1842
      PostponeInterruptsScope no_interrupts(isolate_);
      DisableBreak no_recursive_break(this);
1843
      DCHECK(shared->script().IsScript());
1844
      Handle<Script> script(Script::cast(shared->script()), isolate_);
1845
      DCHECK(script->IsUserJavaScript());
1846 1847
      debug::Location start = GetDebugLocation(script, shared->StartPosition());
      debug::Location end = GetDebugLocation(script, shared->EndPosition());
1848 1849
      is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
          ToApiHandle<debug::Script>(script), start, end);
1850
    }
1851 1852
    debug_info->set_debug_is_blackboxed(is_blackboxed);
    debug_info->set_computed_debug_is_blackboxed(true);
1853
  }
1854
  return debug_info->debug_is_blackboxed();
1855 1856
}

1857 1858 1859
bool Debug::AllFramesOnStackAreBlackboxed() {
  HandleScope scope(isolate_);
  for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
1860
    if (!it.is_javascript()) continue;
1861 1862 1863 1864 1865
    if (!IsFrameBlackboxed(it.javascript_frame())) return false;
  }
  return true;
}

1866 1867
bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
  // Allow break at entry for builtin functions.
1868
  if (shared->native() || shared->IsApiFunction()) {
1869 1870 1871 1872 1873 1874 1875
    // Functions that are subject to debugging can have regular breakpoints.
    DCHECK(!shared->IsSubjectToDebugging());
    return true;
  }
  return false;
}

1876
bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
1877
                            bool preview, debug::LiveEditResult* result) {
1878
  DebugScope debug_scope(this);
1879
  running_live_edit_ = true;
1880
  LiveEdit::PatchScript(isolate_, script, source, preview, result);
1881
  running_live_edit_ = false;
1882
  return result->status == debug::LiveEditResult::OK;
1883 1884
}

1885
void Debug::OnCompileError(Handle<Script> script) {
1886
  ProcessCompileEvent(true, script);
1887 1888 1889
}

void Debug::OnAfterCompile(Handle<Script> script) {
1890
  ProcessCompileEvent(false, script);
1891 1892
}

1893
void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
1894 1895 1896
  // TODO(kozyatinskiy): teach devtools to work with liveedit scripts better
  // first and then remove this fast return.
  if (running_live_edit_) return;
1897 1898
  // Attach the correct debug id to the script. The debug id is used by the
  // inspector to filter scripts by native context.
1899
  script->set_context_data(isolate_->native_context()->debug_context_id());
1900
  if (ignore_events()) return;
1901 1902 1903
  if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) {
    return;
  }
1904
  if (!debug_delegate_) return;
1905
  SuppressDebug while_processing(this);
1906
  DebugScope debug_scope(this);
1907
  HandleScope scope(isolate_);
1908
  DisableBreak no_recursive_break(this);
1909
  AllowJavascriptExecution allow_script(isolate_);
1910
  debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
1911
                                  running_live_edit_, has_compile_error);
1912 1913
}

1914 1915
int Debug::CurrentFrameCount() {
  StackTraceFrameIterator it(isolate_);
1916
  if (break_frame_id() != StackFrameId::NO_ID) {
1917 1918 1919 1920 1921 1922 1923
    // 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()) {
1924
      std::vector<SharedFunctionInfo> infos;
1925
      OptimizedFrame::cast(it.frame())->GetFunctions(&infos);
1926
      counter += infos.size();
1927 1928 1929 1930 1931 1932 1933 1934
    } else {
      counter++;
    }
    it.Advance();
  }
  return counter;
}

1935
void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
1936
  debug_delegate_ = delegate;
1937 1938 1939
  UpdateState();
}

1940
void Debug::UpdateState() {
1941
  bool is_active = debug_delegate_ != nullptr;
1942 1943
  if (is_active == is_active_) return;
  if (is_active) {
1944 1945
    // Note that the debug context could have already been loaded to
    // bootstrap test cases.
1946
    isolate_->compilation_cache()->Disable();
1947 1948 1949
    is_active = true;
    feature_tracker()->Track(DebugFeatureTracker::kActive);
  } else {
1950
    isolate_->compilation_cache()->Enable();
1951
    Unload();
1952
  }
1953
  is_active_ = is_active;
1954
  isolate_->PromiseHookStateUpdated();
1955 1956
}

1957
void Debug::UpdateHookOnFunctionCall() {
1958
  STATIC_ASSERT(LastStepAction == StepIn);
1959 1960
  hook_on_function_call_ =
      thread_local_.last_step_action_ == StepIn ||
1961 1962
      isolate_->debug_execution_mode() == DebugInfo::kSideEffects ||
      thread_local_.break_on_next_function_call_;
1963 1964
}

1965
void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode) {
1966 1967
  // Initialize LiveEdit.
  LiveEdit::InitializeThreadLocal(this);
1968 1969 1970
  // Ignore debug break during bootstrapping.
  if (isolate_->bootstrapper()->IsActive()) return;
  // Just continue if breaks are disabled.
1971
  if (break_disabled()) return;
1972 1973 1974 1975 1976 1977 1978
  // Ignore debug break if debugger is not active.
  if (!is_active()) return;

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

  { JavaScriptFrameIterator it(isolate_);
1979
    DCHECK(!it.done());
1980
    Object fun = it.frame()->function();
1981
    if (fun.IsJSFunction()) {
1982
      HandleScope scope(isolate_);
1983
      Handle<JSFunction> function(JSFunction::cast(fun), isolate_);
1984
      // Don't stop in builtin and blackboxed functions.
1985
      Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
1986 1987 1988
      bool ignore_break = ignore_break_mode == kIgnoreIfTopFrameBlackboxed
                              ? IsBlackboxed(shared)
                              : AllFramesOnStackAreBlackboxed();
1989
      if (ignore_break) return;
1990 1991
      // Don't stop if the break location is muted.
      if (IsMutedAtCurrentLocation(it.frame())) return;
1992 1993 1994
    }
  }

1995 1996 1997
  // Clear stepping to avoid duplicate breaks.
  ClearStepping();

1998
  HandleScope scope(isolate_);
1999
  DebugScope debug_scope(this);
2000

2001
  OnDebugBreak(isolate_->factory()->empty_fixed_array());
2002 2003
}

2004 2005 2006 2007
#ifdef DEBUG
void Debug::PrintBreakLocation() {
  if (!FLAG_print_break_location) return;
  HandleScope scope(isolate_);
2008
  StackTraceFrameIterator iterator(isolate_);
2009
  if (iterator.done()) return;
2010
  StandardFrame* frame = iterator.frame();
2011
  FrameSummary summary = FrameSummary::GetTop(frame);
2012 2013
  int source_position = summary.SourcePosition();
  Handle<Object> script_obj = summary.script();
2014
  PrintF("[debug] break in function '");
2015
  summary.FunctionName()->PrintOn(stdout);
2016 2017 2018
  PrintF("'.\n");
  if (script_obj->IsScript()) {
    Handle<Script> script = Handle<Script>::cast(script_obj);
2019
    Handle<String> source(String::cast(script->source()), isolate_);
2020
    Script::InitLineEnds(script);
2021 2022 2023 2024
    int line =
        Script::GetLineNumber(script, source_position) - script->line_offset();
    int column = Script::GetColumnNumber(script, source_position) -
                 (line == 0 ? script->column_offset() : 0);
2025 2026
    Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()),
                                 isolate_);
jgruber's avatar
jgruber committed
2027 2028
    int line_start = line == 0 ? 0 : Smi::ToInt(line_ends->get(line - 1)) + 1;
    int line_end = Smi::ToInt(line_ends->get(line));
2029
    DisallowHeapAllocation no_gc;
2030
    String::FlatContent content = source->GetFlatContent(no_gc);
2031 2032
    if (content.IsOneByte()) {
      PrintF("[debug] %.*s\n", line_end - line_start,
2033
             content.ToOneByteVector().begin() + line_start);
2034 2035 2036 2037 2038 2039 2040 2041 2042
      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
2043

2044 2045
DebugScope::DebugScope(Debug* debug)
    : debug_(debug),
Yang Guo's avatar
Yang Guo committed
2046 2047
      prev_(reinterpret_cast<DebugScope*>(
          base::Relaxed_Load(&debug->thread_local_.current_debug_scope_))),
2048
      no_interrupts_(debug_->isolate_) {
2049
  // Link recursive debugger entry.
2050 2051
  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
                      reinterpret_cast<base::AtomicWord>(this));
2052

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

2056 2057 2058 2059 2060
  // 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_ =
2061
      has_frames ? it.frame()->id() : StackFrameId::NO_ID;
2062

2063
  debug_->UpdateState();
2064 2065 2066
}


2067
DebugScope::~DebugScope() {
2068
  // Leaving this debugger entry.
2069 2070
  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
                      reinterpret_cast<base::AtomicWord>(prev_));
2071 2072 2073

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

2075
  debug_->UpdateState();
2076 2077
}

2078 2079 2080 2081 2082 2083 2084 2085
ReturnValueScope::ReturnValueScope(Debug* debug) : debug_(debug) {
  return_value_ = debug_->return_value_handle();
}

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

2086 2087 2088 2089 2090 2091 2092 2093
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()) {
2094
      DCHECK(debug_info->shared().HasBytecodeArray());
2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106
      if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) {
        ClearSideEffectChecks(debug_info);
        ApplyBreakPoints(debug_info);
      } else {
        ClearBreakPoints(debug_info);
        ApplySideEffectChecks(debug_info);
      }
    }
    current = current->next();
  }
}

2107 2108 2109 2110 2111 2112 2113 2114 2115
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());
2116 2117
  Handle<FixedArray> array(isolate_->native_context()->regexp_last_match_info(),
                           isolate_);
2118 2119
  regexp_match_info_ =
      Handle<RegExpMatchInfo>::cast(isolate_->factory()->CopyFixedArray(array));
2120 2121 2122

  // Update debug infos to have correct execution mode.
  UpdateDebugInfosForExecutionMode();
2123 2124 2125 2126 2127 2128
}

void Debug::StopSideEffectCheckMode() {
  DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects);
  if (side_effect_check_failed_) {
    DCHECK(isolate_->has_pending_exception());
2129
    DCHECK_EQ(ReadOnlyRoots(isolate_).termination_exception(),
2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142
              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();
2143 2144
  isolate_->native_context()->set_regexp_last_match_info(*regexp_match_info_);
  regexp_match_info_ = Handle<RegExpMatchInfo>::null();
2145 2146 2147

  // Update debug infos to have correct execution mode.
  UpdateDebugInfosForExecutionMode();
2148 2149 2150
}

void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) {
2151
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
2152 2153
  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
                                       isolate_);
2154 2155 2156 2157 2158
  DebugEvaluate::ApplySideEffectChecks(debug_bytecode);
  debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects);
}

void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
2159
  DCHECK(debug_info->HasInstrumentedBytecodeArray());
2160 2161 2162
  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
                                       isolate_);
  Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray(), isolate_);
2163 2164
  for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done();
       it.Advance()) {
2165 2166
    // Restore from original. This may copy only the scaling prefix, which is
    // correct, since we patch scaling prefixes to debug breaks if exists.
2167 2168 2169 2170 2171
    debug_bytecode->set(it.current_offset(),
                        original->get(it.current_offset()));
  }
}

2172 2173
bool Debug::PerformSideEffectCheck(Handle<JSFunction> function,
                                   Handle<Object> receiver) {
2174
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2175
  DisallowJavascriptExecution no_js(isolate_);
2176
  IsCompiledScope is_compiled_scope(function->shared().is_compiled_scope());
2177
  if (!function->is_compiled() &&
2178 2179
      !Compiler::Compile(function, Compiler::KEEP_EXCEPTION,
                         &is_compiled_scope)) {
2180 2181
    return false;
  }
2182
  DCHECK(is_compiled_scope.is_compiled());
2183 2184 2185 2186
  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
  DebugInfo::SideEffectState side_effect_state =
      debug_info->GetSideEffectState(isolate_);
2187
  switch (side_effect_state) {
2188
    case DebugInfo::kHasSideEffects:
2189 2190
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] Function %s failed side effect check.\n",
2191
               function->shared().DebugName().ToCString().get());
2192 2193 2194 2195 2196
      }
      side_effect_check_failed_ = true;
      // Throw an uncatchable termination exception.
      isolate_->TerminateExecution();
      return false;
2197
    case DebugInfo::kRequiresRuntimeChecks: {
2198 2199 2200 2201 2202 2203 2204
      if (!shared->HasBytecodeArray()) {
        return PerformSideEffectCheckForObject(receiver);
      }
      // If function has bytecode array then prepare function for debug
      // execution to perform runtime side effect checks.
      DCHECK(shared->is_compiled());
      PrepareFunctionForDebugExecution(shared);
2205
      ApplySideEffectChecks(debug_info);
2206
      return true;
2207
    }
2208
    case DebugInfo::kHasNoSideEffect:
2209
      return true;
2210
    case DebugInfo::kNotComputed:
2211 2212
      UNREACHABLE();
      return false;
2213
  }
2214 2215
  UNREACHABLE();
  return false;
2216 2217
}

2218 2219 2220 2221
Handle<Object> Debug::return_value_handle() {
  return handle(thread_local_.return_value_, isolate_);
}

2222 2223 2224 2225
bool Debug::PerformSideEffectCheckForCallback(
    Handle<Object> callback_info, Handle<Object> receiver,
    Debug::AccessorKind accessor_kind) {
  DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo());
2226
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2227
  if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() &&
2228
      i::CallHandlerInfo::cast(*callback_info).NextCallHasNoSideEffect()) {
2229 2230
    return true;
  }
2231
  // TODO(7515): always pass a valid callback info object.
2232 2233 2234
  if (!callback_info.is_null()) {
    if (callback_info->IsAccessorInfo()) {
      // List of whitelisted internal accessors can be found in accessors.h.
2235
      AccessorInfo info = AccessorInfo::cast(*callback_info);
2236
      DCHECK_NE(kNotAccessor, accessor_kind);
2237 2238
      switch (accessor_kind == kSetter ? info.setter_side_effect_type()
                                       : info.getter_side_effect_type()) {
2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254
        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 '");
2255
        info.name().ShortPrint();
2256 2257 2258
        PrintF("' may cause side effect.\n");
      }
    } else if (callback_info->IsInterceptorInfo()) {
2259
      InterceptorInfo info = InterceptorInfo::cast(*callback_info);
2260
      if (info.has_no_side_effect()) return true;
2261 2262 2263 2264
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
      }
    } else if (callback_info->IsCallHandlerInfo()) {
2265
      CallHandlerInfo info = CallHandlerInfo::cast(*callback_info);
2266
      if (info.IsSideEffectFreeCallHandlerInfo()) return true;
2267 2268 2269 2270
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
      }
    }
2271
  }
2272 2273 2274 2275 2276 2277 2278
  side_effect_check_failed_ = true;
  // Throw an uncatchable termination exception.
  isolate_->TerminateExecution();
  isolate_->OptionalRescheduleException(false);
  return false;
}

2279 2280 2281
bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
  using interpreter::Bytecode;

2282
  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
2283 2284
  SharedFunctionInfo shared = frame->function().shared();
  BytecodeArray bytecode_array = shared.GetBytecodeArray();
2285
  int offset = frame->GetBytecodeOffset();
2286 2287
  interpreter::BytecodeArrayAccessor bytecode_accessor(
      handle(bytecode_array, isolate_), offset);
2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300

  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_);
2301 2302 2303 2304 2305 2306
  return PerformSideEffectCheckForObject(object);
}

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

2307 2308 2309 2310 2311 2312
  // 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;
2313
  }
2314

2315
  if (FLAG_trace_side_effect_free_debug_evaluate) {
2316
    PrintF("[debug-evaluate] failed runtime side effect check.\n");
2317 2318 2319 2320 2321 2322
  }
  side_effect_check_failed_ = true;
  // Throw an uncatchable termination exception.
  isolate_->TerminateExecution();
  return false;
}
2323 2324
}  // namespace internal
}  // namespace v8