Upgrade V8 to 8.8.278.14

Bug: 162604069
Bug: 167389063
Test: gts-tradefed run gts-dev --module GtsGmscoreHostTestCases
      --test com.google.android.gts.devicepolicy.DeviceOwnerTest#testProxyPacProxyTest
Test: m -j proxy_resolver_v8_unittest && adb sync && adb shell \
      /data/nativetest/proxy_resolver_v8_unittest/proxy_resolver_v8_unittest

Merged-In: Ifb09923b9d7f6d8990fb062d7dc0294edf2c098e
Change-Id: Ifb09923b9d7f6d8990fb062d7dc0294edf2c098e
(cherry picked from commit 9580a23bc5b8874a0979001d3595d027cbb68128)
diff --git a/src/debug/DIR_METADATA b/src/debug/DIR_METADATA
new file mode 100644
index 0000000..3ba1106
--- /dev/null
+++ b/src/debug/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>DevTools>JavaScript"
+}
\ No newline at end of file
diff --git a/src/debug/OWNERS b/src/debug/OWNERS
index 4e493cd..5b93352 100644
--- a/src/debug/OWNERS
+++ b/src/debug/OWNERS
@@ -1,8 +1,6 @@
-set noparent
-
 bmeurer@chromium.org
 jgruber@chromium.org
 mvstanton@chromium.org
-ulan@chromium.org
+szuend@chromium.org
 verwaest@chromium.org
 yangguo@chromium.org
diff --git a/src/debug/arm/OWNERS b/src/debug/arm/OWNERS
deleted file mode 100644
index 906a5ce..0000000
--- a/src/debug/arm/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-rmcilroy@chromium.org
diff --git a/src/debug/arm/debug-arm.cc b/src/debug/arm/debug-arm.cc
index 4839282..23460d6 100644
--- a/src/debug/arm/debug-arm.cc
+++ b/src/debug/arm/debug-arm.cc
@@ -6,8 +6,11 @@
 
 #include "src/debug/debug.h"
 
-#include "src/codegen.h"
+#include "src/codegen/assembler-inl.h"
+#include "src/codegen/macro-assembler.h"
 #include "src/debug/liveedit.h"
+#include "src/execution/frames-inl.h"
+#include "src/objects/objects-inl.h"
 
 namespace v8 {
 namespace internal {
@@ -15,101 +18,6 @@
 #define __ ACCESS_MASM(masm)
 
 
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_size;
-  __ bind(&check_size);
-  for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
-    __ nop(MacroAssembler::DEBUG_BREAK_NOP);
-  }
-  DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
-            masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction. Avoid emitting
-  // the constant pool in the debug break slot code.
-  Assembler::BlockConstPoolScope block_const_pool(masm);
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  // Patch the code changing the debug break slot code from
-  //   mov r2, r2
-  //   mov r2, r2
-  //   mov r2, r2
-  //   mov r2, r2
-  // to a call to the debug break slot code.
-  //   ldr ip, [pc, #0]
-  //   b skip
-  //   <debug break slot code entry point address>
-  //   skip:
-  //   blx ip
-  Label skip_constant;
-  patcher.masm()->ldr(ip, MemOperand(v8::internal::pc, 0));
-  patcher.masm()->b(&skip_constant);
-  patcher.Emit(code->entry());
-  patcher.masm()->bind(&skip_constant);
-  patcher.masm()->blx(ip);
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  Instr current_instr = Assembler::instr_at(pc);
-  return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-  {
-    FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ push(r0);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ mov(r0, Operand(1));
-    __ mov(r1,
-           Operand(ExternalReference(
-               Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
-    CEntryStub ceb(masm->isolate(), 1);
-    __ CallStub(&ceb);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; i++) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber r0 if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function.
-        if (!(reg.is(r0) && (mode == SAVE_RESULT_REGISTER))) {
-          __ mov(reg, Operand(kDebugZapValue));
-        }
-      }
-    }
-    // Leave the internal frame.
-  }
-
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ Ret();
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -128,18 +36,15 @@
   // - Leave the frame.
   // - Restart the frame by calling the function.
   __ mov(fp, r1);
-  __ ldr(r1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+  __ ldr(r1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
   __ LeaveFrame(StackFrame::INTERNAL);
 
   __ ldr(r0, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
-  __ ldr(r0,
-         FieldMemOperand(r0, SharedFunctionInfo::kFormalParameterCountOffset));
+  __ ldrh(r0,
+          FieldMemOperand(r0, SharedFunctionInfo::kFormalParameterCountOffset));
   __ mov(r2, r0);
 
-  ParameterCount dummy1(r2);
-  ParameterCount dummy2(r0);
-  __ InvokeFunction(r1, dummy1, dummy2, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  __ InvokeFunction(r1, r2, r0, JUMP_FUNCTION);
 }
 
 
diff --git a/src/debug/arm64/OWNERS b/src/debug/arm64/OWNERS
deleted file mode 100644
index 906a5ce..0000000
--- a/src/debug/arm64/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-rmcilroy@chromium.org
diff --git a/src/debug/arm64/debug-arm64.cc b/src/debug/arm64/debug-arm64.cc
index 06929c6..251856e 100644
--- a/src/debug/arm64/debug-arm64.cc
+++ b/src/debug/arm64/debug-arm64.cc
@@ -6,122 +6,17 @@
 
 #include "src/debug/debug.h"
 
-#include "src/arm64/frames-arm64.h"
-#include "src/codegen.h"
+#include "src/codegen/arm64/macro-assembler-arm64-inl.h"
 #include "src/debug/liveedit.h"
+#include "src/execution/frame-constants.h"
+#include "src/execution/frames-inl.h"
+#include "src/objects/objects-inl.h"
 
 namespace v8 {
 namespace internal {
 
 #define __ ACCESS_MASM(masm)
 
-
-void EmitDebugBreakSlot(Assembler* masm) {
-  Label check_size;
-  __ bind(&check_size);
-  for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
-    __ nop(Assembler::DEBUG_BREAK_NOP);
-  }
-  DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
-            static_cast<int>(masm->InstructionsGeneratedSince(&check_size)));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction. Avoid emitting
-  // the constant pool in the debug break slot code.
-  InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions);
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  PatchingAssembler patcher(isolate, reinterpret_cast<Instruction*>(pc),
-                            Assembler::kDebugBreakSlotInstructions);
-  EmitDebugBreakSlot(&patcher);
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  PatchingAssembler patcher(isolate, reinterpret_cast<Instruction*>(pc),
-                            Assembler::kDebugBreakSlotInstructions);
-  // Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug
-  // break slot code from
-  //   mov x0, x0    @ nop DEBUG_BREAK_NOP
-  //   mov x0, x0    @ nop DEBUG_BREAK_NOP
-  //   mov x0, x0    @ nop DEBUG_BREAK_NOP
-  //   mov x0, x0    @ nop DEBUG_BREAK_NOP
-  //   mov x0, x0    @ nop DEBUG_BREAK_NOP
-  // to a call to the debug slot code.
-  //   ldr ip0, [pc, #(2 * kInstructionSize)]
-  //   blr ip0
-  //   b skip
-  //   <debug break slot code entry point address (64 bits)>
-  //   skip:
-
-  Label skip_constant;
-  // The first instruction of a patched debug break slot must be a load literal
-  // loading the address of the debug break slot code.
-  patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2);
-  patcher.b(&skip_constant);
-  patcher.dc64(reinterpret_cast<int64_t>(code->entry()));
-  patcher.bind(&skip_constant);
-  // TODO(all): check the following is correct.
-  // The debug break slot code will push a frame and call statically compiled
-  // code. By using blr,  this call site will be registered in the frame.
-  // The debugger can now iterate on the frames to find this call.
-  patcher.blr(ip0);
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  Instruction* current_instr = reinterpret_cast<Instruction*>(pc);
-  return !current_instr->IsNop(Assembler::DEBUG_BREAK_NOP);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-  Register scratch = x10;
-  {
-    FrameScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ Push(x0);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ Mov(x0, 1);
-    __ Mov(x1, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
-                                 masm->isolate()));
-
-    CEntryStub stub(masm->isolate(), 1);
-    __ CallStub(&stub);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; i++) {
-        Register reg = Register::XRegFromCode(JSCallerSavedCode(i));
-        // Do not clobber x0 if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function.
-        if (!(reg.is(x0) && (mode == SAVE_RESULT_REGISTER))) {
-          __ Mov(reg, Operand(kDebugZapValue));
-        }
-      }
-    }
-    // Leave the internal frame.
-  }
-
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ Ret();
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -140,21 +35,18 @@
   // - Leave the frame.
   // - Restart the frame by calling the function.
   __ Mov(fp, x1);
-  __ AssertStackConsistency();
-  __ Ldr(x1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+  __ Ldr(x1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
 
-  __ Mov(masm->StackPointer(), Operand(fp));
-  __ Pop(fp, lr);  // Frame, Return address.
+  __ Mov(sp, fp);
+  __ Pop<TurboAssembler::kAuthLR>(fp, lr);
 
-  __ Ldr(x0, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
-  __ Ldr(x0,
-         FieldMemOperand(x0, SharedFunctionInfo::kFormalParameterCountOffset));
-  __ mov(x2, x0);
+  __ LoadTaggedPointerField(
+      x0, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
+  __ Ldrh(x0,
+          FieldMemOperand(x0, SharedFunctionInfo::kFormalParameterCountOffset));
+  __ mov(x3, x0);
 
-  ParameterCount dummy1(x2);
-  ParameterCount dummy2(x0);
-  __ InvokeFunction(x1, dummy1, dummy2, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  __ InvokeFunctionWithNewTarget(x1, x3, x0, JUMP_FUNCTION);
 }
 
 
@@ -163,4 +55,6 @@
 }  // namespace internal
 }  // namespace v8
 
+#undef __
+
 #endif  // V8_TARGET_ARCH_ARM64
diff --git a/src/debug/debug-coverage.cc b/src/debug/debug-coverage.cc
index 8a13b6c..bee4122 100644
--- a/src/debug/debug-coverage.cc
+++ b/src/debug/debug-coverage.cc
@@ -4,22 +4,26 @@
 
 #include "src/debug/debug-coverage.h"
 
+#include "src/ast/ast-source-ranges.h"
+#include "src/ast/ast.h"
 #include "src/base/hashmap.h"
-#include "src/deoptimizer.h"
-#include "src/isolate.h"
-#include "src/objects-inl.h"
-#include "src/objects.h"
+#include "src/debug/debug.h"
+#include "src/deoptimizer/deoptimizer.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/isolate.h"
+#include "src/objects/debug-objects-inl.h"
+#include "src/objects/objects.h"
 
 namespace v8 {
 namespace internal {
 
 class SharedToCounterMap
-    : public base::TemplateHashMapImpl<SharedFunctionInfo*, uint32_t,
-                                       base::KeyEqualityMatcher<void*>,
+    : public base::TemplateHashMapImpl<SharedFunctionInfo, uint32_t,
+                                       base::KeyEqualityMatcher<Object>,
                                        base::DefaultAllocationPolicy> {
  public:
-  typedef base::TemplateHashMapEntry<SharedFunctionInfo*, uint32_t> Entry;
-  inline void Add(SharedFunctionInfo* key, uint32_t count) {
+  using Entry = base::TemplateHashMapEntry<SharedFunctionInfo, uint32_t>;
+  inline void Add(SharedFunctionInfo key, uint32_t count) {
     Entry* entry = LookupOrInsert(key, Hash(key), []() { return 0; });
     uint32_t old_count = entry->value;
     if (UINT32_MAX - count < old_count) {
@@ -29,140 +33,779 @@
     }
   }
 
-  inline uint32_t Get(SharedFunctionInfo* key) {
+  inline uint32_t Get(SharedFunctionInfo key) {
     Entry* entry = Lookup(key, Hash(key));
     if (entry == nullptr) return 0;
     return entry->value;
   }
 
  private:
-  static uint32_t Hash(SharedFunctionInfo* key) {
-    return static_cast<uint32_t>(reinterpret_cast<intptr_t>(key));
+  static uint32_t Hash(SharedFunctionInfo key) {
+    return static_cast<uint32_t>(key.ptr());
   }
 
   DisallowHeapAllocation no_gc;
 };
 
 namespace {
-int StartPosition(SharedFunctionInfo* info) {
-  int start = info->function_token_position();
-  if (start == kNoSourcePosition) start = info->start_position();
+int StartPosition(SharedFunctionInfo info) {
+  int start = info.function_token_position();
+  if (start == kNoSourcePosition) start = info.StartPosition();
   return start;
 }
 
-bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) {
-  int a_start = StartPosition(a);
-  int b_start = StartPosition(b);
-  if (a_start == b_start) return a->end_position() > b->end_position();
-  return a_start < b_start;
+bool CompareCoverageBlock(const CoverageBlock& a, const CoverageBlock& b) {
+  DCHECK_NE(kNoSourcePosition, a.start);
+  DCHECK_NE(kNoSourcePosition, b.start);
+  if (a.start == b.start) return a.end > b.end;
+  return a.start < b.start;
 }
-}  // anonymous namespace
 
-Coverage* Coverage::Collect(Isolate* isolate, bool reset_count) {
-  SharedToCounterMap counter_map;
+void SortBlockData(
+    std::vector<CoverageBlock>& v) {  // NOLINT(runtime/references)
+  // Sort according to the block nesting structure.
+  std::sort(v.begin(), v.end(), CompareCoverageBlock);
+}
 
-  // Feed invocation count into the counter map.
-  if (isolate->IsCodeCoverageEnabled()) {
-    // Feedback vectors are already listed to prevent losing them to GC.
-    Handle<ArrayList> list =
-        Handle<ArrayList>::cast(isolate->factory()->code_coverage_list());
-    for (int i = 0; i < list->Length(); i++) {
-      FeedbackVector* vector = FeedbackVector::cast(list->Get(i));
-      SharedFunctionInfo* shared = vector->shared_function_info();
-      DCHECK(shared->IsSubjectToDebugging());
-      uint32_t count = static_cast<uint32_t>(vector->invocation_count());
-      if (reset_count) vector->clear_invocation_count();
-      counter_map.Add(shared, count);
-    }
-  } else {
-    // Iterate the heap to find all feedback vectors and accumulate the
-    // invocation counts into the map for each shared function info.
-    HeapIterator heap_iterator(isolate->heap());
-    while (HeapObject* current_obj = heap_iterator.next()) {
-      if (!current_obj->IsFeedbackVector()) continue;
-      FeedbackVector* vector = FeedbackVector::cast(current_obj);
-      SharedFunctionInfo* shared = vector->shared_function_info();
-      if (!shared->IsSubjectToDebugging()) continue;
-      uint32_t count = static_cast<uint32_t>(vector->invocation_count());
-      if (reset_count) vector->clear_invocation_count();
-      counter_map.Add(shared, count);
-    }
+std::vector<CoverageBlock> GetSortedBlockData(SharedFunctionInfo shared) {
+  DCHECK(shared.HasCoverageInfo());
+
+  CoverageInfo coverage_info =
+      CoverageInfo::cast(shared.GetDebugInfo().coverage_info());
+
+  std::vector<CoverageBlock> result;
+  if (coverage_info.slot_count() == 0) return result;
+
+  for (int i = 0; i < coverage_info.slot_count(); i++) {
+    const int start_pos = coverage_info.StartSourcePosition(i);
+    const int until_pos = coverage_info.EndSourcePosition(i);
+    const int count = coverage_info.BlockCount(i);
+
+    DCHECK_NE(kNoSourcePosition, start_pos);
+    result.emplace_back(start_pos, until_pos, count);
   }
 
-  // Iterate shared function infos of every script and build a mapping
-  // between source ranges and invocation counts.
-  Coverage* result = new Coverage();
-  Script::Iterator scripts(isolate);
-  while (Script* script = scripts.Next()) {
-    // Dismiss non-user scripts.
-    if (script->type() != Script::TYPE_NORMAL) continue;
+  SortBlockData(result);
 
-    // Create and add new script data.
-    Handle<Script> script_handle(script, isolate);
-    result->emplace_back(isolate, script_handle);
-    std::vector<CoverageFunction>* functions = &result->back().functions;
+  return result;
+}
 
-    std::vector<SharedFunctionInfo*> sorted;
-    bool has_toplevel = false;
+// A utility class to simplify logic for performing passes over block coverage
+// ranges. Provides access to the implicit tree structure of ranges (i.e. access
+// to parent and sibling blocks), and supports efficient in-place editing and
+// deletion. The underlying backing store is the array of CoverageBlocks stored
+// on the CoverageFunction.
+class CoverageBlockIterator final {
+ public:
+  explicit CoverageBlockIterator(CoverageFunction* function)
+      : function_(function) {
+    DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(),
+                          CompareCoverageBlock));
+  }
 
-    {
-      // Sort functions by start position, from outer to inner functions.
-      SharedFunctionInfo::ScriptIterator infos(script_handle);
-      while (SharedFunctionInfo* info = infos.Next()) {
-        has_toplevel |= info->is_toplevel();
-        sorted.push_back(info);
+  ~CoverageBlockIterator() {
+    Finalize();
+    DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(),
+                          CompareCoverageBlock));
+  }
+
+  bool HasNext() const {
+    return read_index_ + 1 < static_cast<int>(function_->blocks.size());
+  }
+
+  bool Next() {
+    if (!HasNext()) {
+      if (!ended_) MaybeWriteCurrent();
+      ended_ = true;
+      return false;
+    }
+
+    // If a block has been deleted, subsequent iteration moves trailing blocks
+    // to their updated position within the array.
+    MaybeWriteCurrent();
+
+    if (read_index_ == -1) {
+      // Initialize the nesting stack with the function range.
+      nesting_stack_.emplace_back(function_->start, function_->end,
+                                  function_->count);
+    } else if (!delete_current_) {
+      nesting_stack_.emplace_back(GetBlock());
+    }
+
+    delete_current_ = false;
+    read_index_++;
+
+    DCHECK(IsActive());
+
+    CoverageBlock& block = GetBlock();
+    while (nesting_stack_.size() > 1 &&
+           nesting_stack_.back().end <= block.start) {
+      nesting_stack_.pop_back();
+    }
+
+    DCHECK_IMPLIES(block.start >= function_->end,
+                   block.end == kNoSourcePosition);
+    DCHECK_NE(block.start, kNoSourcePosition);
+    DCHECK_LE(block.end, GetParent().end);
+
+    return true;
+  }
+
+  CoverageBlock& GetBlock() {
+    DCHECK(IsActive());
+    return function_->blocks[read_index_];
+  }
+
+  CoverageBlock& GetNextBlock() {
+    DCHECK(IsActive());
+    DCHECK(HasNext());
+    return function_->blocks[read_index_ + 1];
+  }
+
+  CoverageBlock& GetPreviousBlock() {
+    DCHECK(IsActive());
+    DCHECK_GT(read_index_, 0);
+    return function_->blocks[read_index_ - 1];
+  }
+
+  CoverageBlock& GetParent() {
+    DCHECK(IsActive());
+    return nesting_stack_.back();
+  }
+
+  bool HasSiblingOrChild() {
+    DCHECK(IsActive());
+    return HasNext() && GetNextBlock().start < GetParent().end;
+  }
+
+  CoverageBlock& GetSiblingOrChild() {
+    DCHECK(HasSiblingOrChild());
+    DCHECK(IsActive());
+    return GetNextBlock();
+  }
+
+  // A range is considered to be at top level if its parent range is the
+  // function range.
+  bool IsTopLevel() const { return nesting_stack_.size() == 1; }
+
+  void DeleteBlock() {
+    DCHECK(!delete_current_);
+    DCHECK(IsActive());
+    delete_current_ = true;
+  }
+
+ private:
+  void MaybeWriteCurrent() {
+    if (delete_current_) return;
+    if (read_index_ >= 0 && write_index_ != read_index_) {
+      function_->blocks[write_index_] = function_->blocks[read_index_];
+    }
+    write_index_++;
+  }
+
+  void Finalize() {
+    while (Next()) {
+      // Just iterate to the end.
+    }
+    function_->blocks.resize(write_index_);
+  }
+
+  bool IsActive() const { return read_index_ >= 0 && !ended_; }
+
+  CoverageFunction* function_;
+  std::vector<CoverageBlock> nesting_stack_;
+  bool ended_ = false;
+  bool delete_current_ = false;
+  int read_index_ = -1;
+  int write_index_ = -1;
+};
+
+bool HaveSameSourceRange(const CoverageBlock& lhs, const CoverageBlock& rhs) {
+  return lhs.start == rhs.start && lhs.end == rhs.end;
+}
+
+void MergeDuplicateRanges(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
+
+  while (iter.Next() && iter.HasNext()) {
+    CoverageBlock& block = iter.GetBlock();
+    CoverageBlock& next_block = iter.GetNextBlock();
+
+    if (!HaveSameSourceRange(block, next_block)) continue;
+
+    DCHECK_NE(kNoSourcePosition, block.end);  // Non-singleton range.
+    next_block.count = std::max(block.count, next_block.count);
+    iter.DeleteBlock();
+  }
+}
+
+// Rewrite position singletons (produced by unconditional control flow
+// like return statements, and by continuation counters) into source
+// ranges that end at the next sibling range or the end of the parent
+// range, whichever comes first.
+void RewritePositionSingletonsToRanges(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
+
+  while (iter.Next()) {
+    CoverageBlock& block = iter.GetBlock();
+    CoverageBlock& parent = iter.GetParent();
+
+    if (block.start >= function->end) {
+      DCHECK_EQ(block.end, kNoSourcePosition);
+      iter.DeleteBlock();
+    } else if (block.end == kNoSourcePosition) {
+      // The current block ends at the next sibling block (if it exists) or the
+      // end of the parent block otherwise.
+      if (iter.HasSiblingOrChild()) {
+        block.end = iter.GetSiblingOrChild().start;
+      } else if (iter.IsTopLevel()) {
+        // See https://crbug.com/v8/6661. Functions are special-cased because
+        // we never want the closing brace to be uncovered. This is mainly to
+        // avoid a noisy UI.
+        block.end = parent.end - 1;
+      } else {
+        block.end = parent.end;
       }
-      std::sort(sorted.begin(), sorted.end(), CompareSharedFunctionInfo);
     }
+  }
+}
 
-    functions->reserve(sorted.size() + (has_toplevel ? 0 : 1));
+void MergeConsecutiveRanges(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
 
-    if (!has_toplevel) {
-      // Add a replacement toplevel function if it does not exist.
-      int source_end = String::cast(script->source())->length();
-      functions->emplace_back(0, source_end, 1u,
-                              isolate->factory()->empty_string());
+  while (iter.Next()) {
+    CoverageBlock& block = iter.GetBlock();
+
+    if (iter.HasSiblingOrChild()) {
+      CoverageBlock& sibling = iter.GetSiblingOrChild();
+      if (sibling.start == block.end && sibling.count == block.count) {
+        // Best-effort: this pass may miss mergeable siblings in the presence of
+        // child blocks.
+        sibling.start = block.start;
+        iter.DeleteBlock();
+      }
     }
+  }
+}
 
-    // Use sorted list to reconstruct function nesting.
-    for (SharedFunctionInfo* info : sorted) {
-      int start = StartPosition(info);
-      int end = info->end_position();
-      uint32_t count = counter_map.Get(info);
-      Handle<String> name(info->DebugName(), isolate);
-      functions->emplace_back(start, end, count, name);
+void MergeNestedRanges(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
+
+  while (iter.Next()) {
+    CoverageBlock& block = iter.GetBlock();
+    CoverageBlock& parent = iter.GetParent();
+
+    if (parent.count == block.count) {
+      // Transformation may not be valid if sibling blocks exist with a
+      // differing count.
+      iter.DeleteBlock();
     }
   }
+}
+
+void RewriteFunctionScopeCounter(CoverageFunction* function) {
+  // Every function must have at least the top-level function counter.
+  DCHECK(!function->blocks.empty());
+
+  CoverageBlockIterator iter(function);
+  if (iter.Next()) {
+    DCHECK(iter.IsTopLevel());
+
+    CoverageBlock& block = iter.GetBlock();
+    if (block.start == SourceRange::kFunctionLiteralSourcePosition &&
+        block.end == SourceRange::kFunctionLiteralSourcePosition) {
+      // If a function-scope block exists, overwrite the function count. It has
+      // a more reliable count than what we get from the FeedbackVector (which
+      // is imprecise e.g. for generator functions and optimized code).
+      function->count = block.count;
+
+      // Then delete it; for compatibility with non-block coverage modes, the
+      // function-scope block is expected in CoverageFunction, not as a
+      // CoverageBlock.
+      iter.DeleteBlock();
+    }
+  }
+}
+
+void FilterAliasedSingletons(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
+
+  iter.Next();  // Advance once since we reference the previous block later.
+
+  while (iter.Next()) {
+    CoverageBlock& previous_block = iter.GetPreviousBlock();
+    CoverageBlock& block = iter.GetBlock();
+
+    bool is_singleton = block.end == kNoSourcePosition;
+    bool aliases_start = block.start == previous_block.start;
+
+    if (is_singleton && aliases_start) {
+      // The previous block must have a full range since duplicate singletons
+      // have already been merged.
+      DCHECK_NE(previous_block.end, kNoSourcePosition);
+      // Likewise, the next block must have another start position since
+      // singletons are sorted to the end.
+      DCHECK_IMPLIES(iter.HasNext(), iter.GetNextBlock().start != block.start);
+      iter.DeleteBlock();
+    }
+  }
+}
+
+void FilterUncoveredRanges(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
+
+  while (iter.Next()) {
+    CoverageBlock& block = iter.GetBlock();
+    CoverageBlock& parent = iter.GetParent();
+    if (block.count == 0 && parent.count == 0) iter.DeleteBlock();
+  }
+}
+
+void FilterEmptyRanges(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
+
+  while (iter.Next()) {
+    CoverageBlock& block = iter.GetBlock();
+    if (block.start == block.end) iter.DeleteBlock();
+  }
+}
+
+void ClampToBinary(CoverageFunction* function) {
+  CoverageBlockIterator iter(function);
+
+  while (iter.Next()) {
+    CoverageBlock& block = iter.GetBlock();
+    if (block.count > 0) block.count = 1;
+  }
+}
+
+void ResetAllBlockCounts(SharedFunctionInfo shared) {
+  DCHECK(shared.HasCoverageInfo());
+
+  CoverageInfo coverage_info =
+      CoverageInfo::cast(shared.GetDebugInfo().coverage_info());
+
+  for (int i = 0; i < coverage_info.slot_count(); i++) {
+    coverage_info.ResetBlockCount(i);
+  }
+}
+
+bool IsBlockMode(debug::CoverageMode mode) {
+  switch (mode) {
+    case debug::CoverageMode::kBlockBinary:
+    case debug::CoverageMode::kBlockCount:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool IsBinaryMode(debug::CoverageMode mode) {
+  switch (mode) {
+    case debug::CoverageMode::kBlockBinary:
+    case debug::CoverageMode::kPreciseBinary:
+      return true;
+    default:
+      return false;
+  }
+}
+
+void CollectBlockCoverageInternal(CoverageFunction* function,
+                                  SharedFunctionInfo info,
+                                  debug::CoverageMode mode) {
+  DCHECK(IsBlockMode(mode));
+
+  // Functions with empty source ranges are not interesting to report. This can
+  // happen e.g. for internally-generated functions like class constructors.
+  if (!function->HasNonEmptySourceRange()) return;
+
+  function->has_block_coverage = true;
+  function->blocks = GetSortedBlockData(info);
+
+  // If in binary mode, only report counts of 0/1.
+  if (mode == debug::CoverageMode::kBlockBinary) ClampToBinary(function);
+
+  // To stay compatible with non-block coverage modes, the function-scope count
+  // is expected to be in the CoverageFunction, not as part of its blocks.
+  // This finds the function-scope counter, overwrites CoverageFunction::count,
+  // and removes it from the block list.
+  //
+  // Important: Must be called before other transformation passes.
+  RewriteFunctionScopeCounter(function);
+
+  // Functions without blocks don't need to be processed further.
+  if (!function->HasBlocks()) return;
+
+  // Remove singleton ranges with the same start position as a full range and
+  // throw away their counts.
+  // Singleton ranges are only intended to split existing full ranges and should
+  // never expand into a full range. Consider 'if (cond) { ... } else { ... }'
+  // as a problematic example; if the then-block produces a continuation
+  // singleton, it would incorrectly expand into the else range.
+  // For more context, see https://crbug.com/v8/8237.
+  FilterAliasedSingletons(function);
+
+  // Rewrite all singletons (created e.g. by continuations and unconditional
+  // control flow) to ranges.
+  RewritePositionSingletonsToRanges(function);
+
+  // Merge nested and consecutive ranges with identical counts.
+  // Note that it's necessary to merge duplicate ranges prior to merging nested
+  // changes in order to avoid invalid transformations. See crbug.com/827530.
+  MergeConsecutiveRanges(function);
+
+  SortBlockData(function->blocks);
+  MergeDuplicateRanges(function);
+  MergeNestedRanges(function);
+
+  MergeConsecutiveRanges(function);
+
+  // Filter out ranges with count == 0 unless the immediate parent range has
+  // a count != 0.
+  FilterUncoveredRanges(function);
+
+  // Filter out ranges of zero length.
+  FilterEmptyRanges(function);
+}
+
+void CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info,
+                          debug::CoverageMode mode) {
+  CollectBlockCoverageInternal(function, info, mode);
+
+  // Reset all counters on the DebugInfo to zero.
+  ResetAllBlockCounts(info);
+}
+
+void PrintBlockCoverage(const CoverageFunction* function,
+                        SharedFunctionInfo info, bool has_nonempty_source_range,
+                        bool function_is_relevant) {
+  DCHECK(FLAG_trace_block_coverage);
+  std::unique_ptr<char[]> function_name =
+      function->name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+  i::PrintF(
+      "Coverage for function='%s', SFI=%p, has_nonempty_source_range=%d, "
+      "function_is_relevant=%d\n",
+      function_name.get(), reinterpret_cast<void*>(info.ptr()),
+      has_nonempty_source_range, function_is_relevant);
+  i::PrintF("{start: %d, end: %d, count: %d}\n", function->start, function->end,
+            function->count);
+  for (const auto& block : function->blocks) {
+    i::PrintF("{start: %d, end: %d, count: %d}\n", block.start, block.end,
+              block.count);
+  }
+}
+
+void CollectAndMaybeResetCounts(Isolate* isolate,
+                                SharedToCounterMap* counter_map,
+                                v8::debug::CoverageMode coverage_mode) {
+  const bool reset_count =
+      coverage_mode != v8::debug::CoverageMode::kBestEffort;
+
+  switch (isolate->code_coverage_mode()) {
+    case v8::debug::CoverageMode::kBlockBinary:
+    case v8::debug::CoverageMode::kBlockCount:
+    case v8::debug::CoverageMode::kPreciseBinary:
+    case v8::debug::CoverageMode::kPreciseCount: {
+      // Feedback vectors are already listed to prevent losing them to GC.
+      DCHECK(isolate->factory()
+                 ->feedback_vectors_for_profiling_tools()
+                 ->IsArrayList());
+      Handle<ArrayList> list = Handle<ArrayList>::cast(
+          isolate->factory()->feedback_vectors_for_profiling_tools());
+      for (int i = 0; i < list->Length(); i++) {
+        FeedbackVector vector = FeedbackVector::cast(list->Get(i));
+        SharedFunctionInfo shared = vector.shared_function_info();
+        DCHECK(shared.IsSubjectToDebugging());
+        uint32_t count = static_cast<uint32_t>(vector.invocation_count());
+        if (reset_count) vector.clear_invocation_count();
+        counter_map->Add(shared, count);
+      }
+      break;
+    }
+    case v8::debug::CoverageMode::kBestEffort: {
+      DCHECK(!isolate->factory()
+                  ->feedback_vectors_for_profiling_tools()
+                  ->IsArrayList());
+      DCHECK_EQ(v8::debug::CoverageMode::kBestEffort, coverage_mode);
+      HeapObjectIterator heap_iterator(isolate->heap());
+      for (HeapObject current_obj = heap_iterator.Next();
+           !current_obj.is_null(); current_obj = heap_iterator.Next()) {
+        if (!current_obj.IsJSFunction()) continue;
+        JSFunction func = JSFunction::cast(current_obj);
+        SharedFunctionInfo shared = func.shared();
+        if (!shared.IsSubjectToDebugging()) continue;
+        if (!(func.has_feedback_vector() ||
+              func.has_closure_feedback_cell_array())) {
+          continue;
+        }
+        uint32_t count = 0;
+        if (func.has_feedback_vector()) {
+          count =
+              static_cast<uint32_t>(func.feedback_vector().invocation_count());
+        } else if (func.raw_feedback_cell().interrupt_budget() <
+                   FLAG_budget_for_feedback_vector_allocation) {
+          // We haven't allocated feedback vector, but executed the function
+          // atleast once. We don't have precise invocation count here.
+          count = 1;
+        }
+        counter_map->Add(shared, count);
+      }
+
+      // Also check functions on the stack to collect the count map. With lazy
+      // feedback allocation we may miss counting functions if the feedback
+      // vector wasn't allocated yet and the function's interrupt budget wasn't
+      // updated (i.e. it didn't execute return / jump).
+      for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
+        SharedFunctionInfo shared = it.frame()->function().shared();
+        if (counter_map->Get(shared) != 0) continue;
+        counter_map->Add(shared, 1);
+      }
+      break;
+    }
+  }
+}
+
+// A {SFI, count} tuple is used to sort by source range (stored on
+// the SFI) and call count (in the counter map).
+struct SharedFunctionInfoAndCount {
+  SharedFunctionInfoAndCount(SharedFunctionInfo info, uint32_t count)
+      : info(info),
+        count(count),
+        start(StartPosition(info)),
+        end(info.EndPosition()) {}
+
+  // Sort by:
+  // - start, ascending.
+  // - end, descending.
+  // - info.is_toplevel() first
+  // - count, descending.
+  bool operator<(const SharedFunctionInfoAndCount& that) const {
+    if (this->start != that.start) return this->start < that.start;
+    if (this->end != that.end) return this->end > that.end;
+    if (this->info.is_toplevel() != that.info.is_toplevel()) {
+      return this->info.is_toplevel();
+    }
+    return this->count > that.count;
+  }
+
+  SharedFunctionInfo info;
+  uint32_t count;
+  int start;
+  int end;
+};
+
+}  // anonymous namespace
+
+std::unique_ptr<Coverage> Coverage::CollectPrecise(Isolate* isolate) {
+  DCHECK(!isolate->is_best_effort_code_coverage());
+  std::unique_ptr<Coverage> result =
+      Collect(isolate, isolate->code_coverage_mode());
+  if (!isolate->is_collecting_type_profile() &&
+      (isolate->is_precise_binary_code_coverage() ||
+       isolate->is_block_binary_code_coverage())) {
+    // We do not have to hold onto feedback vectors for invocations we already
+    // reported. So we can reset the list.
+    isolate->SetFeedbackVectorsForProfilingTools(*ArrayList::New(isolate, 0));
+  }
   return result;
 }
 
-void Coverage::TogglePrecise(Isolate* isolate, bool enable) {
-  if (enable) {
-    HandleScope scope(isolate);
-    // Remove all optimized function. Optimized and inlined functions do not
-    // increment invocation count.
-    Deoptimizer::DeoptimizeAll(isolate);
-    // Collect existing feedback vectors.
-    std::vector<Handle<FeedbackVector>> vectors;
+std::unique_ptr<Coverage> Coverage::CollectBestEffort(Isolate* isolate) {
+  return Collect(isolate, v8::debug::CoverageMode::kBestEffort);
+}
+
+std::unique_ptr<Coverage> Coverage::Collect(
+    Isolate* isolate, v8::debug::CoverageMode collectionMode) {
+  // Collect call counts for all functions.
+  SharedToCounterMap counter_map;
+  CollectAndMaybeResetCounts(isolate, &counter_map, collectionMode);
+
+  // Iterate shared function infos of every script and build a mapping
+  // between source ranges and invocation counts.
+  std::unique_ptr<Coverage> result(new Coverage());
+  Script::Iterator scripts(isolate);
+  for (Script script = scripts.Next(); !script.is_null();
+       script = scripts.Next()) {
+    if (!script.IsUserJavaScript()) continue;
+
+    // Create and add new script data.
+    Handle<Script> script_handle(script, isolate);
+    result->emplace_back(script_handle);
+    std::vector<CoverageFunction>* functions = &result->back().functions;
+
+    std::vector<SharedFunctionInfoAndCount> sorted;
+
     {
-      HeapIterator heap_iterator(isolate->heap());
-      while (HeapObject* current_obj = heap_iterator.next()) {
-        if (!current_obj->IsFeedbackVector()) continue;
-        FeedbackVector* vector = FeedbackVector::cast(current_obj);
-        SharedFunctionInfo* shared = vector->shared_function_info();
-        if (!shared->IsSubjectToDebugging()) continue;
-        vector->clear_invocation_count();
-        vectors.emplace_back(vector, isolate);
+      // Sort functions by start position, from outer to inner functions.
+      SharedFunctionInfo::ScriptIterator infos(isolate, *script_handle);
+      for (SharedFunctionInfo info = infos.Next(); !info.is_null();
+           info = infos.Next()) {
+        sorted.emplace_back(info, counter_map.Get(info));
+      }
+      std::sort(sorted.begin(), sorted.end());
+    }
+
+    // Stack to track nested functions, referring function by index.
+    std::vector<size_t> nesting;
+
+    // Use sorted list to reconstruct function nesting.
+    for (const SharedFunctionInfoAndCount& v : sorted) {
+      SharedFunctionInfo info = v.info;
+      int start = v.start;
+      int end = v.end;
+      uint32_t count = v.count;
+
+      // Find the correct outer function based on start position.
+      //
+      // This is, in general, not robust when considering two functions with
+      // identical source ranges; then the notion of inner and outer is unclear.
+      // Identical source ranges arise when the source range of top-most entity
+      // (e.g. function) in the script is identical to the whole script, e.g.
+      // <script>function foo() {}<script>. The script has its own shared
+      // function info, which has the same source range as the SFI for `foo`.
+      // Node.js creates an additional wrapper for scripts (again with identical
+      // source range) and those wrappers will have a call count of zero even if
+      // the wrapped script was executed (see v8:9212). We mitigate this issue
+      // by sorting top-level SFIs first among SFIs with the same source range:
+      // This ensures top-level SFIs are processed first. If a top-level SFI has
+      // a non-zero call count, it gets recorded due to `function_is_relevant`
+      // below (e.g. script wrappers), while top-level SFIs with zero call count
+      // do not get reported (this ensures node's extra wrappers do not get
+      // reported). If two SFIs with identical source ranges get reported, we
+      // report them in decreasing order of call count, as in all known cases
+      // this corresponds to the nesting order. In the case of the script tag
+      // example above, we report the zero call count of `foo` last. As it turns
+      // out, embedders started to rely on functions being reported in nesting
+      // order.
+      // TODO(jgruber):  Investigate whether it is possible to remove node's
+      // extra  top-level wrapper script, or change its source range, or ensure
+      // that it follows the invariant that nesting order is descending count
+      // order for SFIs with identical source ranges.
+      while (!nesting.empty() && functions->at(nesting.back()).end <= start) {
+        nesting.pop_back();
+      }
+
+      if (count != 0) {
+        switch (collectionMode) {
+          case v8::debug::CoverageMode::kBlockCount:
+          case v8::debug::CoverageMode::kPreciseCount:
+            break;
+          case v8::debug::CoverageMode::kBlockBinary:
+          case v8::debug::CoverageMode::kPreciseBinary:
+            count = info.has_reported_binary_coverage() ? 0 : 1;
+            info.set_has_reported_binary_coverage(true);
+            break;
+          case v8::debug::CoverageMode::kBestEffort:
+            count = 1;
+            break;
+        }
+      }
+
+      Handle<String> name(info.DebugName(), isolate);
+      CoverageFunction function(start, end, count, name);
+
+      if (IsBlockMode(collectionMode) && info.HasCoverageInfo()) {
+        CollectBlockCoverage(&function, info, collectionMode);
+      }
+
+      // Only include a function range if itself or its parent function is
+      // covered, or if it contains non-trivial block coverage.
+      bool is_covered = (count != 0);
+      bool parent_is_covered =
+          (!nesting.empty() && functions->at(nesting.back()).count != 0);
+      bool has_block_coverage = !function.blocks.empty();
+      bool function_is_relevant =
+          (is_covered || parent_is_covered || has_block_coverage);
+
+      // It must also have a non-empty source range (otherwise it is not
+      // interesting to report).
+      bool has_nonempty_source_range = function.HasNonEmptySourceRange();
+
+      if (has_nonempty_source_range && function_is_relevant) {
+        nesting.push_back(functions->size());
+        functions->emplace_back(function);
+      }
+
+      if (FLAG_trace_block_coverage) {
+        PrintBlockCoverage(&function, info, has_nonempty_source_range,
+                           function_is_relevant);
       }
     }
-    // Add collected feedback vectors to the root list lest we lose them to GC.
-    Handle<ArrayList> list =
-        ArrayList::New(isolate, static_cast<int>(vectors.size()));
-    for (const auto& vector : vectors) list = ArrayList::Add(list, vector);
-    isolate->SetCodeCoverageList(*list);
-  } else {
-    isolate->SetCodeCoverageList(isolate->heap()->undefined_value());
+
+    // Remove entries for scripts that have no coverage.
+    if (functions->empty()) result->pop_back();
   }
+  return result;
+}
+
+void Coverage::SelectMode(Isolate* isolate, debug::CoverageMode mode) {
+  if (mode != isolate->code_coverage_mode()) {
+    // Changing the coverage mode can change the bytecode that would be
+    // generated for a function, which can interfere with lazy source positions,
+    // so just force source position collection whenever there's such a change.
+    isolate->CollectSourcePositionsForAllBytecodeArrays();
+  }
+
+  switch (mode) {
+    case debug::CoverageMode::kBestEffort:
+      // Note that DevTools switches back to best-effort coverage once the
+      // recording is stopped. Since we delete coverage infos at that point, any
+      // following coverage recording (without reloads) will be at function
+      // granularity.
+      isolate->debug()->RemoveAllCoverageInfos();
+      if (!isolate->is_collecting_type_profile()) {
+        isolate->SetFeedbackVectorsForProfilingTools(
+            ReadOnlyRoots(isolate).undefined_value());
+      }
+      break;
+    case debug::CoverageMode::kBlockBinary:
+    case debug::CoverageMode::kBlockCount:
+    case debug::CoverageMode::kPreciseBinary:
+    case debug::CoverageMode::kPreciseCount: {
+      HandleScope scope(isolate);
+
+      // Remove all optimized function. Optimized and inlined functions do not
+      // increment invocation count.
+      Deoptimizer::DeoptimizeAll(isolate);
+
+      std::vector<Handle<JSFunction>> funcs_needing_feedback_vector;
+      {
+        HeapObjectIterator heap_iterator(isolate->heap());
+        for (HeapObject o = heap_iterator.Next(); !o.is_null();
+             o = heap_iterator.Next()) {
+          if (o.IsJSFunction()) {
+            JSFunction func = JSFunction::cast(o);
+            if (func.has_closure_feedback_cell_array()) {
+              funcs_needing_feedback_vector.push_back(
+                  Handle<JSFunction>(func, isolate));
+            }
+          } else if (IsBinaryMode(mode) && o.IsSharedFunctionInfo()) {
+            // If collecting binary coverage, reset
+            // SFI::has_reported_binary_coverage to avoid optimizing / inlining
+            // functions before they have reported coverage.
+            SharedFunctionInfo shared = SharedFunctionInfo::cast(o);
+            shared.set_has_reported_binary_coverage(false);
+          } else if (o.IsFeedbackVector()) {
+            // In any case, clear any collected invocation counts.
+            FeedbackVector::cast(o).clear_invocation_count();
+          }
+        }
+      }
+
+      for (Handle<JSFunction> func : funcs_needing_feedback_vector) {
+        IsCompiledScope is_compiled_scope(
+            func->shared().is_compiled_scope(isolate));
+        CHECK(is_compiled_scope.is_compiled());
+        JSFunction::EnsureFeedbackVector(func, &is_compiled_scope);
+      }
+
+      // Root all feedback vectors to avoid early collection.
+      isolate->MaybeInitializeVectorListFromHeap();
+
+      break;
+    }
+  }
+  isolate->set_code_coverage_mode(mode);
 }
 
 }  // namespace internal
diff --git a/src/debug/debug-coverage.h b/src/debug/debug-coverage.h
index 36128bc..81b1781 100644
--- a/src/debug/debug-coverage.h
+++ b/src/debug/debug-coverage.h
@@ -5,10 +5,12 @@
 #ifndef V8_DEBUG_DEBUG_COVERAGE_H_
 #define V8_DEBUG_DEBUG_COVERAGE_H_
 
+#include <memory>
 #include <vector>
 
 #include "src/debug/debug-interface.h"
-#include "src/objects.h"
+#include "src/handles/handles.h"
+#include "src/objects/objects.h"
 
 namespace v8 {
 namespace internal {
@@ -16,18 +18,34 @@
 // Forward declaration.
 class Isolate;
 
+struct CoverageBlock {
+  CoverageBlock(int s, int e, uint32_t c) : start(s), end(e), count(c) {}
+  CoverageBlock() : CoverageBlock(kNoSourcePosition, kNoSourcePosition, 0) {}
+
+  int start;
+  int end;
+  uint32_t count;
+};
+
 struct CoverageFunction {
   CoverageFunction(int s, int e, uint32_t c, Handle<String> n)
-      : start(s), end(e), count(c), name(n) {}
+      : start(s), end(e), count(c), name(n), has_block_coverage(false) {}
+
+  bool HasNonEmptySourceRange() const { return start < end && start >= 0; }
+  bool HasBlocks() const { return !blocks.empty(); }
+
   int start;
   int end;
   uint32_t count;
   Handle<String> name;
+  // Blocks are sorted by start position, from outer to inner blocks.
+  std::vector<CoverageBlock> blocks;
+  bool has_block_coverage;
 };
 
 struct CoverageScript {
   // Initialize top-level function in case it has been garbage-collected.
-  CoverageScript(Isolate* isolate, Handle<Script> s) : script(s) {}
+  explicit CoverageScript(Handle<Script> s) : script(s) {}
   Handle<Script> script;
   // Functions are sorted by start position, from outer to inner function.
   std::vector<CoverageFunction> functions;
@@ -35,16 +53,24 @@
 
 class Coverage : public std::vector<CoverageScript> {
  public:
-  // Allocate a new Coverage object and populate with result.
-  // The ownership is transferred to the caller.
-  static Coverage* Collect(Isolate* isolate, bool reset_count);
+  // Collecting precise coverage only works if the modes kPreciseCount or
+  // kPreciseBinary is selected. The invocation count is reset on collection.
+  // In case of kPreciseCount, an updated count since last collection is
+  // returned. In case of kPreciseBinary, a count of 1 is returned if a
+  // function has been executed for the first time since last collection.
+  static std::unique_ptr<Coverage> CollectPrecise(Isolate* isolate);
+  // Collecting best effort coverage always works, but may be imprecise
+  // depending on selected mode. The invocation count is not reset.
+  static std::unique_ptr<Coverage> CollectBestEffort(Isolate* isolate);
 
-  // Enable precise code coverage. This disables optimization and makes sure
-  // invocation count is not affected by GC.
-  static void TogglePrecise(Isolate* isolate, bool enable);
+  // Select code coverage mode.
+  static void SelectMode(Isolate* isolate, debug::CoverageMode mode);
 
  private:
-  Coverage() {}
+  static std::unique_ptr<Coverage> Collect(
+      Isolate* isolate, v8::debug::CoverageMode collectionMode);
+
+  Coverage() = default;
 };
 
 }  // namespace internal
diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc
index c6fafa5..7fb0b37 100644
--- a/src/debug/debug-evaluate.cc
+++ b/src/debug/debug-evaluate.cc
@@ -4,48 +4,73 @@
 
 #include "src/debug/debug-evaluate.h"
 
-#include "src/accessors.h"
-#include "src/compiler.h"
-#include "src/contexts.h"
+#include "src/builtins/accessors.h"
+#include "src/codegen/assembler-inl.h"
+#include "src/codegen/compiler.h"
+#include "src/common/globals.h"
 #include "src/debug/debug-frames.h"
 #include "src/debug/debug-scopes.h"
 #include "src/debug/debug.h"
-#include "src/frames-inl.h"
-#include "src/globals.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/isolate-inl.h"
 #include "src/interpreter/bytecode-array-iterator.h"
 #include "src/interpreter/bytecodes.h"
-#include "src/isolate-inl.h"
+#include "src/objects/contexts.h"
+#include "src/snapshot/snapshot.h"
+#include "src/wasm/wasm-debug.h"
+#include "src/wasm/wasm-js.h"
 
 namespace v8 {
 namespace internal {
 
-static inline bool IsDebugContext(Isolate* isolate, Context* context) {
-  return context->native_context() == *isolate->debug()->debug_context();
+namespace {
+static MaybeHandle<SharedFunctionInfo> GetFunctionInfo(Isolate* isolate,
+                                                       Handle<String> source,
+                                                       REPLMode repl_mode) {
+  Compiler::ScriptDetails script_details(isolate->factory()->empty_string());
+  script_details.repl_mode = repl_mode;
+  ScriptOriginOptions origin_options(false, true);
+  return Compiler::GetSharedFunctionInfoForScript(
+      isolate, source, script_details, origin_options, nullptr, nullptr,
+      ScriptCompiler::kNoCompileOptions, ScriptCompiler::kNoCacheNoReason,
+      NOT_NATIVES_CODE);
 }
+}  // namespace
 
 MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
-                                          Handle<String> source) {
-  // Handle the processing of break.
-  DisableBreak disable_break_scope(isolate->debug());
+                                          Handle<String> source,
+                                          debug::EvaluateGlobalMode mode,
+                                          REPLMode repl_mode) {
+  // Disable breaks in side-effect free mode.
+  DisableBreak disable_break_scope(
+      isolate->debug(),
+      mode == debug::EvaluateGlobalMode::kDisableBreaks ||
+          mode ==
+              debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect);
 
-  // Enter the top context from before the debugger was invoked.
-  SaveContext save(isolate);
-  SaveContext* top = &save;
-  while (top != NULL && IsDebugContext(isolate, *top->context())) {
-    top = top->prev();
+  Handle<SharedFunctionInfo> shared_info;
+  if (!GetFunctionInfo(isolate, source, repl_mode).ToHandle(&shared_info)) {
+    return MaybeHandle<Object>();
   }
-  if (top != NULL) isolate->set_context(*top->context());
 
-  // Get the native context now set to the top context from before the
-  // debugger was invoked.
   Handle<Context> context = isolate->native_context();
-  Handle<JSObject> receiver(context->global_proxy());
-  Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate);
-  return Evaluate(isolate, outer_info, context, receiver, source, false);
+  Handle<JSFunction> fun =
+      isolate->factory()->NewFunctionFromSharedFunctionInfo(shared_info,
+                                                            context);
+  if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) {
+    isolate->debug()->StartSideEffectCheckMode();
+  }
+  MaybeHandle<Object> result = Execution::Call(
+      isolate, fun, Handle<JSObject>(context->global_proxy(), isolate), 0,
+      nullptr);
+  if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) {
+    isolate->debug()->StopSideEffectCheckMode();
+  }
+  return result;
 }
 
 MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
-                                         StackFrame::Id frame_id,
+                                         StackFrameId frame_id,
                                          int inlined_jsframe_index,
                                          Handle<String> source,
                                          bool throw_on_side_effect) {
@@ -57,13 +82,6 @@
   if (!it.is_javascript()) return isolate->factory()->undefined_value();
   JavaScriptFrame* frame = it.javascript_frame();
 
-  // Traverse the saved contexts chain to find the active context for the
-  // selected frame.
-  SaveContext* save =
-      DebugFrameHelper::FindSavedContextForFrame(isolate, frame);
-  SaveContext savex(isolate);
-  isolate->set_context(*(save->context()));
-
   // This is not a lot different than DebugEvaluate::Global, except that
   // variables accessible by the function we are evaluating from are
   // materialized and included on top of the native context. Changes to
@@ -74,7 +92,7 @@
   if (isolate->has_pending_exception()) return MaybeHandle<Object>();
 
   Handle<Context> context = context_builder.evaluation_context();
-  Handle<JSObject> receiver(context->global_proxy());
+  Handle<JSObject> receiver(context->global_proxy(), isolate);
   MaybeHandle<Object> maybe_result =
       Evaluate(isolate, context_builder.outer_info(), context, receiver, source,
                throw_on_side_effect);
@@ -82,6 +100,84 @@
   return maybe_result;
 }
 
+V8_EXPORT MaybeHandle<Object> DebugEvaluate::WebAssembly(
+    Handle<WasmInstanceObject> instance, StackFrameId frame_id,
+    Handle<String> source, bool throw_on_side_effect) {
+  Isolate* isolate = instance->GetIsolate();
+
+  StackTraceFrameIterator it(isolate, frame_id);
+  if (!it.is_wasm()) return isolate->factory()->undefined_value();
+  WasmFrame* frame = WasmFrame::cast(it.frame());
+
+  Handle<JSProxy> context_extension = WasmJs::GetJSDebugProxy(frame);
+
+  DisableBreak disable_break_scope(isolate->debug(), /*disable=*/true);
+
+  Handle<SharedFunctionInfo> shared_info;
+  if (!GetFunctionInfo(isolate, source, REPLMode::kNo).ToHandle(&shared_info)) {
+    return {};
+  }
+
+  Handle<ScopeInfo> scope_info =
+      ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
+  Handle<Context> context = isolate->factory()->NewWithContext(
+      isolate->native_context(), scope_info, context_extension);
+
+  Handle<Object> result;
+  if (!DebugEvaluate::Evaluate(isolate, shared_info, context, context_extension,
+                               source, throw_on_side_effect)
+           .ToHandle(&result)) {
+    return {};
+  }
+
+  return result;
+}
+
+MaybeHandle<Object> DebugEvaluate::WithTopmostArguments(Isolate* isolate,
+                                                        Handle<String> source) {
+  // Handle the processing of break.
+  DisableBreak disable_break_scope(isolate->debug());
+  Factory* factory = isolate->factory();
+  JavaScriptFrameIterator it(isolate);
+
+  // Get context and receiver.
+  Handle<Context> native_context(
+      Context::cast(it.frame()->context()).native_context(), isolate);
+
+  // Materialize arguments as property on an extension object.
+  Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
+  Handle<String> arguments_str = factory->arguments_string();
+  JSObject::SetOwnPropertyIgnoreAttributes(
+      materialized, arguments_str,
+      Accessors::FunctionGetArguments(it.frame(), 0), NONE)
+      .Check();
+
+  // Materialize receiver.
+  Handle<Object> this_value(it.frame()->receiver(), isolate);
+  DCHECK_EQ(it.frame()->IsConstructor(), this_value->IsTheHole(isolate));
+  if (!this_value->IsTheHole(isolate)) {
+    Handle<String> this_str = factory->this_string();
+    JSObject::SetOwnPropertyIgnoreAttributes(materialized, this_str, this_value,
+                                             NONE)
+        .Check();
+  }
+
+  // Use extension object in a debug-evaluate scope.
+  Handle<ScopeInfo> scope_info =
+      ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
+  scope_info->SetIsDebugEvaluateScope();
+  Handle<Context> evaluation_context =
+      factory->NewDebugEvaluateContext(native_context, scope_info, materialized,
+                                       Handle<Context>(), Handle<StringSet>());
+  Handle<SharedFunctionInfo> outer_info(
+      native_context->empty_function().shared(), isolate);
+  Handle<JSObject> receiver(native_context->global_proxy(), isolate);
+  const bool throw_on_side_effect = false;
+  MaybeHandle<Object> maybe_result =
+      Evaluate(isolate, outer_info, evaluation_context, receiver, source,
+               throw_on_side_effect);
+  return maybe_result;
+}
 
 // Compile and evaluate source for the given context.
 MaybeHandle<Object> DebugEvaluate::Evaluate(
@@ -91,232 +187,233 @@
   Handle<JSFunction> eval_fun;
   ASSIGN_RETURN_ON_EXCEPTION(
       isolate, eval_fun,
-      Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY,
-                                    NO_PARSE_RESTRICTION, kNoSourcePosition,
-                                    kNoSourcePosition, kNoSourcePosition),
+      Compiler::GetFunctionFromEval(source, outer_info, context,
+                                    LanguageMode::kSloppy, NO_PARSE_RESTRICTION,
+                                    kNoSourcePosition, kNoSourcePosition,
+                                    kNoSourcePosition),
       Object);
 
   Handle<Object> result;
-  {
-    NoSideEffectScope no_side_effect(isolate, throw_on_side_effect);
-    ASSIGN_RETURN_ON_EXCEPTION(
-        isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL),
-        Object);
-  }
-
-  // Skip the global proxy as it has no properties and always delegates to the
-  // real global object.
-  if (result->IsJSGlobalProxy()) {
-    PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result));
-    // TODO(verwaest): This will crash when the global proxy is detached.
-    result = PrototypeIterator::GetCurrent<JSObject>(iter);
-  }
-
-  return result;
+  bool success = false;
+  if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
+  success = Execution::Call(isolate, eval_fun, receiver, 0, nullptr)
+                .ToHandle(&result);
+  if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
+  if (!success) DCHECK(isolate->has_pending_exception());
+  return success ? result : MaybeHandle<Object>();
 }
 
+Handle<SharedFunctionInfo> DebugEvaluate::ContextBuilder::outer_info() const {
+  return handle(frame_inspector_.GetFunction()->shared(), isolate_);
+}
 
 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
                                               JavaScriptFrame* frame,
                                               int inlined_jsframe_index)
     : isolate_(isolate),
-      frame_(frame),
-      inlined_jsframe_index_(inlined_jsframe_index) {
-  FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
-  Handle<JSFunction> local_function = frame_inspector.GetFunction();
-  Handle<Context> outer_context(local_function->context());
+      frame_inspector_(frame, inlined_jsframe_index, isolate),
+      scope_iterator_(isolate, &frame_inspector_,
+                      ScopeIterator::ReparseStrategy::kScript) {
+  Handle<Context> outer_context(frame_inspector_.GetFunction()->context(),
+                                isolate);
   evaluation_context_ = outer_context;
-  outer_info_ = handle(local_function->shared());
   Factory* factory = isolate->factory();
 
+  if (scope_iterator_.Done()) return;
+
   // To evaluate as if we were running eval at the point of the debug break,
   // we reconstruct the context chain as follows:
   //  - To make stack-allocated variables visible, we materialize them and
   //    use a debug-evaluate context to wrap both the materialized object and
   //    the original context.
-  //  - We use the original context chain from the function context to the
-  //    native context.
+  //  - We also wrap all contexts on the chain between the original context
+  //    and the function context.
   //  - Between the function scope and the native context, we only resolve
-  //    variable names that the current function already uses. Only for these
-  //    names we can be sure that they will be correctly resolved. For the
-  //    rest, we only resolve to with, script, and native contexts. We use a
-  //    whitelist to implement that.
+  //    variable names that are guaranteed to not be shadowed by stack-allocated
+  //    variables. Contexts between the function context and the original
+  //    context have a blocklist attached to implement that.
   // Context::Lookup has special handling for debug-evaluate contexts:
   //  - Look up in the materialized stack variables.
+  //  - Check the blocklist to find out whether to abort further lookup.
   //  - Look up in the original context.
-  //  - Check the whitelist to find out whether to skip contexts during lookup.
-  const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
-  for (ScopeIterator it(isolate, &frame_inspector, option); !it.Done();
-       it.Next()) {
-    ScopeIterator::ScopeType scope_type = it.Type();
-    if (scope_type == ScopeIterator::ScopeTypeLocal) {
-      DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
-      Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
-      Handle<Context> local_context =
-          it.HasContext() ? it.CurrentContext() : outer_context;
-      Handle<StringSet> non_locals = it.GetNonLocals();
-      MaterializeReceiver(materialized, local_context, local_function,
-                          non_locals);
-      frame_inspector.MaterializeStackLocals(materialized, local_function);
-      MaterializeArgumentsObject(materialized, local_function);
-      ContextChainElement context_chain_element;
-      context_chain_element.scope_info = it.CurrentScopeInfo();
-      context_chain_element.materialized_object = materialized;
-      // Non-locals that are already being referenced by the current function
-      // are guaranteed to be correctly resolved.
-      context_chain_element.whitelist = non_locals;
-      if (it.HasContext()) {
-        context_chain_element.wrapped_context = it.CurrentContext();
-      }
-      context_chain_.Add(context_chain_element);
-      evaluation_context_ = outer_context;
-      break;
-    } else if (scope_type == ScopeIterator::ScopeTypeCatch ||
-               scope_type == ScopeIterator::ScopeTypeWith) {
-      ContextChainElement context_chain_element;
-      Handle<Context> current_context = it.CurrentContext();
-      if (!current_context->IsDebugEvaluateContext()) {
-        context_chain_element.wrapped_context = current_context;
-      }
-      context_chain_.Add(context_chain_element);
-    } else if (scope_type == ScopeIterator::ScopeTypeBlock ||
-               scope_type == ScopeIterator::ScopeTypeEval) {
-      Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
-      frame_inspector.MaterializeStackLocals(materialized,
-                                             it.CurrentScopeInfo());
-      ContextChainElement context_chain_element;
-      context_chain_element.scope_info = it.CurrentScopeInfo();
-      context_chain_element.materialized_object = materialized;
-      if (it.HasContext()) {
-        context_chain_element.wrapped_context = it.CurrentContext();
-      }
-      context_chain_.Add(context_chain_element);
-    } else {
-      break;
+  for (; !scope_iterator_.Done(); scope_iterator_.Next()) {
+    ScopeIterator::ScopeType scope_type = scope_iterator_.Type();
+    if (scope_type == ScopeIterator::ScopeTypeScript) break;
+    ContextChainElement context_chain_element;
+    if (scope_iterator_.InInnerScope() &&
+        (scope_type == ScopeIterator::ScopeTypeLocal ||
+         scope_iterator_.DeclaresLocals(ScopeIterator::Mode::STACK))) {
+      context_chain_element.materialized_object =
+          scope_iterator_.ScopeObject(ScopeIterator::Mode::STACK);
     }
+    if (scope_iterator_.HasContext()) {
+      context_chain_element.wrapped_context = scope_iterator_.CurrentContext();
+    }
+    if (!scope_iterator_.InInnerScope()) {
+      context_chain_element.blocklist = scope_iterator_.GetLocals();
+    }
+    context_chain_.push_back(context_chain_element);
   }
 
-  for (int i = context_chain_.length() - 1; i >= 0; i--) {
-    Handle<ScopeInfo> scope_info(ScopeInfo::CreateForWithScope(
-        isolate, evaluation_context_->IsNativeContext()
-                     ? Handle<ScopeInfo>::null()
-                     : Handle<ScopeInfo>(evaluation_context_->scope_info())));
+  Handle<ScopeInfo> scope_info =
+      evaluation_context_->IsNativeContext()
+          ? Handle<ScopeInfo>::null()
+          : handle(evaluation_context_->scope_info(), isolate);
+  for (auto rit = context_chain_.rbegin(); rit != context_chain_.rend();
+       rit++) {
+    ContextChainElement element = *rit;
+    scope_info = ScopeInfo::CreateForWithScope(isolate, scope_info);
     scope_info->SetIsDebugEvaluateScope();
     evaluation_context_ = factory->NewDebugEvaluateContext(
-        evaluation_context_, scope_info, context_chain_[i].materialized_object,
-        context_chain_[i].wrapped_context, context_chain_[i].whitelist);
+        evaluation_context_, scope_info, element.materialized_object,
+        element.wrapped_context, element.blocklist);
   }
 }
 
-
 void DebugEvaluate::ContextBuilder::UpdateValues() {
-  // TODO(yangguo): remove updating values.
-  for (int i = 0; i < context_chain_.length(); i++) {
-    ContextChainElement element = context_chain_[i];
+  scope_iterator_.Restart();
+  for (ContextChainElement& element : context_chain_) {
     if (!element.materialized_object.is_null()) {
-      // Write back potential changes to materialized stack locals to the stack.
-      FrameInspector(frame_, inlined_jsframe_index_, isolate_)
-          .UpdateStackLocalsFromMaterializedObject(element.materialized_object,
-                                                   element.scope_info);
+      Handle<FixedArray> keys =
+          KeyAccumulator::GetKeys(element.materialized_object,
+                                  KeyCollectionMode::kOwnOnly,
+                                  ENUMERABLE_STRINGS)
+              .ToHandleChecked();
+
+      for (int i = 0; i < keys->length(); i++) {
+        DCHECK(keys->get(i).IsString());
+        Handle<String> key(String::cast(keys->get(i)), isolate_);
+        Handle<Object> value =
+            JSReceiver::GetDataProperty(element.materialized_object, key);
+        scope_iterator_.SetVariableValue(key, value);
+      }
     }
+    scope_iterator_.Next();
   }
 }
 
-
-void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
-    Handle<JSObject> target, Handle<JSFunction> function) {
-  // Do not materialize the arguments object for eval or top-level code.
-  // Skip if "arguments" is already taken.
-  if (function->shared()->is_toplevel()) return;
-  Maybe<bool> maybe = JSReceiver::HasOwnProperty(
-      target, isolate_->factory()->arguments_string());
-  DCHECK(maybe.IsJust());
-  if (maybe.FromJust()) return;
-
-  // FunctionGetArguments can't throw an exception.
-  Handle<JSObject> arguments = Accessors::FunctionGetArguments(function);
-  Handle<String> arguments_str = isolate_->factory()->arguments_string();
-  JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
-                                           NONE)
-      .Check();
-}
-
-void DebugEvaluate::ContextBuilder::MaterializeReceiver(
-    Handle<JSObject> target, Handle<Context> local_context,
-    Handle<JSFunction> local_function, Handle<StringSet> non_locals) {
-  Handle<Object> recv = isolate_->factory()->undefined_value();
-  Handle<String> name = isolate_->factory()->this_string();
-  if (non_locals->Has(name)) {
-    // 'this' is allocated in an outer context and is is already being
-    // referenced by the current function, so it can be correctly resolved.
-    return;
-  } else if (local_function->shared()->scope_info()->HasReceiver() &&
-             !frame_->receiver()->IsTheHole(isolate_)) {
-    recv = handle(frame_->receiver(), isolate_);
-  }
-  JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check();
-}
-
 namespace {
 
 bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
+// Use macro to include only the non-inlined version of an intrinsic.
+#define INTRINSIC_ALLOWLIST(V)                \
+  /* Conversions */                           \
+  V(NumberToStringSlow)                       \
+  V(ToBigInt)                                 \
+  V(ToLength)                                 \
+  V(ToNumber)                                 \
+  V(ToObject)                                 \
+  V(ToString)                                 \
+  /* Type checks */                           \
+  V(IsArray)                                  \
+  V(IsFunction)                               \
+  V(IsJSProxy)                                \
+  V(IsJSReceiver)                             \
+  V(IsRegExp)                                 \
+  V(IsSmi)                                    \
+  /* Loads */                                 \
+  V(LoadLookupSlotForCall)                    \
+  V(GetProperty)                              \
+  /* Arrays */                                \
+  V(ArraySpeciesConstructor)                  \
+  V(HasFastPackedElements)                    \
+  V(NewArray)                                 \
+  V(NormalizeElements)                        \
+  V(TypedArrayGetBuffer)                      \
+  /* Errors */                                \
+  V(NewTypeError)                             \
+  V(ReThrow)                                  \
+  V(ThrowCalledNonCallable)                   \
+  V(ThrowInvalidStringLength)                 \
+  V(ThrowIteratorError)                       \
+  V(ThrowIteratorResultNotAnObject)           \
+  V(ThrowPatternAssignmentNonCoercible)       \
+  V(ThrowReferenceError)                      \
+  V(ThrowSymbolIteratorInvalid)               \
+  /* Strings */                               \
+  V(StringIncludes)                           \
+  V(StringIndexOf)                            \
+  V(StringReplaceOneCharWithString)           \
+  V(StringSubstring)                          \
+  V(StringToNumber)                           \
+  V(StringTrim)                               \
+  /* BigInts */                               \
+  V(BigIntEqualToBigInt)                      \
+  V(BigIntToBoolean)                          \
+  V(BigIntToNumber)                           \
+  /* Literals */                              \
+  V(CreateArrayLiteral)                       \
+  V(CreateArrayLiteralWithoutAllocationSite)  \
+  V(CreateObjectLiteral)                      \
+  V(CreateObjectLiteralWithoutAllocationSite) \
+  V(CreateRegExpLiteral)                      \
+  /* Called from builtins */                  \
+  V(AllocateInYoungGeneration)                \
+  V(AllocateInOldGeneration)                  \
+  V(AllocateSeqOneByteString)                 \
+  V(AllocateSeqTwoByteString)                 \
+  V(ArrayIncludes_Slow)                       \
+  V(ArrayIndexOf)                             \
+  V(ArrayIsArray)                             \
+  V(GetFunctionName)                          \
+  V(GetOwnPropertyDescriptor)                 \
+  V(GlobalPrint)                              \
+  V(HasProperty)                              \
+  V(ObjectCreate)                             \
+  V(ObjectEntries)                            \
+  V(ObjectEntriesSkipFastPath)                \
+  V(ObjectHasOwnProperty)                     \
+  V(ObjectKeys)                               \
+  V(ObjectValues)                             \
+  V(ObjectValuesSkipFastPath)                 \
+  V(ObjectGetOwnPropertyNames)                \
+  V(ObjectGetOwnPropertyNamesTryFast)         \
+  V(ObjectIsExtensible)                       \
+  V(RegExpInitializeAndCompile)               \
+  V(StackGuard)                               \
+  V(StringAdd)                                \
+  V(StringCharCodeAt)                         \
+  V(StringEqual)                              \
+  V(StringIndexOfUnchecked)                   \
+  V(StringParseFloat)                         \
+  V(StringParseInt)                           \
+  V(SymbolDescriptiveString)                  \
+  V(ThrowRangeError)                          \
+  V(ThrowTypeError)                           \
+  V(ToName)                                   \
+  V(TransitionElementsKind)                   \
+  /* Misc. */                                 \
+  V(Call)                                     \
+  V(CompleteInobjectSlackTrackingForMap)      \
+  V(HasInPrototypeChain)                      \
+  V(IncrementUseCounter)                      \
+  V(MaxSmi)                                   \
+  V(NewObject)                                \
+  V(StringMaxLength)                          \
+  V(StringToArray)                            \
+  V(AsyncFunctionEnter)                       \
+  V(AsyncFunctionReject)                      \
+  V(AsyncFunctionResolve)                     \
+  /* Test */                                  \
+  V(GetOptimizationStatus)                    \
+  V(OptimizeFunctionOnNextCall)               \
+  V(OptimizeOsr)                              \
+  V(UnblockConcurrentRecompilation)
+
+// Intrinsics with inline versions have to be allowlisted here a second time.
+#define INLINE_INTRINSIC_ALLOWLIST(V) \
+  V(Call)                             \
+  V(IsJSReceiver)                     \
+  V(AsyncFunctionEnter)               \
+  V(AsyncFunctionReject)              \
+  V(AsyncFunctionResolve)
+
+#define CASE(Name) case Runtime::k##Name:
+#define INLINE_CASE(Name) case Runtime::kInline##Name:
   switch (id) {
-    // Whitelist for intrinsics amd runtime functions.
-    // Conversions.
-    case Runtime::kToInteger:
-    case Runtime::kInlineToInteger:
-    case Runtime::kToObject:
-    case Runtime::kInlineToObject:
-    case Runtime::kToString:
-    case Runtime::kInlineToString:
-    case Runtime::kToLength:
-    case Runtime::kInlineToLength:
-    case Runtime::kToNumber:
-    // Type checks.
-    case Runtime::kIsJSReceiver:
-    case Runtime::kInlineIsJSReceiver:
-    case Runtime::kIsSmi:
-    case Runtime::kInlineIsSmi:
-    case Runtime::kIsArray:
-    case Runtime::kInlineIsArray:
-    case Runtime::kIsFunction:
-    case Runtime::kIsDate:
-    case Runtime::kIsJSProxy:
-    case Runtime::kIsRegExp:
-    case Runtime::kIsTypedArray:
-    // Loads.
-    case Runtime::kLoadLookupSlotForCall:
-    // Arrays.
-    case Runtime::kArraySpeciesConstructor:
-    case Runtime::kNormalizeElements:
-    case Runtime::kGetArrayKeys:
-    case Runtime::kHasComplexElements:
-    case Runtime::kEstimateNumberOfElements:
-    // Errors.
-    case Runtime::kReThrow:
-    case Runtime::kThrowReferenceError:
-    case Runtime::kThrowSymbolIteratorInvalid:
-    case Runtime::kThrowIteratorResultNotAnObject:
-    case Runtime::kNewTypeError:
-    // Strings.
-    case Runtime::kInlineStringCharCodeAt:
-    case Runtime::kStringCharCodeAt:
-    case Runtime::kStringIndexOf:
-    case Runtime::kStringReplaceOneCharWithString:
-    case Runtime::kSubString:
-    case Runtime::kInlineSubString:
-    case Runtime::kRegExpInternalReplace:
-    // Literals.
-    case Runtime::kCreateArrayLiteral:
-    case Runtime::kCreateObjectLiteral:
-    case Runtime::kCreateRegExpLiteral:
-    // Misc.
-    case Runtime::kForInPrepare:
-    case Runtime::kInlineCall:
-    case Runtime::kCall:
-    case Runtime::kInlineMaxSmi:
-    case Runtime::kMaxSmi:
-      return true;
+    INTRINSIC_ALLOWLIST(CASE)
+    INLINE_INTRINSIC_ALLOWLIST(INLINE_CASE)
+    return true;
     default:
       if (FLAG_trace_side_effect_free_debug_evaluate) {
         PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n",
@@ -324,41 +421,58 @@
       }
       return false;
   }
+
+#undef CASE
+#undef INLINE_CASE
+#undef INTRINSIC_ALLOWLIST
+#undef INLINE_INTRINSIC_ALLOWLIST
 }
 
 bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
-  typedef interpreter::Bytecode Bytecode;
-  typedef interpreter::Bytecodes Bytecodes;
+  using interpreter::Bytecode;
+  using interpreter::Bytecodes;
   if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true;
   if (Bytecodes::IsCallOrConstruct(bytecode)) return true;
-  if (Bytecodes::WritesBooleanToAccumulator(bytecode)) return true;
   if (Bytecodes::IsJumpIfToBoolean(bytecode)) return true;
   if (Bytecodes::IsPrefixScalingBytecode(bytecode)) return true;
   switch (bytecode) {
-    // Whitelist for bytecodes.
+    // Allowlist for bytecodes.
     // Loads.
     case Bytecode::kLdaLookupSlot:
     case Bytecode::kLdaGlobal:
     case Bytecode::kLdaNamedProperty:
+    case Bytecode::kLdaNamedPropertyNoFeedback:
     case Bytecode::kLdaKeyedProperty:
+    case Bytecode::kLdaGlobalInsideTypeof:
+    case Bytecode::kLdaLookupSlotInsideTypeof:
+    case Bytecode::kGetIterator:
     // Arithmetics.
     case Bytecode::kAdd:
     case Bytecode::kAddSmi:
     case Bytecode::kSub:
     case Bytecode::kSubSmi:
     case Bytecode::kMul:
+    case Bytecode::kMulSmi:
     case Bytecode::kDiv:
+    case Bytecode::kDivSmi:
     case Bytecode::kMod:
+    case Bytecode::kModSmi:
+    case Bytecode::kExp:
+    case Bytecode::kExpSmi:
+    case Bytecode::kNegate:
     case Bytecode::kBitwiseAnd:
     case Bytecode::kBitwiseAndSmi:
+    case Bytecode::kBitwiseNot:
     case Bytecode::kBitwiseOr:
     case Bytecode::kBitwiseOrSmi:
     case Bytecode::kBitwiseXor:
+    case Bytecode::kBitwiseXorSmi:
     case Bytecode::kShiftLeft:
     case Bytecode::kShiftLeftSmi:
     case Bytecode::kShiftRight:
     case Bytecode::kShiftRightSmi:
     case Bytecode::kShiftRightLogical:
+    case Bytecode::kShiftRightLogicalSmi:
     case Bytecode::kInc:
     case Bytecode::kDec:
     case Bytecode::kLogicalNot:
@@ -372,47 +486,210 @@
     case Bytecode::kCreateWithContext:
     // Literals.
     case Bytecode::kCreateArrayLiteral:
+    case Bytecode::kCreateEmptyArrayLiteral:
+    case Bytecode::kCreateArrayFromIterable:
     case Bytecode::kCreateObjectLiteral:
+    case Bytecode::kCreateEmptyObjectLiteral:
     case Bytecode::kCreateRegExpLiteral:
     // Allocations.
     case Bytecode::kCreateClosure:
     case Bytecode::kCreateUnmappedArguments:
+    case Bytecode::kCreateRestParameter:
+    // Comparisons.
+    case Bytecode::kTestEqual:
+    case Bytecode::kTestEqualStrict:
+    case Bytecode::kTestLessThan:
+    case Bytecode::kTestLessThanOrEqual:
+    case Bytecode::kTestGreaterThan:
+    case Bytecode::kTestGreaterThanOrEqual:
+    case Bytecode::kTestInstanceOf:
+    case Bytecode::kTestIn:
+    case Bytecode::kTestReferenceEqual:
+    case Bytecode::kTestUndetectable:
+    case Bytecode::kTestTypeOf:
+    case Bytecode::kTestUndefined:
+    case Bytecode::kTestNull:
     // Conversions.
     case Bytecode::kToObject:
+    case Bytecode::kToName:
     case Bytecode::kToNumber:
+    case Bytecode::kToNumeric:
+    case Bytecode::kToString:
     // Misc.
+    case Bytecode::kIncBlockCounter:  // Coverage counters.
+    case Bytecode::kForInEnumerate:
     case Bytecode::kForInPrepare:
     case Bytecode::kForInContinue:
     case Bytecode::kForInNext:
     case Bytecode::kForInStep:
+    case Bytecode::kJumpLoop:
     case Bytecode::kThrow:
     case Bytecode::kReThrow:
+    case Bytecode::kThrowReferenceErrorIfHole:
+    case Bytecode::kThrowSuperNotCalledIfHole:
+    case Bytecode::kThrowSuperAlreadyCalledIfNotHole:
     case Bytecode::kIllegal:
     case Bytecode::kCallJSRuntime:
-    case Bytecode::kStackCheck:
     case Bytecode::kReturn:
     case Bytecode::kSetPendingMessage:
       return true;
     default:
-      if (FLAG_trace_side_effect_free_debug_evaluate) {
-        PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
-               Bytecodes::ToString(bytecode));
-      }
       return false;
   }
 }
 
-bool BuiltinHasNoSideEffect(Builtins::Name id) {
+DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) {
   switch (id) {
-    // Whitelist for builtins.
+    // Allowlist for builtins.
+    // Object builtins.
+    case Builtins::kObjectConstructor:
+    case Builtins::kObjectCreate:
+    case Builtins::kObjectEntries:
+    case Builtins::kObjectGetOwnPropertyDescriptor:
+    case Builtins::kObjectGetOwnPropertyDescriptors:
+    case Builtins::kObjectGetOwnPropertyNames:
+    case Builtins::kObjectGetOwnPropertySymbols:
+    case Builtins::kObjectGetPrototypeOf:
+    case Builtins::kObjectIs:
+    case Builtins::kObjectIsExtensible:
+    case Builtins::kObjectIsFrozen:
+    case Builtins::kObjectIsSealed:
+    case Builtins::kObjectKeys:
+    case Builtins::kObjectPrototypeValueOf:
+    case Builtins::kObjectValues:
+    case Builtins::kObjectPrototypeHasOwnProperty:
+    case Builtins::kObjectPrototypeIsPrototypeOf:
+    case Builtins::kObjectPrototypePropertyIsEnumerable:
+    case Builtins::kObjectPrototypeToString:
+    case Builtins::kObjectPrototypeToLocaleString:
     // Array builtins.
-    case Builtins::kArrayCode:
+    case Builtins::kArrayIsArray:
+    case Builtins::kArrayConstructor:
     case Builtins::kArrayIndexOf:
     case Builtins::kArrayPrototypeValues:
     case Builtins::kArrayIncludes:
     case Builtins::kArrayPrototypeEntries:
+    case Builtins::kArrayPrototypeFill:
+    case Builtins::kArrayPrototypeFind:
+    case Builtins::kArrayPrototypeFindIndex:
+    case Builtins::kArrayPrototypeFlat:
+    case Builtins::kArrayPrototypeFlatMap:
+    case Builtins::kArrayPrototypeJoin:
     case Builtins::kArrayPrototypeKeys:
+    case Builtins::kArrayPrototypeLastIndexOf:
+    case Builtins::kArrayPrototypeSlice:
+    case Builtins::kArrayPrototypeToLocaleString:
+    case Builtins::kArrayPrototypeToString:
     case Builtins::kArrayForEach:
+    case Builtins::kArrayEvery:
+    case Builtins::kArraySome:
+    case Builtins::kArrayConcat:
+    case Builtins::kArrayFilter:
+    case Builtins::kArrayMap:
+    case Builtins::kArrayReduce:
+    case Builtins::kArrayReduceRight:
+    // Trace builtins.
+    case Builtins::kIsTraceCategoryEnabled:
+    case Builtins::kTrace:
+    // TypedArray builtins.
+    case Builtins::kTypedArrayConstructor:
+    case Builtins::kTypedArrayPrototypeBuffer:
+    case Builtins::kTypedArrayPrototypeByteLength:
+    case Builtins::kTypedArrayPrototypeByteOffset:
+    case Builtins::kTypedArrayPrototypeLength:
+    case Builtins::kTypedArrayPrototypeEntries:
+    case Builtins::kTypedArrayPrototypeKeys:
+    case Builtins::kTypedArrayPrototypeValues:
+    case Builtins::kTypedArrayPrototypeFind:
+    case Builtins::kTypedArrayPrototypeFindIndex:
+    case Builtins::kTypedArrayPrototypeIncludes:
+    case Builtins::kTypedArrayPrototypeJoin:
+    case Builtins::kTypedArrayPrototypeIndexOf:
+    case Builtins::kTypedArrayPrototypeLastIndexOf:
+    case Builtins::kTypedArrayPrototypeSlice:
+    case Builtins::kTypedArrayPrototypeSubArray:
+    case Builtins::kTypedArrayPrototypeEvery:
+    case Builtins::kTypedArrayPrototypeSome:
+    case Builtins::kTypedArrayPrototypeToLocaleString:
+    case Builtins::kTypedArrayPrototypeFilter:
+    case Builtins::kTypedArrayPrototypeMap:
+    case Builtins::kTypedArrayPrototypeReduce:
+    case Builtins::kTypedArrayPrototypeReduceRight:
+    case Builtins::kTypedArrayPrototypeForEach:
+    // ArrayBuffer builtins.
+    case Builtins::kArrayBufferConstructor:
+    case Builtins::kArrayBufferPrototypeGetByteLength:
+    case Builtins::kArrayBufferIsView:
+    case Builtins::kArrayBufferPrototypeSlice:
+    case Builtins::kReturnReceiver:
+    // DataView builtins.
+    case Builtins::kDataViewConstructor:
+    case Builtins::kDataViewPrototypeGetBuffer:
+    case Builtins::kDataViewPrototypeGetByteLength:
+    case Builtins::kDataViewPrototypeGetByteOffset:
+    case Builtins::kDataViewPrototypeGetInt8:
+    case Builtins::kDataViewPrototypeGetUint8:
+    case Builtins::kDataViewPrototypeGetInt16:
+    case Builtins::kDataViewPrototypeGetUint16:
+    case Builtins::kDataViewPrototypeGetInt32:
+    case Builtins::kDataViewPrototypeGetUint32:
+    case Builtins::kDataViewPrototypeGetFloat32:
+    case Builtins::kDataViewPrototypeGetFloat64:
+    case Builtins::kDataViewPrototypeGetBigInt64:
+    case Builtins::kDataViewPrototypeGetBigUint64:
+    // Boolean bulitins.
+    case Builtins::kBooleanConstructor:
+    case Builtins::kBooleanPrototypeToString:
+    case Builtins::kBooleanPrototypeValueOf:
+    // Date builtins.
+    case Builtins::kDateConstructor:
+    case Builtins::kDateNow:
+    case Builtins::kDateParse:
+    case Builtins::kDatePrototypeGetDate:
+    case Builtins::kDatePrototypeGetDay:
+    case Builtins::kDatePrototypeGetFullYear:
+    case Builtins::kDatePrototypeGetHours:
+    case Builtins::kDatePrototypeGetMilliseconds:
+    case Builtins::kDatePrototypeGetMinutes:
+    case Builtins::kDatePrototypeGetMonth:
+    case Builtins::kDatePrototypeGetSeconds:
+    case Builtins::kDatePrototypeGetTime:
+    case Builtins::kDatePrototypeGetTimezoneOffset:
+    case Builtins::kDatePrototypeGetUTCDate:
+    case Builtins::kDatePrototypeGetUTCDay:
+    case Builtins::kDatePrototypeGetUTCFullYear:
+    case Builtins::kDatePrototypeGetUTCHours:
+    case Builtins::kDatePrototypeGetUTCMilliseconds:
+    case Builtins::kDatePrototypeGetUTCMinutes:
+    case Builtins::kDatePrototypeGetUTCMonth:
+    case Builtins::kDatePrototypeGetUTCSeconds:
+    case Builtins::kDatePrototypeGetYear:
+    case Builtins::kDatePrototypeToDateString:
+    case Builtins::kDatePrototypeToISOString:
+    case Builtins::kDatePrototypeToUTCString:
+    case Builtins::kDatePrototypeToString:
+#ifdef V8_INTL_SUPPORT
+    case Builtins::kDatePrototypeToLocaleString:
+    case Builtins::kDatePrototypeToLocaleDateString:
+    case Builtins::kDatePrototypeToLocaleTimeString:
+#endif
+    case Builtins::kDatePrototypeToTimeString:
+    case Builtins::kDatePrototypeToJson:
+    case Builtins::kDatePrototypeToPrimitive:
+    case Builtins::kDatePrototypeValueOf:
+    // Map builtins.
+    case Builtins::kMapConstructor:
+    case Builtins::kMapPrototypeForEach:
+    case Builtins::kMapPrototypeGet:
+    case Builtins::kMapPrototypeHas:
+    case Builtins::kMapPrototypeEntries:
+    case Builtins::kMapPrototypeGetSize:
+    case Builtins::kMapPrototypeKeys:
+    case Builtins::kMapPrototypeValues:
+    // WeakMap builtins.
+    case Builtins::kWeakMapConstructor:
+    case Builtins::kWeakMapGet:
+    case Builtins::kWeakMapPrototypeHas:
     // Math builtins.
     case Builtins::kMathAbs:
     case Builtins::kMathAcos:
@@ -440,7 +717,6 @@
     case Builtins::kMathMax:
     case Builtins::kMathMin:
     case Builtins::kMathPow:
-    case Builtins::kMathRandom:
     case Builtins::kMathRound:
     case Builtins::kMathSign:
     case Builtins::kMathSin:
@@ -461,66 +737,181 @@
     case Builtins::kNumberPrototypeToFixed:
     case Builtins::kNumberPrototypeToPrecision:
     case Builtins::kNumberPrototypeToString:
+    case Builtins::kNumberPrototypeToLocaleString:
     case Builtins::kNumberPrototypeValueOf:
+    // BigInt builtins.
+    case Builtins::kBigIntConstructor:
+    case Builtins::kBigIntAsIntN:
+    case Builtins::kBigIntAsUintN:
+    case Builtins::kBigIntPrototypeToString:
+    case Builtins::kBigIntPrototypeValueOf:
+    // Set builtins.
+    case Builtins::kSetConstructor:
+    case Builtins::kSetPrototypeEntries:
+    case Builtins::kSetPrototypeForEach:
+    case Builtins::kSetPrototypeGetSize:
+    case Builtins::kSetPrototypeHas:
+    case Builtins::kSetPrototypeValues:
+    // WeakSet builtins.
+    case Builtins::kWeakSetConstructor:
+    case Builtins::kWeakSetPrototypeHas:
     // String builtins. Strings are immutable.
     case Builtins::kStringFromCharCode:
     case Builtins::kStringFromCodePoint:
     case Builtins::kStringConstructor:
+    case Builtins::kStringPrototypeAnchor:
+    case Builtins::kStringPrototypeBig:
+    case Builtins::kStringPrototypeBlink:
+    case Builtins::kStringPrototypeBold:
     case Builtins::kStringPrototypeCharAt:
     case Builtins::kStringPrototypeCharCodeAt:
+    case Builtins::kStringPrototypeCodePointAt:
+    case Builtins::kStringPrototypeConcat:
     case Builtins::kStringPrototypeEndsWith:
+    case Builtins::kStringPrototypeFixed:
+    case Builtins::kStringPrototypeFontcolor:
+    case Builtins::kStringPrototypeFontsize:
     case Builtins::kStringPrototypeIncludes:
     case Builtins::kStringPrototypeIndexOf:
+    case Builtins::kStringPrototypeItalics:
     case Builtins::kStringPrototypeLastIndexOf:
+    case Builtins::kStringPrototypeLink:
+    case Builtins::kStringPrototypeMatchAll:
+    case Builtins::kStringPrototypePadEnd:
+    case Builtins::kStringPrototypePadStart:
+    case Builtins::kStringPrototypeRepeat:
+    case Builtins::kStringPrototypeSlice:
+    case Builtins::kStringPrototypeSmall:
     case Builtins::kStringPrototypeStartsWith:
+    case Builtins::kStringPrototypeStrike:
+    case Builtins::kStringPrototypeSub:
     case Builtins::kStringPrototypeSubstr:
     case Builtins::kStringPrototypeSubstring:
+    case Builtins::kStringPrototypeSup:
     case Builtins::kStringPrototypeToString:
+#ifndef V8_INTL_SUPPORT
     case Builtins::kStringPrototypeToLowerCase:
     case Builtins::kStringPrototypeToUpperCase:
+#endif
     case Builtins::kStringPrototypeTrim:
-    case Builtins::kStringPrototypeTrimLeft:
-    case Builtins::kStringPrototypeTrimRight:
+    case Builtins::kStringPrototypeTrimEnd:
+    case Builtins::kStringPrototypeTrimStart:
     case Builtins::kStringPrototypeValueOf:
+    case Builtins::kStringToNumber:
+    case Builtins::kStringSubstring:
+    // Symbol builtins.
+    case Builtins::kSymbolConstructor:
+    case Builtins::kSymbolKeyFor:
+    case Builtins::kSymbolPrototypeToString:
+    case Builtins::kSymbolPrototypeValueOf:
+    case Builtins::kSymbolPrototypeToPrimitive:
     // JSON builtins.
     case Builtins::kJsonParse:
     case Builtins::kJsonStringify:
+    // Global function builtins.
+    case Builtins::kGlobalDecodeURI:
+    case Builtins::kGlobalDecodeURIComponent:
+    case Builtins::kGlobalEncodeURI:
+    case Builtins::kGlobalEncodeURIComponent:
+    case Builtins::kGlobalEscape:
+    case Builtins::kGlobalUnescape:
+    case Builtins::kGlobalIsFinite:
+    case Builtins::kGlobalIsNaN:
+    // Function builtins.
+    case Builtins::kFunctionPrototypeToString:
+    case Builtins::kFunctionPrototypeBind:
+    case Builtins::kFastFunctionPrototypeBind:
+    case Builtins::kFunctionPrototypeCall:
+    case Builtins::kFunctionPrototypeApply:
     // Error builtins.
-    case Builtins::kMakeError:
-    case Builtins::kMakeTypeError:
-    case Builtins::kMakeSyntaxError:
-    case Builtins::kMakeRangeError:
-    case Builtins::kMakeURIError:
-      return true;
+    case Builtins::kErrorConstructor:
+    // RegExp builtins.
+    case Builtins::kRegExpConstructor:
+    // Internal.
+    case Builtins::kStrictPoisonPillThrower:
+    case Builtins::kAllocateInYoungGeneration:
+    case Builtins::kAllocateInOldGeneration:
+    case Builtins::kAllocateRegularInYoungGeneration:
+    case Builtins::kAllocateRegularInOldGeneration:
+      return DebugInfo::kHasNoSideEffect;
+
+    // Set builtins.
+    case Builtins::kSetIteratorPrototypeNext:
+    case Builtins::kSetPrototypeAdd:
+    case Builtins::kSetPrototypeClear:
+    case Builtins::kSetPrototypeDelete:
+    // Array builtins.
+    case Builtins::kArrayIteratorPrototypeNext:
+    case Builtins::kArrayPrototypePop:
+    case Builtins::kArrayPrototypePush:
+    case Builtins::kArrayPrototypeReverse:
+    case Builtins::kArrayPrototypeShift:
+    case Builtins::kArrayPrototypeUnshift:
+    case Builtins::kArrayPrototypeSort:
+    case Builtins::kArrayPrototypeSplice:
+    case Builtins::kArrayUnshift:
+    // Map builtins.
+    case Builtins::kMapIteratorPrototypeNext:
+    case Builtins::kMapPrototypeClear:
+    case Builtins::kMapPrototypeDelete:
+    case Builtins::kMapPrototypeSet:
+    // RegExp builtins.
+    case Builtins::kRegExpPrototypeTest:
+    case Builtins::kRegExpPrototypeExec:
+    case Builtins::kRegExpPrototypeSplit:
+    case Builtins::kRegExpPrototypeFlagsGetter:
+    case Builtins::kRegExpPrototypeGlobalGetter:
+    case Builtins::kRegExpPrototypeIgnoreCaseGetter:
+    case Builtins::kRegExpPrototypeMatchAll:
+    case Builtins::kRegExpPrototypeMultilineGetter:
+    case Builtins::kRegExpPrototypeDotAllGetter:
+    case Builtins::kRegExpPrototypeUnicodeGetter:
+    case Builtins::kRegExpPrototypeStickyGetter:
+      return DebugInfo::kRequiresRuntimeChecks;
     default:
       if (FLAG_trace_side_effect_free_debug_evaluate) {
         PrintF("[debug-evaluate] built-in %s may cause side effect.\n",
                Builtins::name(id));
       }
+      return DebugInfo::kHasSideEffects;
+  }
+}
+
+bool BytecodeRequiresRuntimeCheck(interpreter::Bytecode bytecode) {
+  using interpreter::Bytecode;
+  switch (bytecode) {
+    case Bytecode::kStaNamedProperty:
+    case Bytecode::kStaNamedPropertyNoFeedback:
+    case Bytecode::kStaNamedOwnProperty:
+    case Bytecode::kStaKeyedProperty:
+    case Bytecode::kStaInArrayLiteral:
+    case Bytecode::kStaDataPropertyInLiteral:
+    case Bytecode::kStaCurrentContextSlot:
+      return true;
+    default:
       return false;
   }
 }
 
-static const Address accessors_with_no_side_effect[] = {
-    // Whitelist for accessors.
-    FUNCTION_ADDR(Accessors::StringLengthGetter),
-    FUNCTION_ADDR(Accessors::ArrayLengthGetter)};
-
 }  // anonymous namespace
 
 // static
-bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) {
+DebugInfo::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
+    Isolate* isolate, Handle<SharedFunctionInfo> info) {
   if (FLAG_trace_side_effect_free_debug_evaluate) {
     PrintF("[debug-evaluate] Checking function %s for side effect.\n",
-           info->DebugName()->ToCString().get());
+           info->DebugName().ToCString().get());
   }
 
   DCHECK(info->is_compiled());
-
+  DCHECK(!info->needs_script_context());
   if (info->HasBytecodeArray()) {
-    // Check bytecodes against whitelist.
-    Handle<BytecodeArray> bytecode_array(info->bytecode_array());
-    if (FLAG_trace_side_effect_free_debug_evaluate) bytecode_array->Print();
+    // Check bytecodes against allowlist.
+    Handle<BytecodeArray> bytecode_array(info->GetBytecodeArray(), isolate);
+    if (FLAG_trace_side_effect_free_debug_evaluate) {
+      bytecode_array->Print();
+    }
+    bool requires_runtime_checks = false;
     for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
          it.Advance()) {
       interpreter::Bytecode bytecode = it.current_bytecode();
@@ -531,38 +922,210 @@
                 ? it.GetIntrinsicIdOperand(0)
                 : it.GetRuntimeIdOperand(0);
         if (IntrinsicHasNoSideEffect(id)) continue;
-        return false;
+        return DebugInfo::kHasSideEffects;
       }
 
       if (BytecodeHasNoSideEffect(bytecode)) continue;
+      if (BytecodeRequiresRuntimeCheck(bytecode)) {
+        requires_runtime_checks = true;
+        continue;
+      }
 
-      // Did not match whitelist.
-      return false;
+      if (FLAG_trace_side_effect_free_debug_evaluate) {
+        PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
+               interpreter::Bytecodes::ToString(bytecode));
+      }
+
+      // Did not match allowlist.
+      return DebugInfo::kHasSideEffects;
     }
-    return true;
+    return requires_runtime_checks ? DebugInfo::kRequiresRuntimeChecks
+                                   : DebugInfo::kHasNoSideEffect;
+  } else if (info->IsApiFunction()) {
+    if (info->GetCode().is_builtin()) {
+      return info->GetCode().builtin_index() == Builtins::kHandleApiCall
+                 ? DebugInfo::kHasNoSideEffect
+                 : DebugInfo::kHasSideEffects;
+    }
   } else {
-    // Check built-ins against whitelist.
-    int builtin_index = info->code()->builtin_index();
-    if (builtin_index >= 0 && builtin_index < Builtins::builtin_count &&
-        BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) {
-      return true;
-    }
+    // Check built-ins against allowlist.
+    int builtin_index =
+        info->HasBuiltinId() ? info->builtin_id() : Builtins::kNoBuiltinId;
+    if (!Builtins::IsBuiltinId(builtin_index))
+      return DebugInfo::kHasSideEffects;
+    DebugInfo::SideEffectState state =
+        BuiltinGetSideEffectState(static_cast<Builtins::Name>(builtin_index));
+    return state;
   }
 
-  return false;
+  return DebugInfo::kHasSideEffects;
+}
+
+#ifdef DEBUG
+static bool TransitivelyCalledBuiltinHasNoSideEffect(Builtins::Name caller,
+                                                     Builtins::Name callee) {
+  switch (callee) {
+      // Transitively called Builtins:
+    case Builtins::kAbort:
+    case Builtins::kAbortCSAAssert:
+    case Builtins::kAdaptorWithBuiltinExitFrame:
+    case Builtins::kArrayConstructorImpl:
+    case Builtins::kArrayEveryLoopContinuation:
+    case Builtins::kArrayFilterLoopContinuation:
+    case Builtins::kArrayFindIndexLoopContinuation:
+    case Builtins::kArrayFindLoopContinuation:
+    case Builtins::kArrayForEachLoopContinuation:
+    case Builtins::kArrayIncludesHoleyDoubles:
+    case Builtins::kArrayIncludesPackedDoubles:
+    case Builtins::kArrayIncludesSmiOrObject:
+    case Builtins::kArrayIndexOfHoleyDoubles:
+    case Builtins::kArrayIndexOfPackedDoubles:
+    case Builtins::kArrayIndexOfSmiOrObject:
+    case Builtins::kArrayMapLoopContinuation:
+    case Builtins::kArrayReduceLoopContinuation:
+    case Builtins::kArrayReduceRightLoopContinuation:
+    case Builtins::kArraySomeLoopContinuation:
+    case Builtins::kArrayTimSort:
+    case Builtins::kCall_ReceiverIsAny:
+    case Builtins::kCall_ReceiverIsNotNullOrUndefined:
+    case Builtins::kCall_ReceiverIsNullOrUndefined:
+    case Builtins::kCallWithArrayLike:
+    case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
+    case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
+    case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
+    case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
+    case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_BuiltinExit:
+    case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
+    case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
+    case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
+    case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
+    case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_BuiltinExit:
+    case Builtins::kCloneFastJSArray:
+    case Builtins::kConstruct:
+    case Builtins::kConvertToLocaleString:
+    case Builtins::kCreateTypedArray:
+    case Builtins::kDirectCEntry:
+    case Builtins::kDoubleToI:
+    case Builtins::kExtractFastJSArray:
+    case Builtins::kFastNewObject:
+    case Builtins::kFindOrderedHashMapEntry:
+    case Builtins::kFlatMapIntoArray:
+    case Builtins::kFlattenIntoArray:
+    case Builtins::kGetProperty:
+    case Builtins::kHasProperty:
+    case Builtins::kCreateHTML:
+    case Builtins::kNonNumberToNumber:
+    case Builtins::kNonPrimitiveToPrimitive_Number:
+    case Builtins::kNumberToString:
+    case Builtins::kObjectToString:
+    case Builtins::kOrderedHashTableHealIndex:
+    case Builtins::kOrdinaryToPrimitive_Number:
+    case Builtins::kOrdinaryToPrimitive_String:
+    case Builtins::kParseInt:
+    case Builtins::kProxyHasProperty:
+    case Builtins::kProxyIsExtensible:
+    case Builtins::kProxyGetPrototypeOf:
+    case Builtins::kRecordWrite:
+    case Builtins::kStringAdd_CheckNone:
+    case Builtins::kStringEqual:
+    case Builtins::kStringIndexOf:
+    case Builtins::kStringRepeat:
+    case Builtins::kToInteger:
+    case Builtins::kToLength:
+    case Builtins::kToName:
+    case Builtins::kToObject:
+    case Builtins::kToString:
+    case Builtins::kWeakMapLookupHashIndex:
+      return true;
+    case Builtins::kJoinStackPop:
+    case Builtins::kJoinStackPush:
+      switch (caller) {
+        case Builtins::kArrayPrototypeJoin:
+        case Builtins::kArrayPrototypeToLocaleString:
+        case Builtins::kTypedArrayPrototypeJoin:
+        case Builtins::kTypedArrayPrototypeToLocaleString:
+          return true;
+        default:
+          return false;
+      }
+    case Builtins::kFastCreateDataProperty:
+      switch (caller) {
+        case Builtins::kArrayPrototypeSlice:
+        case Builtins::kArrayFilter:
+          return true;
+        default:
+          return false;
+      }
+    case Builtins::kSetProperty:
+      switch (caller) {
+        case Builtins::kArrayPrototypeSlice:
+        case Builtins::kTypedArrayPrototypeMap:
+        case Builtins::kStringPrototypeMatchAll:
+          return true;
+        default:
+          return false;
+      }
+    default:
+      return false;
+  }
 }
 
 // static
-bool DebugEvaluate::CallbackHasNoSideEffect(Address function_addr) {
-  for (size_t i = 0; i < arraysize(accessors_with_no_side_effect); i++) {
-    if (function_addr == accessors_with_no_side_effect[i]) return true;
-  }
+void DebugEvaluate::VerifyTransitiveBuiltins(Isolate* isolate) {
+  // TODO(yangguo): also check runtime calls.
+  bool failed = false;
+  bool sanity_check = false;
+  for (int i = 0; i < Builtins::builtin_count; i++) {
+    Builtins::Name caller = static_cast<Builtins::Name>(i);
+    DebugInfo::SideEffectState state = BuiltinGetSideEffectState(caller);
+    if (state != DebugInfo::kHasNoSideEffect) continue;
+    Code code = isolate->builtins()->builtin(caller);
+    int mode = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
+               RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET);
 
-  if (FLAG_trace_side_effect_free_debug_evaluate) {
-    PrintF("[debug-evaluate] API Callback at %p may cause side effect.\n",
-           reinterpret_cast<void*>(function_addr));
+    for (RelocIterator it(code, mode); !it.done(); it.next()) {
+      RelocInfo* rinfo = it.rinfo();
+      DCHECK(RelocInfo::IsCodeTargetMode(rinfo->rmode()));
+      Code callee_code = isolate->heap()->GcSafeFindCodeForInnerPointer(
+          rinfo->target_address());
+      if (!callee_code.is_builtin()) continue;
+      Builtins::Name callee =
+          static_cast<Builtins::Name>(callee_code.builtin_index());
+      if (BuiltinGetSideEffectState(callee) == DebugInfo::kHasNoSideEffect) {
+        continue;
+      }
+      if (TransitivelyCalledBuiltinHasNoSideEffect(caller, callee)) {
+        sanity_check = true;
+        continue;
+      }
+      PrintF("Allowlisted builtin %s calls non-allowlisted builtin %s\n",
+             Builtins::name(caller), Builtins::name(callee));
+      failed = true;
+    }
   }
-  return false;
+  CHECK(!failed);
+#if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \
+    defined(V8_TARGET_ARCH_MIPS64)
+  // Isolate-independent builtin calls and jumps do not emit reloc infos
+  // on PPC. We try to avoid using PC relative code due to performance
+  // issue with especially older hardwares.
+  // MIPS64 doesn't have PC relative code currently.
+  // TODO(mips): Add PC relative code to MIPS64.
+  USE(sanity_check);
+#else
+  CHECK(sanity_check);
+#endif
+}
+#endif  // DEBUG
+
+// static
+void DebugEvaluate::ApplySideEffectChecks(
+    Handle<BytecodeArray> bytecode_array) {
+  for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
+       it.Advance()) {
+    interpreter::Bytecode bytecode = it.current_bytecode();
+    if (BytecodeRequiresRuntimeCheck(bytecode)) it.ApplyDebugBreak();
+  }
 }
 
 }  // namespace internal
diff --git a/src/debug/debug-evaluate.h b/src/debug/debug-evaluate.h
index 5f5b51e..2f4cc2d 100644
--- a/src/debug/debug-evaluate.h
+++ b/src/debug/debug-evaluate.h
@@ -5,28 +5,55 @@
 #ifndef V8_DEBUG_DEBUG_EVALUATE_H_
 #define V8_DEBUG_DEBUG_EVALUATE_H_
 
-#include "src/frames.h"
-#include "src/objects.h"
+#include <vector>
+
+#include "src/common/globals.h"
+#include "src/debug/debug-frames.h"
+#include "src/debug/debug-scopes.h"
+#include "src/debug/debug.h"
+#include "src/execution/frames.h"
+#include "src/objects/objects.h"
+#include "src/objects/shared-function-info.h"
+#include "src/objects/string-set.h"
 
 namespace v8 {
 namespace internal {
 
+class FrameInspector;
+
 class DebugEvaluate : public AllStatic {
  public:
-  static MaybeHandle<Object> Global(Isolate* isolate, Handle<String> source);
+  static MaybeHandle<Object> Global(Isolate* isolate, Handle<String> source,
+                                    debug::EvaluateGlobalMode mode,
+                                    REPLMode repl_mode = REPLMode::kNo);
 
   // Evaluate a piece of JavaScript in the context of a stack frame for
   // debugging.  Things that need special attention are:
   // - Parameters and stack-allocated locals need to be materialized.  Altered
   //   values need to be written back to the stack afterwards.
   // - The arguments object needs to materialized.
-  static MaybeHandle<Object> Local(Isolate* isolate, StackFrame::Id frame_id,
+  static MaybeHandle<Object> Local(Isolate* isolate, StackFrameId frame_id,
                                    int inlined_jsframe_index,
                                    Handle<String> source,
                                    bool throw_on_side_effect);
 
-  static bool FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info);
-  static bool CallbackHasNoSideEffect(Address function_addr);
+  static V8_EXPORT MaybeHandle<Object> WebAssembly(
+      Handle<WasmInstanceObject> instance, StackFrameId frame_id,
+      Handle<String> source, bool throw_on_side_effect);
+
+  // This is used for break-at-entry for builtins and API functions.
+  // Evaluate a piece of JavaScript in the native context, but with the
+  // materialized arguments object and receiver of the current call.
+  static MaybeHandle<Object> WithTopmostArguments(Isolate* isolate,
+                                                  Handle<String> source);
+
+  static DebugInfo::SideEffectState FunctionGetSideEffectState(
+      Isolate* isolate, Handle<SharedFunctionInfo> info);
+  static void ApplySideEffectChecks(Handle<BytecodeArray> bytecode_array);
+
+#ifdef DEBUG
+  static void VerifyTransitiveBuiltins(Isolate* isolate);
+#endif  // DEBUG
 
  private:
   // This class builds a context chain for evaluation of expressions
@@ -55,32 +82,20 @@
     void UpdateValues();
 
     Handle<Context> evaluation_context() const { return evaluation_context_; }
-    Handle<SharedFunctionInfo> outer_info() const { return outer_info_; }
+    Handle<SharedFunctionInfo> outer_info() const;
 
    private:
     struct ContextChainElement {
-      Handle<ScopeInfo> scope_info;
       Handle<Context> wrapped_context;
       Handle<JSObject> materialized_object;
-      Handle<StringSet> whitelist;
+      Handle<StringSet> blocklist;
     };
 
-    // Helper function to find or create the arguments object for
-    // Runtime_DebugEvaluate.
-    void MaterializeArgumentsObject(Handle<JSObject> target,
-                                    Handle<JSFunction> function);
-
-    void MaterializeReceiver(Handle<JSObject> target,
-                             Handle<Context> local_context,
-                             Handle<JSFunction> local_function,
-                             Handle<StringSet> non_locals);
-
-    Handle<SharedFunctionInfo> outer_info_;
     Handle<Context> evaluation_context_;
-    List<ContextChainElement> context_chain_;
+    std::vector<ContextChainElement> context_chain_;
     Isolate* isolate_;
-    JavaScriptFrame* frame_;
-    int inlined_jsframe_index_;
+    FrameInspector frame_inspector_;
+    ScopeIterator scope_iterator_;
   };
 
   static MaybeHandle<Object> Evaluate(Isolate* isolate,
@@ -91,7 +106,6 @@
                                       bool throw_on_side_effect);
 };
 
-
 }  // namespace internal
 }  // namespace v8
 
diff --git a/src/debug/debug-frames.cc b/src/debug/debug-frames.cc
index d489911..4c8da80 100644
--- a/src/debug/debug-frames.cc
+++ b/src/debug/debug-frames.cc
@@ -4,224 +4,111 @@
 
 #include "src/debug/debug-frames.h"
 
-#include "src/frames-inl.h"
-#include "src/wasm/wasm-interpreter.h"
-#include "src/wasm/wasm-objects.h"
+#include "src/builtins/accessors.h"
+#include "src/execution/frames-inl.h"
+#include "src/wasm/wasm-objects-inl.h"
 
 namespace v8 {
 namespace internal {
 
-FrameInspector::FrameInspector(StandardFrame* frame, int inlined_frame_index,
+FrameInspector::FrameInspector(CommonFrame* frame, int inlined_frame_index,
                                Isolate* isolate)
     : frame_(frame),
-      frame_summary_(FrameSummary::Get(frame, inlined_frame_index)),
+      inlined_frame_index_(inlined_frame_index),
       isolate_(isolate) {
+  // Extract the relevant information from the frame summary and discard it.
+  FrameSummary summary = FrameSummary::Get(frame, inlined_frame_index);
+  summary.EnsureSourcePositionsAvailable();
+
+  is_constructor_ = summary.is_constructor();
+  source_position_ = summary.SourcePosition();
+  function_name_ = summary.FunctionName();
+  script_ = Handle<Script>::cast(summary.script());
+  receiver_ = summary.receiver();
+
+  if (summary.IsJavaScript()) {
+    function_ = summary.AsJavaScript().function();
+  }
+
   JavaScriptFrame* js_frame =
       frame->is_java_script() ? javascript_frame() : nullptr;
   DCHECK(js_frame || frame->is_wasm());
   has_adapted_arguments_ = js_frame && js_frame->has_adapted_arguments();
-  is_bottommost_ = inlined_frame_index == 0;
   is_optimized_ = frame_->is_optimized();
   is_interpreted_ = frame_->is_interpreted();
 
   // Calculate the deoptimized frame.
   if (is_optimized_) {
-    DCHECK(js_frame != nullptr);
-    // TODO(turbofan): Revisit once we support deoptimization.
-    if (js_frame->LookupCode()->is_turbofanned() &&
-        js_frame->function()->shared()->asm_function()) {
-      is_optimized_ = false;
-      return;
-    }
-
+    DCHECK_NOT_NULL(js_frame);
     deoptimized_frame_.reset(Deoptimizer::DebuggerInspectableFrame(
         js_frame, inlined_frame_index, isolate));
-  } else if (frame_->is_wasm_interpreter_entry()) {
-    wasm_interpreted_frame_ =
-        frame_summary_.AsWasm()
-            .wasm_instance()
-            ->debug_info()
-            ->GetInterpretedFrame(frame_->fp(), inlined_frame_index);
-    DCHECK(wasm_interpreted_frame_);
   }
 }
 
-FrameInspector::~FrameInspector() {
-  // Destructor needs to be defined in the .cc file, because it instantiates
-  // std::unique_ptr destructors but the types are not known in the header.
-}
+// Destructor needs to be defined in the .cc file, because it instantiates
+// std::unique_ptr destructors but the types are not known in the header.
+FrameInspector::~FrameInspector() = default;
 
-int FrameInspector::GetParametersCount() {
-  if (is_optimized_) return deoptimized_frame_->parameters_count();
-  if (wasm_interpreted_frame_)
-    return wasm_interpreted_frame_->GetParameterCount();
-  return frame_->ComputeParametersCount();
-}
-
-Handle<Script> FrameInspector::GetScript() {
-  return Handle<Script>::cast(frame_summary_.script());
-}
-
-Handle<JSFunction> FrameInspector::GetFunction() {
-  return frame_summary_.AsJavaScript().function();
+JavaScriptFrame* FrameInspector::javascript_frame() {
+  return frame_->is_arguments_adaptor() ? ArgumentsAdaptorFrame::cast(frame_)
+                                        : JavaScriptFrame::cast(frame_);
 }
 
 Handle<Object> FrameInspector::GetParameter(int index) {
   if (is_optimized_) return deoptimized_frame_->GetParameter(index);
-  // TODO(clemensh): Handle wasm_interpreted_frame_.
-  return handle(frame_->GetParameter(index), isolate_);
+  DCHECK(IsJavaScript());
+  return handle(javascript_frame()->GetParameter(index), isolate_);
 }
 
 Handle<Object> FrameInspector::GetExpression(int index) {
-  // TODO(turbofan): Revisit once we support deoptimization.
-  if (frame_->is_java_script() &&
-      javascript_frame()->LookupCode()->is_turbofanned() &&
-      javascript_frame()->function()->shared()->asm_function()) {
-    return isolate_->factory()->undefined_value();
-  }
   return is_optimized_ ? deoptimized_frame_->GetExpression(index)
                        : handle(frame_->GetExpression(index), isolate_);
 }
 
-int FrameInspector::GetSourcePosition() {
-  return frame_summary_.SourcePosition();
-}
-
-bool FrameInspector::IsConstructor() { return frame_summary_.is_constructor(); }
-
 Handle<Object> FrameInspector::GetContext() {
-  return is_optimized_ ? deoptimized_frame_->GetContext()
-                       : handle(frame_->context(), isolate_);
+  return deoptimized_frame_ ? deoptimized_frame_->GetContext()
+                            : handle(frame_->context(), isolate_);
 }
 
-// To inspect all the provided arguments the frame might need to be
-// replaced with the arguments frame.
-void FrameInspector::SetArgumentsFrame(StandardFrame* frame) {
-  DCHECK(has_adapted_arguments_);
-  DCHECK(frame->is_arguments_adaptor());
-  frame_ = frame;
-  is_optimized_ = frame_->is_optimized();
-  is_interpreted_ = frame_->is_interpreted();
-  DCHECK(!is_optimized_);
-}
+bool FrameInspector::IsWasm() { return frame_->is_wasm(); }
 
-
-// Create a plain JSObject which materializes the local scope for the specified
-// frame.
-void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
-                                            Handle<ScopeInfo> scope_info) {
-  HandleScope scope(isolate_);
-  // First fill all parameters.
-  for (int i = 0; i < scope_info->ParameterCount(); ++i) {
-    // Do not materialize the parameter if it is shadowed by a context local.
-    // TODO(yangguo): check whether this is necessary, now that we materialize
-    //                context locals as well.
-    Handle<String> name(scope_info->ParameterName(i));
-    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
-    if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
-
-    Handle<Object> value =
-        i < GetParametersCount()
-            ? GetParameter(i)
-            : Handle<Object>::cast(isolate_->factory()->undefined_value());
-    DCHECK(!value->IsTheHole(isolate_));
-
-    JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
-  }
-
-  // Second fill all stack locals.
-  for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
-    Handle<String> name(scope_info->StackLocalName(i));
-    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
-    Handle<Object> value = GetExpression(scope_info->StackLocalIndex(i));
-    // TODO(yangguo): We convert optimized out values to {undefined} when they
-    // are passed to the debugger. Eventually we should handle them somehow.
-    if (value->IsTheHole(isolate_)) {
-      value = isolate_->factory()->undefined_value();
-    }
-    if (value->IsOptimizedOut(isolate_)) {
-      value = isolate_->factory()->undefined_value();
-    }
-    JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
-  }
-}
-
-
-void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
-                                            Handle<JSFunction> function) {
-  Handle<SharedFunctionInfo> shared(function->shared());
-  Handle<ScopeInfo> scope_info(shared->scope_info());
-  MaterializeStackLocals(target, scope_info);
-}
-
-
-void FrameInspector::UpdateStackLocalsFromMaterializedObject(
-    Handle<JSObject> target, Handle<ScopeInfo> scope_info) {
-  // Optimized frames and wasm frames are not supported. Simply give up.
-  if (is_optimized_ || frame_->is_wasm()) return;
-
-  HandleScope scope(isolate_);
-
-  // Parameters.
-  for (int i = 0; i < scope_info->ParameterCount(); ++i) {
-    // Shadowed parameters were not materialized.
-    Handle<String> name(scope_info->ParameterName(i));
-    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
-    if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
-
-    DCHECK(!javascript_frame()->GetParameter(i)->IsTheHole(isolate_));
-    Handle<Object> value =
-        Object::GetPropertyOrElement(target, name).ToHandleChecked();
-    javascript_frame()->SetParameterValue(i, *value);
-  }
-
-  // Stack locals.
-  for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
-    Handle<String> name(scope_info->StackLocalName(i));
-    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
-    int index = scope_info->StackLocalIndex(i);
-    if (frame_->GetExpression(index)->IsTheHole(isolate_)) continue;
-    Handle<Object> value =
-        Object::GetPropertyOrElement(target, name).ToHandleChecked();
-    frame_->SetExpression(index, *value);
-  }
-}
-
+bool FrameInspector::IsJavaScript() { return frame_->is_java_script(); }
 
 bool FrameInspector::ParameterIsShadowedByContextLocal(
     Handle<ScopeInfo> info, Handle<String> parameter_name) {
   VariableMode mode;
   InitializationFlag init_flag;
   MaybeAssignedFlag maybe_assigned_flag;
-  return ScopeInfo::ContextSlotIndex(info, parameter_name, &mode, &init_flag,
-                                     &maybe_assigned_flag) != -1;
+  IsStaticFlag is_static_flag;
+  return ScopeInfo::ContextSlotIndex(*info, *parameter_name, &mode, &init_flag,
+                                     &maybe_assigned_flag,
+                                     &is_static_flag) != -1;
 }
 
-SaveContext* DebugFrameHelper::FindSavedContextForFrame(Isolate* isolate,
-                                                        StandardFrame* frame) {
-  SaveContext* save = isolate->save_context();
-  while (save != NULL && !save->IsBelowFrame(frame)) {
-    save = save->prev();
+RedirectActiveFunctions::RedirectActiveFunctions(SharedFunctionInfo shared,
+                                                 Mode mode)
+    : shared_(shared), mode_(mode) {
+  DCHECK(shared.HasBytecodeArray());
+  if (mode == Mode::kUseDebugBytecode) {
+    DCHECK(shared.HasDebugInfo());
   }
-  DCHECK(save != NULL);
-  return save;
 }
 
-int DebugFrameHelper::FindIndexedNonNativeFrame(StackTraceFrameIterator* it,
-                                                int index) {
-  int count = -1;
-  for (; !it->done(); it->Advance()) {
-    List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
-    it->frame()->Summarize(&frames);
-    for (int i = frames.length() - 1; i >= 0; i--) {
-      // Omit functions from native and extension scripts.
-      if (!frames[i].is_subject_to_debugging()) continue;
-      if (++count == index) return i;
-    }
+void RedirectActiveFunctions::VisitThread(Isolate* isolate,
+                                          ThreadLocalTop* top) {
+  for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+    JavaScriptFrame* frame = it.frame();
+    JSFunction function = frame->function();
+    if (!frame->is_interpreted()) continue;
+    if (function.shared() != shared_) continue;
+    InterpretedFrame* interpreted_frame =
+        reinterpret_cast<InterpretedFrame*>(frame);
+    BytecodeArray bytecode = mode_ == Mode::kUseDebugBytecode
+                                 ? shared_.GetDebugInfo().DebugBytecodeArray()
+                                 : shared_.GetBytecodeArray();
+    interpreted_frame->PatchBytecodeArray(bytecode);
   }
-  return -1;
 }
 
-
 }  // namespace internal
 }  // namespace v8
diff --git a/src/debug/debug-frames.h b/src/debug/debug-frames.h
index 2c9e43f..c554ca1 100644
--- a/src/debug/debug-frames.h
+++ b/src/debug/debug-frames.h
@@ -5,89 +5,80 @@
 #ifndef V8_DEBUG_DEBUG_FRAMES_H_
 #define V8_DEBUG_DEBUG_FRAMES_H_
 
-#include "src/deoptimizer.h"
-#include "src/frames.h"
-#include "src/isolate.h"
-#include "src/objects.h"
+#include <memory>
+
+#include "src/deoptimizer/deoptimizer.h"
+#include "src/execution/isolate.h"
+#include "src/execution/v8threads.h"
+#include "src/objects/objects.h"
 
 namespace v8 {
 namespace internal {
 
-// Forward declaration:
-namespace wasm {
-class InterpretedFrame;
-}
+class JavaScriptFrame;
+class CommonFrame;
+class WasmFrame;
 
 class FrameInspector {
  public:
-  FrameInspector(StandardFrame* frame, int inlined_frame_index,
-                 Isolate* isolate);
+  FrameInspector(CommonFrame* frame, int inlined_frame_index, Isolate* isolate);
 
   ~FrameInspector();
 
-  FrameSummary& summary() { return frame_summary_; }
-
-  int GetParametersCount();
-  Handle<JSFunction> GetFunction();
-  Handle<Script> GetScript();
+  Handle<JSFunction> GetFunction() const { return function_; }
+  Handle<Script> GetScript() { return script_; }
   Handle<Object> GetParameter(int index);
   Handle<Object> GetExpression(int index);
-  int GetSourcePosition();
-  bool IsConstructor();
+  int GetSourcePosition() { return source_position_; }
+  bool IsConstructor() { return is_constructor_; }
   Handle<Object> GetContext();
+  Handle<Object> GetReceiver() { return receiver_; }
 
-  inline JavaScriptFrame* javascript_frame() {
-    return frame_->is_arguments_adaptor() ? ArgumentsAdaptorFrame::cast(frame_)
-                                          : JavaScriptFrame::cast(frame_);
-  }
+  Handle<String> GetFunctionName() { return function_name_; }
 
-  JavaScriptFrame* GetArgumentsFrame() { return javascript_frame(); }
-  void SetArgumentsFrame(StandardFrame* frame);
+  bool IsWasm();
+  bool IsJavaScript();
 
-  void MaterializeStackLocals(Handle<JSObject> target,
-                              Handle<ScopeInfo> scope_info);
+  JavaScriptFrame* javascript_frame();
 
-  void MaterializeStackLocals(Handle<JSObject> target,
-                              Handle<JSFunction> function);
-
-  void UpdateStackLocalsFromMaterializedObject(Handle<JSObject> object,
-                                               Handle<ScopeInfo> scope_info);
+  int inlined_frame_index() const { return inlined_frame_index_; }
 
  private:
   bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
                                          Handle<String> parameter_name);
 
-  StandardFrame* frame_;
-  FrameSummary frame_summary_;
+  CommonFrame* frame_;
+  int inlined_frame_index_;
   std::unique_ptr<DeoptimizedFrameInfo> deoptimized_frame_;
-  std::unique_ptr<wasm::InterpretedFrame> wasm_interpreted_frame_;
   Isolate* isolate_;
-  bool is_optimized_;
-  bool is_interpreted_;
-  bool is_bottommost_;
-  bool has_adapted_arguments_;
+  Handle<Script> script_;
+  Handle<Object> receiver_;
+  Handle<JSFunction> function_;
+  Handle<String> function_name_;
+  int source_position_ = -1;
+  bool is_optimized_ = false;
+  bool is_interpreted_ = false;
+  bool has_adapted_arguments_ = false;
+  bool is_constructor_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FrameInspector);
 };
 
-
-class DebugFrameHelper : public AllStatic {
+class RedirectActiveFunctions : public ThreadVisitor {
  public:
-  static SaveContext* FindSavedContextForFrame(Isolate* isolate,
-                                               StandardFrame* frame);
-  // Advances the iterator to the frame that matches the index and returns the
-  // inlined frame index, or -1 if not found.  Skips native JS functions.
-  static int FindIndexedNonNativeFrame(StackTraceFrameIterator* it, int index);
+  enum class Mode {
+    kUseOriginalBytecode,
+    kUseDebugBytecode,
+  };
 
-  // Helper functions for wrapping and unwrapping stack frame ids.
-  static Smi* WrapFrameId(StackFrame::Id id) {
-    DCHECK(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
-    return Smi::FromInt(id >> 2);
-  }
+  explicit RedirectActiveFunctions(SharedFunctionInfo shared, Mode mode);
 
-  static StackFrame::Id UnwrapFrameId(int wrapped) {
-    return static_cast<StackFrame::Id>(wrapped << 2);
-  }
+  void VisitThread(Isolate* isolate, ThreadLocalTop* top) override;
+
+ private:
+  SharedFunctionInfo shared_;
+  Mode mode_;
+  DisallowHeapAllocation no_gc_;
 };
 
 }  // namespace internal
diff --git a/src/debug/debug-interface.h b/src/debug/debug-interface.h
index be8ed90..ded8a31 100644
--- a/src/debug/debug-interface.h
+++ b/src/debug/debug-interface.h
@@ -5,71 +5,46 @@
 #ifndef V8_DEBUG_DEBUG_INTERFACE_H_
 #define V8_DEBUG_DEBUG_INTERFACE_H_
 
-#include <functional>
+#include <memory>
 
-#include "include/v8-debug.h"
+#include "include/v8-inspector.h"
 #include "include/v8-util.h"
 #include "include/v8.h"
-
+#include "src/base/platform/time.h"
+#include "src/common/globals.h"
 #include "src/debug/interface-types.h"
-#include "src/globals.h"
+#include "src/utils/vector.h"
 
 namespace v8 {
 
 namespace internal {
+struct CoverageBlock;
 struct CoverageFunction;
 struct CoverageScript;
+struct TypeProfileEntry;
+struct TypeProfileScript;
 class Coverage;
+class DisableBreak;
+class PostponeInterruptsScope;
 class Script;
-}
+class TypeProfile;
+}  // namespace internal
 
 namespace debug {
 
-/**
- * Debugger is running in its own context which is entered while debugger
- * messages are being dispatched. This is an explicit getter for this
- * debugger context. Note that the content of the debugger context is subject
- * to change. The Context exists only when the debugger is active, i.e. at
- * least one DebugEventListener or MessageHandler is set.
- */
-Local<Context> GetDebugContext(Isolate* isolate);
+void SetContextId(Local<Context> context, int id);
+int GetContextId(Local<Context> context);
 
-/**
- * Run a JavaScript function in the debugger.
- * \param fun the function to call
- * \param data passed as second argument to the function
- * With this call the debugger is entered and the function specified is called
- * with the execution state as the first argument. This makes it possible to
- * get access to information otherwise not available during normal JavaScript
- * execution e.g. details on stack frames. Receiver of the function call will
- * be the debugger context global object, however this is a subject to change.
- * The following example shows a JavaScript function which when passed to
- * v8::Debug::Call will return the current line of JavaScript execution.
- *
- * \code
- *   function frame_source_line(exec_state) {
- *     return exec_state.frame(0).sourceLine();
- *   }
- * \endcode
- */
-// TODO(dcarney): data arg should be a MaybeLocal
-MaybeLocal<Value> Call(Local<Context> context, v8::Local<v8::Function> fun,
-                       Local<Value> data = Local<Value>());
+void SetInspector(Isolate* isolate, v8_inspector::V8Inspector*);
+v8_inspector::V8Inspector* GetInspector(Isolate* isolate);
 
-/**
- * Enable/disable LiveEdit functionality for the given Isolate
- * (default Isolate if not provided). V8 will abort if LiveEdit is
- * unexpectedly used. LiveEdit is enabled by default.
- */
-void SetLiveEditEnabled(Isolate* isolate, bool enable);
-
-// Schedule a debugger break to happen when JavaScript code is run
-// in the given isolate.
-void DebugBreak(Isolate* isolate);
+// Schedule a debugger break to happen when function is called inside given
+// isolate.
+V8_EXPORT_PRIVATE void SetBreakOnNextFunctionCall(Isolate* isolate);
 
 // Remove scheduled debugger break in given isolate if it has not
 // happened yet.
-void CancelDebugBreak(Isolate* isolate);
+V8_EXPORT_PRIVATE void ClearBreakOnNextFunctionCall(Isolate* isolate);
 
 /**
  * Returns array of internal properties specific to the value type. Result has
@@ -78,6 +53,28 @@
  */
 MaybeLocal<Array> GetInternalProperties(Isolate* isolate, Local<Value> value);
 
+/**
+ * Returns through the out parameters names_out a vector of names
+ * in v8::String for private members, including fields, methods,
+ * accessors specific to the value type.
+ * The values are returned through the out parameter values_out in the
+ * corresponding indices. Private fields and methods are returned directly
+ * while accessors are returned as v8::debug::AccessorPair. Missing components
+ * in the accessor pairs are null.
+ * If an exception occurs, false is returned. Otherwise true is returned.
+ * Results will be allocated in the current context and handle scope.
+ */
+V8_EXPORT_PRIVATE bool GetPrivateMembers(Local<Context> context,
+                                         Local<Object> value,
+                                         std::vector<Local<Value>>* names_out,
+                                         std::vector<Local<Value>>* values_out);
+
+/**
+ * Forwards to v8::Object::CreationContext, but with special handling for
+ * JSGlobalProxy objects.
+ */
+Local<Context> GetCreationContext(Local<Object> value);
+
 enum ExceptionBreakState {
   NoBreakOnException = 0,
   BreakOnUncaughtException = 1,
@@ -92,6 +89,7 @@
  */
 void ChangeBreakOnException(Isolate* isolate, ExceptionBreakState state);
 
+void RemoveBreakpoint(Isolate* isolate, BreakpointId id);
 void SetBreakPointsActive(Isolate* isolate, bool is_active);
 
 enum StepAction {
@@ -102,18 +100,40 @@
 };
 
 void PrepareStep(Isolate* isolate, StepAction action);
+void ClearStepping(Isolate* isolate);
+V8_EXPORT_PRIVATE void BreakRightNow(Isolate* isolate);
 
-bool HasNonBlackboxedFrameOnStack(Isolate* isolate);
+// Use `SetTerminateOnResume` to indicate that an TerminateExecution interrupt
+// should be set shortly before resuming, i.e. shortly before returning into
+// the JavaScript stack frames on the stack. In contrast to setting the
+// interrupt with `RequestTerminateExecution` directly, this flag allows
+// the isolate to be entered for further JavaScript execution.
+V8_EXPORT_PRIVATE void SetTerminateOnResume(Isolate* isolate);
 
-/**
- * Out-of-memory callback function.
- * The function is invoked when the heap size is close to the hard limit.
- *
- * \param data the parameter provided during callback installation.
- */
-typedef void (*OutOfMemoryCallback)(void* data);
-void SetOutOfMemoryCallback(Isolate* isolate, OutOfMemoryCallback callback,
-                            void* data);
+bool AllFramesOnStackAreBlackboxed(Isolate* isolate);
+
+class Script;
+
+struct LiveEditResult {
+  enum Status {
+    OK,
+    COMPILE_ERROR,
+    BLOCKED_BY_RUNNING_GENERATOR,
+    BLOCKED_BY_FUNCTION_ABOVE_BREAK_FRAME,
+    BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME,
+    BLOCKED_BY_ACTIVE_FUNCTION,
+    BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME,
+    FRAME_RESTART_IS_NOT_SUPPORTED
+  };
+  Status status = OK;
+  bool stack_changed = false;
+  // Available only for OK.
+  v8::Local<v8::debug::Script> script;
+  // Fields below are available only for COMPILE_ERROR.
+  v8::Local<v8::String> message;
+  int line_number = -1;
+  int column_number = -1;
+};
 
 /**
  * Native wrapper around v8::internal::Script object.
@@ -124,6 +144,7 @@
 
   ScriptOriginOptions OriginOptions() const;
   bool WasCompiled() const;
+  bool IsEmbedded() const;
   int Id() const;
   int LineOffset() const;
   int ColumnOffset() const;
@@ -131,16 +152,22 @@
   MaybeLocal<String> Name() const;
   MaybeLocal<String> SourceURL() const;
   MaybeLocal<String> SourceMappingURL() const;
-  MaybeLocal<Value> ContextData() const;
+  Maybe<int> ContextId() const;
   MaybeLocal<String> Source() const;
   bool IsWasm() const;
   bool IsModule() const;
-  bool GetPossibleBreakpoints(const debug::Location& start,
-                              const debug::Location& end,
-                              std::vector<debug::Location>* locations) const;
-
- private:
-  int GetSourcePosition(const debug::Location& location) const;
+  bool GetPossibleBreakpoints(
+      const debug::Location& start, const debug::Location& end,
+      bool restrict_to_function,
+      std::vector<debug::BreakLocation>* locations) const;
+  int GetSourceOffset(const debug::Location& location) const;
+  v8::debug::Location GetSourceLocation(int offset) const;
+  bool SetScriptSource(v8::Local<v8::String> newSource, bool preview,
+                       LiveEditResult* result) const;
+  bool SetBreakpoint(v8::Local<v8::String> condition, debug::Location* location,
+                     BreakpointId* id) const;
+  void RemoveWasmBreakpoint(BreakpointId id);
+  bool SetBreakpointOnScriptEntry(BreakpointId* id) const;
 };
 
 // Specialization for wasm Scripts.
@@ -148,51 +175,88 @@
  public:
   static WasmScript* Cast(Script* script);
 
+  enum class DebugSymbolsType { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
+  DebugSymbolsType GetDebugSymbolType() const;
+  MemorySpan<const char> ExternalSymbolsURL() const;
   int NumFunctions() const;
   int NumImportedFunctions() const;
+  MemorySpan<const uint8_t> Bytecode() const;
 
   std::pair<int, int> GetFunctionRange(int function_index) const;
+  int GetContainingFunction(int byte_offset) const;
 
-  debug::WasmDisassembly DisassembleFunction(int function_index) const;
+  uint32_t GetFunctionHash(int function_index);
+
+  int CodeOffset() const;
+  int CodeLength() const;
 };
 
-void GetLoadedScripts(Isolate* isolate, PersistentValueVector<Script>& scripts);
+V8_EXPORT_PRIVATE void GetLoadedScripts(
+    Isolate* isolate,
+    PersistentValueVector<Script>& scripts);  // NOLINT(runtime/references)
 
 MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate,
                                                  Local<String> source);
 
+enum ExceptionType { kException, kPromiseRejection };
+
 class DebugDelegate {
  public:
-  virtual ~DebugDelegate() {}
-  virtual void PromiseEventOccurred(debug::PromiseDebugActionType type, int id,
-                                    int parent_id) {}
-  virtual void ScriptCompiled(v8::Local<Script> script,
+  virtual ~DebugDelegate() = default;
+  virtual void ScriptCompiled(v8::Local<Script> script, bool is_live_edited,
                               bool has_compile_error) {}
-  virtual void BreakProgramRequested(v8::Local<v8::Context> paused_context,
-                                     v8::Local<v8::Object> exec_state,
-                                     v8::Local<v8::Value> break_points_hit) {}
+  // |inspector_break_points_hit| contains id of breakpoints installed with
+  // debug::Script::SetBreakpoint API.
+  virtual void BreakProgramRequested(
+      v8::Local<v8::Context> paused_context,
+      const std::vector<debug::BreakpointId>& inspector_break_points_hit) {}
   virtual void ExceptionThrown(v8::Local<v8::Context> paused_context,
-                               v8::Local<v8::Object> exec_state,
                                v8::Local<v8::Value> exception,
-                               v8::Local<v8::Value> promise, bool is_uncaught) {
-  }
+                               v8::Local<v8::Value> promise, bool is_uncaught,
+                               ExceptionType exception_type) {}
   virtual bool IsFunctionBlackboxed(v8::Local<debug::Script> script,
                                     const debug::Location& start,
                                     const debug::Location& end) {
     return false;
   }
+  virtual bool ShouldBeSkipped(v8::Local<v8::debug::Script> script, int line,
+                               int column) {
+    return false;
+  }
 };
 
-void SetDebugDelegate(Isolate* isolate, DebugDelegate* listener);
+V8_EXPORT_PRIVATE void SetDebugDelegate(Isolate* isolate,
+                                        DebugDelegate* listener);
+
+V8_EXPORT_PRIVATE void TierDownAllModulesPerIsolate(Isolate* isolate);
+V8_EXPORT_PRIVATE void TierUpAllModulesPerIsolate(Isolate* isolate);
+
+class AsyncEventDelegate {
+ public:
+  virtual ~AsyncEventDelegate() = default;
+  virtual void AsyncEventOccurred(debug::DebugAsyncActionType type, int id,
+                                  bool is_blackboxed) = 0;
+};
+
+void SetAsyncEventDelegate(Isolate* isolate, AsyncEventDelegate* delegate);
 
 void ResetBlackboxedStateCache(Isolate* isolate,
                                v8::Local<debug::Script> script);
 
 int EstimatedValueSize(Isolate* isolate, v8::Local<v8::Value> value);
 
-v8::MaybeLocal<v8::Array> EntriesPreview(Isolate* isolate,
-                                         v8::Local<v8::Value> value,
-                                         bool* is_key_value);
+enum Builtin { kStringToLowerCase };
+
+Local<Function> GetBuiltin(Isolate* isolate, Builtin builtin);
+
+V8_EXPORT_PRIVATE void SetConsoleDelegate(Isolate* isolate,
+                                          ConsoleDelegate* delegate);
+
+V8_DEPRECATED("See http://crbug.com/v8/10566.")
+int GetStackFrameId(v8::Local<v8::StackFrame> frame);
+
+v8::Local<v8::StackTrace> GetDetailedStackTrace(Isolate* isolate,
+                                                v8::Local<v8::Object> error);
 
 /**
  * Native wrapper around v8::internal::JSGeneratorObject object.
@@ -212,52 +276,379 @@
  */
 class V8_EXPORT_PRIVATE Coverage {
  public:
-  class ScriptData;  // Forward declaration.
+  MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(Coverage);
+
+  // Forward declarations.
+  class ScriptData;
+  class FunctionData;
+
+  class V8_EXPORT_PRIVATE BlockData {
+   public:
+    MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(BlockData);
+
+    int StartOffset() const;
+    int EndOffset() const;
+    uint32_t Count() const;
+
+   private:
+    explicit BlockData(i::CoverageBlock* block,
+                       std::shared_ptr<i::Coverage> coverage)
+        : block_(block), coverage_(std::move(coverage)) {}
+
+    i::CoverageBlock* block_;
+    std::shared_ptr<i::Coverage> coverage_;
+
+    friend class v8::debug::Coverage::FunctionData;
+  };
 
   class V8_EXPORT_PRIVATE FunctionData {
    public:
-    // 0-based line and colum numbers.
-    Location Start() { return start_; }
-    Location End() { return end_; }
-    uint32_t Count();
-    MaybeLocal<String> Name();
+    MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(FunctionData);
+
+    int StartOffset() const;
+    int EndOffset() const;
+    uint32_t Count() const;
+    MaybeLocal<String> Name() const;
+    size_t BlockCount() const;
+    bool HasBlockCoverage() const;
+    BlockData GetBlockData(size_t i) const;
 
    private:
-    FunctionData(i::CoverageFunction* function, Local<debug::Script> script);
+    explicit FunctionData(i::CoverageFunction* function,
+                          std::shared_ptr<i::Coverage> coverage)
+        : function_(function), coverage_(std::move(coverage)) {}
+
     i::CoverageFunction* function_;
-    Location start_;
-    Location end_;
+    std::shared_ptr<i::Coverage> coverage_;
 
     friend class v8::debug::Coverage::ScriptData;
   };
 
   class V8_EXPORT_PRIVATE ScriptData {
    public:
-    Local<debug::Script> GetScript();
-    size_t FunctionCount();
-    FunctionData GetFunctionData(size_t i);
+    MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(ScriptData);
+
+    Local<debug::Script> GetScript() const;
+    size_t FunctionCount() const;
+    FunctionData GetFunctionData(size_t i) const;
 
    private:
-    explicit ScriptData(i::CoverageScript* script) : script_(script) {}
+    explicit ScriptData(size_t index, std::shared_ptr<i::Coverage> c);
+
     i::CoverageScript* script_;
+    std::shared_ptr<i::Coverage> coverage_;
 
     friend class v8::debug::Coverage;
   };
 
-  static Coverage Collect(Isolate* isolate, bool reset_count);
+  static Coverage CollectPrecise(Isolate* isolate);
+  static Coverage CollectBestEffort(Isolate* isolate);
 
-  static void TogglePrecise(Isolate* isolate, bool enable);
+  static void SelectMode(Isolate* isolate, CoverageMode mode);
 
-  size_t ScriptCount();
-  ScriptData GetScriptData(size_t i);
-  bool IsEmpty() { return coverage_ == nullptr; }
-
-  ~Coverage();
+  size_t ScriptCount() const;
+  ScriptData GetScriptData(size_t i) const;
+  bool IsEmpty() const { return coverage_ == nullptr; }
 
  private:
-  explicit Coverage(i::Coverage* coverage) : coverage_(coverage) {}
-  i::Coverage* coverage_;
+  explicit Coverage(std::shared_ptr<i::Coverage> coverage)
+      : coverage_(std::move(coverage)) {}
+  std::shared_ptr<i::Coverage> coverage_;
 };
+
+/*
+ * Provide API layer between inspector and type profile.
+ */
+class V8_EXPORT_PRIVATE TypeProfile {
+ public:
+  MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(TypeProfile);
+
+  class ScriptData;  // Forward declaration.
+
+  class V8_EXPORT_PRIVATE Entry {
+   public:
+    MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(Entry);
+
+    int SourcePosition() const;
+    std::vector<MaybeLocal<String>> Types() const;
+
+   private:
+    explicit Entry(const i::TypeProfileEntry* entry,
+                   std::shared_ptr<i::TypeProfile> type_profile)
+        : entry_(entry), type_profile_(std::move(type_profile)) {}
+
+    const i::TypeProfileEntry* entry_;
+    std::shared_ptr<i::TypeProfile> type_profile_;
+
+    friend class v8::debug::TypeProfile::ScriptData;
+  };
+
+  class V8_EXPORT_PRIVATE ScriptData {
+   public:
+    MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(ScriptData);
+
+    Local<debug::Script> GetScript() const;
+    std::vector<Entry> Entries() const;
+
+   private:
+    explicit ScriptData(size_t index,
+                        std::shared_ptr<i::TypeProfile> type_profile);
+
+    i::TypeProfileScript* script_;
+    std::shared_ptr<i::TypeProfile> type_profile_;
+
+    friend class v8::debug::TypeProfile;
+  };
+
+  static TypeProfile Collect(Isolate* isolate);
+
+  static void SelectMode(Isolate* isolate, TypeProfileMode mode);
+
+  size_t ScriptCount() const;
+  ScriptData GetScriptData(size_t i) const;
+
+ private:
+  explicit TypeProfile(std::shared_ptr<i::TypeProfile> type_profile)
+      : type_profile_(std::move(type_profile)) {}
+
+  std::shared_ptr<i::TypeProfile> type_profile_;
+};
+
+class V8_EXPORT_PRIVATE ScopeIterator {
+ public:
+  static std::unique_ptr<ScopeIterator> CreateForFunction(
+      v8::Isolate* isolate, v8::Local<v8::Function> func);
+  static std::unique_ptr<ScopeIterator> CreateForGeneratorObject(
+      v8::Isolate* isolate, v8::Local<v8::Object> generator);
+
+  ScopeIterator() = default;
+  virtual ~ScopeIterator() = default;
+
+  enum ScopeType {
+    ScopeTypeGlobal = 0,
+    ScopeTypeLocal,
+    ScopeTypeWith,
+    ScopeTypeClosure,
+    ScopeTypeCatch,
+    ScopeTypeBlock,
+    ScopeTypeScript,
+    ScopeTypeEval,
+    ScopeTypeModule,
+    ScopeTypeWasmExpressionStack
+  };
+
+  virtual bool Done() = 0;
+  virtual void Advance() = 0;
+  virtual ScopeType GetType() = 0;
+  virtual v8::Local<v8::Object> GetObject() = 0;
+  virtual v8::Local<v8::Value> GetFunctionDebugName() = 0;
+  virtual int GetScriptId() = 0;
+  virtual bool HasLocationInfo() = 0;
+  virtual debug::Location GetStartLocation() = 0;
+  virtual debug::Location GetEndLocation() = 0;
+
+  virtual bool SetVariableValue(v8::Local<v8::String> name,
+                                v8::Local<v8::Value> value) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopeIterator);
+};
+
+class V8_EXPORT_PRIVATE StackTraceIterator {
+ public:
+  static bool SupportsWasmDebugEvaluate();
+  static std::unique_ptr<StackTraceIterator> Create(Isolate* isolate,
+                                                    int index = 0);
+  StackTraceIterator() = default;
+  virtual ~StackTraceIterator() = default;
+
+  virtual bool Done() const = 0;
+  virtual void Advance() = 0;
+
+  virtual int GetContextId() const = 0;
+  virtual v8::MaybeLocal<v8::Value> GetReceiver() const = 0;
+  virtual v8::Local<v8::Value> GetReturnValue() const = 0;
+  virtual v8::Local<v8::String> GetFunctionDebugName() const = 0;
+  virtual v8::Local<v8::debug::Script> GetScript() const = 0;
+  virtual debug::Location GetSourceLocation() const = 0;
+  virtual v8::Local<v8::Function> GetFunction() const = 0;
+  virtual std::unique_ptr<ScopeIterator> GetScopeIterator() const = 0;
+
+  virtual bool Restart() = 0;
+  virtual v8::MaybeLocal<v8::Value> Evaluate(v8::Local<v8::String> source,
+                                             bool throw_on_side_effect) = 0;
+  virtual v8::MaybeLocal<v8::String> EvaluateWasm(
+      internal::Vector<const internal::byte> source, int frame_index) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StackTraceIterator);
+};
+
+class QueryObjectPredicate {
+ public:
+  virtual ~QueryObjectPredicate() = default;
+  virtual bool Filter(v8::Local<v8::Object> object) = 0;
+};
+
+void QueryObjects(v8::Local<v8::Context> context,
+                  QueryObjectPredicate* predicate,
+                  v8::PersistentValueVector<v8::Object>* objects);
+
+void GlobalLexicalScopeNames(v8::Local<v8::Context> context,
+                             v8::PersistentValueVector<v8::String>* names);
+
+void SetReturnValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
+
+enum class NativeAccessorType {
+  None = 0,
+  HasGetter = 1 << 0,
+  HasSetter = 1 << 1
+};
+
+int64_t GetNextRandomInt64(v8::Isolate* isolate);
+
+using RuntimeCallCounterCallback =
+    std::function<void(const char* name, int64_t count, base::TimeDelta time)>;
+void EnumerateRuntimeCallCounters(v8::Isolate* isolate,
+                                  RuntimeCallCounterCallback callback);
+
+enum class EvaluateGlobalMode {
+  kDefault,
+  kDisableBreaks,
+  kDisableBreaksAndThrowOnSideEffect
+};
+
+V8_EXPORT_PRIVATE v8::MaybeLocal<v8::Value> EvaluateGlobal(
+    v8::Isolate* isolate, v8::Local<v8::String> source, EvaluateGlobalMode mode,
+    bool repl_mode = false);
+
+int GetDebuggingId(v8::Local<v8::Function> function);
+
+bool SetFunctionBreakpoint(v8::Local<v8::Function> function,
+                           v8::Local<v8::String> condition, BreakpointId* id);
+
+v8::Platform* GetCurrentPlatform();
+
+void ForceGarbageCollection(
+    v8::Isolate* isolate,
+    v8::EmbedderHeapTracer::EmbedderStackState embedder_stack_state);
+
+class PostponeInterruptsScope {
+ public:
+  explicit PostponeInterruptsScope(v8::Isolate* isolate);
+  ~PostponeInterruptsScope();
+
+ private:
+  std::unique_ptr<i::PostponeInterruptsScope> scope_;
+};
+
+class DisableBreakScope {
+ public:
+  explicit DisableBreakScope(v8::Isolate* isolate);
+  ~DisableBreakScope();
+
+ private:
+  std::unique_ptr<i::DisableBreak> scope_;
+};
+
+class WeakMap : public v8::Object {
+ public:
+  WeakMap() = delete;
+  V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT v8::MaybeLocal<v8::Value> Get(
+      v8::Local<v8::Context> context, v8::Local<v8::Value> key);
+  V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT v8::MaybeLocal<WeakMap> Set(
+      v8::Local<v8::Context> context, v8::Local<v8::Value> key,
+      v8::Local<v8::Value> value);
+
+  V8_EXPORT_PRIVATE static Local<WeakMap> New(v8::Isolate* isolate);
+  V8_INLINE static WeakMap* Cast(Value* obj);
+};
+
+/**
+ * Pairs of accessors.
+ *
+ * In the case of private accessors, getters and setters are either null or
+ * Functions.
+ */
+class V8_EXPORT_PRIVATE AccessorPair : public v8::Value {
+ public:
+  AccessorPair() = delete;
+  v8::Local<v8::Value> getter();
+  v8::Local<v8::Value> setter();
+
+  static bool IsAccessorPair(v8::Local<v8::Value> obj);
+  V8_INLINE static AccessorPair* Cast(v8::Value* obj);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+struct PropertyDescriptor {
+  bool enumerable : 1;
+  bool has_enumerable : 1;
+  bool configurable : 1;
+  bool has_configurable : 1;
+  bool writable : 1;
+  bool has_writable : 1;
+  v8::Local<v8::Value> value;
+  v8::Local<v8::Value> get;
+  v8::Local<v8::Value> set;
+};
+
+class PropertyIterator {
+ public:
+  static std::unique_ptr<PropertyIterator> Create(v8::Local<v8::Object> object);
+
+  virtual ~PropertyIterator() = default;
+
+  virtual bool Done() const = 0;
+  virtual void Advance() = 0;
+
+  virtual v8::Local<v8::Name> name() const = 0;
+
+  virtual bool is_native_accessor() = 0;
+  virtual bool has_native_getter() = 0;
+  virtual bool has_native_setter() = 0;
+  virtual Maybe<PropertyAttribute> attributes() = 0;
+  virtual Maybe<PropertyDescriptor> descriptor() = 0;
+
+  virtual bool is_own() = 0;
+  virtual bool is_array_index() = 0;
+};
+
+// Wrapper around v8::internal::WasmValue.
+class V8_EXPORT_PRIVATE WasmValue : public v8::Value {
+ public:
+  WasmValue() = delete;
+  static bool IsWasmValue(v8::Local<v8::Value> obj);
+  V8_INLINE static WasmValue* Cast(v8::Value* obj);
+  int value_type();
+  // Get the underlying values as a byte array, this is only valid if value_type
+  // is i32, i64, f32, f64, or s128.
+  v8::Local<v8::Array> bytes();
+  // Get the underlying externref, only valid if value_type is externref.
+  v8::Local<v8::Value> ref();
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+AccessorPair* AccessorPair::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<AccessorPair*>(value);
+}
+
+WasmValue* WasmValue::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<WasmValue*>(value);
+}
+
+MaybeLocal<Message> GetMessageFromPromise(Local<Promise> promise);
+
 }  // namespace debug
 }  // namespace v8
 
diff --git a/src/debug/debug-property-iterator.cc b/src/debug/debug-property-iterator.cc
new file mode 100644
index 0000000..a3605df
--- /dev/null
+++ b/src/debug/debug-property-iterator.cc
@@ -0,0 +1,212 @@
+// Copyright 2018 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/debug-property-iterator.h"
+
+#include "src/api/api-inl.h"
+#include "src/base/flags.h"
+#include "src/objects/js-array-buffer-inl.h"
+#include "src/objects/keys.h"
+#include "src/objects/property-descriptor.h"
+#include "src/objects/property-details.h"
+
+namespace v8 {
+
+std::unique_ptr<debug::PropertyIterator> debug::PropertyIterator::Create(
+    v8::Local<v8::Object> v8_object) {
+  internal::Isolate* isolate =
+      reinterpret_cast<internal::Isolate*>(v8_object->GetIsolate());
+  return std::unique_ptr<debug::PropertyIterator>(
+      new internal::DebugPropertyIterator(isolate,
+                                          Utils::OpenHandle(*v8_object)));
+}
+
+namespace internal {
+
+DebugPropertyIterator::DebugPropertyIterator(Isolate* isolate,
+                                             Handle<JSReceiver> receiver)
+    : isolate_(isolate),
+      prototype_iterator_(isolate, receiver, kStartAtReceiver,
+                          PrototypeIterator::END_AT_NULL) {
+  if (receiver->IsJSProxy()) {
+    is_own_ = false;
+    prototype_iterator_.AdvanceIgnoringProxies();
+  }
+  if (prototype_iterator_.IsAtEnd()) return;
+  FillKeysForCurrentPrototypeAndStage();
+  if (should_move_to_next_stage()) Advance();
+}
+
+bool DebugPropertyIterator::Done() const {
+  return prototype_iterator_.IsAtEnd();
+}
+
+void DebugPropertyIterator::Advance() {
+  ++current_key_index_;
+  calculated_native_accessor_flags_ = false;
+  while (should_move_to_next_stage()) {
+    switch (stage_) {
+      case Stage::kExoticIndices:
+        stage_ = Stage::kEnumerableStrings;
+        break;
+      case Stage::kEnumerableStrings:
+        stage_ = Stage::kAllProperties;
+        break;
+      case Stage::kAllProperties:
+        stage_ = kExoticIndices;
+        is_own_ = false;
+        prototype_iterator_.AdvanceIgnoringProxies();
+        break;
+    }
+    FillKeysForCurrentPrototypeAndStage();
+  }
+}
+
+bool DebugPropertyIterator::is_native_accessor() {
+  if (stage_ == kExoticIndices) return false;
+  CalculateNativeAccessorFlags();
+  return native_accessor_flags_;
+}
+
+bool DebugPropertyIterator::has_native_getter() {
+  if (stage_ == kExoticIndices) return false;
+  CalculateNativeAccessorFlags();
+  return native_accessor_flags_ &
+         static_cast<int>(debug::NativeAccessorType::HasGetter);
+}
+
+bool DebugPropertyIterator::has_native_setter() {
+  if (stage_ == kExoticIndices) return false;
+  CalculateNativeAccessorFlags();
+  return native_accessor_flags_ &
+         static_cast<int>(debug::NativeAccessorType::HasSetter);
+}
+
+Handle<Name> DebugPropertyIterator::raw_name() const {
+  DCHECK(!Done());
+  if (stage_ == kExoticIndices) {
+    return isolate_->factory()->SizeToString(current_key_index_);
+  } else {
+    return Handle<Name>::cast(FixedArray::get(
+        *keys_, static_cast<int>(current_key_index_), isolate_));
+  }
+}
+
+v8::Local<v8::Name> DebugPropertyIterator::name() const {
+  return Utils::ToLocal(raw_name());
+}
+
+v8::Maybe<v8::PropertyAttribute> DebugPropertyIterator::attributes() {
+  Handle<JSReceiver> receiver =
+      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
+  auto result = JSReceiver::GetPropertyAttributes(receiver, raw_name());
+  if (result.IsNothing()) return Nothing<v8::PropertyAttribute>();
+  DCHECK(result.FromJust() != ABSENT);
+  return Just(static_cast<v8::PropertyAttribute>(result.FromJust()));
+}
+
+v8::Maybe<v8::debug::PropertyDescriptor> DebugPropertyIterator::descriptor() {
+  Handle<JSReceiver> receiver =
+      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
+
+  PropertyDescriptor descriptor;
+  Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
+      isolate_, receiver, raw_name(), &descriptor);
+  if (did_get_descriptor.IsNothing()) {
+    return Nothing<v8::debug::PropertyDescriptor>();
+  }
+  DCHECK(did_get_descriptor.FromJust());
+  return Just(v8::debug::PropertyDescriptor{
+      descriptor.enumerable(), descriptor.has_enumerable(),
+      descriptor.configurable(), descriptor.has_configurable(),
+      descriptor.writable(), descriptor.has_writable(),
+      descriptor.has_value() ? Utils::ToLocal(descriptor.value())
+                             : v8::Local<v8::Value>(),
+      descriptor.has_get() ? Utils::ToLocal(descriptor.get())
+                           : v8::Local<v8::Value>(),
+      descriptor.has_set() ? Utils::ToLocal(descriptor.set())
+                           : v8::Local<v8::Value>(),
+  });
+}
+
+bool DebugPropertyIterator::is_own() { return is_own_; }
+
+bool DebugPropertyIterator::is_array_index() {
+  if (stage_ == kExoticIndices) return true;
+  uint32_t index = 0;
+  return raw_name()->AsArrayIndex(&index);
+}
+
+void DebugPropertyIterator::FillKeysForCurrentPrototypeAndStage() {
+  current_key_index_ = 0;
+  exotic_length_ = 0;
+  keys_ = Handle<FixedArray>::null();
+  if (prototype_iterator_.IsAtEnd()) return;
+  Handle<JSReceiver> receiver =
+      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
+  bool has_exotic_indices = receiver->IsJSTypedArray();
+  if (stage_ == kExoticIndices) {
+    if (!has_exotic_indices) return;
+    Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(receiver);
+    exotic_length_ = typed_array->WasDetached() ? 0 : typed_array->length();
+    return;
+  }
+  bool skip_indices = has_exotic_indices;
+  PropertyFilter filter =
+      stage_ == kEnumerableStrings ? ENUMERABLE_STRINGS : ALL_PROPERTIES;
+  if (!KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
+                               GetKeysConversion::kConvertToString, false,
+                               skip_indices)
+           .ToHandle(&keys_)) {
+    keys_ = Handle<FixedArray>::null();
+  }
+}
+
+bool DebugPropertyIterator::should_move_to_next_stage() const {
+  if (prototype_iterator_.IsAtEnd()) return false;
+  if (stage_ == kExoticIndices) return current_key_index_ >= exotic_length_;
+  return keys_.is_null() ||
+         current_key_index_ >= static_cast<size_t>(keys_->length());
+}
+
+namespace {
+base::Flags<debug::NativeAccessorType, int> GetNativeAccessorDescriptorInternal(
+    Handle<JSReceiver> object, Handle<Name> name) {
+  Isolate* isolate = object->GetIsolate();
+  LookupIterator::Key key(isolate, name);
+  if (key.is_element()) return debug::NativeAccessorType::None;
+  LookupIterator it(isolate, object, key, LookupIterator::OWN);
+  if (!it.IsFound()) return debug::NativeAccessorType::None;
+  if (it.state() != LookupIterator::ACCESSOR) {
+    return debug::NativeAccessorType::None;
+  }
+  Handle<Object> structure = it.GetAccessors();
+  if (!structure->IsAccessorInfo()) return debug::NativeAccessorType::None;
+  base::Flags<debug::NativeAccessorType, int> result;
+#define IS_BUILTIN_ACCESSOR(_, name, ...)                   \
+  if (*structure == *isolate->factory()->name##_accessor()) \
+    return debug::NativeAccessorType::None;
+  ACCESSOR_INFO_LIST_GENERATOR(IS_BUILTIN_ACCESSOR, /* not used */)
+#undef IS_BUILTIN_ACCESSOR
+  Handle<AccessorInfo> accessor_info = Handle<AccessorInfo>::cast(structure);
+  if (accessor_info->getter() != Object()) {
+    result |= debug::NativeAccessorType::HasGetter;
+  }
+  if (accessor_info->setter() != Object()) {
+    result |= debug::NativeAccessorType::HasSetter;
+  }
+  return result;
+}
+}  // anonymous namespace
+
+void DebugPropertyIterator::CalculateNativeAccessorFlags() {
+  if (calculated_native_accessor_flags_) return;
+  Handle<JSReceiver> receiver =
+      PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
+  native_accessor_flags_ =
+      GetNativeAccessorDescriptorInternal(receiver, raw_name());
+  calculated_native_accessor_flags_ = true;
+}
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/debug-property-iterator.h b/src/debug/debug-property-iterator.h
new file mode 100644
index 0000000..67d3cb8
--- /dev/null
+++ b/src/debug/debug-property-iterator.h
@@ -0,0 +1,62 @@
+// Copyright 2018 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_DEBUG_PROPERTY_ITERATOR_H_
+#define V8_DEBUG_DEBUG_PROPERTY_ITERATOR_H_
+
+#include "src/debug/debug-interface.h"
+#include "src/execution/isolate.h"
+#include "src/handles/handles.h"
+#include "src/objects/prototype.h"
+
+#include "include/v8.h"
+
+namespace v8 {
+namespace internal {
+
+class JSReceiver;
+
+class DebugPropertyIterator final : public debug::PropertyIterator {
+ public:
+  DebugPropertyIterator(Isolate* isolate, Handle<JSReceiver> receiver);
+  ~DebugPropertyIterator() override = default;
+
+  bool Done() const override;
+  void Advance() override;
+
+  v8::Local<v8::Name> name() const override;
+  bool is_native_accessor() override;
+  bool has_native_getter() override;
+  bool has_native_setter() override;
+  v8::Maybe<v8::PropertyAttribute> attributes() override;
+  v8::Maybe<v8::debug::PropertyDescriptor> descriptor() override;
+
+  bool is_own() override;
+  bool is_array_index() override;
+
+ private:
+  void FillKeysForCurrentPrototypeAndStage();
+  bool should_move_to_next_stage() const;
+  void CalculateNativeAccessorFlags();
+  Handle<Name> raw_name() const;
+
+  Isolate* isolate_;
+  PrototypeIterator prototype_iterator_;
+  enum Stage { kExoticIndices = 0, kEnumerableStrings = 1, kAllProperties = 2 };
+  Stage stage_ = kExoticIndices;
+
+  size_t current_key_index_ = 0;
+  Handle<FixedArray> keys_;
+  size_t exotic_length_ = 0;
+
+  bool calculated_native_accessor_flags_ = false;
+  int native_accessor_flags_ = 0;
+  bool is_own_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(DebugPropertyIterator);
+};
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_DEBUG_PROPERTY_ITERATOR_H_
diff --git a/src/debug/debug-scope-iterator.cc b/src/debug/debug-scope-iterator.cc
new file mode 100644
index 0000000..ab3191d
--- /dev/null
+++ b/src/debug/debug-scope-iterator.cc
@@ -0,0 +1,220 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/debug-scope-iterator.h"
+
+#include "src/api/api-inl.h"
+#include "src/debug/debug.h"
+#include "src/debug/liveedit.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/isolate.h"
+#include "src/objects/js-generator-inl.h"
+#include "src/wasm/wasm-debug.h"
+#include "src/wasm/wasm-objects-inl.h"
+
+namespace v8 {
+
+std::unique_ptr<debug::ScopeIterator> debug::ScopeIterator::CreateForFunction(
+    v8::Isolate* v8_isolate, v8::Local<v8::Function> v8_func) {
+  internal::Handle<internal::JSReceiver> receiver =
+      internal::Handle<internal::JSReceiver>::cast(Utils::OpenHandle(*v8_func));
+
+  // Besides JSFunction and JSBoundFunction, {v8_func} could be an
+  // ObjectTemplate with a CallAsFunctionHandler. We only handle plain
+  // JSFunctions.
+  if (!receiver->IsJSFunction()) return nullptr;
+
+  internal::Handle<internal::JSFunction> function =
+      internal::Handle<internal::JSFunction>::cast(receiver);
+
+  // Blink has function objects with callable map, JS_SPECIAL_API_OBJECT_TYPE
+  // but without context on heap.
+  if (!function->has_context()) return nullptr;
+  return std::unique_ptr<debug::ScopeIterator>(new internal::DebugScopeIterator(
+      reinterpret_cast<internal::Isolate*>(v8_isolate), function));
+}
+
+std::unique_ptr<debug::ScopeIterator>
+debug::ScopeIterator::CreateForGeneratorObject(
+    v8::Isolate* v8_isolate, v8::Local<v8::Object> v8_generator) {
+  internal::Handle<internal::Object> generator =
+      Utils::OpenHandle(*v8_generator);
+  DCHECK(generator->IsJSGeneratorObject());
+  return std::unique_ptr<debug::ScopeIterator>(new internal::DebugScopeIterator(
+      reinterpret_cast<internal::Isolate*>(v8_isolate),
+      internal::Handle<internal::JSGeneratorObject>::cast(generator)));
+}
+
+namespace internal {
+
+DebugScopeIterator::DebugScopeIterator(Isolate* isolate,
+                                       FrameInspector* frame_inspector)
+    : iterator_(
+          isolate, frame_inspector,
+          ::v8::internal::ScopeIterator::ReparseStrategy::kFunctionLiteral) {
+  if (!Done() && ShouldIgnore()) Advance();
+}
+
+DebugScopeIterator::DebugScopeIterator(Isolate* isolate,
+                                       Handle<JSFunction> function)
+    : iterator_(isolate, function) {
+  if (!Done() && ShouldIgnore()) Advance();
+}
+
+DebugScopeIterator::DebugScopeIterator(Isolate* isolate,
+                                       Handle<JSGeneratorObject> generator)
+    : iterator_(isolate, generator) {
+  if (!Done() && ShouldIgnore()) Advance();
+}
+
+bool DebugScopeIterator::Done() { return iterator_.Done(); }
+
+void DebugScopeIterator::Advance() {
+  DCHECK(!Done());
+  iterator_.Next();
+  while (!Done() && ShouldIgnore()) {
+    iterator_.Next();
+  }
+}
+
+bool DebugScopeIterator::ShouldIgnore() {
+  if (GetType() == debug::ScopeIterator::ScopeTypeLocal) return false;
+  return !iterator_.DeclaresLocals(i::ScopeIterator::Mode::ALL);
+}
+
+v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() {
+  DCHECK(!Done());
+  return static_cast<v8::debug::ScopeIterator::ScopeType>(iterator_.Type());
+}
+
+v8::Local<v8::Object> DebugScopeIterator::GetObject() {
+  DCHECK(!Done());
+  Handle<JSObject> value = iterator_.ScopeObject(i::ScopeIterator::Mode::ALL);
+  return Utils::ToLocal(value);
+}
+
+int DebugScopeIterator::GetScriptId() {
+  DCHECK(!Done());
+  return iterator_.GetScript()->id();
+}
+
+v8::Local<v8::Value> DebugScopeIterator::GetFunctionDebugName() {
+  DCHECK(!Done());
+  Handle<Object> name = iterator_.GetFunctionDebugName();
+  return Utils::ToLocal(name);
+}
+
+bool DebugScopeIterator::HasLocationInfo() {
+  return iterator_.HasPositionInfo();
+}
+
+debug::Location DebugScopeIterator::GetStartLocation() {
+  DCHECK(!Done());
+  return ToApiHandle<v8::debug::Script>(iterator_.GetScript())
+      ->GetSourceLocation(iterator_.start_position());
+}
+
+debug::Location DebugScopeIterator::GetEndLocation() {
+  DCHECK(!Done());
+  return ToApiHandle<v8::debug::Script>(iterator_.GetScript())
+      ->GetSourceLocation(iterator_.end_position());
+}
+
+bool DebugScopeIterator::SetVariableValue(v8::Local<v8::String> name,
+                                          v8::Local<v8::Value> value) {
+  DCHECK(!Done());
+  return iterator_.SetVariableValue(Utils::OpenHandle(*name),
+                                    Utils::OpenHandle(*value));
+}
+
+DebugWasmScopeIterator::DebugWasmScopeIterator(Isolate* isolate,
+                                               WasmFrame* frame)
+    : isolate_(isolate),
+      frame_(frame),
+      type_(debug::ScopeIterator::ScopeTypeModule) {}
+
+bool DebugWasmScopeIterator::Done() {
+  return type_ == debug::ScopeIterator::ScopeTypeWith;
+}
+
+void DebugWasmScopeIterator::Advance() {
+  DCHECK(!Done());
+  switch (type_) {
+    case ScopeTypeModule:
+      // Skip local scope and expression stack scope if the frame is not
+      // inspectable.
+      type_ = frame_->is_inspectable() ? debug::ScopeIterator::ScopeTypeLocal
+                                       : debug::ScopeIterator::ScopeTypeWith;
+      break;
+    case ScopeTypeLocal:
+      type_ = debug::ScopeIterator::ScopeTypeWasmExpressionStack;
+      break;
+    case ScopeTypeWasmExpressionStack:
+      // We use ScopeTypeWith type as marker for done.
+      type_ = debug::ScopeIterator::ScopeTypeWith;
+      break;
+    default:
+      UNREACHABLE();
+  }
+}
+
+v8::debug::ScopeIterator::ScopeType DebugWasmScopeIterator::GetType() {
+  DCHECK(!Done());
+  return type_;
+}
+
+v8::Local<v8::Object> DebugWasmScopeIterator::GetObject() {
+  DCHECK(!Done());
+  switch (type_) {
+    case debug::ScopeIterator::ScopeTypeModule: {
+      Handle<WasmInstanceObject> instance =
+          FrameSummary::GetTop(frame_).AsWasm().wasm_instance();
+      return Utils::ToLocal(wasm::GetModuleScopeObject(instance));
+    }
+    case debug::ScopeIterator::ScopeTypeLocal: {
+      DCHECK(frame_->is_wasm());
+      wasm::DebugInfo* debug_info = frame_->native_module()->GetDebugInfo();
+      return Utils::ToLocal(debug_info->GetLocalScopeObject(
+          isolate_, frame_->pc(), frame_->fp(), frame_->callee_fp()));
+    }
+    case debug::ScopeIterator::ScopeTypeWasmExpressionStack: {
+      DCHECK(frame_->is_wasm());
+      wasm::DebugInfo* debug_info = frame_->native_module()->GetDebugInfo();
+      return Utils::ToLocal(debug_info->GetStackScopeObject(
+          isolate_, frame_->pc(), frame_->fp(), frame_->callee_fp()));
+    }
+    default:
+      return {};
+  }
+}
+
+int DebugWasmScopeIterator::GetScriptId() {
+  DCHECK(!Done());
+  return -1;
+}
+
+v8::Local<v8::Value> DebugWasmScopeIterator::GetFunctionDebugName() {
+  DCHECK(!Done());
+  return Utils::ToLocal(isolate_->factory()->empty_string());
+}
+
+bool DebugWasmScopeIterator::HasLocationInfo() { return false; }
+
+debug::Location DebugWasmScopeIterator::GetStartLocation() {
+  DCHECK(!Done());
+  return debug::Location();
+}
+
+debug::Location DebugWasmScopeIterator::GetEndLocation() {
+  DCHECK(!Done());
+  return debug::Location();
+}
+
+bool DebugWasmScopeIterator::SetVariableValue(v8::Local<v8::String> name,
+                                              v8::Local<v8::Value> value) {
+  DCHECK(!Done());
+  return false;
+}
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/debug-scope-iterator.h b/src/debug/debug-scope-iterator.h
new file mode 100644
index 0000000..a2b5ebc
--- /dev/null
+++ b/src/debug/debug-scope-iterator.h
@@ -0,0 +1,64 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_DEBUG_SCOPE_ITERATOR_H_
+#define V8_DEBUG_DEBUG_SCOPE_ITERATOR_H_
+
+#include "src/debug/debug-frames.h"
+#include "src/debug/debug-interface.h"
+#include "src/debug/debug-scopes.h"
+
+namespace v8 {
+namespace internal {
+
+class DebugScopeIterator final : public debug::ScopeIterator {
+ public:
+  DebugScopeIterator(Isolate* isolate, FrameInspector* frame_inspector);
+  DebugScopeIterator(Isolate* isolate, Handle<JSFunction> function);
+  DebugScopeIterator(Isolate* isolate, Handle<JSGeneratorObject> generator);
+
+  bool Done() override;
+  void Advance() override;
+  ScopeType GetType() override;
+  v8::Local<v8::Object> GetObject() override;
+  v8::Local<v8::Value> GetFunctionDebugName() override;
+  int GetScriptId() override;
+  bool HasLocationInfo() override;
+  debug::Location GetStartLocation() override;
+  debug::Location GetEndLocation() override;
+
+  bool SetVariableValue(v8::Local<v8::String> name,
+                        v8::Local<v8::Value> value) override;
+
+ private:
+  bool ShouldIgnore();
+
+  v8::internal::ScopeIterator iterator_;
+};
+
+class DebugWasmScopeIterator final : public debug::ScopeIterator {
+ public:
+  DebugWasmScopeIterator(Isolate* isolate, WasmFrame* frame);
+
+  bool Done() override;
+  void Advance() override;
+  ScopeType GetType() override;
+  v8::Local<v8::Object> GetObject() override;
+  v8::Local<v8::Value> GetFunctionDebugName() override;
+  int GetScriptId() override;
+  bool HasLocationInfo() override;
+  debug::Location GetStartLocation() override;
+  debug::Location GetEndLocation() override;
+
+  bool SetVariableValue(v8::Local<v8::String> name,
+                        v8::Local<v8::Value> value) override;
+ private:
+  Isolate* isolate_;
+  WasmFrame* frame_;
+  ScopeType type_;
+};
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_DEBUG_SCOPE_ITERATOR_H_
diff --git a/src/debug/debug-scopes.cc b/src/debug/debug-scopes.cc
index cf957bc..3142240 100644
--- a/src/debug/debug-scopes.cc
+++ b/src/debug/debug-scopes.cc
@@ -8,60 +8,224 @@
 
 #include "src/ast/ast.h"
 #include "src/ast/scopes.h"
+#include "src/common/globals.h"
 #include "src/debug/debug.h"
-#include "src/frames-inl.h"
-#include "src/globals.h"
-#include "src/isolate-inl.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/isolate-inl.h"
+#include "src/objects/js-generator-inl.h"
+#include "src/objects/source-text-module.h"
+#include "src/objects/string-set-inl.h"
 #include "src/parsing/parse-info.h"
 #include "src/parsing/parsing.h"
 #include "src/parsing/rewriter.h"
+#include "src/utils/ostreams.h"
 
 namespace v8 {
 namespace internal {
 
 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
-                             ScopeIterator::Option option)
+                             ReparseStrategy strategy)
     : isolate_(isolate),
       frame_inspector_(frame_inspector),
-      nested_scope_chain_(4),
-      seen_script_scope_(false) {
+      function_(frame_inspector_->GetFunction()),
+      script_(frame_inspector_->GetScript()) {
   if (!frame_inspector->GetContext()->IsContext()) {
     // Optimized frame, context or function cannot be materialized. Give up.
     return;
   }
-
   context_ = Handle<Context>::cast(frame_inspector->GetContext());
 
   // We should not instantiate a ScopeIterator for wasm frames.
-  DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM);
+  DCHECK_NE(Script::TYPE_WASM, frame_inspector->GetScript()->type());
 
-  // Catch the case when the debugger stops in an internal function.
-  Handle<JSFunction> function = GetFunction();
-  Handle<SharedFunctionInfo> shared_info(function->shared());
-  Handle<ScopeInfo> scope_info(shared_info->scope_info());
-  if (shared_info->script()->IsUndefined(isolate)) {
-    while (context_->closure() == *function) {
-      context_ = Handle<Context>(context_->previous(), isolate_);
+  TryParseAndRetrieveScopes(strategy);
+}
+
+ScopeIterator::~ScopeIterator() = default;
+
+Handle<Object> ScopeIterator::GetFunctionDebugName() const {
+  if (!function_.is_null()) return JSFunction::GetDebugName(function_);
+
+  if (!context_->IsNativeContext()) {
+    DisallowHeapAllocation no_gc;
+    ScopeInfo closure_info = context_->closure_context().scope_info();
+    Handle<String> debug_name(closure_info.FunctionDebugName(), isolate_);
+    if (debug_name->length() > 0) return debug_name;
+  }
+  return isolate_->factory()->undefined_value();
+}
+
+ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
+    : isolate_(isolate), context_(function->context(), isolate) {
+  if (!function->shared().IsSubjectToDebugging()) {
+    context_ = Handle<Context>();
+    return;
+  }
+  script_ = handle(Script::cast(function->shared().script()), isolate);
+  UnwrapEvaluationContext();
+}
+
+ScopeIterator::ScopeIterator(Isolate* isolate,
+                             Handle<JSGeneratorObject> generator)
+    : isolate_(isolate),
+      generator_(generator),
+      function_(generator->function(), isolate),
+      context_(generator->context(), isolate),
+      script_(Script::cast(function_->shared().script()), isolate) {
+  CHECK(function_->shared().IsSubjectToDebugging());
+  TryParseAndRetrieveScopes(ReparseStrategy::kFunctionLiteral);
+}
+
+void ScopeIterator::Restart() {
+  DCHECK_NOT_NULL(frame_inspector_);
+  function_ = frame_inspector_->GetFunction();
+  context_ = Handle<Context>::cast(frame_inspector_->GetContext());
+  current_scope_ = start_scope_;
+  DCHECK_NOT_NULL(current_scope_);
+  UnwrapEvaluationContext();
+}
+
+namespace {
+
+// Takes the scope of a parsed script, a function and a break location
+// inside the function. The result is the innermost lexical scope around
+// the break point, which serves as the starting point of the ScopeIterator.
+// And the scope of the function that was passed in (called closure scope).
+//
+// The start scope is guaranteed to be either the closure scope itself,
+// or a child of the closure scope.
+class ScopeChainRetriever {
+ public:
+  ScopeChainRetriever(DeclarationScope* scope, Handle<JSFunction> function,
+                      int position)
+      : scope_(scope),
+        break_scope_start_(function->shared().StartPosition()),
+        break_scope_end_(function->shared().EndPosition()),
+        is_default_constructor_(
+            IsDefaultConstructor(function->shared().kind())),
+        position_(position) {
+    DCHECK_NOT_NULL(scope);
+    RetrieveScopes();
+  }
+
+  DeclarationScope* ClosureScope() { return closure_scope_; }
+  Scope* StartScope() { return start_scope_; }
+
+ private:
+  DeclarationScope* scope_;
+  const int break_scope_start_;
+  const int break_scope_end_;
+  const bool is_default_constructor_;
+  const int position_;
+
+  DeclarationScope* closure_scope_ = nullptr;
+  Scope* start_scope_ = nullptr;
+
+  void RetrieveScopes() {
+    if (is_default_constructor_) {
+      // Even though the DefaultBaseConstructor is a child of a Class scope, the
+      // source positions are *not* nested. This means the actual scope for the
+      // DefaultBaseConstructor needs to be found by doing a DFS.
+      RetrieveScopeChainDefaultConstructor(scope_);
+    } else {
+      RetrieveScopeChain();
     }
+    DCHECK_NOT_NULL(closure_scope_);
+    DCHECK_NOT_NULL(start_scope_);
+  }
+
+  bool RetrieveScopeChainDefaultConstructor(Scope* scope) {
+    const int beg_pos = scope->start_position();
+    const int end_pos = scope->end_position();
+    if (beg_pos == position_ && end_pos == position_) {
+      DCHECK(scope->is_function_scope());
+      DCHECK(
+          IsDefaultConstructor(scope->AsDeclarationScope()->function_kind()));
+      start_scope_ = scope;
+      closure_scope_ = scope->AsDeclarationScope();
+      return true;
+    }
+
+    for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
+         inner_scope = inner_scope->sibling()) {
+      if (RetrieveScopeChainDefaultConstructor(inner_scope)) return true;
+    }
+    return false;
+  }
+
+  void RetrieveScopeChain() {
+    Scope* parent = nullptr;
+    Scope* current = scope_;
+    SetClosureScopeIfFound(current);
+
+    while (parent != current) {
+      parent = current;
+      for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr;
+           inner_scope = inner_scope->sibling()) {
+        if (SetClosureScopeIfFound(inner_scope) ||
+            ContainsPosition(inner_scope)) {
+          current = inner_scope;
+          break;
+        }
+      }
+    }
+    start_scope_ = current;
+  }
+
+  bool SetClosureScopeIfFound(Scope* scope) {
+    const int start = scope->start_position();
+    const int end = scope->end_position();
+    if (start == break_scope_start_ && end == break_scope_end_) {
+      closure_scope_ = scope->AsDeclarationScope();
+      return true;
+    }
+    return false;
+  }
+
+  bool ContainsPosition(Scope* scope) {
+    const int start = scope->start_position();
+    const int end = scope->end_position();
+    // In case the closure_scope_ hasn't been found yet, we are less strict
+    // about recursing downwards. This might be the case for nested arrow
+    // functions that have the same end position.
+    const bool position_fits_end =
+        closure_scope_ ? position_ < end : position_ <= end;
+    return start < position_ && position_fits_end;
+  }
+};
+
+}  // namespace
+
+void ScopeIterator::TryParseAndRetrieveScopes(ReparseStrategy strategy) {
+  // Catch the case when the debugger stops in an internal function.
+  Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_);
+  Handle<ScopeInfo> scope_info(shared_info->scope_info(), isolate_);
+  if (shared_info->script().IsUndefined(isolate_)) {
+    current_scope_ = closure_scope_ = nullptr;
+    context_ = handle(function_->context(), isolate_);
+    function_ = Handle<JSFunction>();
     return;
   }
 
-  // Currently it takes too much time to find nested scopes due to script
-  // parsing. Sometimes we want to run the ScopeIterator as fast as possible
-  // (for example, while collecting async call stacks on every
-  // addEventListener call), even if we drop some nested scopes.
-  // Later we may optimize getting the nested scopes (cache the result?)
-  // and include nested scopes into the "fast" iteration case as well.
-  bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES);
-  bool collect_non_locals = (option == COLLECT_NON_LOCALS);
-  if (!ignore_nested_scopes && shared_info->HasDebugInfo()) {
+  // Class fields initializer functions don't have any scope
+  // information. We short circuit the parsing of the class literal
+  // and return an empty context here.
+  if (IsClassMembersInitializerFunction(shared_info->kind())) {
+    current_scope_ = closure_scope_ = nullptr;
+    context_ = Handle<Context>();
+    function_ = Handle<JSFunction>();
+    return;
+  }
+
+  bool ignore_nested_scopes = false;
+  if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) {
     // The source position at return is always the end of the function,
     // which is not consistent with the current scope chain. Therefore all
     // nested with, catch and block contexts are skipped, and we can only
     // inspect the function scope.
     // This can only happen if we set a break point inside right before the
     // return, which requires a debug info to be available.
-    Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
+    Handle<DebugInfo> debug_info(shared_info->GetDebugInfo(), isolate_);
 
     // Find the break point where execution has stopped.
     BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
@@ -69,219 +233,289 @@
     ignore_nested_scopes = location.IsReturn();
   }
 
-  if (ignore_nested_scopes) {
-    if (scope_info->HasContext()) {
-      context_ = Handle<Context>(context_->declaration_context(), isolate_);
-    } else {
-      while (context_->closure() == *function) {
-        context_ = Handle<Context>(context_->previous(), isolate_);
-      }
+  // Reparse the code and analyze the scopes.
+  // Depending on the choosen strategy, the whole script or just
+  // the closure is re-parsed for function scopes.
+  Handle<Script> script(Script::cast(shared_info->script()), isolate_);
+
+  // Pick between flags for a single function compilation, or an eager
+  // compilation of the whole script.
+  UnoptimizedCompileFlags flags =
+      (scope_info->scope_type() == FUNCTION_SCOPE &&
+       strategy == ReparseStrategy::kFunctionLiteral)
+          ? UnoptimizedCompileFlags::ForFunctionCompile(isolate_, *shared_info)
+          : UnoptimizedCompileFlags::ForScriptCompile(isolate_, *script)
+                .set_is_eager(true);
+
+  MaybeHandle<ScopeInfo> maybe_outer_scope;
+  if (scope_info->scope_type() == EVAL_SCOPE || script->is_wrapped()) {
+    flags.set_is_eval(true);
+    if (!context_->IsNativeContext()) {
+      maybe_outer_scope = handle(context_->scope_info(), isolate_);
     }
-    if (scope_info->scope_type() == FUNCTION_SCOPE) {
-      nested_scope_chain_.Add(ExtendedScopeInfo(scope_info,
-                                                shared_info->start_position(),
-                                                shared_info->end_position()));
-    }
-    if (!collect_non_locals) return;
+    // Language mode may be inherited from the eval caller.
+    // Retrieve it from shared function info.
+    flags.set_outer_language_mode(shared_info->language_mode());
+  } else if (scope_info->scope_type() == MODULE_SCOPE) {
+    DCHECK(flags.is_module());
+  } else {
+    DCHECK(scope_info->scope_type() == SCRIPT_SCOPE ||
+           scope_info->scope_type() == FUNCTION_SCOPE);
   }
 
-  // Reparse the code and analyze the scopes.
-  // Check whether we are in global, eval or function code.
-  std::unique_ptr<ParseInfo> info;
-  if (scope_info->scope_type() != FUNCTION_SCOPE) {
-    // Global or eval code.
-    Handle<Script> script(Script::cast(shared_info->script()));
-    info.reset(new ParseInfo(script));
-    if (scope_info->scope_type() == EVAL_SCOPE) {
-      info->set_eval();
-      if (!function->context()->IsNativeContext()) {
-        info->set_outer_scope_info(handle(function->context()->scope_info()));
+  UnoptimizedCompileState compile_state(isolate_);
+
+  info_ = std::make_unique<ParseInfo>(isolate_, flags, &compile_state);
+
+  const bool parse_result =
+      flags.is_toplevel()
+          ? parsing::ParseProgram(info_.get(), script, maybe_outer_scope,
+                                  isolate_, parsing::ReportStatisticsMode::kNo)
+          : parsing::ParseFunction(info_.get(), shared_info, isolate_,
+                                   parsing::ReportStatisticsMode::kNo);
+
+  if (parse_result) {
+    DeclarationScope* literal_scope = info_->literal()->scope();
+
+    ScopeChainRetriever scope_chain_retriever(literal_scope, function_,
+                                              GetSourcePosition());
+    start_scope_ = scope_chain_retriever.StartScope();
+    current_scope_ = start_scope_;
+
+    // In case of a FUNCTION_SCOPE, the ScopeIterator expects
+    // {closure_scope_} to be set to the scope of the function.
+    closure_scope_ = scope_info->scope_type() == FUNCTION_SCOPE
+                         ? scope_chain_retriever.ClosureScope()
+                         : literal_scope;
+
+    if (ignore_nested_scopes) {
+      current_scope_ = closure_scope_;
+      start_scope_ = current_scope_;
+      // ignore_nested_scopes is only used for the return-position breakpoint,
+      // so we can safely assume that the closure context for the current
+      // function exists if it needs one.
+      if (closure_scope_->NeedsContext()) {
+        context_ = handle(context_->closure_context(), isolate_);
       }
-      // Language mode may be inherited from the eval caller.
-      // Retrieve it from shared function info.
-      info->set_language_mode(shared_info->language_mode());
-    } else if (scope_info->scope_type() == MODULE_SCOPE) {
-      info->set_module();
-    } else {
-      DCHECK(scope_info->scope_type() == SCRIPT_SCOPE);
     }
-  } else {
-    // Inner function.
-    info.reset(new ParseInfo(shared_info));
-  }
-  if (parsing::ParseAny(info.get()) && Rewriter::Rewrite(info.get())) {
-    DeclarationScope* scope = info->literal()->scope();
-    if (!ignore_nested_scopes || collect_non_locals) {
-      CollectNonLocals(info.get(), scope);
-    }
-    if (!ignore_nested_scopes) {
-      DeclarationScope::Analyze(info.get(), AnalyzeMode::kDebugger);
-      RetrieveScopeChain(scope);
-    }
+
+    UnwrapEvaluationContext();
   } else {
     // A failed reparse indicates that the preparser has diverged from the
-    // parser or that the preparse data given to the initial parse has been
-    // faulty. We fail in debug mode but in release mode we only provide the
-    // information we get from the context chain but nothing about
-    // completely stack allocated scopes or stack allocated locals.
-    // Or it could be due to stack overflow.
+    // parser, that the preparse data given to the initial parse was faulty, or
+    // a stack overflow.
+    // TODO(leszeks): This error is pretty unexpected, so we could report the
+    // error in debug mode. Better to not fail in release though, in case it's
+    // just a stack overflow.
+
     // Silently fail by presenting an empty context chain.
-    CHECK(isolate_->has_pending_exception());
-    isolate_->clear_pending_exception();
     context_ = Handle<Context>();
   }
-  UnwrapEvaluationContext();
-}
-
-ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
-    : isolate_(isolate),
-      frame_inspector_(NULL),
-      context_(function->context()),
-      seen_script_scope_(false) {
-  if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
-  UnwrapEvaluationContext();
-}
-
-ScopeIterator::ScopeIterator(Isolate* isolate,
-                             Handle<JSGeneratorObject> generator)
-    : isolate_(isolate),
-      frame_inspector_(NULL),
-      context_(generator->context()),
-      seen_script_scope_(false) {
-  if (!generator->function()->shared()->IsSubjectToDebugging()) {
-    context_ = Handle<Context>();
-  }
-  UnwrapEvaluationContext();
 }
 
 void ScopeIterator::UnwrapEvaluationContext() {
-  while (true) {
-    if (context_.is_null()) return;
-    if (!context_->IsDebugEvaluateContext()) return;
-    Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX),
-                           isolate_);
-    if (wrapped->IsContext()) {
-      context_ = Handle<Context>::cast(wrapped);
+  if (!context_->IsDebugEvaluateContext()) return;
+  Context current = *context_;
+  do {
+    Object wrapped = current.get(Context::WRAPPED_CONTEXT_INDEX);
+    if (wrapped.IsContext()) {
+      current = Context::cast(wrapped);
     } else {
-      context_ = Handle<Context>(context_->previous(), isolate_);
+      DCHECK(!current.previous().is_null());
+      current = current.previous();
     }
-  }
+  } while (current.IsDebugEvaluateContext());
+  context_ = handle(current, isolate_);
 }
 
-
-MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
+Handle<JSObject> ScopeIterator::MaterializeScopeDetails() {
   // Calculate the size of the result.
   Handle<FixedArray> details =
       isolate_->factory()->NewFixedArray(kScopeDetailsSize);
   // Fill in scope details.
   details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
-  Handle<JSObject> scope_object;
-  ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
+  Handle<JSObject> scope_object = ScopeObject(Mode::ALL);
   details->set(kScopeDetailsObjectIndex, *scope_object);
-  Handle<JSFunction> js_function = HasContext()
-                                       ? handle(CurrentContext()->closure())
-                                       : Handle<JSFunction>::null();
   if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
     return isolate_->factory()->NewJSArrayWithElements(details);
-  }
-
-  int start_position = 0;
-  int end_position = 0;
-  if (!nested_scope_chain_.is_empty()) {
-    js_function = GetFunction();
-    start_position = nested_scope_chain_.last().start_position;
-    end_position = nested_scope_chain_.last().end_position;
-  } else if (!js_function.is_null()) {
-    start_position = js_function->shared()->start_position();
-    end_position = js_function->shared()->end_position();
-  }
-
-  if (!js_function.is_null()) {
-    Handle<String> closure_name = JSFunction::GetDebugName(js_function);
-    if (!closure_name.is_null() && closure_name->length() != 0) {
-      details->set(kScopeDetailsNameIndex, *closure_name);
+  } else if (HasContext()) {
+    Handle<Object> closure_name = GetFunctionDebugName();
+    details->set(kScopeDetailsNameIndex, *closure_name);
+    details->set(kScopeDetailsStartPositionIndex,
+                 Smi::FromInt(start_position()));
+    details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
+    if (InInnerScope()) {
+      details->set(kScopeDetailsFunctionIndex, *function_);
     }
-    details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position));
-    details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position));
-    details->set(kScopeDetailsFunctionIndex, *js_function);
   }
   return isolate_->factory()->NewJSArrayWithElements(details);
 }
 
+bool ScopeIterator::HasPositionInfo() {
+  return InInnerScope() || !context_->IsNativeContext();
+}
+
+int ScopeIterator::start_position() {
+  if (InInnerScope()) return current_scope_->start_position();
+  if (context_->IsNativeContext()) return 0;
+  return context_->closure_context().scope_info().StartPosition();
+}
+
+int ScopeIterator::end_position() {
+  if (InInnerScope()) return current_scope_->end_position();
+  if (context_->IsNativeContext()) return 0;
+  return context_->closure_context().scope_info().EndPosition();
+}
+
+bool ScopeIterator::DeclaresLocals(Mode mode) const {
+  ScopeType type = Type();
+
+  if (type == ScopeTypeWith) return mode == Mode::ALL;
+  if (type == ScopeTypeGlobal) return mode == Mode::ALL;
+
+  bool declares_local = false;
+  auto visitor = [&](Handle<String> name, Handle<Object> value,
+                     ScopeType scope_type) {
+    declares_local = true;
+    return true;
+  };
+  VisitScope(visitor, mode);
+  return declares_local;
+}
+
+bool ScopeIterator::HasContext() const {
+  return !InInnerScope() || NeedsAndHasContext();
+}
+
+bool ScopeIterator::NeedsAndHasContext() const {
+  if (!current_scope_->NeedsContext()) return false;
+  // Generally, if a scope needs a context, then we can assume that it has a
+  // context. However, the stack check during function entry happens before the
+  // function has a chance to create and push its own context, so we must check
+  // for the case where the function is executing in its parent context. This
+  // case is only possible in function scopes; top-level code (modules and
+  // non-module scripts) begin execution in the context they need and don't have
+  // a separate step to push the correct context.
+  return !(current_scope_ == closure_scope_ &&
+           current_scope_->is_function_scope() && !function_.is_null() &&
+           function_->context() == *context_);
+}
+
+void ScopeIterator::AdvanceOneScope() {
+  if (NeedsAndHasContext()) {
+    DCHECK(!context_->previous().is_null());
+    context_ = handle(context_->previous(), isolate_);
+  }
+  DCHECK(current_scope_->outer_scope() != nullptr);
+  current_scope_ = current_scope_->outer_scope();
+}
+
+void ScopeIterator::AdvanceToNonHiddenScope() {
+  do {
+    AdvanceOneScope();
+  } while (current_scope_->is_hidden());
+}
+
+void ScopeIterator::AdvanceContext() {
+  DCHECK(!context_->IsNativeContext());
+  context_ = handle(context_->previous(), isolate_);
+
+  // While advancing one context, we need to advance at least one
+  // scope, but until we hit the next scope that actually requires
+  // a context. All the locals collected along the way build the
+  // blocklist for debug-evaluate for this context.
+  locals_ = StringSet::New(isolate_);
+  do {
+    if (!current_scope_ || !current_scope_->outer_scope()) break;
+
+    current_scope_ = current_scope_->outer_scope();
+    CollectLocalsFromCurrentScope();
+  } while (!NeedsAndHasContext());
+}
 
 void ScopeIterator::Next() {
   DCHECK(!Done());
+
   ScopeType scope_type = Type();
+
   if (scope_type == ScopeTypeGlobal) {
     // The global scope is always the last in the chain.
     DCHECK(context_->IsNativeContext());
     context_ = Handle<Context>();
-  } else if (scope_type == ScopeTypeScript) {
+    DCHECK(Done());
+    return;
+  }
+
+  bool leaving_closure = current_scope_ == closure_scope_;
+
+  if (scope_type == ScopeTypeScript) {
+    DCHECK_IMPLIES(InInnerScope() && !leaving_closure,
+                   current_scope_->is_script_scope());
     seen_script_scope_ = true;
     if (context_->IsScriptContext()) {
-      context_ = Handle<Context>(context_->previous(), isolate_);
+      context_ = handle(context_->previous(), isolate_);
     }
-    if (!nested_scope_chain_.is_empty()) {
-      DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(),
-                SCRIPT_SCOPE);
-      nested_scope_chain_.RemoveLast();
-      DCHECK(nested_scope_chain_.is_empty());
-    }
-    CHECK(context_->IsNativeContext());
-  } else if (nested_scope_chain_.is_empty()) {
-    context_ = Handle<Context>(context_->previous(), isolate_);
+  } else if (!InInnerScope()) {
+    AdvanceContext();
   } else {
-    do {
-      if (nested_scope_chain_.last().scope_info->HasContext()) {
-        DCHECK(context_->previous() != NULL);
-        context_ = Handle<Context>(context_->previous(), isolate_);
-      }
-      nested_scope_chain_.RemoveLast();
-      if (nested_scope_chain_.is_empty()) break;
-      // Repeat to skip hidden scopes.
-    } while (nested_scope_chain_.last().is_hidden());
+    DCHECK_NOT_NULL(current_scope_);
+    AdvanceToNonHiddenScope();
+
+    if (leaving_closure) {
+      DCHECK(current_scope_ != closure_scope_);
+      // Edge case when we just go past {closure_scope_}. This case
+      // already needs to start collecting locals for the blocklist.
+      locals_ = StringSet::New(isolate_);
+      CollectLocalsFromCurrentScope();
+    }
   }
+
+  if (leaving_closure) function_ = Handle<JSFunction>();
+
   UnwrapEvaluationContext();
 }
 
-
 // Return the type of the current scope.
-ScopeIterator::ScopeType ScopeIterator::Type() {
+ScopeIterator::ScopeType ScopeIterator::Type() const {
   DCHECK(!Done());
-  if (!nested_scope_chain_.is_empty()) {
-    Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
-    switch (scope_info->scope_type()) {
+  if (InInnerScope()) {
+    switch (current_scope_->scope_type()) {
       case FUNCTION_SCOPE:
-        DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
+        DCHECK_IMPLIES(NeedsAndHasContext(),
+                       context_->IsFunctionContext() ||
+                           context_->IsDebugEvaluateContext());
         return ScopeTypeLocal;
       case MODULE_SCOPE:
-        DCHECK(context_->IsModuleContext());
+        DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsModuleContext());
         return ScopeTypeModule;
       case SCRIPT_SCOPE:
-        DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
+        DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsScriptContext() ||
+                                                 context_->IsNativeContext());
         return ScopeTypeScript;
       case WITH_SCOPE:
-        DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
+        DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsWithContext());
         return ScopeTypeWith;
       case CATCH_SCOPE:
         DCHECK(context_->IsCatchContext());
         return ScopeTypeCatch;
       case BLOCK_SCOPE:
-        DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
+      case CLASS_SCOPE:
+        DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsBlockContext());
         return ScopeTypeBlock;
       case EVAL_SCOPE:
-        DCHECK(!scope_info->HasContext() || context_->IsEvalContext());
+        DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsEvalContext());
         return ScopeTypeEval;
     }
     UNREACHABLE();
   }
   if (context_->IsNativeContext()) {
-    DCHECK(context_->global_object()->IsJSGlobalObject());
+    DCHECK(context_->global_object().IsJSGlobalObject());
     // If we are at the native context and have not yet seen script scope,
     // fake it.
     return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
   }
-  if (context_->IsFunctionContext() || context_->IsEvalContext()) {
+  if (context_->IsFunctionContext() || context_->IsEvalContext() ||
+      context_->IsDebugEvaluateContext()) {
     return ScopeTypeClosure;
   }
   if (context_->IsCatchContext()) {
@@ -296,126 +530,142 @@
   if (context_->IsScriptContext()) {
     return ScopeTypeScript;
   }
-  DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
+  DCHECK(context_->IsWithContext());
   return ScopeTypeWith;
 }
 
-
-MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
+Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) {
   DCHECK(!Done());
-  switch (Type()) {
-    case ScopeIterator::ScopeTypeGlobal:
-      return Handle<JSObject>(CurrentContext()->global_proxy());
-    case ScopeIterator::ScopeTypeScript:
-      return MaterializeScriptScope();
-    case ScopeIterator::ScopeTypeLocal:
-      // Materialize the content of the local scope into a JSObject.
-      DCHECK(nested_scope_chain_.length() == 1);
-      return MaterializeLocalScope();
-    case ScopeIterator::ScopeTypeWith:
-      return WithContextExtension();
-    case ScopeIterator::ScopeTypeCatch:
-      return MaterializeCatchScope();
-    case ScopeIterator::ScopeTypeClosure:
-      // Materialize the content of the closure scope into a JSObject.
-      return MaterializeClosure();
-    case ScopeIterator::ScopeTypeBlock:
-    case ScopeIterator::ScopeTypeEval:
-      return MaterializeInnerScope();
-    case ScopeIterator::ScopeTypeModule:
-      return MaterializeModuleScope();
-  }
-  UNREACHABLE();
-  return Handle<JSObject>();
-}
 
-
-bool ScopeIterator::HasContext() {
   ScopeType type = Type();
-  if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
-      type == ScopeTypeEval) {
-    if (!nested_scope_chain_.is_empty()) {
-      return nested_scope_chain_.last().scope_info->HasContext();
-    }
+  if (type == ScopeTypeGlobal) {
+    DCHECK_EQ(Mode::ALL, mode);
+    return handle(context_->global_proxy(), isolate_);
   }
-  return true;
+  if (type == ScopeTypeWith) {
+    DCHECK_EQ(Mode::ALL, mode);
+    return WithContextExtension();
+  }
+
+  Handle<JSObject> scope = isolate_->factory()->NewJSObjectWithNullProto();
+  auto visitor = [=](Handle<String> name, Handle<Object> value,
+                     ScopeType scope_type) {
+    if (value->IsTheHole(isolate_)) {
+      // Reflect variables under TDZ as undefined in scope object.
+      if (scope_type == ScopeTypeScript &&
+          JSReceiver::HasOwnProperty(scope, name).FromMaybe(true)) {
+        // We also use the hole to represent overridden let-declarations via
+        // REPL mode in a script context. Catch this case.
+        return false;
+      }
+      value = isolate_->factory()->undefined_value();
+    }
+    JSObject::AddProperty(isolate_, scope, name, value, NONE);
+    return false;
+  };
+
+  VisitScope(visitor, mode);
+  return scope;
 }
 
-
-bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
-                                     Handle<Object> new_value) {
-  DCHECK(!Done());
+void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const {
   switch (Type()) {
-    case ScopeIterator::ScopeTypeGlobal:
+    case ScopeTypeLocal:
+    case ScopeTypeClosure:
+    case ScopeTypeCatch:
+    case ScopeTypeBlock:
+    case ScopeTypeEval:
+      return VisitLocalScope(visitor, mode, Type());
+    case ScopeTypeModule:
+      if (InInnerScope()) {
+        return VisitLocalScope(visitor, mode, Type());
+      }
+      DCHECK_EQ(Mode::ALL, mode);
+      return VisitModuleScope(visitor);
+    case ScopeTypeScript:
+      DCHECK_EQ(Mode::ALL, mode);
+      return VisitScriptScope(visitor);
+    case ScopeTypeWith:
+    case ScopeTypeGlobal:
+      UNREACHABLE();
+  }
+}
+
+bool ScopeIterator::SetVariableValue(Handle<String> name,
+                                     Handle<Object> value) {
+  DCHECK(!Done());
+  name = isolate_->factory()->InternalizeString(name);
+  switch (Type()) {
+    case ScopeTypeGlobal:
+    case ScopeTypeWith:
       break;
-    case ScopeIterator::ScopeTypeLocal:
-      return SetLocalVariableValue(variable_name, new_value);
-    case ScopeIterator::ScopeTypeWith:
-      break;
-    case ScopeIterator::ScopeTypeCatch:
-      return SetCatchVariableValue(variable_name, new_value);
-    case ScopeIterator::ScopeTypeClosure:
-      return SetClosureVariableValue(variable_name, new_value);
-    case ScopeIterator::ScopeTypeScript:
-      return SetScriptVariableValue(variable_name, new_value);
-    case ScopeIterator::ScopeTypeBlock:
-    case ScopeIterator::ScopeTypeEval:
-      return SetInnerScopeVariableValue(variable_name, new_value);
-    case ScopeIterator::ScopeTypeModule:
-      // TODO(neis): Implement.
-      break;
+
+    case ScopeTypeEval:
+    case ScopeTypeBlock:
+    case ScopeTypeCatch:
+    case ScopeTypeModule:
+      if (InInnerScope()) return SetLocalVariableValue(name, value);
+      if (Type() == ScopeTypeModule && SetModuleVariableValue(name, value)) {
+        return true;
+      }
+      return SetContextVariableValue(name, value);
+
+    case ScopeTypeLocal:
+    case ScopeTypeClosure:
+      if (InInnerScope()) {
+        DCHECK_EQ(ScopeTypeLocal, Type());
+        if (SetLocalVariableValue(name, value)) return true;
+        // There may not be an associated context since we're InInnerScope().
+        if (!NeedsAndHasContext()) return false;
+      } else {
+        DCHECK_EQ(ScopeTypeClosure, Type());
+        if (SetContextVariableValue(name, value)) return true;
+      }
+      // The above functions only set variables statically declared in the
+      // function. There may be eval-introduced variables. Check them in
+      // SetContextExtensionValue.
+      return SetContextExtensionValue(name, value);
+
+    case ScopeTypeScript:
+      return SetScriptVariableValue(name, value);
   }
   return false;
 }
 
-
-Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
-  DCHECK(!Done());
-  if (!nested_scope_chain_.is_empty()) {
-    return nested_scope_chain_.last().scope_info;
-  } else if (context_->IsBlockContext() || context_->IsFunctionContext() ||
-             context_->IsEvalContext()) {
-    return Handle<ScopeInfo>(context_->scope_info());
-  }
-  return Handle<ScopeInfo>::null();
+bool ScopeIterator::ClosureScopeHasThisReference() const {
+  return !closure_scope_->has_this_declaration() &&
+         closure_scope_->HasThisReference();
 }
 
-
-Handle<Context> ScopeIterator::CurrentContext() {
-  DCHECK(!Done());
-  if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
-      nested_scope_chain_.is_empty()) {
-    return context_;
-  } else if (nested_scope_chain_.last().scope_info->HasContext()) {
-    return context_;
-  } else {
-    return Handle<Context>();
+void ScopeIterator::CollectLocalsFromCurrentScope() {
+  DCHECK(locals_->IsStringSet());
+  for (Variable* var : *current_scope_->locals()) {
+    if (var->location() == VariableLocation::PARAMETER ||
+        var->location() == VariableLocation::LOCAL) {
+      locals_ = StringSet::Add(isolate_, locals_, var->name());
+    }
   }
 }
 
-Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
-
 #ifdef DEBUG
 // Debug print of the content of the current scope.
 void ScopeIterator::DebugPrint() {
-  OFStream os(stdout);
+  StdoutStream os;
   DCHECK(!Done());
   switch (Type()) {
     case ScopeIterator::ScopeTypeGlobal:
       os << "Global:\n";
-      CurrentContext()->Print(os);
+      context_->Print(os);
       break;
 
     case ScopeIterator::ScopeTypeLocal: {
       os << "Local:\n";
-      GetFunction()->shared()->scope_info()->Print();
-      if (!CurrentContext().is_null()) {
-        CurrentContext()->Print(os);
-        if (CurrentContext()->has_extension()) {
-          Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
-          if (extension->IsJSContextExtensionObject()) {
-            extension->Print(os);
-          }
+      if (NeedsAndHasContext()) {
+        context_->Print(os);
+        if (context_->has_extension()) {
+          Handle<HeapObject> extension(context_->extension(), isolate_);
+          DCHECK(extension->IsJSContextExtensionObject());
+          extension->Print(os);
         }
       }
       break;
@@ -423,33 +673,29 @@
 
     case ScopeIterator::ScopeTypeWith:
       os << "With:\n";
-      CurrentContext()->extension()->Print(os);
+      context_->extension().Print(os);
       break;
 
     case ScopeIterator::ScopeTypeCatch:
       os << "Catch:\n";
-      CurrentContext()->extension()->Print(os);
-      CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
+      context_->extension().Print(os);
+      context_->get(Context::THROWN_OBJECT_INDEX).Print(os);
       break;
 
     case ScopeIterator::ScopeTypeClosure:
       os << "Closure:\n";
-      CurrentContext()->Print(os);
-      if (CurrentContext()->has_extension()) {
-        Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
-        if (extension->IsJSContextExtensionObject()) {
-          extension->Print(os);
-        }
+      context_->Print(os);
+      if (context_->has_extension()) {
+        Handle<HeapObject> extension(context_->extension(), isolate_);
+        DCHECK(extension->IsJSContextExtensionObject());
+        extension->Print(os);
       }
       break;
 
     case ScopeIterator::ScopeTypeScript:
       os << "Script:\n";
-      CurrentContext()
-          ->global_object()
-          ->native_context()
-          ->script_context_table()
-          ->Print(os);
+      context_->global_object().native_context().script_context_table().Print(
+          os);
       break;
 
     default:
@@ -459,290 +705,395 @@
 }
 #endif
 
-void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
-  DCHECK_NOT_NULL(scope);
-  int source_position = frame_inspector_->GetSourcePosition();
-  GetNestedScopeChain(isolate_, scope, source_position);
+int ScopeIterator::GetSourcePosition() {
+  if (frame_inspector_) {
+    return frame_inspector_->GetSourcePosition();
+  } else {
+    DCHECK(!generator_.is_null());
+    SharedFunctionInfo::EnsureSourcePositionsAvailable(
+        isolate_, handle(generator_->function().shared(), isolate_));
+    return generator_->source_position();
+  }
 }
 
-void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) {
-  DCHECK_NOT_NULL(scope);
-  DCHECK(non_locals_.is_null());
-  non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_));
-}
-
-
-MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
-  Handle<JSGlobalObject> global(CurrentContext()->global_object());
+void ScopeIterator::VisitScriptScope(const Visitor& visitor) const {
+  Handle<JSGlobalObject> global(context_->global_object(), isolate_);
   Handle<ScriptContextTable> script_contexts(
-      global->native_context()->script_context_table());
+      global->native_context().script_context_table(), isolate_);
 
-  Handle<JSObject> script_scope =
-      isolate_->factory()->NewJSObjectWithNullProto();
-
-  for (int context_index = 0; context_index < script_contexts->used();
-       context_index++) {
-    Handle<Context> context =
-        ScriptContextTable::GetContext(script_contexts, context_index);
-    Handle<ScopeInfo> scope_info(context->scope_info());
-    CopyContextLocalsToScopeObject(scope_info, context, script_scope);
+  // Skip the first script since that just declares 'this'.
+  for (int context_index = 1;
+       context_index < script_contexts->synchronized_used(); context_index++) {
+    Handle<Context> context = ScriptContextTable::GetContext(
+        isolate_, script_contexts, context_index);
+    Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
+    if (VisitContextLocals(visitor, scope_info, context, ScopeTypeScript))
+      return;
   }
-  return script_scope;
 }
 
+void ScopeIterator::VisitModuleScope(const Visitor& visitor) const {
+  DCHECK(context_->IsModuleContext());
 
-MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
-  Handle<JSFunction> function = GetFunction();
+  Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
+  if (VisitContextLocals(visitor, scope_info, context_, ScopeTypeModule))
+    return;
 
-  Handle<JSObject> local_scope =
-      isolate_->factory()->NewJSObjectWithNullProto();
-  frame_inspector_->MaterializeStackLocals(local_scope, function);
+  int count_index = scope_info->ModuleVariableCountIndex();
+  int module_variable_count = Smi::cast(scope_info->get(count_index)).value();
 
-  Handle<Context> frame_context =
-      Handle<Context>::cast(frame_inspector_->GetContext());
+  Handle<SourceTextModule> module(context_->module(), isolate_);
 
-  HandleScope scope(isolate_);
-  Handle<SharedFunctionInfo> shared(function->shared());
-  Handle<ScopeInfo> scope_info(shared->scope_info());
+  for (int i = 0; i < module_variable_count; ++i) {
+    int index;
+    Handle<String> name;
+    {
+      String raw_name;
+      scope_info->ModuleVariable(i, &raw_name, &index);
+      if (ScopeInfo::VariableIsSynthetic(raw_name)) continue;
+      name = handle(raw_name, isolate_);
+    }
+    Handle<Object> value =
+        SourceTextModule::LoadVariable(isolate_, module, index);
 
-  if (!scope_info->HasContext()) return local_scope;
-
-  // Fill all context locals.
-  Handle<Context> function_context(frame_context->closure_context());
-  CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
-
-  // Finally copy any properties from the function context extension.
-  // These will be variables introduced by eval.
-  if (function_context->closure() == *function &&
-      !function_context->IsNativeContext()) {
-    CopyContextExtensionToScopeObject(function_context, local_scope,
-                                      KeyCollectionMode::kIncludePrototypes);
+    if (visitor(name, value, ScopeTypeModule)) return;
   }
-
-  return local_scope;
 }
 
-
-// Create a plain JSObject which materializes the closure content for the
-// context.
-Handle<JSObject> ScopeIterator::MaterializeClosure() {
-  Handle<Context> context = CurrentContext();
-  DCHECK(context->IsFunctionContext() || context->IsEvalContext());
-
-  Handle<SharedFunctionInfo> shared(context->closure()->shared());
-  Handle<ScopeInfo> scope_info(shared->scope_info());
-
-  // Allocate and initialize a JSObject with all the content of this function
-  // closure.
-  Handle<JSObject> closure_scope =
-      isolate_->factory()->NewJSObjectWithNullProto();
-
+bool ScopeIterator::VisitContextLocals(const Visitor& visitor,
+                                       Handle<ScopeInfo> scope_info,
+                                       Handle<Context> context,
+                                       ScopeType scope_type) const {
   // Fill all context locals to the context extension.
-  CopyContextLocalsToScopeObject(scope_info, context, closure_scope);
-
-  // Finally copy any properties from the function context extension. This will
-  // be variables introduced by eval.
-  CopyContextExtensionToScopeObject(context, closure_scope,
-                                    KeyCollectionMode::kOwnOnly);
-
-  return closure_scope;
+  for (int i = 0; i < scope_info->ContextLocalCount(); ++i) {
+    Handle<String> name(scope_info->ContextLocalName(i), isolate_);
+    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
+    int context_index = scope_info->ContextHeaderLength() + i;
+    Handle<Object> value(context->get(context_index), isolate_);
+    if (visitor(name, value, scope_type)) return true;
+  }
+  return false;
 }
 
+bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode,
+                                ScopeType scope_type) const {
+  if (mode == Mode::STACK && current_scope_->is_declaration_scope() &&
+      current_scope_->AsDeclarationScope()->has_this_declaration()) {
+    // TODO(bmeurer): We should refactor the general variable lookup
+    // around "this", since the current way is rather hacky when the
+    // receiver is context-allocated.
+    auto this_var = current_scope_->AsDeclarationScope()->receiver();
+    Handle<Object> receiver =
+        this_var->location() == VariableLocation::CONTEXT
+            ? handle(context_->get(this_var->index()), isolate_)
+            : frame_inspector_ == nullptr
+                  ? handle(generator_->receiver(), isolate_)
+                  : frame_inspector_->GetReceiver();
+    if (receiver->IsOptimizedOut(isolate_)) {
+      receiver = isolate_->factory()->undefined_value();
+    }
+    if (visitor(isolate_->factory()->this_string(), receiver, scope_type))
+      return true;
+  }
 
-// Create a plain JSObject which materializes the scope for the specified
-// catch context.
-Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
-  Handle<Context> context = CurrentContext();
-  DCHECK(context->IsCatchContext());
-  Handle<String> name(context->catch_name());
-  Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
-                               isolate_);
-  Handle<JSObject> catch_scope =
-      isolate_->factory()->NewJSObjectWithNullProto();
-  JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
-                                           NONE)
-      .Check();
-  return catch_scope;
+  if (current_scope_->is_function_scope()) {
+    Variable* function_var =
+        current_scope_->AsDeclarationScope()->function_var();
+    if (function_var != nullptr) {
+      Handle<JSFunction> function = frame_inspector_ == nullptr
+                                        ? function_
+                                        : frame_inspector_->GetFunction();
+      Handle<String> name = function_var->name();
+      if (visitor(name, function, scope_type)) return true;
+    }
+  }
+
+  for (Variable* var : *current_scope_->locals()) {
+    DCHECK(!var->is_this());
+    if (ScopeInfo::VariableIsSynthetic(*var->name())) continue;
+
+    int index = var->index();
+    Handle<Object> value;
+    switch (var->location()) {
+      case VariableLocation::LOOKUP:
+        UNREACHABLE();
+        break;
+
+      case VariableLocation::REPL_GLOBAL:
+        // REPL declared variables are ignored for now.
+      case VariableLocation::UNALLOCATED:
+        continue;
+
+      case VariableLocation::PARAMETER: {
+        if (frame_inspector_ == nullptr) {
+          // Get the variable from the suspended generator.
+          DCHECK(!generator_.is_null());
+          FixedArray parameters_and_registers =
+              generator_->parameters_and_registers();
+          DCHECK_LT(index, parameters_and_registers.length());
+          value = handle(parameters_and_registers.get(index), isolate_);
+        } else {
+          value = frame_inspector_->GetParameter(index);
+
+          if (value->IsOptimizedOut(isolate_)) {
+            value = isolate_->factory()->undefined_value();
+          }
+        }
+        break;
+      }
+
+      case VariableLocation::LOCAL:
+        if (frame_inspector_ == nullptr) {
+          // Get the variable from the suspended generator.
+          DCHECK(!generator_.is_null());
+          FixedArray parameters_and_registers =
+              generator_->parameters_and_registers();
+          int parameter_count =
+              function_->shared().scope_info().ParameterCount();
+          index += parameter_count;
+          DCHECK_LT(index, parameters_and_registers.length());
+          value = handle(parameters_and_registers.get(index), isolate_);
+        } else {
+          value = frame_inspector_->GetExpression(index);
+          if (value->IsOptimizedOut(isolate_)) {
+            // We'll rematerialize this later.
+            if (current_scope_->is_declaration_scope() &&
+                current_scope_->AsDeclarationScope()->arguments() == var) {
+              continue;
+            }
+            value = isolate_->factory()->undefined_value();
+          }
+        }
+        break;
+
+      case VariableLocation::CONTEXT:
+        if (mode == Mode::STACK) continue;
+        DCHECK(var->IsContextSlot());
+        value = handle(context_->get(index), isolate_);
+        break;
+
+      case VariableLocation::MODULE: {
+        if (mode == Mode::STACK) continue;
+        // if (var->IsExport()) continue;
+        Handle<SourceTextModule> module(context_->module(), isolate_);
+        value = SourceTextModule::LoadVariable(isolate_, module, var->index());
+        break;
+      }
+    }
+
+    if (visitor(var->name(), value, scope_type)) return true;
+  }
+  return false;
 }
 
 // Retrieve the with-context extension object. If the extension object is
 // a proxy, return an empty object.
 Handle<JSObject> ScopeIterator::WithContextExtension() {
-  Handle<Context> context = CurrentContext();
-  DCHECK(context->IsWithContext());
-  if (context->extension_receiver()->IsJSProxy()) {
+  DCHECK(context_->IsWithContext());
+  if (context_->extension_receiver().IsJSProxy()) {
     return isolate_->factory()->NewJSObjectWithNullProto();
   }
-  return handle(JSObject::cast(context->extension_receiver()));
+  return handle(JSObject::cast(context_->extension_receiver()), isolate_);
 }
 
 // Create a plain JSObject which materializes the block scope for the specified
 // block context.
-Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
-  Handle<JSObject> inner_scope =
-      isolate_->factory()->NewJSObjectWithNullProto();
-
-  Handle<Context> context = Handle<Context>::null();
-  if (!nested_scope_chain_.is_empty()) {
-    Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
-    frame_inspector_->MaterializeStackLocals(inner_scope, scope_info);
-    if (scope_info->HasContext()) context = CurrentContext();
+void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode,
+                                    ScopeType scope_type) const {
+  if (InInnerScope()) {
+    if (VisitLocals(visitor, mode, scope_type)) return;
+    if (mode == Mode::STACK && Type() == ScopeTypeLocal) {
+      // Hide |this| in arrow functions that may be embedded in other functions
+      // but don't force |this| to be context-allocated. Otherwise we'd find the
+      // wrong |this| value.
+      if (!closure_scope_->has_this_declaration() &&
+          !closure_scope_->HasThisReference()) {
+        if (visitor(isolate_->factory()->this_string(),
+                    isolate_->factory()->undefined_value(), scope_type))
+          return;
+      }
+      // Add |arguments| to the function scope even if it wasn't used.
+      // Currently we don't yet support materializing the arguments object of
+      // suspended generators. We'd need to read the arguments out from the
+      // suspended generator rather than from an activation as
+      // FunctionGetArguments does.
+      if (frame_inspector_ != nullptr && !closure_scope_->is_arrow_scope() &&
+          (closure_scope_->arguments() == nullptr ||
+           frame_inspector_->GetExpression(closure_scope_->arguments()->index())
+               ->IsOptimizedOut(isolate_))) {
+        JavaScriptFrame* frame = GetFrame();
+        Handle<JSObject> arguments = Accessors::FunctionGetArguments(
+            frame, frame_inspector_->inlined_frame_index());
+        if (visitor(isolate_->factory()->arguments_string(), arguments,
+                    scope_type))
+          return;
+      }
+    }
   } else {
-    context = CurrentContext();
+    DCHECK_EQ(Mode::ALL, mode);
+    Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
+    if (VisitContextLocals(visitor, scope_info, context_, scope_type)) return;
   }
 
-  if (!context.is_null()) {
-    // Fill all context locals.
-    CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope);
-    CopyContextExtensionToScopeObject(context, inner_scope,
-                                      KeyCollectionMode::kOwnOnly);
-  }
-  return inner_scope;
-}
+  if (mode == Mode::ALL && HasContext()) {
+    DCHECK(!context_->IsScriptContext());
+    DCHECK(!context_->IsNativeContext());
+    DCHECK(!context_->IsWithContext());
+    if (!context_->scope_info().SloppyEvalCanExtendVars()) return;
+    if (context_->extension_object().is_null()) return;
+    Handle<JSObject> extension(context_->extension_object(), isolate_);
+    Handle<FixedArray> keys =
+        KeyAccumulator::GetKeys(extension, KeyCollectionMode::kOwnOnly,
+                                ENUMERABLE_STRINGS)
+            .ToHandleChecked();
 
-
-// Create a plain JSObject which materializes the module scope for the specified
-// module context.
-MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
-  Handle<Context> context = CurrentContext();
-  DCHECK(context->IsModuleContext());
-  Handle<ScopeInfo> scope_info(context->scope_info());
-  Handle<JSObject> module_scope =
-      isolate_->factory()->NewJSObjectWithNullProto();
-  CopyContextLocalsToScopeObject(scope_info, context, module_scope);
-  CopyModuleVarsToScopeObject(scope_info, context, module_scope);
-  return module_scope;
-}
-
-bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info,
-                                      JavaScriptFrame* frame,
-                                      Handle<String> parameter_name,
-                                      Handle<Object> new_value) {
-  // Setting stack locals of optimized frames is not supported.
-  if (frame->is_optimized()) return false;
-  HandleScope scope(isolate_);
-  for (int i = 0; i < scope_info->ParameterCount(); ++i) {
-    if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) {
-      frame->SetParameterValue(i, *new_value);
-      return true;
+    for (int i = 0; i < keys->length(); i++) {
+      // Names of variables introduced by eval are strings.
+      DCHECK(keys->get(i).IsString());
+      Handle<String> key(String::cast(keys->get(i)), isolate_);
+      Handle<Object> value = JSReceiver::GetDataProperty(extension, key);
+      if (visitor(key, value, scope_type)) return;
     }
   }
-  return false;
-}
-
-bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info,
-                                          Handle<String> variable_name,
-                                          Handle<Object> new_value) {
-  if (frame_inspector_ == nullptr) return false;
-  JavaScriptFrame* frame = GetFrame();
-  // Setting stack locals of optimized frames is not supported.
-  if (frame->is_optimized()) return false;
-  HandleScope scope(isolate_);
-  for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
-    if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
-      frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
-      return true;
-    }
-  }
-  return false;
-}
-
-bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info,
-                                            Handle<Context> context,
-                                            Handle<String> variable_name,
-                                            Handle<Object> new_value) {
-  HandleScope scope(isolate_);
-  for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
-    Handle<String> next_name(scope_info->ContextLocalName(i));
-    if (String::Equals(variable_name, next_name)) {
-      VariableMode mode;
-      InitializationFlag init_flag;
-      MaybeAssignedFlag maybe_assigned_flag;
-      int context_index = ScopeInfo::ContextSlotIndex(
-          scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag);
-      context->set(context_index, *new_value);
-      return true;
-    }
-  }
-
-  if (context->has_extension()) {
-    Handle<JSObject> ext(context->extension_object());
-    Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
-    DCHECK(maybe.IsJust());
-    if (maybe.FromJust()) {
-      // We don't expect this to do anything except replacing property value.
-      JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
-                                               NONE)
-          .Check();
-      return true;
-    }
-  }
-
-  return false;
 }
 
 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
-  JavaScriptFrame* frame = GetFrame();
-  Handle<ScopeInfo> scope_info(frame->function()->shared()->scope_info());
+  // TODO(verwaest): Walk parameters backwards, not forwards.
+  // TODO(verwaest): Use VariableMap rather than locals() list for lookup.
+  for (Variable* var : *current_scope_->locals()) {
+    if (String::Equals(isolate_, var->name(), variable_name)) {
+      int index = var->index();
+      switch (var->location()) {
+        case VariableLocation::LOOKUP:
+        case VariableLocation::UNALLOCATED:
+          // Drop assignments to unallocated locals.
+          DCHECK(var->is_this() ||
+                 *variable_name == ReadOnlyRoots(isolate_).arguments_string());
+          return false;
 
-  // Parameter might be shadowed in context. Don't stop here.
-  bool result = SetParameterValue(scope_info, frame, variable_name, new_value);
+        case VariableLocation::REPL_GLOBAL:
+          // Assignments to REPL declared variables are ignored for now.
+          return false;
 
-  // Stack locals.
-  if (SetStackVariableValue(scope_info, variable_name, new_value)) {
-    return true;
-  }
+        case VariableLocation::PARAMETER: {
+          if (var->is_this()) return false;
+          if (frame_inspector_ == nullptr) {
+            // Set the variable in the suspended generator.
+            DCHECK(!generator_.is_null());
+            Handle<FixedArray> parameters_and_registers(
+                generator_->parameters_and_registers(), isolate_);
+            DCHECK_LT(index, parameters_and_registers->length());
+            parameters_and_registers->set(index, *new_value);
+          } else {
+            JavaScriptFrame* frame = GetFrame();
+            if (frame->is_optimized()) return false;
 
-  if (scope_info->HasContext() &&
-      SetContextVariableValue(scope_info, CurrentContext(), variable_name,
-                              new_value)) {
-    return true;
-  }
+            frame->SetParameterValue(index, *new_value);
+          }
+          return true;
+        }
 
-  return result;
-}
+        case VariableLocation::LOCAL:
+          if (frame_inspector_ == nullptr) {
+            // Set the variable in the suspended generator.
+            DCHECK(!generator_.is_null());
+            int parameter_count =
+                function_->shared().scope_info().ParameterCount();
+            index += parameter_count;
+            Handle<FixedArray> parameters_and_registers(
+                generator_->parameters_and_registers(), isolate_);
+            DCHECK_LT(index, parameters_and_registers->length());
+            parameters_and_registers->set(index, *new_value);
+          } else {
+            // Set the variable on the stack.
+            JavaScriptFrame* frame = GetFrame();
+            if (frame->is_optimized()) return false;
 
-bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name,
-                                               Handle<Object> new_value) {
-  Handle<ScopeInfo> scope_info = CurrentScopeInfo();
-  DCHECK(scope_info->scope_type() == BLOCK_SCOPE ||
-         scope_info->scope_type() == EVAL_SCOPE);
+            frame->SetExpression(index, *new_value);
+          }
+          return true;
 
-  // Setting stack locals of optimized frames is not supported.
-  if (SetStackVariableValue(scope_info, variable_name, new_value)) {
-    return true;
-  }
+        case VariableLocation::CONTEXT:
+          DCHECK(var->IsContextSlot());
+          context_->set(index, *new_value);
+          return true;
 
-  if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(),
-                                              variable_name, new_value)) {
-    return true;
+        case VariableLocation::MODULE:
+          if (!var->IsExport()) return false;
+          Handle<SourceTextModule> module(context_->module(), isolate_);
+          SourceTextModule::StoreVariable(module, var->index(), new_value);
+          return true;
+      }
+      UNREACHABLE();
+    }
   }
 
   return false;
 }
 
-// This method copies structure of MaterializeClosure method above.
-bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
+bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name,
+                                             Handle<Object> new_value) {
+  if (!context_->has_extension()) return false;
+
+  DCHECK(context_->extension_object().IsJSContextExtensionObject());
+  Handle<JSObject> ext(context_->extension_object(), isolate_);
+  LookupIterator it(isolate_, ext, variable_name, LookupIterator::OWN);
+  Maybe<bool> maybe = JSReceiver::HasProperty(&it);
+  DCHECK(maybe.IsJust());
+  if (!maybe.FromJust()) return false;
+
+  CHECK(Object::SetDataProperty(&it, new_value).ToChecked());
+  return true;
+}
+
+bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name,
                                             Handle<Object> new_value) {
-  DCHECK(CurrentContext()->IsFunctionContext() ||
-         CurrentContext()->IsEvalContext());
-  return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(),
-                                 variable_name, new_value);
+  DisallowHeapAllocation no_gc;
+  VariableMode mode;
+  InitializationFlag flag;
+  MaybeAssignedFlag maybe_assigned_flag;
+  IsStaticFlag is_static_flag;
+  int slot_index =
+      ScopeInfo::ContextSlotIndex(context_->scope_info(), *variable_name, &mode,
+                                  &flag, &maybe_assigned_flag, &is_static_flag);
+  if (slot_index < 0) return false;
+
+  context_->set(slot_index, *new_value);
+  return true;
+}
+
+bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
+                                           Handle<Object> new_value) {
+  DisallowHeapAllocation no_gc;
+  int cell_index;
+  VariableMode mode;
+  InitializationFlag init_flag;
+  MaybeAssignedFlag maybe_assigned_flag;
+  cell_index = context_->scope_info().ModuleIndex(
+      *variable_name, &mode, &init_flag, &maybe_assigned_flag);
+
+  // Setting imports is currently not supported.
+  if (SourceTextModuleDescriptor::GetCellIndexKind(cell_index) !=
+      SourceTextModuleDescriptor::kExport) {
+    return false;
+  }
+
+  Handle<SourceTextModule> module(context_->module(), isolate_);
+  SourceTextModule::StoreVariable(module, cell_index, new_value);
+  return true;
 }
 
 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
                                            Handle<Object> new_value) {
-  Handle<String> internalized_variable_name =
-      isolate_->factory()->InternalizeString(variable_name);
-  Handle<Context> context = CurrentContext();
   Handle<ScriptContextTable> script_contexts(
-      context->global_object()->native_context()->script_context_table());
+      context_->global_object().native_context().script_context_table(),
+      isolate_);
   ScriptContextTable::LookupResult lookup_result;
-  if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name,
+  if (ScriptContextTable::Lookup(isolate_, *script_contexts, *variable_name,
                                  &lookup_result)) {
     Handle<Context> script_context = ScriptContextTable::GetContext(
-        script_contexts, lookup_result.context_index);
+        isolate_, script_contexts, lookup_result.context_index);
     script_context->set(lookup_result.slot_index, *new_value);
     return true;
   }
@@ -750,120 +1101,5 @@
   return false;
 }
 
-bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
-                                          Handle<Object> new_value) {
-  Handle<Context> context = CurrentContext();
-  DCHECK(context->IsCatchContext());
-  Handle<String> name(context->catch_name());
-  if (!String::Equals(name, variable_name)) {
-    return false;
-  }
-  context->set(Context::THROWN_OBJECT_INDEX, *new_value);
-  return true;
-}
-
-
-void ScopeIterator::CopyContextLocalsToScopeObject(
-    Handle<ScopeInfo> scope_info, Handle<Context> context,
-    Handle<JSObject> scope_object) {
-  Isolate* isolate = scope_info->GetIsolate();
-  int local_count = scope_info->ContextLocalCount();
-  if (local_count == 0) return;
-  // Fill all context locals to the context extension.
-  for (int i = 0; i < local_count; ++i) {
-    Handle<String> name(scope_info->ContextLocalName(i));
-    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
-    int context_index = Context::MIN_CONTEXT_SLOTS + i;
-    Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
-    // Reflect variables under TDZ as undefined in scope object.
-    if (value->IsTheHole(isolate)) continue;
-    // This should always succeed.
-    // TODO(verwaest): Use AddDataProperty instead.
-    JSObject::SetOwnPropertyIgnoreAttributes(scope_object, name, value, NONE)
-        .Check();
-  }
-}
-
-void ScopeIterator::CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,
-                                                Handle<Context> context,
-                                                Handle<JSObject> scope_object) {
-  Isolate* isolate = scope_info->GetIsolate();
-
-  int module_variable_count =
-      Smi::cast(scope_info->get(scope_info->ModuleVariableCountIndex()))
-          ->value();
-  for (int i = 0; i < module_variable_count; ++i) {
-    Handle<String> local_name;
-    Handle<Object> value;
-    {
-      String* name;
-      int index;
-      scope_info->ModuleVariable(i, &name, &index);
-      CHECK(!ScopeInfo::VariableIsSynthetic(name));
-      local_name = handle(name, isolate);
-      value = Module::LoadVariable(handle(context->module(), isolate), index);
-    }
-
-    // Reflect variables under TDZ as undefined in scope object.
-    if (value->IsTheHole(isolate)) continue;
-    // This should always succeed.
-    // TODO(verwaest): Use AddDataProperty instead.
-    JSObject::SetOwnPropertyIgnoreAttributes(scope_object, local_name, value,
-                                             NONE)
-        .Check();
-  }
-}
-
-void ScopeIterator::CopyContextExtensionToScopeObject(
-    Handle<Context> context, Handle<JSObject> scope_object,
-    KeyCollectionMode mode) {
-  if (context->extension_object() == nullptr) return;
-  Handle<JSObject> extension(context->extension_object());
-  Handle<FixedArray> keys =
-      KeyAccumulator::GetKeys(extension, mode, ENUMERABLE_STRINGS)
-          .ToHandleChecked();
-
-  for (int i = 0; i < keys->length(); i++) {
-    // Names of variables introduced by eval are strings.
-    DCHECK(keys->get(i)->IsString());
-    Handle<String> key(String::cast(keys->get(i)));
-    Handle<Object> value =
-        Object::GetPropertyOrElement(extension, key).ToHandleChecked();
-    JSObject::SetOwnPropertyIgnoreAttributes(scope_object, key, value, NONE)
-        .Check();
-  }
-}
-
-void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
-                                        int position) {
-  if (scope->is_function_scope()) {
-    // Do not collect scopes of nested inner functions inside the current one.
-    // Nested arrow functions could have the same end positions.
-    Handle<JSFunction> function = frame_inspector_->GetFunction();
-    if (scope->start_position() > function->shared()->start_position() &&
-        scope->end_position() <= function->shared()->end_position()) {
-      return;
-    }
-  }
-  if (scope->is_hidden()) {
-    // We need to add this chain element in case the scope has a context
-    // associated. We need to keep the scope chain and context chain in sync.
-    nested_scope_chain_.Add(ExtendedScopeInfo(scope->scope_info()));
-  } else {
-    nested_scope_chain_.Add(ExtendedScopeInfo(
-        scope->scope_info(), scope->start_position(), scope->end_position()));
-  }
-  for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
-       inner_scope = inner_scope->sibling()) {
-    int beg_pos = inner_scope->start_position();
-    int end_pos = inner_scope->end_position();
-    DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
-    if (beg_pos <= position && position < end_pos) {
-      GetNestedScopeChain(isolate, inner_scope, position);
-      return;
-    }
-  }
-}
-
 }  // namespace internal
 }  // namespace v8
diff --git a/src/debug/debug-scopes.h b/src/debug/debug-scopes.h
index d187f3e..590e9e9 100644
--- a/src/debug/debug-scopes.h
+++ b/src/debug/debug-scopes.h
@@ -5,12 +5,14 @@
 #ifndef V8_DEBUG_DEBUG_SCOPES_H_
 #define V8_DEBUG_DEBUG_SCOPES_H_
 
+#include <vector>
+
 #include "src/debug/debug-frames.h"
-#include "src/frames.h"
 
 namespace v8 {
 namespace internal {
 
+class JavaScriptFrame;
 class ParseInfo;
 
 // Iterate over the actual scopes visible from a stack frame or from a closure.
@@ -39,127 +41,132 @@
   static const int kScopeDetailsFunctionIndex = 5;
   static const int kScopeDetailsSize = 6;
 
-  enum Option { DEFAULT, IGNORE_NESTED_SCOPES, COLLECT_NON_LOCALS };
+  enum class ReparseStrategy {
+    kScript,
+    kFunctionLiteral,
+  };
 
   ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
-                Option options = DEFAULT);
+                ReparseStrategy strategy);
 
   ScopeIterator(Isolate* isolate, Handle<JSFunction> function);
   ScopeIterator(Isolate* isolate, Handle<JSGeneratorObject> generator);
+  ~ScopeIterator();
 
-  MUST_USE_RESULT MaybeHandle<JSObject> MaterializeScopeDetails();
+  Handle<JSObject> MaterializeScopeDetails();
 
   // More scopes?
-  bool Done() { return context_.is_null(); }
+  bool Done() const { return context_.is_null(); }
 
   // Move to the next scope.
   void Next();
 
+  // Restart to the first scope and context.
+  void Restart();
+
   // Return the type of the current scope.
-  ScopeType Type();
+  ScopeType Type() const;
+
+  // Indicates which variables should be visited. Either only variables from the
+  // scope that are available on the stack, or all variables.
+  enum class Mode { STACK, ALL };
 
   // Return the JavaScript object with the content of the current scope.
-  MaybeHandle<JSObject> ScopeObject();
+  Handle<JSObject> ScopeObject(Mode mode);
 
-  bool HasContext();
+  // Returns whether the current scope declares any variables.
+  bool DeclaresLocals(Mode mode) const;
 
   // Set variable value and return true on success.
   bool SetVariableValue(Handle<String> variable_name, Handle<Object> new_value);
 
-  Handle<ScopeInfo> CurrentScopeInfo();
-
-  // Return the context for this scope. For the local context there might not
-  // be an actual context.
-  Handle<Context> CurrentContext();
+  bool ClosureScopeHasThisReference() const;
 
   // Populate the set with collected non-local variable names.
-  Handle<StringSet> GetNonLocals();
+  Handle<StringSet> GetLocals() { return locals_; }
+
+  // Similar to JSFunction::GetName return the function's name or it's inferred
+  // name.
+  Handle<Object> GetFunctionDebugName() const;
+
+  Handle<Script> GetScript() const { return script_; }
+
+  bool HasPositionInfo();
+  int start_position();
+  int end_position();
 
 #ifdef DEBUG
   // Debug print of the content of the current scope.
   void DebugPrint();
 #endif
 
+  bool InInnerScope() const { return !function_.is_null(); }
+  bool HasContext() const;
+  bool NeedsAndHasContext() const;
+  Handle<Context> CurrentContext() const {
+    DCHECK(HasContext());
+    return context_;
+  }
+
  private:
-  struct ExtendedScopeInfo {
-    ExtendedScopeInfo(Handle<ScopeInfo> info, int start, int end)
-        : scope_info(info), start_position(start), end_position(end) {}
-    explicit ExtendedScopeInfo(Handle<ScopeInfo> info)
-        : scope_info(info), start_position(-1), end_position(-1) {}
-    Handle<ScopeInfo> scope_info;
-    int start_position;
-    int end_position;
-    bool is_hidden() { return start_position == -1 && end_position == -1; }
-  };
-
   Isolate* isolate_;
-  FrameInspector* const frame_inspector_;
+  std::unique_ptr<ParseInfo> info_;
+  FrameInspector* const frame_inspector_ = nullptr;
+  Handle<JSGeneratorObject> generator_;
+
+  // The currently-executing function from the inspected frame, or null if this
+  // ScopeIterator has already iterated to any Scope outside that function.
+  Handle<JSFunction> function_;
+
   Handle<Context> context_;
-  List<ExtendedScopeInfo> nested_scope_chain_;
-  Handle<StringSet> non_locals_;
-  bool seen_script_scope_;
+  Handle<Script> script_;
+  Handle<StringSet> locals_;
+  DeclarationScope* closure_scope_ = nullptr;
+  Scope* start_scope_ = nullptr;
+  Scope* current_scope_ = nullptr;
+  bool seen_script_scope_ = false;
 
-  inline JavaScriptFrame* GetFrame() {
-    return frame_inspector_->GetArgumentsFrame();
+  inline JavaScriptFrame* GetFrame() const {
+    return frame_inspector_->javascript_frame();
   }
 
-  inline Handle<JSFunction> GetFunction() {
-    return frame_inspector_->GetFunction();
-  }
+  void AdvanceOneScope();
+  void AdvanceToNonHiddenScope();
+  void AdvanceContext();
+  void CollectLocalsFromCurrentScope();
 
-  void RetrieveScopeChain(DeclarationScope* scope);
+  int GetSourcePosition();
 
-  void CollectNonLocals(ParseInfo* info, DeclarationScope* scope);
+  void TryParseAndRetrieveScopes(ReparseStrategy strategy);
 
   void UnwrapEvaluationContext();
 
-  MUST_USE_RESULT MaybeHandle<JSObject> MaterializeScriptScope();
-  MUST_USE_RESULT MaybeHandle<JSObject> MaterializeLocalScope();
-  MUST_USE_RESULT MaybeHandle<JSObject> MaterializeModuleScope();
-  Handle<JSObject> MaterializeClosure();
-  Handle<JSObject> MaterializeCatchScope();
-  Handle<JSObject> MaterializeInnerScope();
+  using Visitor = std::function<bool(Handle<String> name, Handle<Object> value,
+                                     ScopeType scope_type)>;
+
   Handle<JSObject> WithContextExtension();
 
   bool SetLocalVariableValue(Handle<String> variable_name,
                              Handle<Object> new_value);
-  bool SetInnerScopeVariableValue(Handle<String> variable_name,
-                                  Handle<Object> new_value);
-  bool SetClosureVariableValue(Handle<String> variable_name,
+  bool SetContextVariableValue(Handle<String> variable_name,
                                Handle<Object> new_value);
+  bool SetContextExtensionValue(Handle<String> variable_name,
+                                Handle<Object> new_value);
   bool SetScriptVariableValue(Handle<String> variable_name,
                               Handle<Object> new_value);
-  bool SetCatchVariableValue(Handle<String> variable_name,
-                             Handle<Object> new_value);
+  bool SetModuleVariableValue(Handle<String> variable_name,
+                              Handle<Object> new_value);
 
   // Helper functions.
-  bool SetParameterValue(Handle<ScopeInfo> scope_info, JavaScriptFrame* frame,
-                         Handle<String> parameter_name,
-                         Handle<Object> new_value);
-  bool SetStackVariableValue(Handle<ScopeInfo> scope_info,
-                             Handle<String> variable_name,
-                             Handle<Object> new_value);
-  bool SetContextVariableValue(Handle<ScopeInfo> scope_info,
-                               Handle<Context> context,
-                               Handle<String> variable_name,
-                               Handle<Object> new_value);
-
-  void CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
-                                      Handle<Context> context,
-                                      Handle<JSObject> scope_object);
-  void CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,
-                                   Handle<Context> context,
-                                   Handle<JSObject> scope_object);
-  void CopyContextExtensionToScopeObject(Handle<Context> context,
-                                         Handle<JSObject> scope_object,
-                                         KeyCollectionMode mode);
-
-  // Get the chain of nested scopes within this scope for the source statement
-  // position. The scopes will be added to the list from the outermost scope to
-  // the innermost scope. Only nested block, catch or with scopes are tracked
-  // and will be returned, but no inner function scopes.
-  void GetNestedScopeChain(Isolate* isolate, Scope* scope,
-                           int statement_position);
+  void VisitScope(const Visitor& visitor, Mode mode) const;
+  void VisitLocalScope(const Visitor& visitor, Mode mode,
+                       ScopeType scope_type) const;
+  void VisitScriptScope(const Visitor& visitor) const;
+  void VisitModuleScope(const Visitor& visitor) const;
+  bool VisitLocals(const Visitor& visitor, Mode mode,
+                   ScopeType scope_type) const;
+  bool VisitContextLocals(const Visitor& visitor, Handle<ScopeInfo> scope_info,
+                          Handle<Context> context, ScopeType scope_type) const;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
 };
diff --git a/src/debug/debug-stack-trace-iterator.cc b/src/debug/debug-stack-trace-iterator.cc
new file mode 100644
index 0000000..ea0f4d3
--- /dev/null
+++ b/src/debug/debug-stack-trace-iterator.cc
@@ -0,0 +1,233 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/debug-stack-trace-iterator.h"
+
+#include "src/api/api-inl.h"
+#include "src/debug/debug-evaluate.h"
+#include "src/debug/debug-interface.h"
+#include "src/debug/debug-scope-iterator.h"
+#include "src/debug/debug.h"
+#include "src/debug/liveedit.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/frames.h"
+#include "src/execution/isolate.h"
+#include "src/wasm/wasm-debug-evaluate.h"
+#include "src/wasm/wasm-debug.h"
+
+namespace v8 {
+
+bool debug::StackTraceIterator::SupportsWasmDebugEvaluate() {
+  return i::FLAG_wasm_expose_debug_eval;
+}
+
+std::unique_ptr<debug::StackTraceIterator> debug::StackTraceIterator::Create(
+    v8::Isolate* isolate, int index) {
+  return std::unique_ptr<debug::StackTraceIterator>(
+      new internal::DebugStackTraceIterator(
+          reinterpret_cast<internal::Isolate*>(isolate), index));
+}
+
+namespace internal {
+
+DebugStackTraceIterator::DebugStackTraceIterator(Isolate* isolate, int index)
+    : isolate_(isolate),
+      iterator_(isolate, isolate->debug()->break_frame_id()),
+      is_top_frame_(true) {
+  if (iterator_.done()) return;
+  std::vector<FrameSummary> frames;
+  iterator_.frame()->Summarize(&frames);
+  inlined_frame_index_ = static_cast<int>(frames.size());
+  Advance();
+  for (; !Done() && index > 0; --index) Advance();
+}
+
+DebugStackTraceIterator::~DebugStackTraceIterator() = default;
+
+bool DebugStackTraceIterator::Done() const { return iterator_.done(); }
+
+void DebugStackTraceIterator::Advance() {
+  while (true) {
+    --inlined_frame_index_;
+    for (; inlined_frame_index_ >= 0; --inlined_frame_index_) {
+      // Omit functions from native and extension scripts.
+      if (FrameSummary::Get(iterator_.frame(), inlined_frame_index_)
+              .is_subject_to_debugging()) {
+        break;
+      }
+      is_top_frame_ = false;
+    }
+    if (inlined_frame_index_ >= 0) {
+      frame_inspector_.reset(new FrameInspector(
+          iterator_.frame(), inlined_frame_index_, isolate_));
+      break;
+    }
+    is_top_frame_ = false;
+    frame_inspector_.reset();
+    iterator_.Advance();
+    if (iterator_.done()) break;
+    std::vector<FrameSummary> frames;
+    iterator_.frame()->Summarize(&frames);
+    inlined_frame_index_ = static_cast<int>(frames.size());
+  }
+}
+
+int DebugStackTraceIterator::GetContextId() const {
+  DCHECK(!Done());
+  Handle<Object> context = frame_inspector_->GetContext();
+  if (context->IsContext()) {
+    Object value = Context::cast(*context).native_context().debug_context_id();
+    if (value.IsSmi()) return Smi::ToInt(value);
+  }
+  return 0;
+}
+
+v8::MaybeLocal<v8::Value> DebugStackTraceIterator::GetReceiver() const {
+  DCHECK(!Done());
+  if (frame_inspector_->IsJavaScript() &&
+      frame_inspector_->GetFunction()->shared().kind() == kArrowFunction) {
+    // FrameInspector is not able to get receiver for arrow function.
+    // So let's try to fetch it using same logic as is used to retrieve 'this'
+    // during DebugEvaluate::Local.
+    Handle<JSFunction> function = frame_inspector_->GetFunction();
+    Handle<Context> context(function->context(), isolate_);
+    // Arrow function defined in top level function without references to
+    // variables may have NativeContext as context.
+    if (!context->IsFunctionContext()) return v8::MaybeLocal<v8::Value>();
+    ScopeIterator scope_iterator(
+        isolate_, frame_inspector_.get(),
+        ScopeIterator::ReparseStrategy::kFunctionLiteral);
+    // We lookup this variable in function context only when it is used in arrow
+    // function otherwise V8 can optimize it out.
+    if (!scope_iterator.ClosureScopeHasThisReference()) {
+      return v8::MaybeLocal<v8::Value>();
+    }
+    DisallowHeapAllocation no_gc;
+    VariableMode mode;
+    InitializationFlag flag;
+    MaybeAssignedFlag maybe_assigned_flag;
+    IsStaticFlag is_static_flag;
+    int slot_index = ScopeInfo::ContextSlotIndex(
+        context->scope_info(), ReadOnlyRoots(isolate_->heap()).this_string(),
+        &mode, &flag, &maybe_assigned_flag, &is_static_flag);
+    if (slot_index < 0) return v8::MaybeLocal<v8::Value>();
+    Handle<Object> value = handle(context->get(slot_index), isolate_);
+    if (value->IsTheHole(isolate_)) return v8::MaybeLocal<v8::Value>();
+    return Utils::ToLocal(value);
+  }
+
+  Handle<Object> value = frame_inspector_->GetReceiver();
+  if (value.is_null() || (value->IsSmi() || !value->IsTheHole(isolate_))) {
+    return Utils::ToLocal(value);
+  }
+  return v8::MaybeLocal<v8::Value>();
+}
+
+v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const {
+  CHECK(!Done());
+  if (frame_inspector_ && frame_inspector_->IsWasm()) {
+    return v8::Local<v8::Value>();
+  }
+  CHECK_NOT_NULL(iterator_.frame());
+  bool is_optimized = iterator_.frame()->is_optimized();
+  if (is_optimized || !is_top_frame_ ||
+      !isolate_->debug()->IsBreakAtReturn(iterator_.javascript_frame())) {
+    return v8::Local<v8::Value>();
+  }
+  return Utils::ToLocal(isolate_->debug()->return_value_handle());
+}
+
+v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const {
+  DCHECK(!Done());
+  return Utils::ToLocal(frame_inspector_->GetFunctionName());
+}
+
+v8::Local<v8::debug::Script> DebugStackTraceIterator::GetScript() const {
+  DCHECK(!Done());
+  Handle<Object> value = frame_inspector_->GetScript();
+  if (!value->IsScript()) return v8::Local<v8::debug::Script>();
+  return ToApiHandle<debug::Script>(Handle<Script>::cast(value));
+}
+
+debug::Location DebugStackTraceIterator::GetSourceLocation() const {
+  DCHECK(!Done());
+  v8::Local<v8::debug::Script> script = GetScript();
+  if (script.IsEmpty()) return v8::debug::Location();
+  return script->GetSourceLocation(frame_inspector_->GetSourcePosition());
+}
+
+v8::Local<v8::Function> DebugStackTraceIterator::GetFunction() const {
+  DCHECK(!Done());
+  if (!frame_inspector_->IsJavaScript()) return v8::Local<v8::Function>();
+  return Utils::ToLocal(frame_inspector_->GetFunction());
+}
+
+std::unique_ptr<v8::debug::ScopeIterator>
+DebugStackTraceIterator::GetScopeIterator() const {
+  DCHECK(!Done());
+  CommonFrame* frame = iterator_.frame();
+  if (frame->is_wasm()) {
+    return std::make_unique<DebugWasmScopeIterator>(isolate_,
+                                                    WasmFrame::cast(frame));
+  }
+  return std::make_unique<DebugScopeIterator>(isolate_, frame_inspector_.get());
+}
+
+bool DebugStackTraceIterator::Restart() {
+  DCHECK(!Done());
+  if (iterator_.is_wasm()) return false;
+  return LiveEdit::RestartFrame(iterator_.javascript_frame());
+}
+
+v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate(
+    v8::Local<v8::String> source, bool throw_on_side_effect) {
+  DCHECK(!Done());
+  Handle<Object> value;
+
+  i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
+  bool success = false;
+  if (iterator_.is_wasm()) {
+    FrameSummary summary = FrameSummary::Get(iterator_.frame(), 0);
+    const FrameSummary::WasmFrameSummary& wasmSummary = summary.AsWasm();
+    Handle<WasmInstanceObject> instance = wasmSummary.wasm_instance();
+
+    success = DebugEvaluate::WebAssembly(instance, iterator_.frame()->id(),
+                                         Utils::OpenHandle(*source),
+                                         throw_on_side_effect)
+                  .ToHandle(&value);
+  } else {
+    success = DebugEvaluate::Local(
+                  isolate_, iterator_.frame()->id(), inlined_frame_index_,
+                  Utils::OpenHandle(*source), throw_on_side_effect)
+                  .ToHandle(&value);
+  }
+  if (!success) {
+    isolate_->OptionalRescheduleException(false);
+    return v8::MaybeLocal<v8::Value>();
+  }
+  return Utils::ToLocal(value);
+}
+
+v8::MaybeLocal<v8::String> DebugStackTraceIterator::EvaluateWasm(
+    internal::Vector<const internal::byte> source, int frame_index) {
+  DCHECK(!Done());
+  if (!i::FLAG_wasm_expose_debug_eval || !iterator_.is_wasm()) {
+    return v8::MaybeLocal<v8::String>();
+  }
+  Handle<String> value;
+  i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
+
+  FrameSummary summary = FrameSummary::Get(iterator_.frame(), 0);
+  const FrameSummary::WasmFrameSummary& wasmSummary = summary.AsWasm();
+  Handle<WasmInstanceObject> instance = wasmSummary.wasm_instance();
+
+  if (!v8::internal::wasm::DebugEvaluate(source, instance, iterator_.frame())
+           .ToHandle(&value)) {
+    isolate_->OptionalRescheduleException(false);
+    return v8::MaybeLocal<v8::String>();
+  }
+  return Utils::ToLocal(value);
+}
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/debug-stack-trace-iterator.h b/src/debug/debug-stack-trace-iterator.h
new file mode 100644
index 0000000..2317af3
--- /dev/null
+++ b/src/debug/debug-stack-trace-iterator.h
@@ -0,0 +1,50 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_DEBUG_STACK_TRACE_ITERATOR_H_
+#define V8_DEBUG_DEBUG_STACK_TRACE_ITERATOR_H_
+
+#include <memory>
+
+#include "src/debug/debug-frames.h"
+#include "src/debug/debug-interface.h"
+#include "src/execution/frames.h"
+
+namespace v8 {
+namespace internal {
+
+class DebugStackTraceIterator final : public debug::StackTraceIterator {
+ public:
+  DebugStackTraceIterator(Isolate* isolate, int index);
+  ~DebugStackTraceIterator() override;
+
+  bool Done() const override;
+  void Advance() override;
+
+  int GetContextId() const override;
+  v8::MaybeLocal<v8::Value> GetReceiver() const override;
+  v8::Local<v8::Value> GetReturnValue() const override;
+  v8::Local<v8::String> GetFunctionDebugName() const override;
+  v8::Local<v8::debug::Script> GetScript() const override;
+  debug::Location GetSourceLocation() const override;
+  v8::Local<v8::Function> GetFunction() const override;
+  std::unique_ptr<v8::debug::ScopeIterator> GetScopeIterator() const override;
+
+  bool Restart() override;
+  v8::MaybeLocal<v8::Value> Evaluate(v8::Local<v8::String> source,
+                                     bool throw_on_side_effect) override;
+  v8::MaybeLocal<v8::String> EvaluateWasm(
+      internal::Vector<const internal::byte> source, int frame_index) override;
+
+ private:
+  Isolate* isolate_;
+  StackTraceFrameIterator iterator_;
+  std::unique_ptr<FrameInspector> frame_inspector_;
+  int inlined_frame_index_;
+  bool is_top_frame_;
+};
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_DEBUG_STACK_TRACE_ITERATOR_H_
diff --git a/src/debug/debug-type-profile.cc b/src/debug/debug-type-profile.cc
new file mode 100644
index 0000000..c0ba96c
--- /dev/null
+++ b/src/debug/debug-type-profile.cc
@@ -0,0 +1,122 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/debug-type-profile.h"
+
+#include "src/execution/isolate.h"
+#include "src/objects/feedback-vector.h"
+#include "src/objects/objects-inl.h"
+#include "src/objects/objects.h"
+
+namespace v8 {
+namespace internal {
+
+std::unique_ptr<TypeProfile> TypeProfile::Collect(Isolate* isolate) {
+  std::unique_ptr<TypeProfile> result(new TypeProfile());
+
+  // Feedback vectors are already listed to prevent losing them to GC.
+  DCHECK(isolate->factory()
+             ->feedback_vectors_for_profiling_tools()
+             ->IsArrayList());
+  Handle<ArrayList> list = Handle<ArrayList>::cast(
+      isolate->factory()->feedback_vectors_for_profiling_tools());
+
+  Script::Iterator scripts(isolate);
+
+  for (Script script = scripts.Next(); !script.is_null();
+       script = scripts.Next()) {
+    if (!script.IsUserJavaScript()) {
+      continue;
+    }
+
+    Handle<Script> script_handle(script, isolate);
+
+    TypeProfileScript type_profile_script(script_handle);
+    std::vector<TypeProfileEntry>* entries = &type_profile_script.entries;
+
+    // TODO(franzih): Sort the vectors by script first instead of iterating
+    // the list multiple times.
+    for (int i = 0; i < list->Length(); i++) {
+      FeedbackVector vector = FeedbackVector::cast(list->Get(i));
+      SharedFunctionInfo info = vector.shared_function_info();
+      DCHECK(info.IsSubjectToDebugging());
+
+      // Match vectors with script.
+      if (script != info.script()) {
+        continue;
+      }
+      if (!info.HasFeedbackMetadata() || info.feedback_metadata().is_empty() ||
+          !info.feedback_metadata().HasTypeProfileSlot()) {
+        continue;
+      }
+      FeedbackSlot slot = vector.GetTypeProfileSlot();
+      FeedbackNexus nexus(vector, slot);
+      Handle<String> name(info.DebugName(), isolate);
+      std::vector<int> source_positions = nexus.GetSourcePositions();
+      for (int position : source_positions) {
+        DCHECK_GE(position, 0);
+        entries->emplace_back(position, nexus.GetTypesForSourcePositions(
+                                            static_cast<uint32_t>(position)));
+      }
+
+      // Releases type profile data collected so far.
+      nexus.ResetTypeProfile();
+    }
+    if (!entries->empty()) {
+      result->emplace_back(type_profile_script);
+    }
+  }
+  return result;
+}
+
+void TypeProfile::SelectMode(Isolate* isolate, debug::TypeProfileMode mode) {
+  if (mode != isolate->type_profile_mode()) {
+    // Changing the type profile mode can change the bytecode that would be
+    // generated for a function, which can interfere with lazy source positions,
+    // so just force source position collection whenever there's such a change.
+    isolate->CollectSourcePositionsForAllBytecodeArrays();
+  }
+
+  HandleScope handle_scope(isolate);
+
+  if (mode == debug::TypeProfileMode::kNone) {
+    if (!isolate->factory()
+             ->feedback_vectors_for_profiling_tools()
+             ->IsUndefined(isolate)) {
+      // Release type profile data collected so far.
+
+      // Feedback vectors are already listed to prevent losing them to GC.
+      DCHECK(isolate->factory()
+                 ->feedback_vectors_for_profiling_tools()
+                 ->IsArrayList());
+      Handle<ArrayList> list = Handle<ArrayList>::cast(
+          isolate->factory()->feedback_vectors_for_profiling_tools());
+
+      for (int i = 0; i < list->Length(); i++) {
+        FeedbackVector vector = FeedbackVector::cast(list->Get(i));
+        SharedFunctionInfo info = vector.shared_function_info();
+        DCHECK(info.IsSubjectToDebugging());
+        if (info.feedback_metadata().HasTypeProfileSlot()) {
+          FeedbackSlot slot = vector.GetTypeProfileSlot();
+          FeedbackNexus nexus(vector, slot);
+          nexus.ResetTypeProfile();
+        }
+      }
+
+      // Delete the feedback vectors from the list if they're not used by code
+      // coverage.
+      if (isolate->is_best_effort_code_coverage()) {
+        isolate->SetFeedbackVectorsForProfilingTools(
+            ReadOnlyRoots(isolate).undefined_value());
+      }
+    }
+  } else {
+    DCHECK_EQ(debug::TypeProfileMode::kCollect, mode);
+    isolate->MaybeInitializeVectorListFromHeap();
+  }
+  isolate->set_type_profile_mode(mode);
+}
+
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/debug-type-profile.h b/src/debug/debug-type-profile.h
new file mode 100644
index 0000000..f06af0c
--- /dev/null
+++ b/src/debug/debug-type-profile.h
@@ -0,0 +1,47 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_DEBUG_TYPE_PROFILE_H_
+#define V8_DEBUG_DEBUG_TYPE_PROFILE_H_
+
+#include <memory>
+#include <vector>
+
+#include "src/debug/debug-interface.h"
+#include "src/handles/handles.h"
+#include "src/objects/objects.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declaration.
+class Isolate;
+
+struct TypeProfileEntry {
+  explicit TypeProfileEntry(
+      int pos, std::vector<v8::internal::Handle<internal::String>> t)
+      : position(pos), types(std::move(t)) {}
+  int position;
+  std::vector<v8::internal::Handle<internal::String>> types;
+};
+
+struct TypeProfileScript {
+  explicit TypeProfileScript(Handle<Script> s) : script(s) {}
+  Handle<Script> script;
+  std::vector<TypeProfileEntry> entries;
+};
+
+class TypeProfile : public std::vector<TypeProfileScript> {
+ public:
+  static std::unique_ptr<TypeProfile> Collect(Isolate* isolate);
+  static void SelectMode(Isolate* isolate, debug::TypeProfileMode mode);
+
+ private:
+  TypeProfile() = default;
+};
+
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_DEBUG_TYPE_PROFILE_H_
diff --git a/src/debug/debug.cc b/src/debug/debug.cc
index dd1f5cf..a65c1b3 100644
--- a/src/debug/debug.cc
+++ b/src/debug/debug.cc
@@ -5,87 +5,146 @@
 #include "src/debug/debug.h"
 
 #include <memory>
+#include <unordered_set>
 
-#include "src/api.h"
-#include "src/arguments.h"
-#include "src/assembler-inl.h"
-#include "src/bootstrapper.h"
-#include "src/code-stubs.h"
-#include "src/codegen.h"
-#include "src/compilation-cache.h"
-#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
-#include "src/compiler.h"
+#include "src/api/api-inl.h"
+#include "src/api/api-natives.h"
+#include "src/base/platform/mutex.h"
+#include "src/builtins/builtins.h"
+#include "src/codegen/assembler-inl.h"
+#include "src/codegen/compilation-cache.h"
+#include "src/codegen/compiler.h"
+#include "src/common/globals.h"
+#include "src/common/message-template.h"
 #include "src/debug/debug-evaluate.h"
 #include "src/debug/liveedit.h"
-#include "src/deoptimizer.h"
-#include "src/execution.h"
-#include "src/frames-inl.h"
-#include "src/full-codegen/full-codegen.h"
-#include "src/global-handles.h"
-#include "src/globals.h"
+#include "src/deoptimizer/deoptimizer.h"
+#include "src/execution/arguments.h"
+#include "src/execution/execution.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/isolate-inl.h"
+#include "src/execution/v8threads.h"
+#include "src/handles/global-handles.h"
+#include "src/heap/heap-inl.h"  // For NextDebuggingId.
+#include "src/init/bootstrapper.h"
+#include "src/interpreter/bytecode-array-accessor.h"
+#include "src/interpreter/bytecode-array-iterator.h"
 #include "src/interpreter/interpreter.h"
-#include "src/isolate-inl.h"
-#include "src/list.h"
-#include "src/log.h"
-#include "src/messages.h"
-#include "src/snapshot/natives.h"
-#include "src/wasm/wasm-module.h"
-#include "src/wasm/wasm-objects.h"
-
-#include "include/v8-debug.h"
+#include "src/logging/counters.h"
+#include "src/objects/api-callbacks-inl.h"
+#include "src/objects/debug-objects-inl.h"
+#include "src/objects/js-generator-inl.h"
+#include "src/objects/js-promise-inl.h"
+#include "src/objects/slots.h"
+#include "src/snapshot/snapshot.h"
+#include "src/wasm/wasm-debug.h"
+#include "src/wasm/wasm-objects-inl.h"
 
 namespace v8 {
 namespace internal {
 
+class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker {
+ public:
+  TemporaryObjectsTracker() = default;
+  ~TemporaryObjectsTracker() override = default;
+
+  void AllocationEvent(Address addr, int) override { objects_.insert(addr); }
+
+  void MoveEvent(Address from, Address to, int) override {
+    if (from == to) return;
+    base::MutexGuard guard(&mutex_);
+    auto it = objects_.find(from);
+    if (it == objects_.end()) {
+      // If temporary object was collected we can get MoveEvent which moves
+      // existing non temporary object to the address where we had temporary
+      // object. So we should mark new address as non temporary.
+      objects_.erase(to);
+      return;
+    }
+    objects_.erase(it);
+    objects_.insert(to);
+  }
+
+  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();
+  }
+
+ private:
+  std::unordered_set<Address> objects_;
+  base::Mutex mutex_;
+  DISALLOW_COPY_AND_ASSIGN(TemporaryObjectsTracker);
+};
+
 Debug::Debug(Isolate* isolate)
-    : debug_context_(Handle<Context>()),
-      is_active_(false),
+    : is_active_(false),
       hook_on_function_call_(false),
       is_suppressed_(false),
-      live_edit_enabled_(true),  // TODO(yangguo): set to false by default.
       break_disabled_(false),
       break_points_active_(true),
       break_on_exception_(false),
       break_on_uncaught_exception_(false),
       side_effect_check_failed_(false),
-      debug_info_list_(NULL),
+      debug_info_list_(nullptr),
       feature_tracker_(isolate),
       isolate_(isolate) {
   ThreadInit();
 }
 
+Debug::~Debug() { DCHECK_NULL(debug_delegate_); }
+
 BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
                                        JavaScriptFrame* frame) {
+  if (debug_info->CanBreakAtEntry()) {
+    return BreakLocation(Debug::kBreakAtEntryPosition, DEBUG_BREAK_AT_ENTRY);
+  }
   auto summary = FrameSummary::GetTop(frame).AsJavaScript();
   int offset = summary.code_offset();
   Handle<AbstractCode> abstract_code = summary.abstract_code();
-  if (abstract_code->IsCode()) offset = offset - 1;
-  auto it = BreakIterator::GetIterator(debug_info, abstract_code);
-  it->SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
-  return it->GetBreakLocation();
+  BreakIterator it(debug_info);
+  it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
+  return it.GetBreakLocation();
 }
 
-void BreakLocation::AllAtCurrentStatement(Handle<DebugInfo> debug_info,
-                                          JavaScriptFrame* frame,
-                                          List<BreakLocation>* result_out) {
+void BreakLocation::AllAtCurrentStatement(
+    Handle<DebugInfo> debug_info, JavaScriptFrame* frame,
+    std::vector<BreakLocation>* result_out) {
+  DCHECK(!debug_info->CanBreakAtEntry());
   auto summary = FrameSummary::GetTop(frame).AsJavaScript();
   int offset = summary.code_offset();
   Handle<AbstractCode> abstract_code = summary.abstract_code();
   if (abstract_code->IsCode()) offset = offset - 1;
   int statement_position;
   {
-    auto it = BreakIterator::GetIterator(debug_info, abstract_code);
-    it->SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
-    statement_position = it->statement_position();
+    BreakIterator it(debug_info);
+    it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset));
+    statement_position = it.statement_position();
   }
-  for (auto it = BreakIterator::GetIterator(debug_info, abstract_code);
-       !it->Done(); it->Next()) {
-    if (it->statement_position() == statement_position) {
-      result_out->Add(it->GetBreakLocation());
+  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
+    if (it.statement_position() == statement_position) {
+      result_out->push_back(it.GetBreakLocation());
     }
   }
 }
 
+JSGeneratorObject BreakLocation::GetGeneratorObjectForSuspendedFrame(
+    JavaScriptFrame* frame) const {
+  DCHECK(IsSuspend());
+  DCHECK_GE(generator_obj_reg_index_, 0);
+
+  Object generator_obj = InterpretedFrame::cast(frame)->ReadInterpreterRegister(
+      generator_obj_reg_index_);
+
+  return JSGeneratorObject::cast(generator_obj);
+}
+
 int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,
                                             Handle<AbstractCode> abstract_code,
                                             int offset) {
@@ -93,12 +152,11 @@
   int closest_break = 0;
   int distance = kMaxInt;
   DCHECK(0 <= offset && offset < abstract_code->Size());
-  for (auto it = BreakIterator::GetIterator(debug_info, abstract_code);
-       !it->Done(); it->Next()) {
+  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
     // Check if this break point is closer that what was previously found.
-    if (it->code_offset() <= offset && offset - it->code_offset() < distance) {
-      closest_break = it->break_index();
-      distance = offset - it->code_offset();
+    if (it.code_offset() <= offset && offset - it.code_offset() < distance) {
+      closest_break = it.break_index();
+      distance = offset - it.code_offset();
       // Check whether we can't get any closer.
       if (distance == 0) break;
     }
@@ -106,56 +164,57 @@
   return closest_break;
 }
 
-bool BreakLocation::HasBreakPoint(Handle<DebugInfo> debug_info) const {
+bool BreakLocation::HasBreakPoint(Isolate* isolate,
+                                  Handle<DebugInfo> debug_info) const {
   // First check whether there is a break point with the same source position.
-  if (!debug_info->HasBreakPoint(position_)) return false;
-  // 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.
-  if (abstract_code_->IsCode()) {
-    DCHECK_EQ(debug_info->DebugCode(), abstract_code_->GetCode());
-    CodeBreakIterator it(debug_info);
-    it.SkipToPosition(position_, BREAK_POSITION_ALIGNED);
-    return it.code_offset() == code_offset_;
+  if (!debug_info->HasBreakPoint(isolate, position_)) return false;
+  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());
-    BytecodeArrayBreakIterator it(debug_info);
-    it.SkipToPosition(position_, BREAK_POSITION_ALIGNED);
+    BreakIterator it(debug_info);
+    it.SkipToPosition(position_);
     return it.code_offset() == code_offset_;
   }
 }
 
-std::unique_ptr<BreakIterator> BreakIterator::GetIterator(
-    Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code) {
-  if (abstract_code->IsBytecodeArray()) {
-    DCHECK(debug_info->HasDebugBytecodeArray());
-    return std::unique_ptr<BreakIterator>(
-        new BytecodeArrayBreakIterator(debug_info));
-  } else {
-    DCHECK(abstract_code->IsCode());
-    DCHECK(debug_info->HasDebugCode());
-    return std::unique_ptr<BreakIterator>(new CodeBreakIterator(debug_info));
+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;
+
+    // Externally, suspend breaks should look like normal breaks.
+    case DEBUG_BREAK_SLOT_AT_SUSPEND:
+    default:
+      return debug::kCommonBreakLocation;
   }
 }
 
 BreakIterator::BreakIterator(Handle<DebugInfo> debug_info)
-    : debug_info_(debug_info), break_index_(-1) {
-  position_ = debug_info->shared()->start_position();
+    : debug_info_(debug_info),
+      break_index_(-1),
+      source_position_iterator_(
+          debug_info->DebugBytecodeArray().SourcePositionTable()) {
+  position_ = debug_info->shared().StartPosition();
   statement_position_ = position_;
+  // There is at least one break location.
+  DCHECK(!Done());
+  Next();
 }
 
-int BreakIterator::BreakIndexFromPosition(int source_position,
-                                          BreakPositionAlignment alignment) {
+int BreakIterator::BreakIndexFromPosition(int source_position) {
   int distance = kMaxInt;
   int closest_break = break_index();
   while (!Done()) {
-    int next_position;
-    if (alignment == STATEMENT_ALIGNED) {
-      next_position = statement_position();
-    } else {
-      DCHECK(alignment == BREAK_POSITION_ALIGNED);
-      next_position = position();
-    }
+    int next_position = position();
     if (source_position <= next_position &&
         next_position - source_position < distance) {
       closest_break = break_index();
@@ -168,108 +227,7 @@
   return closest_break;
 }
 
-CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info)
-    : BreakIterator(debug_info),
-      reloc_iterator_(debug_info->DebugCode(), GetModeMask()),
-      source_position_iterator_(
-          debug_info->DebugCode()->source_position_table()) {
-  // There is at least one break location.
-  DCHECK(!Done());
-  Next();
-}
-
-int CodeBreakIterator::GetModeMask() {
-  int mask = 0;
-  mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
-  mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
-  mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL);
-  mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
-  return mask;
-}
-
-void CodeBreakIterator::Next() {
-  DisallowHeapAllocation no_gc;
-  DCHECK(!Done());
-
-  // Iterate through reloc info stopping at each breakable code target.
-  bool first = break_index_ == -1;
-
-  if (!first) reloc_iterator_.next();
-  first = false;
-  if (Done()) return;
-
-  int offset = code_offset();
-  while (!source_position_iterator_.done() &&
-         source_position_iterator_.code_offset() <= offset) {
-    position_ = source_position_iterator_.source_position().ScriptOffset();
-    if (source_position_iterator_.is_statement()) {
-      statement_position_ = position_;
-    }
-    source_position_iterator_.Advance();
-  }
-
-  DCHECK(RelocInfo::IsDebugBreakSlot(rmode()));
-  break_index_++;
-}
-
-DebugBreakType CodeBreakIterator::GetDebugBreakType() {
-  if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) {
-    return DEBUG_BREAK_SLOT_AT_RETURN;
-  } else if (RelocInfo::IsDebugBreakSlotAtCall(rmode())) {
-    return DEBUG_BREAK_SLOT_AT_CALL;
-  } else if (RelocInfo::IsDebugBreakSlotAtTailCall(rmode())) {
-    return isolate()->is_tail_call_elimination_enabled()
-               ? DEBUG_BREAK_SLOT_AT_TAIL_CALL
-               : DEBUG_BREAK_SLOT_AT_CALL;
-  } else if (RelocInfo::IsDebugBreakSlot(rmode())) {
-    return DEBUG_BREAK_SLOT;
-  } else {
-    return NOT_DEBUG_BREAK;
-  }
-}
-
-void CodeBreakIterator::SkipToPosition(int position,
-                                       BreakPositionAlignment alignment) {
-  CodeBreakIterator it(debug_info_);
-  SkipTo(it.BreakIndexFromPosition(position, alignment));
-}
-
-void CodeBreakIterator::SetDebugBreak() {
-  DebugBreakType debug_break_type = GetDebugBreakType();
-  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
-  Builtins* builtins = isolate()->builtins();
-  Handle<Code> target = debug_break_type == DEBUG_BREAK_SLOT_AT_RETURN
-                            ? builtins->Return_DebugBreak()
-                            : builtins->Slot_DebugBreak();
-  DebugCodegen::PatchDebugBreakSlot(isolate(), rinfo()->pc(), target);
-}
-
-void CodeBreakIterator::ClearDebugBreak() {
-  DCHECK(GetDebugBreakType() >= DEBUG_BREAK_SLOT);
-  DebugCodegen::ClearDebugBreakSlot(isolate(), rinfo()->pc());
-}
-
-bool CodeBreakIterator::IsDebugBreak() {
-  DCHECK(GetDebugBreakType() >= DEBUG_BREAK_SLOT);
-  return DebugCodegen::DebugBreakSlotIsPatched(rinfo()->pc());
-}
-
-BreakLocation CodeBreakIterator::GetBreakLocation() {
-  Handle<AbstractCode> code(AbstractCode::cast(debug_info_->DebugCode()));
-  return BreakLocation(code, GetDebugBreakType(), code_offset(), position_);
-}
-
-BytecodeArrayBreakIterator::BytecodeArrayBreakIterator(
-    Handle<DebugInfo> debug_info)
-    : BreakIterator(debug_info),
-      source_position_iterator_(
-          debug_info->DebugBytecodeArray()->source_position_table()) {
-  // There is at least one break location.
-  DCHECK(!Done());
-  Next();
-}
-
-void BytecodeArrayBreakIterator::Next() {
+void BreakIterator::Next() {
   DisallowHeapAllocation no_gc;
   DCHECK(!Done());
   bool first = break_index_ == -1;
@@ -281,8 +239,8 @@
     if (source_position_iterator_.is_statement()) {
       statement_position_ = position_;
     }
-    DCHECK(position_ >= 0);
-    DCHECK(statement_position_ >= 0);
+    DCHECK_LE(0, position_);
+    DCHECK_LE(0, statement_position_);
 
     DebugBreakType type = GetDebugBreakType();
     if (type != NOT_DEBUG_BREAK) break;
@@ -290,19 +248,23 @@
   break_index_++;
 }
 
-DebugBreakType BytecodeArrayBreakIterator::GetDebugBreakType() {
-  BytecodeArray* bytecode_array = debug_info_->OriginalBytecodeArray();
+DebugBreakType BreakIterator::GetDebugBreakType() {
+  BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
   interpreter::Bytecode bytecode =
-      interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
+      interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset()));
+
+  // Make sure we read the actual bytecode, not a prefix scaling bytecode.
+  if (interpreter::Bytecodes::IsPrefixScalingBytecode(bytecode)) {
+    bytecode =
+        interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset() + 1));
+  }
 
   if (bytecode == interpreter::Bytecode::kDebugger) {
     return DEBUGGER_STATEMENT;
   } else if (bytecode == interpreter::Bytecode::kReturn) {
     return DEBUG_BREAK_SLOT_AT_RETURN;
-  } else if (bytecode == interpreter::Bytecode::kTailCall) {
-    return isolate()->is_tail_call_elimination_enabled()
-               ? DEBUG_BREAK_SLOT_AT_TAIL_CALL
-               : DEBUG_BREAK_SLOT_AT_CALL;
+  } else if (bytecode == interpreter::Bytecode::kSuspendGenerator) {
+    return DEBUG_BREAK_SLOT_AT_SUSPEND;
   } else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) {
     return DEBUG_BREAK_SLOT_AT_CALL;
   } else if (source_position_iterator_.is_statement()) {
@@ -312,51 +274,56 @@
   }
 }
 
-void BytecodeArrayBreakIterator::SkipToPosition(
-    int position, BreakPositionAlignment alignment) {
-  BytecodeArrayBreakIterator it(debug_info_);
-  SkipTo(it.BreakIndexFromPosition(position, alignment));
+void BreakIterator::SkipToPosition(int position) {
+  BreakIterator it(debug_info_);
+  SkipTo(it.BreakIndexFromPosition(position));
 }
 
-void BytecodeArrayBreakIterator::SetDebugBreak() {
+void BreakIterator::SetDebugBreak() {
+  DebugBreakType debug_break_type = GetDebugBreakType();
+  if (debug_break_type == DEBUGGER_STATEMENT) return;
+  HandleScope scope(isolate());
+  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
+  Handle<BytecodeArray> bytecode_array(debug_info_->DebugBytecodeArray(),
+                                       isolate());
+  interpreter::BytecodeArrayAccessor(bytecode_array, code_offset())
+      .ApplyDebugBreak();
+}
+
+void BreakIterator::ClearDebugBreak() {
   DebugBreakType debug_break_type = GetDebugBreakType();
   if (debug_break_type == DEBUGGER_STATEMENT) return;
   DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
-  BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
-  interpreter::Bytecode bytecode =
-      interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
-  if (interpreter::Bytecodes::IsDebugBreak(bytecode)) return;
-  interpreter::Bytecode debugbreak =
-      interpreter::Bytecodes::GetDebugBreak(bytecode);
-  bytecode_array->set(code_offset(),
-                      interpreter::Bytecodes::ToByte(debugbreak));
+  BytecodeArray bytecode_array = debug_info_->DebugBytecodeArray();
+  BytecodeArray original = debug_info_->OriginalBytecodeArray();
+  bytecode_array.set(code_offset(), original.get(code_offset()));
 }
 
-void BytecodeArrayBreakIterator::ClearDebugBreak() {
-  DebugBreakType debug_break_type = GetDebugBreakType();
-  if (debug_break_type == DEBUGGER_STATEMENT) return;
-  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
-  BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
-  BytecodeArray* original = debug_info_->OriginalBytecodeArray();
-  bytecode_array->set(code_offset(), original->get(code_offset()));
-}
-
-bool BytecodeArrayBreakIterator::IsDebugBreak() {
-  DebugBreakType debug_break_type = GetDebugBreakType();
-  if (debug_break_type == DEBUGGER_STATEMENT) return false;
-  DCHECK(debug_break_type >= DEBUG_BREAK_SLOT);
-  BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray();
-  interpreter::Bytecode bytecode =
-      interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset()));
-  return interpreter::Bytecodes::IsDebugBreak(bytecode);
-}
-
-BreakLocation BytecodeArrayBreakIterator::GetBreakLocation() {
+BreakLocation BreakIterator::GetBreakLocation() {
   Handle<AbstractCode> code(
-      AbstractCode::cast(debug_info_->DebugBytecodeArray()));
-  return BreakLocation(code, GetDebugBreakType(), code_offset(), position_);
+      AbstractCode::cast(debug_info_->DebugBytecodeArray()), isolate());
+  DebugBreakType type = GetDebugBreakType();
+  int generator_object_reg_index = -1;
+  if (type == DEBUG_BREAK_SLOT_AT_SUSPEND) {
+    // For suspend break, we'll need the generator object to be able to step
+    // over the suspend as if it didn't return. We get the interpreter register
+    // index that holds the generator object by reading it directly off the
+    // bytecode array, and we'll read the actual generator object off the
+    // interpreter stack frame in GetGeneratorObjectForSuspendedFrame.
+    BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray();
+    interpreter::BytecodeArrayAccessor accessor(
+        handle(bytecode_array, isolate()), code_offset());
+
+    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);
 }
 
+Isolate* BreakIterator::isolate() { return debug_info_->GetIsolate(); }
 
 void DebugFeatureTracker::Track(DebugFeatureTracker::Feature feature) {
   uint32_t mask = 1 << feature;
@@ -369,110 +336,95 @@
 
 // Threading support.
 void Debug::ThreadInit() {
-  thread_local_.break_count_ = 0;
-  thread_local_.break_id_ = 0;
-  thread_local_.break_frame_id_ = StackFrame::NO_ID;
+  thread_local_.break_frame_id_ = StackFrameId::NO_ID;
   thread_local_.last_step_action_ = StepNone;
   thread_local_.last_statement_position_ = kNoSourcePosition;
   thread_local_.last_frame_count_ = -1;
+  thread_local_.fast_forward_to_return_ = false;
+  thread_local_.ignore_step_into_function_ = Smi::zero();
   thread_local_.target_frame_count_ = -1;
-  thread_local_.return_value_ = Smi::kZero;
-  thread_local_.async_task_count_ = 0;
+  thread_local_.return_value_ = Smi::zero();
+  thread_local_.last_breakpoint_id_ = 0;
   clear_suspended_generator();
-  thread_local_.restart_fp_ = nullptr;
-  base::NoBarrier_Store(&thread_local_.current_debug_scope_,
-                        static_cast<base::AtomicWord>(0));
+  thread_local_.restart_fp_ = kNullAddress;
+  base::Relaxed_Store(&thread_local_.current_debug_scope_,
+                      static_cast<base::AtomicWord>(0));
+  thread_local_.break_on_next_function_call_ = false;
   UpdateHookOnFunctionCall();
 }
 
 
 char* Debug::ArchiveDebug(char* storage) {
-  // Simply reset state. Don't archive anything.
-  ThreadInit();
+  MemCopy(storage, reinterpret_cast<char*>(&thread_local_),
+          ArchiveSpacePerThread());
   return storage + ArchiveSpacePerThread();
 }
 
-
 char* Debug::RestoreDebug(char* storage) {
-  // Simply reset state. Don't restore anything.
-  ThreadInit();
+  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) {
+    int current_frame_count = CurrentFrameCount();
+    int target_frame_count = thread_local_.target_frame_count_;
+    DCHECK(current_frame_count >= target_frame_count);
+    StackTraceFrameIterator frames_it(isolate_);
+    while (current_frame_count > target_frame_count) {
+      current_frame_count -= frames_it.FrameFunctionCount();
+      frames_it.Advance();
+    }
+    DCHECK(current_frame_count == target_frame_count);
+    // Set frame to what it was at Step break
+    thread_local_.break_frame_id_ = frames_it.frame()->id();
+
+    // Reset the previous step action for this thread.
+    PrepareStep(thread_local_.last_step_action_);
+  }
+
   return storage + ArchiveSpacePerThread();
 }
 
-int Debug::ArchiveSpacePerThread() { return 0; }
+int Debug::ArchiveSpacePerThread() { return sizeof(ThreadLocal); }
 
-void Debug::Iterate(ObjectVisitor* v) {
-  v->VisitPointer(&thread_local_.return_value_);
-  v->VisitPointer(&thread_local_.suspended_generator_);
+void Debug::Iterate(RootVisitor* v) {
+  v->VisitRootPointer(Root::kDebug, nullptr,
+                      FullObjectSlot(&thread_local_.return_value_));
+  v->VisitRootPointer(Root::kDebug, nullptr,
+                      FullObjectSlot(&thread_local_.suspended_generator_));
+  v->VisitRootPointer(
+      Root::kDebug, nullptr,
+      FullObjectSlot(&thread_local_.ignore_step_into_function_));
 }
 
-DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) {
+DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo debug_info)
+    : next_(nullptr) {
   // Globalize the request debug info object and make it weak.
-  GlobalHandles* global_handles = debug_info->GetIsolate()->global_handles();
-  debug_info_ =
-      Handle<DebugInfo>::cast(global_handles->Create(debug_info)).location();
+  GlobalHandles* global_handles = isolate->global_handles();
+  debug_info_ = global_handles->Create(debug_info).location();
 }
 
-
 DebugInfoListNode::~DebugInfoListNode() {
   if (debug_info_ == nullptr) return;
-  GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_));
+  GlobalHandles::Destroy(debug_info_);
   debug_info_ = nullptr;
 }
 
-
-bool Debug::Load() {
-  // Return if debugger is already loaded.
-  if (is_loaded()) return true;
-
-  // Bail out if we're already in the process of compiling the native
-  // JavaScript source code for the debugger.
-  if (is_suppressed_) return false;
-  SuppressDebug while_loading(this);
-
-  // Disable breakpoints and interrupts while compiling and running the
-  // debugger scripts including the context creation code.
-  DisableBreak disable(this);
-  PostponeInterruptsScope postpone(isolate_);
-
-  // Create the debugger context.
-  HandleScope scope(isolate_);
-  ExtensionConfiguration no_extensions;
-  // TODO(yangguo): we rely on the fact that first context snapshot is usable
-  //                as debug context. This dependency is gone once we remove
-  //                debug context completely.
-  static const int kFirstContextSnapshotIndex = 0;
-  Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment(
-      MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), &no_extensions,
-      kFirstContextSnapshotIndex, v8::DeserializeInternalFieldsCallback(),
-      DEBUG_CONTEXT);
-
-  // Fail if no context could be created.
-  if (context.is_null()) return false;
-
-  debug_context_ = Handle<Context>::cast(
-      isolate_->global_handles()->Create(*context));
-
-  feature_tracker()->Track(DebugFeatureTracker::kActive);
-
-  return true;
-}
-
-
 void Debug::Unload() {
   ClearAllBreakPoints();
   ClearStepping();
-  RemoveDebugDelegate();
-
-  // Return debugger is not loaded.
-  if (!is_loaded()) return;
-
-  // Clear debugger context global handle.
-  GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location());
-  debug_context_ = Handle<Context>();
+  RemoveAllCoverageInfos();
+  ClearAllDebuggerHints();
+  debug_delegate_ = nullptr;
 }
 
-void Debug::Break(JavaScriptFrame* frame) {
+void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) {
   // Initialize LiveEdit.
   LiveEdit::InitializeThreadLocal(this);
 
@@ -481,16 +433,13 @@
 
   // Enter the debugger.
   DebugScope debug_scope(this);
-  if (debug_scope.failed()) return;
-
-  // Postpone interrupt during breakpoint processing.
-  PostponeInterruptsScope postpone(isolate_);
   DisableBreak no_recursive_break(this);
 
   // Return if we fail to retrieve debug info.
-  Handle<JSFunction> function(frame->function());
-  Handle<SharedFunctionInfo> shared(function->shared());
-  if (!EnsureDebugInfo(shared)) return;
+  Handle<SharedFunctionInfo> shared(break_target->shared(), isolate_);
+  if (!EnsureBreakInfo(shared)) return;
+  PrepareFunctionForDebugExecution(shared);
+
   Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
 
   // Find the break location where execution has stopped.
@@ -499,22 +448,43 @@
   // Find actual break points, if any, and trigger debug break event.
   MaybeHandle<FixedArray> break_points_hit =
       CheckBreakPoints(debug_info, &location);
-  if (!break_points_hit.is_null()) {
+  if (!break_points_hit.is_null() || break_on_next_function_call()) {
+    StepAction lastStepAction = last_step_action();
     // Clear all current stepping setup.
     ClearStepping();
     // Notify the debug event listeners.
-    Handle<JSArray> jsarr = isolate_->factory()->NewJSArrayWithElements(
-        break_points_hit.ToHandleChecked());
-    OnDebugBreak(jsarr);
+    OnDebugBreak(!break_points_hit.is_null()
+                     ? break_points_hit.ToHandleChecked()
+                     : isolate_->factory()->empty_fixed_array(),
+                 lastStepAction);
     return;
   }
 
+  // Debug break at function entry, do not worry about stepping.
+  if (location.IsDebugBreakAtEntry()) {
+    DCHECK(debug_info->BreakAtEntry());
+    return;
+  }
+
+  DCHECK_NOT_NULL(frame);
+
   // No break point. Check for stepping.
   StepAction step_action = last_step_action();
   int current_frame_count = CurrentFrameCount();
   int target_frame_count = thread_local_.target_frame_count_;
   int last_frame_count = thread_local_.last_frame_count_;
 
+  // StepOut at not return position was requested and return break locations
+  // were flooded with one shots.
+  if (thread_local_.fast_forward_to_return_) {
+    DCHECK(location.IsReturnOrSuspend());
+    // We have to ignore recursive calls to function.
+    if (current_frame_count > target_frame_count) return;
+    ClearStepping();
+    PrepareStep(StepOut);
+    return;
+  }
+
   bool step_break = false;
   switch (step_action) {
     case StepNone:
@@ -527,10 +497,17 @@
     case StepNext:
       // Step next should not break in a deeper frame than target frame.
       if (current_frame_count > target_frame_count) return;
-      // For step-next, a tail call is like a return and should break.
-      step_break = location.IsTailCall();
-    // Fall through.
+      V8_FALLTHROUGH;
     case StepIn: {
+      // 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;
+      }
+
       FrameSummary summary = FrameSummary::GetTop(frame);
       step_break = step_break || location.IsReturn() ||
                    current_frame_count != last_frame_count ||
@@ -540,12 +517,13 @@
     }
   }
 
+  StepAction lastStepAction = last_step_action();
   // Clear all current stepping setup.
   ClearStepping();
 
   if (step_break) {
     // Notify the debug event listeners.
-    OnDebugBreak(isolate_->factory()->undefined_value());
+    OnDebugBreak(isolate_->factory()->empty_fixed_array(), lastStepAction);
   } else {
     // Re-prepare to continue.
     PrepareStep(step_action);
@@ -560,13 +538,11 @@
                                                 BreakLocation* location,
                                                 bool* has_break_points) {
   bool has_break_points_to_check =
-      break_points_active_ && location->HasBreakPoint(debug_info);
+      break_points_active_ && location->HasBreakPoint(isolate_, debug_info);
   if (has_break_points) *has_break_points = has_break_points_to_check;
   if (!has_break_points_to_check) return {};
 
-  Handle<Object> break_point_objects =
-      debug_info->GetBreakPointObjects(location->position());
-  return Debug::GetHitBreakPointObjects(break_point_objects);
+  return Debug::GetHitBreakPoints(debug_info, location->position());
 }
 
 
@@ -580,15 +556,14 @@
   FrameSummary summary = FrameSummary::GetTop(frame);
   DCHECK(!summary.IsWasm());
   Handle<JSFunction> function = summary.AsJavaScript().function();
-  if (!function->shared()->HasDebugInfo()) return false;
-  Handle<DebugInfo> debug_info(function->shared()->GetDebugInfo());
+  if (!function->shared().HasBreakInfo()) return false;
+  Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(), isolate_);
   // Enter the debugger.
   DebugScope debug_scope(this);
-  if (debug_scope.failed()) return false;
-  List<BreakLocation> break_locations;
+  std::vector<BreakLocation> break_locations;
   BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations);
   bool has_break_points_at_all = false;
-  for (int i = 0; i < break_locations.length(); i++) {
+  for (size_t i = 0; i < break_locations.size(); i++) {
     bool has_break_points;
     MaybeHandle<FixedArray> check_result =
         CheckBreakPoints(debug_info, &break_locations[i], &has_break_points);
@@ -598,65 +573,55 @@
   return has_break_points_at_all;
 }
 
-
-MaybeHandle<Object> Debug::CallFunction(const char* name, int argc,
-                                        Handle<Object> args[]) {
-  PostponeInterruptsScope no_interrupts(isolate_);
-  AssertDebugContext();
-  Handle<JSReceiver> holder =
-      Handle<JSReceiver>::cast(isolate_->natives_utils_object());
-  Handle<JSFunction> fun = Handle<JSFunction>::cast(
-      JSReceiver::GetProperty(isolate_, holder, name).ToHandleChecked());
-  Handle<Object> undefined = isolate_->factory()->undefined_value();
-  MaybeHandle<Object> maybe_exception;
-  return Execution::TryCall(isolate_, fun, undefined, argc, args,
-                            Execution::MessageHandling::kReport,
-                            &maybe_exception);
-}
-
-
 // Check whether a single break point object is triggered.
-bool Debug::CheckBreakPoint(Handle<Object> break_point_object) {
-  Factory* factory = isolate_->factory();
+bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
+                            bool is_break_at_entry) {
   HandleScope scope(isolate_);
 
-  // Ignore check if break point object is not a JSObject.
-  if (!break_point_object->IsJSObject()) return true;
-
-  // Get the break id as an object.
-  Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id());
-
-  // Call IsBreakPointTriggered.
-  Handle<Object> argv[] = { break_id, break_point_object };
+  if (!break_point->condition().length()) return true;
+  Handle<String> condition(break_point->condition(), isolate_);
+  MaybeHandle<Object> maybe_result;
   Handle<Object> result;
-  if (!CallFunction("IsBreakPointTriggered", arraysize(argv), argv)
-           .ToHandle(&result)) {
-    return false;
+
+  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);
   }
 
-  // Return whether the break point is triggered.
-  return result->IsTrue(isolate_);
+  if (!maybe_result.ToHandle(&result)) {
+    if (isolate_->has_pending_exception()) {
+      isolate_->clear_pending_exception();
+    }
+    return false;
+  }
+  return result->BooleanValue(isolate_);
 }
 
-
-bool Debug::SetBreakPoint(Handle<JSFunction> function,
-                          Handle<Object> break_point_object,
+bool Debug::SetBreakpoint(Handle<SharedFunctionInfo> shared,
+                          Handle<BreakPoint> break_point,
                           int* source_position) {
   HandleScope scope(isolate_);
 
   // Make sure the function is compiled and has set up the debug info.
-  Handle<SharedFunctionInfo> shared(function->shared());
-  if (!EnsureDebugInfo(shared)) return true;
-  Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+  if (!EnsureBreakInfo(shared)) return false;
+  PrepareFunctionForDebugExecution(shared);
+
+  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
   // Source positions starts with zero.
-  DCHECK(*source_position >= 0);
+  DCHECK_LE(0, *source_position);
 
   // Find the break point and change it.
-  *source_position =
-      FindBreakablePosition(debug_info, *source_position, STATEMENT_ALIGNED);
-  DebugInfo::SetBreakPoint(debug_info, *source_position, break_point_object);
+  *source_position = FindBreakablePosition(debug_info, *source_position);
+  DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
   // At least one active break point now.
-  DCHECK(debug_info->GetBreakPointCount() > 0);
+  DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
 
   ClearBreakPoints(debug_info);
   ApplyBreakPoints(debug_info);
@@ -665,16 +630,15 @@
   return true;
 }
 
-
 bool Debug::SetBreakPointForScript(Handle<Script> script,
-                                   Handle<Object> break_point_object,
-                                   int* source_position,
-                                   BreakPositionAlignment alignment) {
+                                   Handle<String> condition,
+                                   int* source_position, int* id) {
+  *id = ++thread_local_.last_breakpoint_id_;
+  Handle<BreakPoint> break_point =
+      isolate_->factory()->NewBreakPoint(*id, condition);
   if (script->type() == Script::TYPE_WASM) {
-    Handle<WasmCompiledModule> compiled_module(
-        WasmCompiledModule::cast(script->wasm_compiled_module()), isolate_);
-    return WasmCompiledModule::SetBreakPoint(compiled_module, source_position,
-                                             break_point_object);
+    RecordWasmScriptWithBreakpoints(script);
+    return WasmScript::SetBreakPoint(script, source_position, break_point);
   }
 
   HandleScope scope(isolate_);
@@ -686,22 +650,27 @@
 
   // Make sure the function has set up the debug info.
   Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
-  if (!EnsureDebugInfo(shared)) return false;
+  if (!EnsureBreakInfo(shared)) return false;
+  PrepareFunctionForDebugExecution(shared);
 
   // Find position within function. The script position might be before the
   // source position of the first function.
-  if (shared->start_position() > *source_position) {
-    *source_position = shared->start_position();
+  if (shared->StartPosition() > *source_position) {
+    *source_position = shared->StartPosition();
   }
 
-  Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
 
-  // Find the break point and change it.
-  *source_position =
-      FindBreakablePosition(debug_info, *source_position, alignment);
-  DebugInfo::SetBreakPoint(debug_info, *source_position, break_point_object);
+  // 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;
+
+  DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point);
   // At least one active break point now.
-  DCHECK(debug_info->GetBreakPointCount() > 0);
+  DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_));
 
   ClearBreakPoints(debug_info);
   ApplyBreakPoints(debug_info);
@@ -711,73 +680,69 @@
 }
 
 int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
-                                 int source_position,
-                                 BreakPositionAlignment alignment) {
-  int statement_position;
-  int position;
-  if (debug_info->HasDebugCode()) {
-    CodeBreakIterator it(debug_info);
-    it.SkipToPosition(source_position, alignment);
-    statement_position = it.statement_position();
-    position = it.position();
+                                 int source_position) {
+  if (debug_info->CanBreakAtEntry()) {
+    return kBreakAtEntryPosition;
   } else {
-    DCHECK(debug_info->HasDebugBytecodeArray());
-    BytecodeArrayBreakIterator it(debug_info);
-    it.SkipToPosition(source_position, alignment);
-    statement_position = it.statement_position();
-    position = it.position();
+    DCHECK(debug_info->HasInstrumentedBytecodeArray());
+    BreakIterator it(debug_info);
+    it.SkipToPosition(source_position);
+    return it.position();
   }
-  return alignment == STATEMENT_ALIGNED ? statement_position : position;
 }
 
 void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
   DisallowHeapAllocation no_gc;
-  if (debug_info->break_points()->IsUndefined(isolate_)) return;
-  FixedArray* break_points = debug_info->break_points();
-  for (int i = 0; i < break_points->length(); i++) {
-    if (break_points->get(i)->IsUndefined(isolate_)) continue;
-    BreakPointInfo* info = BreakPointInfo::cast(break_points->get(i));
-    if (info->GetBreakPointCount() == 0) continue;
-    if (debug_info->HasDebugCode()) {
-      CodeBreakIterator it(debug_info);
-      it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED);
-      it.SetDebugBreak();
-    }
-    if (debug_info->HasDebugBytecodeArray()) {
-      BytecodeArrayBreakIterator it(debug_info);
-      it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED);
+  if (debug_info->CanBreakAtEntry()) {
+    debug_info->SetBreakAtEntry();
+  } else {
+    if (!debug_info->HasInstrumentedBytecodeArray()) return;
+    FixedArray break_points = debug_info->break_points();
+    for (int i = 0; i < break_points.length(); i++) {
+      if (break_points.get(i).IsUndefined(isolate_)) continue;
+      BreakPointInfo info = BreakPointInfo::cast(break_points.get(i));
+      if (info.GetBreakPointCount(isolate_) == 0) continue;
+      DCHECK(debug_info->HasInstrumentedBytecodeArray());
+      BreakIterator it(debug_info);
+      it.SkipToPosition(info.source_position());
       it.SetDebugBreak();
     }
   }
+  debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints);
 }
 
 void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
-  DisallowHeapAllocation no_gc;
-  if (debug_info->HasDebugCode()) {
-    for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) {
-      it.ClearDebugBreak();
+  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.
+    if (!debug_info->HasInstrumentedBytecodeArray() ||
+        !debug_info->HasBreakInfo()) {
+      return;
     }
-  }
-  if (debug_info->HasDebugBytecodeArray()) {
-    for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) {
+
+    DisallowHeapAllocation no_gc;
+    for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
       it.ClearDebugBreak();
     }
   }
 }
 
-void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
+void Debug::ClearBreakPoint(Handle<BreakPoint> break_point) {
   HandleScope scope(isolate_);
 
-  for (DebugInfoListNode* node = debug_info_list_; node != NULL;
+  for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
        node = node->next()) {
-    Handle<Object> result =
-        DebugInfo::FindBreakPointInfo(node->debug_info(), break_point_object);
+    if (!node->debug_info()->HasBreakInfo()) continue;
+    Handle<Object> result = DebugInfo::FindBreakPointInfo(
+        isolate_, node->debug_info(), break_point);
     if (result->IsUndefined(isolate_)) continue;
     Handle<DebugInfo> debug_info = node->debug_info();
-    if (DebugInfo::ClearBreakPoint(debug_info, break_point_object)) {
+    if (DebugInfo::ClearBreakPoint(isolate_, debug_info, break_point)) {
       ClearBreakPoints(debug_info);
-      if (debug_info->GetBreakPointCount() == 0) {
-        RemoveDebugInfoAndClearFromShared(debug_info);
+      if (debug_info->GetBreakPointCount(isolate_) == 0) {
+        RemoveBreakInfoAndMaybeFree(debug_info);
       } else {
         ApplyBreakPoints(debug_info);
       }
@@ -786,35 +751,113 @@
   }
 }
 
-// Clear out all the debug break code. This is ONLY supposed to be used when
-// shutting down the debugger as it will leave the break point information in
-// DebugInfo even though the code is patched back to the non break point state.
-void Debug::ClearAllBreakPoints() {
-  for (DebugInfoListNode* node = debug_info_list_; node != NULL;
-       node = node->next()) {
-    ClearBreakPoints(node->debug_info());
+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);
   }
-  // Remove all debug info.
-  while (debug_info_list_ != NULL) {
-    RemoveDebugInfoAndClearFromShared(debug_info_list_->debug_info());
+  return id;
+}
+
+bool Debug::SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
+                                     Handle<String> condition, int* id) {
+  *id = ++thread_local_.last_breakpoint_id_;
+  Handle<BreakPoint> breakpoint =
+      isolate_->factory()->NewBreakPoint(*id, condition);
+  int source_position = 0;
+  // Handle wasm function.
+  if (shared->HasWasmExportedFunctionData()) {
+    int func_index = shared->wasm_exported_function_data().function_index();
+    Handle<WasmInstanceObject> wasm_instance(
+        shared->wasm_exported_function_data().instance(), isolate_);
+    Handle<Script> script(Script::cast(wasm_instance->module_object().script()),
+                          isolate_);
+    return WasmScript::SetBreakPointOnFirstBreakableForFunction(
+        script, func_index, breakpoint);
+  }
+  return SetBreakpoint(shared, breakpoint, &source_position);
+}
+
+void Debug::RemoveBreakpoint(int id) {
+  Handle<BreakPoint> breakpoint = isolate_->factory()->NewBreakPoint(
+      id, isolate_->factory()->empty_string());
+  ClearBreakPoint(breakpoint);
+}
+
+void Debug::RemoveBreakpointForWasmScript(Handle<Script> script, int id) {
+  if (script->type() == Script::TYPE_WASM) {
+    WasmScript::ClearBreakPointById(script, id);
   }
 }
 
-void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
-  if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) return;
-  // Make sure the function is compiled and has set up the debug info.
-  if (!EnsureDebugInfo(shared)) return;
-  Handle<DebugInfo> debug_info(shared->GetDebugInfo());
-  // Flood the function with break points.
-  if (debug_info->HasDebugCode()) {
-    for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) {
-      it.SetDebugBreak();
+void Debug::RecordWasmScriptWithBreakpoints(Handle<Script> script) {
+  if (wasm_scripts_with_breakpoints_.is_null()) {
+    Handle<WeakArrayList> new_list = isolate_->factory()->NewWeakArrayList(4);
+    wasm_scripts_with_breakpoints_ =
+        isolate_->global_handles()->Create(*new_list);
+  }
+  {
+    DisallowHeapAllocation no_gc;
+    for (int idx = wasm_scripts_with_breakpoints_->length() - 1; idx >= 0;
+         --idx) {
+      HeapObject wasm_script;
+      if (wasm_scripts_with_breakpoints_->Get(idx).GetHeapObject(
+              &wasm_script) &&
+          wasm_script == *script) {
+        return;
+      }
     }
   }
-  if (debug_info->HasDebugBytecodeArray()) {
-    for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) {
-      it.SetDebugBreak();
+  Handle<WeakArrayList> new_list = WeakArrayList::Append(
+      isolate_, wasm_scripts_with_breakpoints_, MaybeObjectHandle{script});
+  if (*new_list != *wasm_scripts_with_breakpoints_) {
+    isolate_->global_handles()->Destroy(
+        wasm_scripts_with_breakpoints_.location());
+    wasm_scripts_with_breakpoints_ =
+        isolate_->global_handles()->Create(*new_list);
+  }
+}
+
+// Clear out all the debug break code.
+void Debug::ClearAllBreakPoints() {
+  ClearAllDebugInfos([=](Handle<DebugInfo> info) {
+    ClearBreakPoints(info);
+    info->ClearBreakInfo(isolate_);
+  });
+  // Clear all wasm breakpoints.
+  if (!wasm_scripts_with_breakpoints_.is_null()) {
+    DisallowHeapAllocation no_gc;
+    for (int idx = wasm_scripts_with_breakpoints_->length() - 1; idx >= 0;
+         --idx) {
+      HeapObject raw_wasm_script;
+      if (wasm_scripts_with_breakpoints_->Get(idx).GetHeapObject(
+              &raw_wasm_script)) {
+        Script wasm_script = Script::cast(raw_wasm_script);
+        WasmScript::ClearAllBreakpoints(wasm_script);
+        wasm_script.wasm_native_module()->GetDebugInfo()->RemoveIsolate(
+            isolate_);
+      }
     }
+    wasm_scripts_with_breakpoints_ = Handle<WeakArrayList>{};
+  }
+}
+
+void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
+                             bool returns_only) {
+  if (IsBlackboxed(shared)) return;
+  // Make sure the function is compiled and has set up the debug info.
+  if (!EnsureBreakInfo(shared)) return;
+  PrepareFunctionForDebugExecution(shared);
+
+  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
+  // Flood the function with break points.
+  DCHECK(debug_info->HasInstrumentedBytecodeArray());
+  for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
+    if (returns_only && !it.GetBreakLocation().IsReturnOrSuspend()) continue;
+    it.SetDebugBreak();
   }
 }
 
@@ -835,37 +878,63 @@
   }
 }
 
-MaybeHandle<FixedArray> Debug::GetHitBreakPointObjects(
-    Handle<Object> break_point_objects) {
-  DCHECK(!break_point_objects->IsUndefined(isolate_));
-  if (!break_point_objects->IsFixedArray()) {
-    if (!CheckBreakPoint(break_point_objects)) return {};
+MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<DebugInfo> debug_info,
+                                                 int position) {
+  Handle<Object> break_points = debug_info->GetBreakPoints(isolate_, position);
+  bool is_break_at_entry = debug_info->BreakAtEntry();
+  DCHECK(!break_points->IsUndefined(isolate_));
+  if (!break_points->IsFixedArray()) {
+    if (!CheckBreakPoint(Handle<BreakPoint>::cast(break_points),
+                         is_break_at_entry)) {
+      return {};
+    }
     Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1);
-    break_points_hit->set(0, *break_point_objects);
+    break_points_hit->set(0, *break_points);
     return break_points_hit;
   }
 
-  Handle<FixedArray> array(FixedArray::cast(*break_point_objects));
+  Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_);
   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) {
-    Handle<Object> break_point_object(array->get(i), isolate_);
-    if (CheckBreakPoint(break_point_object)) {
-      break_points_hit->set(break_points_hit_count++, *break_point_object);
+    Handle<Object> break_point(array->get(i), isolate_);
+    if (CheckBreakPoint(Handle<BreakPoint>::cast(break_point),
+                        is_break_at_entry)) {
+      break_points_hit->set(break_points_hit_count++, *break_point);
     }
   }
   if (break_points_hit_count == 0) return {};
-  break_points_hit->Shrink(break_points_hit_count);
+  break_points_hit->Shrink(isolate_, break_points_hit_count);
   return break_points_hit;
 }
 
+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();
+}
+
 void Debug::PrepareStepIn(Handle<JSFunction> function) {
-  CHECK(last_step_action() >= StepIn);
+  CHECK(last_step_action() >= StepIn || break_on_next_function_call());
   if (ignore_events()) return;
   if (in_debug_scope()) return;
   if (break_disabled()) return;
+  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
+  if (IsBlackboxed(shared)) return;
+  if (*function == thread_local_.ignore_step_into_function_) return;
+  thread_local_.ignore_step_into_function_ = Smi::zero();
   FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
 }
 
@@ -877,7 +946,8 @@
   thread_local_.last_step_action_ = StepIn;
   UpdateHookOnFunctionCall();
   Handle<JSFunction> function(
-      JSGeneratorObject::cast(thread_local_.suspended_generator_)->function());
+      JSGeneratorObject::cast(thread_local_.suspended_generator_).function(),
+      isolate_);
   FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_));
   clear_suspended_generator();
 }
@@ -897,9 +967,9 @@
   while (!it.done()) {
     JavaScriptFrame* frame = it.frame();
     if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break;
-    List<SharedFunctionInfo*> infos;
+    std::vector<SharedFunctionInfo> infos;
     frame->GetFunctions(&infos);
-    current_frame_count -= infos.length();
+    current_frame_count -= infos.size();
     it.Advance();
   }
 
@@ -915,22 +985,21 @@
       // Deoptimize frame to ensure calls are checked for step-in.
       Deoptimizer::DeoptimizeFunction(frame->function());
     }
-    List<FrameSummary> summaries;
+    std::vector<FrameSummary> summaries;
     frame->Summarize(&summaries);
-    for (int i = summaries.length() - 1; i >= 0; i--, current_frame_count--) {
+    for (size_t i = summaries.size(); i != 0; i--, current_frame_count--) {
+      const FrameSummary& summary = summaries[i - 1];
       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.
-        if (summaries.length() > 1) {
-          Handle<AbstractCode> code =
-              summaries[i].AsJavaScript().abstract_code();
-          CHECK_EQ(AbstractCode::INTERPRETED_FUNCTION, code->kind());
-          BytecodeArray* bytecode = code->GetBytecodeArray();
-          HandlerTable* table = HandlerTable::cast(bytecode->handler_table());
-          int code_offset = summaries[i].code_offset();
+        if (summaries.size() > 1) {
+          Handle<AbstractCode> code = summary.AsJavaScript().abstract_code();
+          CHECK_EQ(CodeKind::INTERPRETED_FUNCTION, code->kind());
+          HandlerTable table(code->GetBytecodeArray());
+          int code_offset = summary.code_offset();
           HandlerTable::CatchPrediction prediction;
-          int index = table->LookupRange(code_offset, nullptr, &prediction);
+          int index = table.LookupRange(code_offset, nullptr, &prediction);
           if (index > 0) found_handler = true;
         } else {
           found_handler = true;
@@ -945,8 +1014,8 @@
           continue;
         }
         Handle<SharedFunctionInfo> info(
-            summaries[i].AsJavaScript().function()->shared());
-        if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue;
+            summary.AsJavaScript().function()->shared(), isolate_);
+        if (IsBlackboxed(info)) continue;
         FloodWithOneShot(info);
         return;
       }
@@ -954,7 +1023,6 @@
   }
 }
 
-
 void Debug::PrepareStep(StepAction step_action) {
   HandleScope scope(isolate_);
 
@@ -964,84 +1032,115 @@
   // 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.
-  StackFrame::Id frame_id = break_frame_id();
+  StackFrameId frame_id = break_frame_id();
   // If there is no JavaScript stack don't do anything.
-  if (frame_id == StackFrame::NO_ID) return;
+  if (frame_id == StackFrameId::NO_ID) return;
 
   feature_tracker()->Track(DebugFeatureTracker::kStepping);
 
   thread_local_.last_step_action_ = step_action;
-  UpdateHookOnFunctionCall();
 
   StackTraceFrameIterator frames_it(isolate_, frame_id);
-  StandardFrame* frame = frames_it.frame();
+  CommonFrame* frame = frames_it.frame();
 
-  // 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);
-    wasm_frame->wasm_instance()->debug_info()->PrepareStep(step_action);
-    return;
-  }
-
-  JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
-  DCHECK(js_frame->function()->IsJSFunction());
-
-  // Get the debug info (create it if it does not exist).
-  auto summary = FrameSummary::GetTop(frame).AsJavaScript();
-  Handle<JSFunction> function(summary.function());
-  Handle<SharedFunctionInfo> shared(function->shared());
-  if (!EnsureDebugInfo(shared)) return;
-  Handle<DebugInfo> debug_info(shared->GetDebugInfo());
-
-  BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame);
-
-  // Any step at a return is a step-out.
-  if (location.IsReturn()) step_action = StepOut;
-  // A step-next at a tail call is a step-out.
-  if (location.IsTailCall() && step_action == StepNext) step_action = StepOut;
-  // A step-next in blackboxed function is a step-out.
-  if (step_action == StepNext && IsBlackboxed(shared)) step_action = StepOut;
-
-  thread_local_.last_statement_position_ =
-      summary.abstract_code()->SourceStatementPosition(summary.code_offset());
+  BreakLocation location = BreakLocation::Invalid();
+  Handle<SharedFunctionInfo> shared;
   int current_frame_count = CurrentFrameCount();
-  thread_local_.last_frame_count_ = current_frame_count;
-  // No longer perform the current async step.
-  clear_suspended_generator();
+
+  if (frame->is_java_script()) {
+    JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
+    DCHECK(js_frame->function().IsJSFunction());
+
+    // Get the debug info (create it if it does not exist).
+    auto summary = FrameSummary::GetTop(frame).AsJavaScript();
+    Handle<JSFunction> function(summary.function());
+    shared = Handle<SharedFunctionInfo>(function->shared(), isolate_);
+    if (!EnsureBreakInfo(shared)) return;
+    PrepareFunctionForDebugExecution(shared);
+
+    Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
+
+    location = BreakLocation::FromFrame(debug_info, js_frame);
+
+    // Any step at a return is a step-out, and a step-out at a suspend behaves
+    // like a return.
+    if (location.IsReturn() ||
+        (location.IsSuspend() && step_action == StepOut)) {
+      // 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;
+    }
+
+    // We need to schedule DebugOnFunction call callback
+    UpdateHookOnFunctionCall();
+
+    // A step-next in blackboxed function is a step-out.
+    if (step_action == StepNext && IsBlackboxed(shared)) step_action = StepOut;
+
+    thread_local_.last_statement_position_ =
+        summary.abstract_code()->SourceStatementPosition(summary.code_offset());
+    thread_local_.last_frame_count_ = current_frame_count;
+    // No longer perform the current async step.
+    clear_suspended_generator();
+  } else if (frame->is_wasm()) {
+    // Handle stepping in Liftoff code.
+    WasmFrame* wasm_frame = WasmFrame::cast(frame);
+    wasm::WasmCodeRefScope code_ref_scope;
+    wasm::WasmCode* code = wasm_frame->wasm_code();
+    if (code->is_liftoff()) {
+      wasm_frame->native_module()->GetDebugInfo()->PrepareStep(isolate_,
+                                                               frame_id);
+    }
+    // In case the wasm code returns, prepare the next frame (if JS) to break.
+    step_action = StepOut;
+    UpdateHookOnFunctionCall();
+  }
 
   switch (step_action) {
     case StepNone:
       UNREACHABLE();
-      break;
     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;
+      if (!shared.is_null() && !location.IsReturnOrSuspend() &&
+          !IsBlackboxed(shared)) {
+        // At not return position we flood return positions with one shots and
+        // will repeat StepOut automatically at next break.
+        thread_local_.target_frame_count_ = current_frame_count;
+        thread_local_.fast_forward_to_return_ = true;
+        FloodWithOneShot(shared, true);
+        return;
+      }
       // 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()) {
-        // TODO(clemensh): Implement stepping out from JS to WASM.
-        if (frames_it.frame()->is_wasm()) continue;
+        if (frames_it.frame()->is_wasm()) {
+          in_current_frame = false;
+          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());
         }
         HandleScope scope(isolate_);
-        List<Handle<SharedFunctionInfo>> infos;
+        std::vector<Handle<SharedFunctionInfo>> infos;
         frame->GetFunctions(&infos);
-        for (; !infos.is_empty(); current_frame_count--) {
-          Handle<SharedFunctionInfo> info = infos.RemoveLast();
+        for (; !infos.empty(); current_frame_count--) {
+          Handle<SharedFunctionInfo> info = infos.back();
+          infos.pop_back();
           if (in_current_frame) {
             // We want to skip out, so skip the current frame.
             in_current_frame = false;
             continue;
           }
-          if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue;
+          if (IsBlackboxed(info)) continue;
           FloodWithOneShot(info);
           thread_local_.target_frame_count_ = current_frame_count;
           return;
@@ -1051,9 +1150,9 @@
     }
     case StepNext:
       thread_local_.target_frame_count_ = current_frame_count;
-    // Fall through.
+      V8_FALLTHROUGH;
     case StepIn:
-      // TODO(clemensh): Implement stepping from JS into WASM.
+      // TODO(clemensb): Implement stepping from JS into wasm.
       FloodWithOneShot(shared);
       break;
   }
@@ -1061,44 +1160,28 @@
 
 // Simple function for returning the source positions for active break points.
 Handle<Object> Debug::GetSourceBreakLocations(
-    Handle<SharedFunctionInfo> shared,
-    BreakPositionAlignment position_alignment) {
-  Isolate* isolate = shared->GetIsolate();
-  if (!shared->HasDebugInfo()) {
+    Isolate* isolate, Handle<SharedFunctionInfo> shared) {
+  if (!shared->HasBreakInfo()) {
     return isolate->factory()->undefined_value();
   }
-  Handle<DebugInfo> debug_info(shared->GetDebugInfo());
-  if (debug_info->GetBreakPointCount() == 0) {
+
+  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate);
+  if (debug_info->GetBreakPointCount(isolate) == 0) {
     return isolate->factory()->undefined_value();
   }
-  Handle<FixedArray> locations =
-      isolate->factory()->NewFixedArray(debug_info->GetBreakPointCount());
+  Handle<FixedArray> locations = isolate->factory()->NewFixedArray(
+      debug_info->GetBreakPointCount(isolate));
   int count = 0;
-  for (int i = 0; i < debug_info->break_points()->length(); ++i) {
-    if (!debug_info->break_points()->get(i)->IsUndefined(isolate)) {
-      BreakPointInfo* break_point_info =
-          BreakPointInfo::cast(debug_info->break_points()->get(i));
-      int break_points = break_point_info->GetBreakPointCount();
+  for (int i = 0; i < debug_info->break_points().length(); ++i) {
+    if (!debug_info->break_points().get(i).IsUndefined(isolate)) {
+      BreakPointInfo break_point_info =
+          BreakPointInfo::cast(debug_info->break_points().get(i));
+      int break_points = break_point_info.GetBreakPointCount(isolate);
       if (break_points == 0) continue;
-      Smi* position = NULL;
-      if (position_alignment == STATEMENT_ALIGNED) {
-        if (debug_info->HasDebugCode()) {
-          CodeBreakIterator it(debug_info);
-          it.SkipToPosition(break_point_info->source_position(),
-                            BREAK_POSITION_ALIGNED);
-          position = Smi::FromInt(it.statement_position());
-        } else {
-          DCHECK(debug_info->HasDebugBytecodeArray());
-          BytecodeArrayBreakIterator it(debug_info);
-          it.SkipToPosition(break_point_info->source_position(),
-                            BREAK_POSITION_ALIGNED);
-          position = Smi::FromInt(it.statement_position());
-        }
-      } else {
-        DCHECK_EQ(BREAK_POSITION_ALIGNED, position_alignment);
-        position = Smi::FromInt(break_point_info->source_position());
+      for (int j = 0; j < break_points; ++j) {
+        locations->set(count++,
+                       Smi::FromInt(break_point_info.source_position()));
       }
-      for (int j = 0; j < break_points; ++j) locations->set(count++, position);
     }
   }
   return locations;
@@ -1110,8 +1193,11 @@
 
   thread_local_.last_step_action_ = StepNone;
   thread_local_.last_statement_position_ = kNoSourcePosition;
+  thread_local_.ignore_step_into_function_ = Smi::zero();
+  thread_local_.fast_forward_to_return_ = false;
   thread_local_.last_frame_count_ = -1;
   thread_local_.target_frame_count_ = -1;
+  thread_local_.break_on_next_function_call_ = false;
   UpdateHookOnFunctionCall();
 }
 
@@ -1123,7 +1209,7 @@
   // The current implementation just runs through all the breakpoints. When the
   // last break point for a function is removed that function is automatically
   // removed from the list.
-  for (DebugInfoListNode* node = debug_info_list_; node != NULL;
+  for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
        node = node->next()) {
     Handle<DebugInfo> debug_info = node->debug_info();
     ClearBreakPoints(debug_info);
@@ -1131,303 +1217,290 @@
   }
 }
 
+void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
+  // Deoptimize all code compiled from this shared function info including
+  // inlining.
+  isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock);
 
-bool MatchingCodeTargets(Code* target1, Code* target2) {
-  if (target1 == target2) return true;
-  if (target1->kind() != target2->kind()) return false;
-  return target1->is_handler() || target1->is_inline_cache_stub();
+  bool found_something = false;
+  Code::OptimizedCodeIterator iterator(isolate_);
+  do {
+    Code code = iterator.Next();
+    if (code.is_null()) break;
+    if (code.Inlines(*shared)) {
+      code.set_marked_for_deoptimization(true);
+      found_something = true;
+    }
+  } while (true);
+
+  if (found_something) {
+    // Only go through with the deoptimization if something was found.
+    Deoptimizer::DeoptimizeMarkedCode(isolate_);
+  }
 }
 
+void Debug::PrepareFunctionForDebugExecution(
+    Handle<SharedFunctionInfo> shared) {
+  // 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);
+  if (debug_info->flags() & DebugInfo::kPreparedForDebugExecution) return;
 
-// Count the number of calls before the current frame PC to find the
-// corresponding PC in the newly recompiled code.
-static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code,
-                                       Address old_pc) {
-  DCHECK_EQ(old_code->kind(), Code::FUNCTION);
-  DCHECK_EQ(new_code->kind(), Code::FUNCTION);
-  DCHECK(new_code->has_debug_break_slots());
-  static const int mask = RelocInfo::kCodeTargetMask;
-
-  // Find the target of the current call.
-  Code* target = NULL;
-  intptr_t delta = 0;
-  for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
-    RelocInfo* rinfo = it.rinfo();
-    Address current_pc = rinfo->pc();
-    // The frame PC is behind the call instruction by the call instruction size.
-    if (current_pc > old_pc) break;
-    delta = old_pc - current_pc;
-    target = Code::GetCodeFromTargetAddress(rinfo->target_address());
+  // Make a copy of the bytecode array if available.
+  Handle<HeapObject> maybe_original_bytecode_array =
+      isolate_->factory()->undefined_value();
+  if (shared->HasBytecodeArray()) {
+    Handle<BytecodeArray> original_bytecode_array =
+        handle(shared->GetBytecodeArray(), isolate_);
+    Handle<BytecodeArray> debug_bytecode_array =
+        isolate_->factory()->CopyBytecodeArray(original_bytecode_array);
+    debug_info->set_debug_bytecode_array(*debug_bytecode_array);
+    shared->SetDebugBytecodeArray(*debug_bytecode_array);
+    maybe_original_bytecode_array = original_bytecode_array;
   }
+  debug_info->set_original_bytecode_array(*maybe_original_bytecode_array);
 
-  // Count the number of calls to the same target before the current call.
-  int index = 0;
-  for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
-    RelocInfo* rinfo = it.rinfo();
-    Address current_pc = rinfo->pc();
-    if (current_pc > old_pc) break;
-    Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
-    if (MatchingCodeTargets(target, current)) index++;
+  if (debug_info->CanBreakAtEntry()) {
+    // Deopt everything in case the function is inlined anywhere.
+    Deoptimizer::DeoptimizeAll(isolate_);
+    InstallDebugBreakTrampoline();
+  } else {
+    DeoptimizeFunction(shared);
+    // Update PCs on the stack to point to recompiled code.
+    RedirectActiveFunctions redirect_visitor(
+        *shared, RedirectActiveFunctions::Mode::kUseDebugBytecode);
+    redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
+    isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
   }
-
-  DCHECK(index > 0);
-
-  // Repeat the count on the new code to find corresponding call.
-  for (RelocIterator it(new_code, mask); !it.done(); it.next()) {
-    RelocInfo* rinfo = it.rinfo();
-    Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
-    if (MatchingCodeTargets(target, current)) index--;
-    if (index == 0) return rinfo->pc() + delta;
-  }
-
-  UNREACHABLE();
-  return NULL;
+  debug_info->set_flags(debug_info->flags() |
+                        DebugInfo::kPreparedForDebugExecution);
 }
 
-
-class RedirectActiveFunctions : public ThreadVisitor {
- public:
-  explicit RedirectActiveFunctions(SharedFunctionInfo* shared)
-      : shared_(shared) {
-    DCHECK(shared->HasDebugCode());
+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_);
+  // 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;
+  for (DebugInfoListNode* current = debug_info_list_; current != nullptr;
+       current = current->next()) {
+    if (current->debug_info()->CanBreakAtEntry()) {
+      needs_to_use_trampoline = true;
+      if (current->debug_info()->shared().IsApiFunction()) {
+        needs_to_clear_ic = true;
+        break;
+      }
+    }
   }
 
-  void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
-    for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
-      JavaScriptFrame* frame = it.frame();
-      JSFunction* function = frame->function();
-      if (frame->is_optimized()) continue;
-      if (!function->Inlines(shared_)) continue;
+  if (!needs_to_use_trampoline) return;
 
-      if (frame->is_interpreted()) {
-        InterpretedFrame* interpreted_frame =
-            reinterpret_cast<InterpretedFrame*>(frame);
-        BytecodeArray* debug_copy =
-            shared_->GetDebugInfo()->DebugBytecodeArray();
-        interpreted_frame->PatchBytecodeArray(debug_copy);
+  Handle<Code> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline);
+  std::vector<Handle<JSFunction>> needs_compile;
+  using AccessorPairWithContext =
+      std::pair<Handle<AccessorPair>, Handle<NativeContext>>;
+  std::vector<AccessorPairWithContext> needs_instantiate;
+  {
+    // Deduplicate {needs_instantiate} by recording all collected AccessorPairs.
+    std::set<AccessorPair> recorded;
+    HeapObjectIterator iterator(isolate_->heap());
+    for (HeapObject obj = iterator.Next(); !obj.is_null();
+         obj = iterator.Next()) {
+      if (needs_to_clear_ic && obj.IsFeedbackVector()) {
+        FeedbackVector::cast(obj).ClearSlots(isolate_);
         continue;
-      }
-
-      Code* frame_code = frame->LookupCode();
-      DCHECK(frame_code->kind() == Code::FUNCTION);
-      if (frame_code->has_debug_break_slots()) continue;
-
-      Code* new_code = function->shared()->code();
-      Address old_pc = frame->pc();
-      Address new_pc = ComputeNewPcForRedirect(new_code, frame_code, old_pc);
-
-      if (FLAG_trace_deopt) {
-        PrintF("Replacing pc for debugging: %08" V8PRIxPTR " => %08" V8PRIxPTR
-               "\n",
-               reinterpret_cast<intptr_t>(old_pc),
-               reinterpret_cast<intptr_t>(new_pc));
-      }
-
-      if (FLAG_enable_embedded_constant_pool) {
-        // Update constant pool pointer for new code.
-        frame->set_constant_pool(new_code->constant_pool());
-      }
-
-      // Patch the return address to return into the code with
-      // debug break slots.
-      frame->set_pc(new_pc);
-    }
-  }
-
- private:
-  SharedFunctionInfo* shared_;
-  DisallowHeapAllocation no_gc_;
-};
-
-
-bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
-  DCHECK(shared->is_compiled());
-
-  if (isolate_->concurrent_recompilation_enabled()) {
-    isolate_->optimizing_compile_dispatcher()->Flush(
-        OptimizingCompileDispatcher::BlockingBehavior::kBlock);
-  }
-
-  List<Handle<JSFunction> > functions;
-
-  // Flush all optimized code maps. Note that the below heap iteration does not
-  // cover this, because the given function might have been inlined into code
-  // for which no JSFunction exists.
-  {
-    SharedFunctionInfo::GlobalIterator iterator(isolate_);
-    while (SharedFunctionInfo* shared = iterator.Next()) {
-      shared->ClearCodeFromOptimizedCodeMap();
-    }
-  }
-
-  // The native context also has a list of OSR'd optimized code. Clear it.
-  isolate_->ClearOSROptimizedCode();
-
-  // Make sure we abort incremental marking.
-  isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
-                                      GarbageCollectionReason::kDebugger);
-
-  DCHECK(shared->is_compiled());
-  bool baseline_exists = shared->HasBaselineCode();
-
-  {
-    // TODO(yangguo): with bytecode, we still walk the heap to find all
-    // optimized code for the function to deoptimize. We can probably be
-    // smarter here and avoid the heap walk.
-    HeapIterator iterator(isolate_->heap());
-    HeapObject* obj;
-
-    while ((obj = iterator.next())) {
-      if (obj->IsJSFunction()) {
-        JSFunction* function = JSFunction::cast(obj);
-        if (!function->Inlines(*shared)) continue;
-        if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
-          Deoptimizer::DeoptimizeFunction(function);
+      } else if (obj.IsJSFunction()) {
+        JSFunction fun = JSFunction::cast(obj);
+        SharedFunctionInfo shared = fun.shared();
+        if (!shared.HasDebugInfo()) continue;
+        if (!shared.GetDebugInfo().CanBreakAtEntry()) continue;
+        if (!fun.is_compiled()) {
+          needs_compile.push_back(handle(fun, isolate_));
+        } else {
+          fun.set_code(*trampoline);
         }
-        if (baseline_exists && function->shared() == *shared) {
-          functions.Add(handle(function));
+      } else if (obj.IsJSObject()) {
+        JSObject object = JSObject::cast(obj);
+        DescriptorArray descriptors =
+            object.map().instance_descriptors(kRelaxedLoad);
+
+        for (InternalIndex i : object.map().IterateOwnDescriptors()) {
+          if (descriptors.GetDetails(i).kind() == PropertyKind::kAccessor) {
+            Object value = descriptors.GetStrongValue(i);
+            if (!value.IsAccessorPair()) continue;
+
+            AccessorPair accessor_pair = AccessorPair::cast(value);
+            if (!accessor_pair.getter().IsFunctionTemplateInfo() &&
+                !accessor_pair.setter().IsFunctionTemplateInfo()) {
+              continue;
+            }
+            if (recorded.find(accessor_pair) != recorded.end()) continue;
+
+            needs_instantiate.emplace_back(handle(accessor_pair, isolate_),
+                                           object.GetCreationContext());
+            recorded.insert(accessor_pair);
+          }
         }
       }
     }
   }
 
-  // We do not need to replace code to debug bytecode.
-  DCHECK(baseline_exists || functions.is_empty());
-
-  // We do not need to recompile to debug bytecode.
-  if (baseline_exists && !shared->code()->has_debug_break_slots()) {
-    if (!Compiler::CompileDebugCode(shared)) return false;
+  // Forcibly instantiate all lazy accessor pairs to make sure that they
+  // properly hit the debug break trampoline.
+  for (AccessorPairWithContext tuple : needs_instantiate) {
+    Handle<AccessorPair> accessor_pair = tuple.first;
+    Handle<NativeContext> native_context = tuple.second;
+    if (accessor_pair->getter().IsFunctionTemplateInfo()) {
+      Handle<JSFunction> fun =
+          ApiNatives::InstantiateFunction(
+              isolate_, native_context,
+              handle(FunctionTemplateInfo::cast(accessor_pair->getter()),
+                     isolate_))
+              .ToHandleChecked();
+      accessor_pair->set_getter(*fun);
+    }
+    if (accessor_pair->setter().IsFunctionTemplateInfo()) {
+      Handle<JSFunction> fun =
+          ApiNatives::InstantiateFunction(
+              isolate_, native_context,
+              handle(FunctionTemplateInfo::cast(accessor_pair->setter()),
+                     isolate_))
+              .ToHandleChecked();
+      accessor_pair->set_setter(*fun);
+    }
   }
 
-  for (Handle<JSFunction> const function : functions) {
-    function->ReplaceCode(shared->code());
-    JSFunction::EnsureLiterals(function);
+  // By overwriting the function code with DebugBreakTrampoline, which tailcalls
+  // to shared code, we bypass CompileLazy. Perform CompileLazy here instead.
+  for (Handle<JSFunction> fun : needs_compile) {
+    IsCompiledScope is_compiled_scope;
+    Compiler::Compile(fun, Compiler::CLEAR_EXCEPTION, &is_compiled_scope);
+    DCHECK(is_compiled_scope.is_compiled());
+    fun->set_code(*trampoline);
   }
-
-  // Update PCs on the stack to point to recompiled code.
-  RedirectActiveFunctions redirect_visitor(*shared);
-  redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
-  isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
-
-  return true;
 }
 
 namespace {
 template <typename Iterator>
 void GetBreakablePositions(Iterator* it, int start_position, int end_position,
-                           BreakPositionAlignment alignment,
-                           std::set<int>* positions) {
-  it->SkipToPosition(start_position, alignment);
-  while (!it->Done() && it->position() < end_position &&
-         it->position() >= start_position) {
-    positions->insert(alignment == STATEMENT_ALIGNED ? it->statement_position()
-                                                     : it->position());
+                           std::vector<BreakLocation>* locations) {
+  while (!it->Done()) {
+    if (it->position() >= start_position && it->position() < end_position) {
+      locations->push_back(it->GetBreakLocation());
+    }
     it->Next();
   }
 }
 
 void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position,
-                            int end_position, BreakPositionAlignment alignment,
-                            std::set<int>* positions) {
-  if (debug_info->HasDebugCode()) {
-    CodeBreakIterator it(debug_info);
-    GetBreakablePositions(&it, start_position, end_position, alignment,
-                          positions);
-  } else {
-    DCHECK(debug_info->HasDebugBytecodeArray());
-    BytecodeArrayBreakIterator it(debug_info);
-    GetBreakablePositions(&it, start_position, end_position, alignment,
-                          positions);
-  }
+                            int end_position,
+                            std::vector<BreakLocation>* locations) {
+  DCHECK(debug_info->HasInstrumentedBytecodeArray());
+  BreakIterator it(debug_info);
+  GetBreakablePositions(&it, start_position, end_position, locations);
 }
 }  // namespace
 
 bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
-                                   int end_position, std::set<int>* positions) {
+                                   int end_position, bool restrict_to_function,
+                                   std::vector<BreakLocation>* locations) {
+  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);
+    if (!EnsureBreakInfo(shared)) return false;
+    PrepareFunctionForDebugExecution(shared);
+
+    Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
+    FindBreakablePositions(debug_info, start_position, end_position, locations);
+    return true;
+  }
+
   while (true) {
     HandleScope scope(isolate_);
-    List<Handle<SharedFunctionInfo>> candidates;
-    SharedFunctionInfo::ScriptIterator iterator(script);
-    for (SharedFunctionInfo* info = iterator.Next(); info != nullptr;
+    std::vector<Handle<SharedFunctionInfo>> candidates;
+    std::vector<IsCompiledScope> compiled_scopes;
+    SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
+    for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
          info = iterator.Next()) {
-      if (info->end_position() < start_position ||
-          info->start_position() >= end_position) {
+      if (info.EndPosition() < start_position ||
+          info.StartPosition() >= end_position) {
         continue;
       }
-      if (!info->IsSubjectToDebugging()) continue;
-      if (!info->HasDebugCode() && !info->allows_lazy_compilation()) continue;
-      candidates.Add(i::handle(info));
+      if (!info.IsSubjectToDebugging()) continue;
+      if (!info.is_compiled() && !info.allows_lazy_compilation()) continue;
+      candidates.push_back(i::handle(info, isolate_));
     }
 
     bool was_compiled = false;
-    for (int i = 0; i < candidates.length(); ++i) {
-      // Code that cannot be compiled lazily are internal and not debuggable.
-      DCHECK(candidates[i]->allows_lazy_compilation());
-      if (!candidates[i]->HasDebugCode()) {
-        if (!Compiler::CompileDebugCode(candidates[i])) {
+    for (const auto& candidate : candidates) {
+      IsCompiledScope is_compiled_scope(candidate->is_compiled_scope(isolate_));
+      if (!is_compiled_scope.is_compiled()) {
+        // Code that cannot be compiled lazily are internal and not debuggable.
+        DCHECK(candidate->allows_lazy_compilation());
+        if (!Compiler::Compile(candidate, Compiler::CLEAR_EXCEPTION,
+                               &is_compiled_scope)) {
           return false;
         } else {
           was_compiled = true;
         }
       }
-      if (!EnsureDebugInfo(candidates[i])) return false;
+      DCHECK(is_compiled_scope.is_compiled());
+      compiled_scopes.push_back(is_compiled_scope);
+      if (!EnsureBreakInfo(candidate)) return false;
+      PrepareFunctionForDebugExecution(candidate);
     }
     if (was_compiled) continue;
 
-    for (int i = 0; i < candidates.length(); ++i) {
-      CHECK(candidates[i]->HasDebugInfo());
-      Handle<DebugInfo> debug_info(candidates[i]->GetDebugInfo());
+    for (const auto& candidate : candidates) {
+      CHECK(candidate->HasBreakInfo());
+      Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_);
       FindBreakablePositions(debug_info, start_position, end_position,
-                             BREAK_POSITION_ALIGNED, positions);
+                             locations);
     }
     return true;
   }
   UNREACHABLE();
-  return false;
-}
-
-void Debug::RecordGenerator(Handle<JSGeneratorObject> generator_object) {
-  if (last_step_action() <= StepOut) return;
-
-  if (last_step_action() == StepNext) {
-    // Only consider this generator a step-next target if not stepping in.
-    if (thread_local_.target_frame_count_ < CurrentFrameCount()) return;
-  }
-
-  DCHECK(!has_suspended_generator());
-  thread_local_.suspended_generator_ = *generator_object;
-  ClearStepping();
 }
 
 class SharedFunctionInfoFinder {
  public:
   explicit SharedFunctionInfoFinder(int target_position)
-      : current_candidate_(NULL),
-        current_candidate_closure_(NULL),
-        current_start_position_(kNoSourcePosition),
+      : current_start_position_(kNoSourcePosition),
         target_position_(target_position) {}
 
-  void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) {
-    if (!shared->IsSubjectToDebugging()) return;
-    int start_position = shared->function_token_position();
+  void NewCandidate(SharedFunctionInfo shared,
+                    JSFunction closure = JSFunction()) {
+    if (!shared.IsSubjectToDebugging()) return;
+    int start_position = shared.function_token_position();
     if (start_position == kNoSourcePosition) {
-      start_position = shared->start_position();
+      start_position = shared.StartPosition();
     }
 
     if (start_position > target_position_) return;
-    if (target_position_ > shared->end_position()) return;
+    if (target_position_ > shared.EndPosition()) return;
 
-    if (current_candidate_ != NULL) {
+    if (!current_candidate_.is_null()) {
       if (current_start_position_ == start_position &&
-          shared->end_position() == current_candidate_->end_position()) {
+          shared.EndPosition() == current_candidate_.EndPosition()) {
         // If we already have a matching closure, do not throw it away.
-        if (current_candidate_closure_ != NULL && closure == NULL) return;
+        if (!current_candidate_closure_.is_null() && closure.is_null()) return;
         // 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.
-        if (!current_candidate_->is_toplevel() && shared->is_toplevel()) return;
+        if (!current_candidate_.is_toplevel() && shared.is_toplevel()) return;
       } else if (start_position < current_start_position_ ||
-                 current_candidate_->end_position() < shared->end_position()) {
+                 current_candidate_.EndPosition() < shared.EndPosition()) {
         return;
       }
     }
@@ -1437,13 +1510,13 @@
     current_candidate_closure_ = closure;
   }
 
-  SharedFunctionInfo* Result() { return current_candidate_; }
+  SharedFunctionInfo Result() { return current_candidate_; }
 
-  JSFunction* ResultClosure() { return current_candidate_closure_; }
+  JSFunction ResultClosure() { return current_candidate_closure_; }
 
  private:
-  SharedFunctionInfo* current_candidate_;
-  JSFunction* current_candidate_closure_;
+  SharedFunctionInfo current_candidate_;
+  JSFunction current_candidate_closure_;
   int current_start_position_;
   int target_position_;
   DisallowHeapAllocation no_gc_;
@@ -1464,26 +1537,28 @@
     // If there is no shared function info for this script at all, there is
     // no point in looking for it by walking the heap.
 
-    SharedFunctionInfo* shared;
+    SharedFunctionInfo shared;
+    IsCompiledScope is_compiled_scope;
     {
       SharedFunctionInfoFinder finder(position);
-      SharedFunctionInfo::ScriptIterator iterator(script);
-      for (SharedFunctionInfo* info = iterator.Next(); info != nullptr;
+      SharedFunctionInfo::ScriptIterator iterator(isolate_, *script);
+      for (SharedFunctionInfo info = iterator.Next(); !info.is_null();
            info = iterator.Next()) {
         finder.NewCandidate(info);
       }
       shared = finder.Result();
-      if (shared == NULL) break;
-      // We found it if it's already compiled and has debug code.
-      if (shared->HasDebugCode()) {
-        Handle<SharedFunctionInfo> shared_handle(shared);
+      if (shared.is_null()) break;
+      // We found it if it's already compiled.
+      is_compiled_scope = shared.is_compiled_scope(isolate_);
+      if (is_compiled_scope.is_compiled()) {
+        Handle<SharedFunctionInfo> shared_handle(shared, isolate_);
         // 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
-        // info while bypassing PrepareFunctionForBreakpoints.
+        // info while bypassing PrepareFunctionForDebugExecution.
         if (iteration > 1) {
           AllowHeapAllocation allow_before_return;
-          CreateDebugInfo(shared_handle);
+          CreateBreakInfo(shared_handle);
         }
         return shared_handle;
       }
@@ -1491,83 +1566,163 @@
     // If not, compile to reveal inner functions.
     HandleScope scope(isolate_);
     // Code that cannot be compiled lazily are internal and not debuggable.
-    DCHECK(shared->allows_lazy_compilation());
-    if (!Compiler::CompileDebugCode(handle(shared))) break;
+    DCHECK(shared.allows_lazy_compilation());
+    if (!Compiler::Compile(handle(shared, isolate_), Compiler::CLEAR_EXCEPTION,
+                           &is_compiled_scope)) {
+      break;
+    }
   }
   return isolate_->factory()->undefined_value();
 }
 
 
 // Ensures the debug information is present for shared.
-bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
-  // Return if we already have the debug info for shared.
-  if (shared->HasDebugInfo()) return true;
-  if (!shared->IsSubjectToDebugging()) return false;
-  if (!shared->is_compiled() && !Compiler::CompileDebugCode(shared)) {
+bool Debug::EnsureBreakInfo(Handle<SharedFunctionInfo> shared) {
+  // Return if we already have the break info for shared.
+  if (shared->HasBreakInfo()) return true;
+  if (!shared->IsSubjectToDebugging() && !CanBreakAtEntry(shared)) {
     return false;
   }
-
-  // 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.
-  CreateDebugInfo(shared);
-  CHECK(PrepareFunctionForBreakPoints(shared));
+  IsCompiledScope is_compiled_scope = shared->is_compiled_scope(isolate_);
+  if (!is_compiled_scope.is_compiled() &&
+      !Compiler::Compile(shared, Compiler::CLEAR_EXCEPTION,
+                         &is_compiled_scope)) {
+    return false;
+  }
+  CreateBreakInfo(shared);
   return true;
 }
 
+void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) {
+  HandleScope scope(isolate_);
+  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
 
-void Debug::CreateDebugInfo(Handle<SharedFunctionInfo> shared) {
-  // Create the debug info object.
-  Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
+  // Initialize with break information.
 
-  // Add debug info to the list.
-  DebugInfoListNode* node = new DebugInfoListNode(*debug_info);
-  node->set_next(debug_info_list_);
-  debug_info_list_ = node;
+  DCHECK(!debug_info->HasBreakInfo());
+
+  Factory* factory = isolate_->factory();
+  Handle<FixedArray> break_points(
+      factory->NewFixedArray(DebugInfo::kEstimatedNofBreakPointsInFunction));
+
+  int flags = debug_info->flags();
+  flags |= DebugInfo::kHasBreakInfo;
+  if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry;
+  debug_info->set_flags(flags);
+  debug_info->set_break_points(*break_points);
+
+  SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared);
 }
 
+Handle<DebugInfo> Debug::GetOrCreateDebugInfo(
+    Handle<SharedFunctionInfo> shared) {
+  if (shared->HasDebugInfo()) return handle(shared->GetDebugInfo(), isolate_);
 
-void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) {
+  // Create debug info and add it to the list.
+  Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
+  DebugInfoListNode* node = new DebugInfoListNode(isolate_, *debug_info);
+  node->set_next(debug_info_list_);
+  debug_info_list_ = node;
+
+  return debug_info;
+}
+
+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() {
+  ClearAllDebugInfos(
+      [=](Handle<DebugInfo> info) { info->ClearCoverageInfo(isolate_); });
+}
+
+void Debug::ClearAllDebuggerHints() {
+  ClearAllDebugInfos(
+      [=](Handle<DebugInfo> info) { info->set_debugger_hints(0); });
+}
+
+void Debug::FindDebugInfo(Handle<DebugInfo> debug_info,
+                          DebugInfoListNode** prev, DebugInfoListNode** curr) {
   HandleScope scope(isolate_);
-  Handle<SharedFunctionInfo> shared(debug_info->shared());
-
-  DCHECK_NOT_NULL(debug_info_list_);
-  // Run through the debug info objects to find this one and remove it.
-  DebugInfoListNode* prev = NULL;
-  DebugInfoListNode* current = debug_info_list_;
-  while (current != NULL) {
-    if (current->debug_info().is_identical_to(debug_info)) {
-      // Unlink from list. If prev is NULL we are looking at the first element.
-      if (prev == NULL) {
-        debug_info_list_ = current->next();
-      } else {
-        prev->set_next(current->next());
-      }
-      shared->set_debug_info(Smi::FromInt(debug_info->debugger_hints()));
-      delete current;
-      return;
-    }
-    // Move to next in list.
-    prev = current;
-    current = current->next();
+  *prev = nullptr;
+  *curr = debug_info_list_;
+  while (*curr != nullptr) {
+    if ((*curr)->debug_info().is_identical_to(debug_info)) return;
+    *prev = *curr;
+    *curr = (*curr)->next();
   }
 
   UNREACHABLE();
 }
 
+void Debug::ClearAllDebugInfos(const DebugInfoClearFunction& clear_function) {
+  DebugInfoListNode* prev = nullptr;
+  DebugInfoListNode* current = debug_info_list_;
+  while (current != nullptr) {
+    DebugInfoListNode* next = current->next();
+    Handle<DebugInfo> debug_info = current->debug_info();
+    clear_function(debug_info);
+    if (debug_info->IsEmpty()) {
+      FreeDebugInfoListNode(prev, current);
+      current = next;
+    } else {
+      prev = current;
+      current = next;
+    }
+  }
+}
+
+void Debug::RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info) {
+  debug_info->ClearBreakInfo(isolate_);
+  if (debug_info->IsEmpty()) {
+    DebugInfoListNode* prev;
+    DebugInfoListNode* node;
+    FindDebugInfo(debug_info, &prev, &node);
+    FreeDebugInfoListNode(prev, node);
+  }
+}
+
+void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev,
+                                  DebugInfoListNode* node) {
+  DCHECK(node->debug_info()->IsEmpty());
+
+  // Unlink from list. If prev is nullptr we are looking at the first element.
+  if (prev == nullptr) {
+    debug_info_list_ = node->next();
+  } else {
+    prev->set_next(node->next());
+  }
+
+  // Pack script back into the
+  // SFI::script_or_debug_info field.
+  Handle<DebugInfo> debug_info(node->debug_info());
+  debug_info->shared().set_script_or_debug_info(debug_info->script(),
+                                                kReleaseStore);
+
+  delete node;
+}
+
 bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
   HandleScope scope(isolate_);
 
   // Get the executing function in which the debug break occurred.
-  Handle<SharedFunctionInfo> shared(frame->function()->shared());
+  Handle<SharedFunctionInfo> shared(frame->function().shared(), isolate_);
 
   // With no debug info there are no break points, so we can't be at a return.
-  if (!shared->HasDebugInfo()) return false;
+  if (!shared->HasBreakInfo()) return false;
 
   DCHECK(!frame->is_optimized());
-  Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+  Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
   BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
-  return location.IsReturn() || location.IsTailCall();
+  return location.IsReturn();
 }
 
 void Debug::ScheduleFrameRestart(StackFrame* frame) {
@@ -1582,7 +1737,7 @@
 
   // Reset break frame ID to the frame below the restarted frame.
   StackTraceFrameIterator it(isolate_);
-  thread_local_.break_frame_id_ = StackFrame::NO_ID;
+  thread_local_.break_frame_id_ = StackFrameId::NO_ID;
   for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
     if (it.frame()->fp() > thread_local_.restart_fp_) {
       thread_local_.break_frame_id_ = it.frame()->id();
@@ -1591,82 +1746,29 @@
   }
 }
 
-
-bool Debug::IsDebugGlobal(JSGlobalObject* global) {
-  return is_loaded() && global == debug_context()->global_object();
-}
-
-
 Handle<FixedArray> Debug::GetLoadedScripts() {
-  isolate_->heap()->CollectAllGarbage(Heap::kFinalizeIncrementalMarkingMask,
+  isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
                                       GarbageCollectionReason::kDebugger);
   Factory* factory = isolate_->factory();
-  if (!factory->script_list()->IsWeakFixedArray()) {
+  if (!factory->script_list()->IsWeakArrayList()) {
     return factory->empty_fixed_array();
   }
-  Handle<WeakFixedArray> array =
-      Handle<WeakFixedArray>::cast(factory->script_list());
-  Handle<FixedArray> results = factory->NewFixedArray(array->Length());
+  Handle<WeakArrayList> array =
+      Handle<WeakArrayList>::cast(factory->script_list());
+  Handle<FixedArray> results = factory->NewFixedArray(array->length());
   int length = 0;
   {
     Script::Iterator iterator(isolate_);
-    Script* script;
-    while ((script = iterator.Next())) {
-      if (script->HasValidSource()) results->set(length++, script);
+    for (Script script = iterator.Next(); !script.is_null();
+         script = iterator.Next()) {
+      if (script.HasValidSource()) results->set(length++, script);
     }
   }
-  results->Shrink(length);
-  return results;
+  return FixedArray::ShrinkOrEmpty(isolate_, results, length);
 }
 
-
-MaybeHandle<Object> Debug::MakeExecutionState() {
-  // Create the execution state object.
-  Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()) };
-  return CallFunction("MakeExecutionState", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeBreakEvent(Handle<Object> break_points_hit) {
-  // Create the new break event object.
-  Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
-                            break_points_hit };
-  return CallFunction("MakeBreakEvent", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeExceptionEvent(Handle<Object> exception,
-                                              bool uncaught,
-                                              Handle<Object> promise) {
-  // Create the new exception event object.
-  Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
-                            exception,
-                            isolate_->factory()->ToBoolean(uncaught),
-                            promise };
-  return CallFunction("MakeExceptionEvent", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
-                                            v8::DebugEvent type) {
-  // Create the compile event object.
-  Handle<Object> script_wrapper = Script::GetWrapper(script);
-  Handle<Object> argv[] = { script_wrapper,
-                            isolate_->factory()->NewNumberFromInt(type) };
-  return CallFunction("MakeCompileEvent", arraysize(argv), argv);
-}
-
-MaybeHandle<Object> Debug::MakeAsyncTaskEvent(
-    v8::debug::PromiseDebugActionType type, int id) {
-  // Create the async task event object.
-  Handle<Object> argv[] = {Handle<Smi>(Smi::FromInt(type), isolate_),
-                           Handle<Smi>(Smi::FromInt(id), isolate_)};
-  return CallFunction("MakeAsyncTaskEvent", arraysize(argv), argv);
-}
-
-
-void Debug::OnThrow(Handle<Object> exception) {
-  if (in_debug_scope() || ignore_events()) return;
+base::Optional<Object> Debug::OnThrow(Handle<Object> exception) {
+  if (in_debug_scope() || ignore_events()) return {};
   // Temporarily clear any scheduled_exception to allow evaluating
   // JavaScript from the debug event handler.
   HandleScope scope(isolate_);
@@ -1675,11 +1777,22 @@
     scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
     isolate_->clear_scheduled_exception();
   }
-  OnException(exception, isolate_->GetPromiseOnStackOnThrow());
+  Handle<Object> maybe_promise = isolate_->GetPromiseOnStackOnThrow();
+  OnException(exception, maybe_promise,
+              maybe_promise->IsJSPromise() ? v8::debug::kPromiseRejection
+                                           : v8::debug::kException);
   if (!scheduled_exception.is_null()) {
     isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
   }
   PrepareStepOnThrow();
+  // If the OnException handler requested termination, then indicated this to
+  // our caller Isolate::Throw so it can deal with it immediatelly instead of
+  // throwing the original exception.
+  if (isolate_->stack_guard()->CheckTerminateExecution()) {
+    isolate_->stack_guard()->ClearTerminateExecution();
+    return isolate_->TerminateExecution();
+  }
+  return {};
 }
 
 void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
@@ -1690,55 +1803,33 @@
   if (!promise->IsJSObject() ||
       JSReceiver::GetDataProperty(Handle<JSObject>::cast(promise), key)
           ->IsUndefined(isolate_)) {
-    OnException(value, promise);
+    OnException(value, promise, v8::debug::kPromiseRejection);
   }
 }
 
-namespace {
-v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) {
-  Handle<Context> context = isolate->debug()->debugger_entry()->GetContext();
-  // Isolate::context() may have been NULL when "script collected" event
-  // occured.
-  if (context.is_null()) return v8::Local<v8::Context>();
-  Handle<Context> native_context(context->native_context());
-  return v8::Utils::ToLocal(native_context);
-}
-}  // anonymous namespace
-
 bool Debug::IsExceptionBlackboxed(bool uncaught) {
-  JavaScriptFrameIterator it(isolate_);
-  if (it.done()) return false;
   // Uncaught exception is blackboxed if all current frames are blackboxed,
   // caught exception if top frame is blackboxed.
-  bool is_top_frame_blackboxed = IsFrameBlackboxed(it.frame());
+  StackTraceFrameIterator it(isolate_);
+  while (!it.done() && it.is_wasm()) it.Advance();
+  bool is_top_frame_blackboxed =
+      !it.done() ? IsFrameBlackboxed(it.javascript_frame()) : true;
   if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
-  it.Advance();
-  while (!it.done()) {
-    if (!IsFrameBlackboxed(it.frame())) return false;
-    it.Advance();
-  }
-  return true;
+  return AllFramesOnStackAreBlackboxed();
 }
 
 bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
   HandleScope scope(isolate_);
-  if (!frame->HasInlinedFrames()) {
-    Handle<SharedFunctionInfo> shared(frame->function()->shared(), isolate_);
-    return IsBlackboxed(shared);
-  }
-  List<Handle<SharedFunctionInfo>> infos;
+  std::vector<Handle<SharedFunctionInfo>> infos;
   frame->GetFunctions(&infos);
-  for (const auto& info : infos)
+  for (const auto& info : infos) {
     if (!IsBlackboxed(info)) return false;
+  }
   return true;
 }
 
-void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
-  // We cannot generate debug events when JS execution is disallowed.
-  // TODO(5530): Reenable debug events within DisallowJSScopes once relevant
-  // code (MakeExceptionEvent and ProcessDebugEvent) have been moved to C++.
-  if (!AllowJavascriptExecution::IsAllowed(isolate_)) return;
-
+void Debug::OnException(Handle<Object> exception, Handle<Object> promise,
+                        v8::debug::ExceptionType exception_type) {
   Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
 
   // Don't notify listener of exceptions that are internal to a desugaring.
@@ -1749,9 +1840,16 @@
     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();
-    JSObject::SetProperty(jspromise, key, key, STRICT).Assert();
+    Object::SetProperty(isolate_, jspromise, key, key, StoreOrigin::kMaybeKeyed,
+                        Just(ShouldThrow::kThrowOnError))
+        .Assert();
     // Check whether the promise reject is considered an uncaught exception.
-    uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
+    if (jspromise->IsJSPromise()) {
+      uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(
+          Handle<JSPromise>::cast(jspromise));
+    } else {
+      uncaught = true;
+    }
   }
 
   if (!debug_delegate_) return;
@@ -1775,24 +1873,24 @@
     if (it.done()) return;  // Do not trigger an event with an empty stack.
   }
 
+  // Do not trigger exception event on stack overflow. We cannot perform
+  // anything useful for debugging in that situation.
+  StackLimitCheck stack_limit_check(isolate_);
+  if (stack_limit_check.JsHasOverflowed()) return;
+
   DebugScope debug_scope(this);
-  if (debug_scope.failed()) return;
   HandleScope scope(isolate_);
-  PostponeInterruptsScope postpone(isolate_);
   DisableBreak no_recursive_break(this);
 
-  // Create the execution state.
-  Handle<Object> exec_state;
-  // Bail out and don't call debugger if exception.
-  if (!MakeExecutionState().ToHandle(&exec_state)) return;
-
+  Handle<Context> native_context(isolate_->native_context());
   debug_delegate_->ExceptionThrown(
-      GetDebugEventContext(isolate_),
-      v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
-      v8::Utils::ToLocal(exception), v8::Utils::ToLocal(promise), uncaught);
+      v8::Utils::ToLocal(native_context), v8::Utils::ToLocal(exception),
+      v8::Utils::ToLocal(promise), uncaught, exception_type);
 }
 
-void Debug::OnDebugBreak(Handle<Object> break_points_hit) {
+void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit,
+                         StepAction lastStepAction) {
+  DCHECK(!break_points_hit.is_null());
   // The caller provided for DebugScope.
   AssertDebugContext();
   // Bail out if there is no listener for this event
@@ -1803,294 +1901,196 @@
 #endif  // DEBUG
 
   if (!debug_delegate_) return;
+  DCHECK(in_debug_scope());
   HandleScope scope(isolate_);
-  PostponeInterruptsScope no_interrupts(isolate_);
   DisableBreak no_recursive_break(this);
 
-  // Create the execution state.
-  Handle<Object> exec_state;
-  // Bail out and don't call debugger if exception.
-  if (!MakeExecutionState().ToHandle(&exec_state)) return;
-
-  debug_delegate_->BreakProgramRequested(
-      GetDebugEventContext(isolate_),
-      v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
-      v8::Utils::ToLocal(break_points_hit));
-}
-
-
-void Debug::OnCompileError(Handle<Script> script) {
-  ProcessCompileEvent(v8::CompileError, script);
-}
-
-
-// Handle debugger actions when a new script is compiled.
-void Debug::OnAfterCompile(Handle<Script> script) {
-  ProcessCompileEvent(v8::AfterCompile, script);
-}
-
-namespace {
-struct CollectedCallbackData {
-  Object** location;
-  int id;
-  Debug* debug;
-  Isolate* isolate;
-
-  CollectedCallbackData(Object** location, int id, Debug* debug,
-                        Isolate* isolate)
-      : location(location), id(id), debug(debug), isolate(isolate) {}
-};
-
-void SendAsyncTaskEventCancel(const v8::WeakCallbackInfo<void>& info) {
-  std::unique_ptr<CollectedCallbackData> data(
-      reinterpret_cast<CollectedCallbackData*>(info.GetParameter()));
-  if (!data->debug->is_active()) return;
-  HandleScope scope(data->isolate);
-  data->debug->OnAsyncTaskEvent(debug::kDebugPromiseCollected, data->id, 0);
-}
-
-void ResetPromiseHandle(const v8::WeakCallbackInfo<void>& info) {
-  CollectedCallbackData* data =
-      reinterpret_cast<CollectedCallbackData*>(info.GetParameter());
-  GlobalHandles::Destroy(data->location);
-  info.SetSecondPassCallback(&SendAsyncTaskEventCancel);
-}
-
-// In an async function, reuse the existing stack related to the outer
-// Promise. Otherwise, e.g. in a direct call to then, save a new stack.
-// Promises with multiple reactions with one or more of them being async
-// functions will not get a good stack trace, as async functions require
-// different stacks from direct Promise use, but we save and restore a
-// stack once for all reactions.
-//
-// If this isn't a case of async function, we return false, otherwise
-// we set the correct id and return true.
-//
-// TODO(littledan): Improve this case.
-int GetReferenceAsyncTaskId(Isolate* isolate, Handle<JSPromise> promise) {
-  Handle<Symbol> handled_by_symbol =
-      isolate->factory()->promise_handled_by_symbol();
-  Handle<Object> handled_by_promise =
-      JSObject::GetDataProperty(promise, handled_by_symbol);
-  if (!handled_by_promise->IsJSPromise()) {
-    return isolate->debug()->NextAsyncTaskId(promise);
+  if ((lastStepAction == StepAction::StepNext ||
+       lastStepAction == StepAction::StepIn) &&
+      ShouldBeSkipped()) {
+    PrepareStep(lastStepAction);
+    return;
   }
-  Handle<JSPromise> handled_by_promise_js =
-      Handle<JSPromise>::cast(handled_by_promise);
-  Handle<Symbol> async_stack_id_symbol =
-      isolate->factory()->promise_async_stack_id_symbol();
-  Handle<Object> async_task_id =
-      JSObject::GetDataProperty(handled_by_promise_js, async_stack_id_symbol);
-  if (!async_task_id->IsSmi()) {
-    return isolate->debug()->NextAsyncTaskId(promise);
-  }
-  return Handle<Smi>::cast(async_task_id)->value();
-}
-}  //  namespace
 
-void Debug::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
-                           Handle<Object> parent) {
-  if (!debug_delegate_) return;
-  int id = GetReferenceAsyncTaskId(isolate_, promise);
-  switch (type) {
-    case PromiseHookType::kInit:
-      OnAsyncTaskEvent(debug::kDebugPromiseCreated, id,
-                       parent->IsJSPromise()
-                           ? GetReferenceAsyncTaskId(
-                                 isolate_, Handle<JSPromise>::cast(parent))
-                           : 0);
-      return;
-    case PromiseHookType::kResolve:
-      // We can't use this hook because it's called before promise object will
-      // get resolved status.
-      return;
-    case PromiseHookType::kBefore:
-      OnAsyncTaskEvent(debug::kDebugWillHandle, id, 0);
-      return;
-    case PromiseHookType::kAfter:
-      OnAsyncTaskEvent(debug::kDebugDidHandle, id, 0);
-      return;
+  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) {
+    BreakPoint break_point = BreakPoint::cast(break_points_hit->get(i));
+    inspector_break_points_hit.push_back(break_point.id());
+    ++inspector_break_points_count;
   }
-}
 
-int Debug::NextAsyncTaskId(Handle<JSObject> promise) {
-  LookupIterator it(promise, isolate_->factory()->promise_async_id_symbol());
-  Maybe<bool> maybe = JSReceiver::HasProperty(&it);
-  if (maybe.ToChecked()) {
-    MaybeHandle<Object> result = Object::GetProperty(&it);
-    return Handle<Smi>::cast(result.ToHandleChecked())->value();
-  }
-  Handle<Smi> async_id =
-      handle(Smi::FromInt(++thread_local_.async_task_count_), isolate_);
-  Object::SetProperty(&it, async_id, SLOPPY, Object::MAY_BE_STORE_FROM_KEYED)
-      .ToChecked();
-  Handle<Object> global_handle = isolate_->global_handles()->Create(*promise);
-  // We send EnqueueRecurring async task event when promise is fulfilled or
-  // rejected, WillHandle and DidHandle for every scheduled microtask for this
-  // promise.
-  // We need to send a cancel event when no other microtasks can be
-  // started for this promise and all current microtasks are finished.
-  // Since we holding promise when at least one microtask is scheduled (inside
-  // PromiseReactionJobInfo), we can send cancel event in weak callback.
-  GlobalHandles::MakeWeak(
-      global_handle.location(),
-      new CollectedCallbackData(global_handle.location(), async_id->value(),
-                                this, isolate_),
-      &ResetPromiseHandle, v8::WeakCallbackType::kParameter);
-  return async_id->value();
+  Handle<Context> native_context(isolate_->native_context());
+  debug_delegate_->BreakProgramRequested(v8::Utils::ToLocal(native_context),
+                                         inspector_break_points_hit);
 }
 
 namespace {
 debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
   Script::PositionInfo info;
   Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
-  return debug::Location(info.line, info.column);
+  // 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));
 }
 }  // namespace
 
 bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
-  if (!debug_delegate_) return false;
-  if (!shared->computed_debug_is_blackboxed()) {
-    bool is_blackboxed = false;
-    if (shared->script()->IsScript()) {
+  if (!debug_delegate_) return !shared->IsSubjectToDebugging();
+  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
+  if (!debug_info->computed_debug_is_blackboxed()) {
+    bool is_blackboxed =
+        !shared->IsSubjectToDebugging() || !shared->script().IsScript();
+    if (!is_blackboxed) {
       SuppressDebug while_processing(this);
       HandleScope handle_scope(isolate_);
       PostponeInterruptsScope no_interrupts(isolate_);
       DisableBreak no_recursive_break(this);
-      Handle<Script> script(Script::cast(shared->script()));
-      if (script->type() == i::Script::TYPE_NORMAL) {
-        debug::Location start =
-            GetDebugLocation(script, shared->start_position());
-        debug::Location end = GetDebugLocation(script, shared->end_position());
-        is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
-            ToApiHandle<debug::Script>(script), start, end);
-      }
+      DCHECK(shared->script().IsScript());
+      Handle<Script> script(Script::cast(shared->script()), isolate_);
+      DCHECK(script->IsUserJavaScript());
+      debug::Location start = GetDebugLocation(script, shared->StartPosition());
+      debug::Location end = GetDebugLocation(script, shared->EndPosition());
+      is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
+          ToApiHandle<debug::Script>(script), start, end);
     }
-    shared->set_debug_is_blackboxed(is_blackboxed);
-    shared->set_computed_debug_is_blackboxed(true);
+    debug_info->set_debug_is_blackboxed(is_blackboxed);
+    debug_info->set_computed_debug_is_blackboxed(true);
   }
-  return shared->debug_is_blackboxed();
+  return debug_info->debug_is_blackboxed();
 }
 
-void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
-                             int parent_id) {
-  if (in_debug_scope() || ignore_events()) return;
-  if (!debug_delegate_) return;
+bool Debug::ShouldBeSkipped() {
   SuppressDebug while_processing(this);
-  DebugScope debug_scope(isolate_->debug());
-  if (debug_scope.failed()) return;
-  HandleScope scope(isolate_);
   PostponeInterruptsScope no_interrupts(isolate_);
   DisableBreak no_recursive_break(this);
-  debug_delegate_->PromiseEventOccurred(type, id, parent_id);
+
+  StackTraceFrameIterator iterator(isolate_);
+  CommonFrame* frame = iterator.frame();
+  FrameSummary summary = FrameSummary::GetTop(frame);
+  Handle<Object> script_obj = summary.script();
+  if (!script_obj->IsScript()) return false;
+
+  Handle<Script> script = Handle<Script>::cast(script_obj);
+  summary.EnsureSourcePositionsAvailable();
+  int source_position = summary.SourcePosition();
+  int line = Script::GetLineNumber(script, source_position);
+  int column = Script::GetColumnNumber(script, source_position);
+
+  return debug_delegate_->ShouldBeSkipped(ToApiHandle<debug::Script>(script),
+                                          line, column);
 }
 
-void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
+bool Debug::AllFramesOnStackAreBlackboxed() {
+  HandleScope scope(isolate_);
+  for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
+    if (!it.is_javascript()) continue;
+    if (!IsFrameBlackboxed(it.javascript_frame())) return false;
+  }
+  return true;
+}
+
+bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
+  // Allow break at entry for builtin functions.
+  if (shared->native() || shared->IsApiFunction()) {
+    // Functions that are subject to debugging can have regular breakpoints.
+    DCHECK(!shared->IsSubjectToDebugging());
+    return true;
+  }
+  return false;
+}
+
+bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
+                            bool preview, debug::LiveEditResult* result) {
+  DebugScope debug_scope(this);
+  feature_tracker()->Track(DebugFeatureTracker::kLiveEdit);
+  running_live_edit_ = true;
+  LiveEdit::PatchScript(isolate_, script, source, preview, result);
+  running_live_edit_ = false;
+  return result->status == debug::LiveEditResult::OK;
+}
+
+void Debug::OnCompileError(Handle<Script> script) {
+  ProcessCompileEvent(true, script);
+}
+
+void Debug::OnAfterCompile(Handle<Script> script) {
+  ProcessCompileEvent(false, script);
+}
+
+void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
+  // Ignore temporary scripts.
+  if (script->id() == Script::kTemporaryScriptId) return;
+  // TODO(kozyatinskiy): teach devtools to work with liveedit scripts better
+  // first and then remove this fast return.
+  if (running_live_edit_) return;
+  // Attach the correct debug id to the script. The debug id is used by the
+  // inspector to filter scripts by native context.
+  script->set_context_data(isolate_->native_context()->debug_context_id());
   if (ignore_events()) return;
-  if (script->type() != i::Script::TYPE_NORMAL &&
-      script->type() != i::Script::TYPE_WASM) {
+  if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) {
     return;
   }
   if (!debug_delegate_) return;
   SuppressDebug while_processing(this);
   DebugScope debug_scope(this);
-  if (debug_scope.failed()) return;
   HandleScope scope(isolate_);
-  PostponeInterruptsScope postpone(isolate_);
   DisableBreak no_recursive_break(this);
+  AllowJavascriptExecution allow_script(isolate_);
   debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
-                                  event != v8::AfterCompile);
-}
-
-
-Handle<Context> Debug::GetDebugContext() {
-  if (!is_loaded()) return Handle<Context>();
-  DebugScope debug_scope(this);
-  if (debug_scope.failed()) return Handle<Context>();
-  // The global handle may be destroyed soon after.  Return it reboxed.
-  return handle(*debug_context(), isolate_);
+                                  running_live_edit_, has_compile_error);
 }
 
 int Debug::CurrentFrameCount() {
   StackTraceFrameIterator it(isolate_);
-  if (break_frame_id() != StackFrame::NO_ID) {
+  if (break_frame_id() != StackFrameId::NO_ID) {
     // Skip to break frame.
     DCHECK(in_debug_scope());
     while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance();
   }
   int counter = 0;
-  while (!it.done()) {
-    if (it.frame()->is_optimized()) {
-      List<SharedFunctionInfo*> infos;
-      OptimizedFrame::cast(it.frame())->GetFunctions(&infos);
-      counter += infos.length();
-    } else {
-      counter++;
-    }
-    it.Advance();
+  for (; !it.done(); it.Advance()) {
+    counter += it.FrameFunctionCount();
   }
   return counter;
 }
 
-void Debug::SetDebugDelegate(debug::DebugDelegate* delegate,
-                             bool pass_ownership) {
-  RemoveDebugDelegate();
+void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
   debug_delegate_ = delegate;
-  owns_debug_delegate_ = pass_ownership;
   UpdateState();
 }
 
-void Debug::RemoveDebugDelegate() {
-  if (debug_delegate_ == nullptr) return;
-  if (owns_debug_delegate_) {
-    owns_debug_delegate_ = false;
-    delete debug_delegate_;
-  }
-  debug_delegate_ = nullptr;
-}
-
 void Debug::UpdateState() {
   bool is_active = debug_delegate_ != nullptr;
-  if (is_active || in_debug_scope()) {
+  if (is_active == is_active_) return;
+  if (is_active) {
     // Note that the debug context could have already been loaded to
     // bootstrap test cases.
-    isolate_->compilation_cache()->Disable();
-    is_active = Load();
-  } else if (is_loaded()) {
-    isolate_->compilation_cache()->Enable();
+    isolate_->compilation_cache()->DisableScriptAndEval();
+    is_active = true;
+    feature_tracker()->Track(DebugFeatureTracker::kActive);
+  } else {
+    isolate_->compilation_cache()->EnableScriptAndEval();
     Unload();
   }
   is_active_ = is_active;
-  isolate_->DebugStateUpdated();
+  isolate_->PromiseHookStateUpdated();
 }
 
 void Debug::UpdateHookOnFunctionCall() {
   STATIC_ASSERT(LastStepAction == StepIn);
-  hook_on_function_call_ = thread_local_.last_step_action_ == StepIn ||
-                           isolate_->needs_side_effect_check();
+  hook_on_function_call_ =
+      thread_local_.last_step_action_ == StepIn ||
+      isolate_->debug_execution_mode() == DebugInfo::kSideEffects ||
+      thread_local_.break_on_next_function_call_;
 }
 
-MaybeHandle<Object> Debug::Call(Handle<Object> fun, Handle<Object> data) {
-  DebugScope debug_scope(this);
-  if (debug_scope.failed()) return isolate_->factory()->undefined_value();
-
-  // Create the execution state.
-  Handle<Object> exec_state;
-  if (!MakeExecutionState().ToHandle(&exec_state)) {
-    return isolate_->factory()->undefined_value();
-  }
-
-  Handle<Object> argv[] = { exec_state, data };
-  return Execution::Call(
-      isolate_,
-      fun,
-      Handle<Object>(debug_context()->global_proxy(), isolate_),
-      arraysize(argv),
-      argv);
-}
-
-
-void Debug::HandleDebugBreak() {
+void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode) {
   // Initialize LiveEdit.
   LiveEdit::InitializeThreadLocal(this);
   // Ignore debug break during bootstrapping.
@@ -2105,42 +2105,30 @@
 
   { JavaScriptFrameIterator it(isolate_);
     DCHECK(!it.done());
-    Object* fun = it.frame()->function();
-    if (fun && fun->IsJSFunction()) {
+    Object fun = it.frame()->function();
+    if (fun.IsJSFunction()) {
       HandleScope scope(isolate_);
+      Handle<JSFunction> function(JSFunction::cast(fun), isolate_);
       // Don't stop in builtin and blackboxed functions.
-      Handle<SharedFunctionInfo> shared(JSFunction::cast(fun)->shared(),
-                                        isolate_);
-      if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) {
-        // Inspector uses pause on next statement for asynchronous breakpoints.
-        // When breakpoint is fired we try to break on first not blackboxed
-        // statement. To achieve this goal we need to deoptimize current
-        // function and don't clear requested DebugBreak even if it's blackboxed
-        // to be able to break on not blackboxed function call.
-        // TODO(yangguo): introduce break_on_function_entry since current
-        // implementation is slow.
-        Deoptimizer::DeoptimizeFunction(JSFunction::cast(fun));
-        return;
-      }
-      JSGlobalObject* global =
-          JSFunction::cast(fun)->context()->global_object();
-      // Don't stop in debugger functions.
-      if (IsDebugGlobal(global)) return;
+      Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
+      bool ignore_break = ignore_break_mode == kIgnoreIfTopFrameBlackboxed
+                              ? IsBlackboxed(shared)
+                              : AllFramesOnStackAreBlackboxed();
+      if (ignore_break) return;
       // Don't stop if the break location is muted.
       if (IsMutedAtCurrentLocation(it.frame())) return;
     }
   }
 
-  isolate_->stack_guard()->ClearDebugBreak();
+  StepAction lastStepAction = last_step_action();
 
   // Clear stepping to avoid duplicate breaks.
   ClearStepping();
 
   HandleScope scope(isolate_);
   DebugScope debug_scope(this);
-  if (debug_scope.failed()) return;
 
-  OnDebugBreak(isolate_->factory()->undefined_value());
+  OnDebugBreak(isolate_->factory()->empty_fixed_array(), lastStepAction);
 }
 
 #ifdef DEBUG
@@ -2149,8 +2137,9 @@
   HandleScope scope(isolate_);
   StackTraceFrameIterator iterator(isolate_);
   if (iterator.done()) return;
-  StandardFrame* frame = iterator.frame();
+  CommonFrame* frame = iterator.frame();
   FrameSummary summary = FrameSummary::GetTop(frame);
+  summary.EnsureSourcePositionsAvailable();
   int source_position = summary.SourcePosition();
   Handle<Object> script_obj = summary.script();
   PrintF("[debug] break in function '");
@@ -2158,21 +2147,21 @@
   PrintF("'.\n");
   if (script_obj->IsScript()) {
     Handle<Script> script = Handle<Script>::cast(script_obj);
-    Handle<String> source(String::cast(script->source()));
-    Script::InitLineEnds(script);
+    Handle<String> source(String::cast(script->source()), isolate_);
+    Script::InitLineEnds(isolate_, script);
     int line =
         Script::GetLineNumber(script, source_position) - script->line_offset();
     int column = Script::GetColumnNumber(script, source_position) -
                  (line == 0 ? script->column_offset() : 0);
-    Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
-    int line_start =
-        line == 0 ? 0 : Smi::cast(line_ends->get(line - 1))->value() + 1;
-    int line_end = Smi::cast(line_ends->get(line))->value();
+    Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()),
+                                 isolate_);
+    int line_start = line == 0 ? 0 : Smi::ToInt(line_ends->get(line - 1)) + 1;
+    int line_end = Smi::ToInt(line_ends->get(line));
     DisallowHeapAllocation no_gc;
-    String::FlatContent content = source->GetFlatContent();
+    String::FlatContent content = source->GetFlatContent(no_gc);
     if (content.IsOneByte()) {
       PrintF("[debug] %.*s\n", line_end - line_start,
-             content.ToOneByteVector().start() + line_start);
+             content.ToOneByteVector().begin() + line_start);
       PrintF("[debug] ");
       for (int i = 0; i < column; i++) PrintF(" ");
       PrintF("^\n");
@@ -2185,16 +2174,13 @@
 
 DebugScope::DebugScope(Debug* debug)
     : debug_(debug),
-      prev_(debug->debugger_entry()),
-      save_(debug_->isolate_),
-      no_termination_exceptons_(debug_->isolate_,
-                                StackGuard::TERMINATE_EXECUTION) {
+      prev_(reinterpret_cast<DebugScope*>(
+          base::Relaxed_Load(&debug->thread_local_.current_debug_scope_))),
+      no_interrupts_(debug_->isolate_) {
   // Link recursive debugger entry.
-  base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
-                        reinterpret_cast<base::AtomicWord>(this));
-
-  // Store the previous break id, frame id and return value.
-  break_id_ = debug_->break_id();
+  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
+                      reinterpret_cast<base::AtomicWord>(this));
+  // Store the previous frame id and return value.
   break_frame_id_ = debug_->break_frame_id();
 
   // Create the new break info. If there is no proper frames there is no break
@@ -2202,25 +2188,29 @@
   StackTraceFrameIterator it(isolate());
   bool has_frames = !it.done();
   debug_->thread_local_.break_frame_id_ =
-      has_frames ? it.frame()->id() : StackFrame::NO_ID;
-  debug_->SetNextBreakId();
+      has_frames ? it.frame()->id() : StackFrameId::NO_ID;
 
   debug_->UpdateState();
-  // Make sure that debugger is loaded and enter the debugger context.
-  // The previous context is kept in save_.
-  failed_ = !debug_->is_loaded();
-  if (!failed_) isolate()->set_context(*debug->debug_context());
 }
 
+void DebugScope::set_terminate_on_resume() { terminate_on_resume_ = true; }
 
 DebugScope::~DebugScope() {
+  // Terminate on resume must have been handled by retrieving it, if this is
+  // the outer scope.
+  if (terminate_on_resume_) {
+    if (!prev_) {
+      debug_->isolate_->stack_guard()->RequestTerminateExecution();
+    } else {
+      prev_->set_terminate_on_resume();
+    }
+  }
   // Leaving this debugger entry.
-  base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
-                        reinterpret_cast<base::AtomicWord>(prev_));
+  base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_,
+                      reinterpret_cast<base::AtomicWord>(prev_));
 
   // Restore to the previous break state.
   debug_->thread_local_.break_frame_id_ = break_frame_id_;
-  debug_->thread_local_.break_id_ = break_id_;
 
   debug_->UpdateState();
 }
@@ -2233,27 +2223,200 @@
   debug_->set_return_value(*return_value_);
 }
 
-bool Debug::PerformSideEffectCheck(Handle<JSFunction> function) {
-  DCHECK(isolate_->needs_side_effect_check());
-  DisallowJavascriptExecution no_js(isolate_);
-  if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) return false;
-  Deoptimizer::DeoptimizeFunction(*function);
-  if (!function->shared()->HasNoSideEffect()) {
-    if (FLAG_trace_side_effect_free_debug_evaluate) {
-      PrintF("[debug-evaluate] Function %s failed side effect check.\n",
-             function->shared()->DebugName()->ToCString().get());
+void Debug::UpdateDebugInfosForExecutionMode() {
+  // Walk all debug infos and update their execution mode if it is different
+  // from the isolate execution mode.
+  DebugInfoListNode* current = debug_info_list_;
+  while (current != nullptr) {
+    Handle<DebugInfo> debug_info = current->debug_info();
+    if (debug_info->HasInstrumentedBytecodeArray() &&
+        debug_info->DebugExecutionMode() != isolate_->debug_execution_mode()) {
+      DCHECK(debug_info->shared().HasBytecodeArray());
+      if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) {
+        ClearSideEffectChecks(debug_info);
+        ApplyBreakPoints(debug_info);
+      } else {
+        ClearBreakPoints(debug_info);
+        ApplySideEffectChecks(debug_info);
+      }
     }
-    side_effect_check_failed_ = true;
-    // Throw an uncatchable termination exception.
-    isolate_->TerminateExecution();
-    return false;
+    current = current->next();
   }
-  return true;
 }
 
-bool Debug::PerformSideEffectCheckForCallback(Address function) {
-  DCHECK(isolate_->needs_side_effect_check());
-  if (DebugEvaluate::CallbackHasNoSideEffect(function)) return true;
+void Debug::SetTerminateOnResume() {
+  DebugScope* scope = reinterpret_cast<DebugScope*>(
+      base::Acquire_Load(&thread_local_.current_debug_scope_));
+  CHECK_NOT_NULL(scope);
+  scope->set_terminate_on_resume();
+}
+
+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());
+  Handle<FixedArray> array(isolate_->native_context()->regexp_last_match_info(),
+                           isolate_);
+  regexp_match_info_ =
+      Handle<RegExpMatchInfo>::cast(isolate_->factory()->CopyFixedArray(array));
+
+  // Update debug infos to have correct execution mode.
+  UpdateDebugInfosForExecutionMode();
+}
+
+void Debug::StopSideEffectCheckMode() {
+  DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects);
+  if (side_effect_check_failed_) {
+    DCHECK(isolate_->has_pending_exception());
+    DCHECK_EQ(ReadOnlyRoots(isolate_).termination_exception(),
+              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();
+  isolate_->native_context()->set_regexp_last_match_info(*regexp_match_info_);
+  regexp_match_info_ = Handle<RegExpMatchInfo>::null();
+
+  // Update debug infos to have correct execution mode.
+  UpdateDebugInfosForExecutionMode();
+}
+
+void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) {
+  DCHECK(debug_info->HasInstrumentedBytecodeArray());
+  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
+                                       isolate_);
+  DebugEvaluate::ApplySideEffectChecks(debug_bytecode);
+  debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects);
+}
+
+void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
+  DCHECK(debug_info->HasInstrumentedBytecodeArray());
+  Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(),
+                                       isolate_);
+  Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray(), isolate_);
+  for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done();
+       it.Advance()) {
+    // Restore from original. This may copy only the scaling prefix, which is
+    // correct, since we patch scaling prefixes to debug breaks if exists.
+    debug_bytecode->set(it.current_offset(),
+                        original->get(it.current_offset()));
+  }
+}
+
+bool Debug::PerformSideEffectCheck(Handle<JSFunction> function,
+                                   Handle<Object> receiver) {
+  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
+  DisallowJavascriptExecution no_js(isolate_);
+  IsCompiledScope is_compiled_scope(
+      function->shared().is_compiled_scope(isolate_));
+  if (!function->is_compiled() &&
+      !Compiler::Compile(function, Compiler::KEEP_EXCEPTION,
+                         &is_compiled_scope)) {
+    return false;
+  }
+  DCHECK(is_compiled_scope.is_compiled());
+  Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
+  Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
+  DebugInfo::SideEffectState side_effect_state =
+      debug_info->GetSideEffectState(isolate_);
+  switch (side_effect_state) {
+    case DebugInfo::kHasSideEffects:
+      if (FLAG_trace_side_effect_free_debug_evaluate) {
+        PrintF("[debug-evaluate] Function %s failed side effect check.\n",
+               function->shared().DebugName().ToCString().get());
+      }
+      side_effect_check_failed_ = true;
+      // Throw an uncatchable termination exception.
+      isolate_->TerminateExecution();
+      return false;
+    case DebugInfo::kRequiresRuntimeChecks: {
+      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);
+      ApplySideEffectChecks(debug_info);
+      return true;
+    }
+    case DebugInfo::kHasNoSideEffect:
+      return true;
+    case DebugInfo::kNotComputed:
+      UNREACHABLE();
+      return false;
+  }
+  UNREACHABLE();
+  return false;
+}
+
+Handle<Object> Debug::return_value_handle() {
+  return handle(thread_local_.return_value_, isolate_);
+}
+
+bool Debug::PerformSideEffectCheckForCallback(
+    Handle<Object> callback_info, Handle<Object> receiver,
+    Debug::AccessorKind accessor_kind) {
+  DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo());
+  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
+  if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() &&
+      i::CallHandlerInfo::cast(*callback_info).NextCallHasNoSideEffect()) {
+    return true;
+  }
+  // TODO(7515): always pass a valid callback info object.
+  if (!callback_info.is_null()) {
+    if (callback_info->IsAccessorInfo()) {
+      // List of allowlisted internal accessors can be found in accessors.h.
+      AccessorInfo info = AccessorInfo::cast(*callback_info);
+      DCHECK_NE(kNotAccessor, accessor_kind);
+      switch (accessor_kind == kSetter ? info.setter_side_effect_type()
+                                       : info.getter_side_effect_type()) {
+        case SideEffectType::kHasNoSideEffect:
+          // We do not support setter accessors with no side effects, since
+          // calling set accessors go through a store bytecode. Store bytecodes
+          // are considered to cause side effects (to non-temporary objects).
+          DCHECK_NE(kSetter, accessor_kind);
+          return true;
+        case SideEffectType::kHasSideEffectToReceiver:
+          DCHECK(!receiver.is_null());
+          if (PerformSideEffectCheckForObject(receiver)) return true;
+          isolate_->OptionalRescheduleException(false);
+          return false;
+        case SideEffectType::kHasSideEffect:
+          break;
+      }
+      if (FLAG_trace_side_effect_free_debug_evaluate) {
+        PrintF("[debug-evaluate] API Callback '");
+        info.name().ShortPrint();
+        PrintF("' may cause side effect.\n");
+      }
+    } else if (callback_info->IsInterceptorInfo()) {
+      InterceptorInfo info = InterceptorInfo::cast(*callback_info);
+      if (info.has_no_side_effect()) return true;
+      if (FLAG_trace_side_effect_free_debug_evaluate) {
+        PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
+      }
+    } else if (callback_info->IsCallHandlerInfo()) {
+      CallHandlerInfo info = CallHandlerInfo::cast(*callback_info);
+      if (info.IsSideEffectFreeCallHandlerInfo()) return true;
+      if (FLAG_trace_side_effect_free_debug_evaluate) {
+        PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
+      }
+    }
+  }
   side_effect_check_failed_ = true;
   // Throw an uncatchable termination exception.
   isolate_->TerminateExecution();
@@ -2261,158 +2424,49 @@
   return false;
 }
 
-void LegacyDebugDelegate::PromiseEventOccurred(
-    v8::debug::PromiseDebugActionType type, int id, int parent_id) {
-  Handle<Object> event_data;
-  if (isolate_->debug()->MakeAsyncTaskEvent(type, id).ToHandle(&event_data)) {
-    ProcessDebugEvent(v8::AsyncTaskEvent, Handle<JSObject>::cast(event_data));
+bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
+  using interpreter::Bytecode;
+
+  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
+  SharedFunctionInfo shared = frame->function().shared();
+  BytecodeArray bytecode_array = shared.GetBytecodeArray();
+  int offset = frame->GetBytecodeOffset();
+  interpreter::BytecodeArrayAccessor bytecode_accessor(
+      handle(bytecode_array, isolate_), offset);
+
+  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_);
+  return PerformSideEffectCheckForObject(object);
 }
 
-void LegacyDebugDelegate::ScriptCompiled(v8::Local<v8::debug::Script> script,
-                                         bool is_compile_error) {
-  Handle<Object> event_data;
-  v8::DebugEvent event = is_compile_error ? v8::CompileError : v8::AfterCompile;
-  if (isolate_->debug()
-          ->MakeCompileEvent(v8::Utils::OpenHandle(*script), event)
-          .ToHandle(&event_data)) {
-    ProcessDebugEvent(event, Handle<JSObject>::cast(event_data));
+bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
+  DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
+
+  // 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;
   }
-}
 
-void LegacyDebugDelegate::BreakProgramRequested(
-    v8::Local<v8::Context> paused_context, v8::Local<v8::Object> exec_state,
-    v8::Local<v8::Value> break_points_hit) {
-  Handle<Object> event_data;
-  if (isolate_->debug()
-          ->MakeBreakEvent(v8::Utils::OpenHandle(*break_points_hit))
-          .ToHandle(&event_data)) {
-    ProcessDebugEvent(
-        v8::Break, Handle<JSObject>::cast(event_data),
-        Handle<JSObject>::cast(v8::Utils::OpenHandle(*exec_state)));
+  if (FLAG_trace_side_effect_free_debug_evaluate) {
+    PrintF("[debug-evaluate] failed runtime side effect check.\n");
   }
+  side_effect_check_failed_ = true;
+  // Throw an uncatchable termination exception.
+  isolate_->TerminateExecution();
+  return false;
 }
-
-void LegacyDebugDelegate::ExceptionThrown(v8::Local<v8::Context> paused_context,
-                                          v8::Local<v8::Object> exec_state,
-                                          v8::Local<v8::Value> exception,
-                                          v8::Local<v8::Value> promise,
-                                          bool is_uncaught) {
-  Handle<Object> event_data;
-  if (isolate_->debug()
-          ->MakeExceptionEvent(v8::Utils::OpenHandle(*exception), is_uncaught,
-                               v8::Utils::OpenHandle(*promise))
-          .ToHandle(&event_data)) {
-    ProcessDebugEvent(
-        v8::Exception, Handle<JSObject>::cast(event_data),
-        Handle<JSObject>::cast(v8::Utils::OpenHandle(*exec_state)));
-  }
-}
-
-void LegacyDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
-                                            Handle<JSObject> event_data) {
-  Handle<Object> exec_state;
-  if (isolate_->debug()->MakeExecutionState().ToHandle(&exec_state)) {
-    ProcessDebugEvent(event, event_data, Handle<JSObject>::cast(exec_state));
-  }
-}
-
-JavaScriptDebugDelegate::JavaScriptDebugDelegate(Isolate* isolate,
-                                                 Handle<JSFunction> listener,
-                                                 Handle<Object> data)
-    : LegacyDebugDelegate(isolate) {
-  GlobalHandles* global_handles = isolate->global_handles();
-  listener_ = Handle<JSFunction>::cast(global_handles->Create(*listener));
-  data_ = global_handles->Create(*data);
-}
-
-JavaScriptDebugDelegate::~JavaScriptDebugDelegate() {
-  GlobalHandles::Destroy(Handle<Object>::cast(listener_).location());
-  GlobalHandles::Destroy(data_.location());
-}
-
-void JavaScriptDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
-                                                Handle<JSObject> event_data,
-                                                Handle<JSObject> exec_state) {
-  Handle<Object> argv[] = {Handle<Object>(Smi::FromInt(event), isolate_),
-                           exec_state, event_data, data_};
-  Handle<JSReceiver> global = isolate_->global_proxy();
-  // Listener must not throw.
-  Execution::Call(isolate_, listener_, global, arraysize(argv), argv)
-      .ToHandleChecked();
-}
-
-NativeDebugDelegate::NativeDebugDelegate(Isolate* isolate,
-                                         v8::Debug::EventCallback callback,
-                                         Handle<Object> data)
-    : LegacyDebugDelegate(isolate), callback_(callback) {
-  data_ = isolate->global_handles()->Create(*data);
-}
-
-NativeDebugDelegate::~NativeDebugDelegate() {
-  GlobalHandles::Destroy(data_.location());
-}
-
-NativeDebugDelegate::EventDetails::EventDetails(DebugEvent event,
-                                                Handle<JSObject> exec_state,
-                                                Handle<JSObject> event_data,
-                                                Handle<Object> callback_data)
-    : event_(event),
-      exec_state_(exec_state),
-      event_data_(event_data),
-      callback_data_(callback_data) {}
-
-DebugEvent NativeDebugDelegate::EventDetails::GetEvent() const {
-  return event_;
-}
-
-v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetExecutionState()
-    const {
-  return v8::Utils::ToLocal(exec_state_);
-}
-
-v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetEventData() const {
-  return v8::Utils::ToLocal(event_data_);
-}
-
-v8::Local<v8::Context> NativeDebugDelegate::EventDetails::GetEventContext()
-    const {
-  return GetDebugEventContext(exec_state_->GetIsolate());
-}
-
-v8::Local<v8::Value> NativeDebugDelegate::EventDetails::GetCallbackData()
-    const {
-  return v8::Utils::ToLocal(callback_data_);
-}
-
-v8::Isolate* NativeDebugDelegate::EventDetails::GetIsolate() const {
-  return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate());
-}
-
-void NativeDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
-                                            Handle<JSObject> event_data,
-                                            Handle<JSObject> exec_state) {
-  EventDetails event_details(event, exec_state, event_data, data_);
-  Isolate* isolate = isolate_;
-  callback_(event_details);
-  CHECK(!isolate->has_scheduled_exception());
-}
-
-NoSideEffectScope::~NoSideEffectScope() {
-  if (isolate_->needs_side_effect_check() &&
-      isolate_->debug()->side_effect_check_failed_) {
-    DCHECK(isolate_->has_pending_exception());
-    DCHECK_EQ(isolate_->heap()->termination_exception(),
-              isolate_->pending_exception());
-    // Convert the termination exception into a regular exception.
-    isolate_->CancelTerminateExecution();
-    isolate_->Throw(*isolate_->factory()->NewEvalError(
-        MessageTemplate::kNoSideEffectDebugEvaluate));
-  }
-  isolate_->set_needs_side_effect_check(old_needs_side_effect_check_);
-  isolate_->debug()->UpdateHookOnFunctionCall();
-  isolate_->debug()->side_effect_check_failed_ = false;
-}
-
 }  // namespace internal
 }  // namespace v8
diff --git a/src/debug/debug.h b/src/debug/debug.h
index 43338d7..c95affc 100644
--- a/src/debug/debug.h
+++ b/src/debug/debug.h
@@ -5,32 +5,28 @@
 #ifndef V8_DEBUG_DEBUG_H_
 #define V8_DEBUG_DEBUG_H_
 
-#include "src/allocation.h"
-#include "src/assembler.h"
-#include "src/base/atomicops.h"
-#include "src/base/hashmap.h"
-#include "src/base/platform/platform.h"
+#include <memory>
+#include <vector>
+
+#include "src/codegen/source-position-table.h"
+#include "src/common/globals.h"
 #include "src/debug/debug-interface.h"
 #include "src/debug/interface-types.h"
-#include "src/execution.h"
-#include "src/factory.h"
-#include "src/flags.h"
-#include "src/frames.h"
-#include "src/globals.h"
-#include "src/runtime/runtime.h"
-#include "src/source-position-table.h"
-#include "src/string-stream.h"
-#include "src/v8threads.h"
-
-#include "include/v8-debug.h"
+#include "src/execution/interrupts-scope.h"
+#include "src/execution/isolate.h"
+#include "src/handles/handles.h"
+#include "src/objects/debug-objects.h"
 
 namespace v8 {
 namespace internal {
 
-
 // Forward declarations.
+class AbstractCode;
 class DebugScope;
-
+class InterpretedFrame;
+class JavaScriptFrame;
+class JSGeneratorObject;
+class StackFrame;
 
 // Step actions. NOTE: These values are in macros.py as well.
 enum StepAction : int8_t {
@@ -48,56 +44,72 @@
   BreakUncaughtException = 1
 };
 
-
-// The different types of breakpoint position alignments.
-// Must match Debug.BreakPositionAlignment in debug.js
-enum BreakPositionAlignment {
-  STATEMENT_ALIGNED = 0,
-  BREAK_POSITION_ALIGNED = 1
-};
-
 enum DebugBreakType {
   NOT_DEBUG_BREAK,
   DEBUGGER_STATEMENT,
   DEBUG_BREAK_SLOT,
   DEBUG_BREAK_SLOT_AT_CALL,
   DEBUG_BREAK_SLOT_AT_RETURN,
-  DEBUG_BREAK_SLOT_AT_TAIL_CALL,
+  DEBUG_BREAK_SLOT_AT_SUSPEND,
+  DEBUG_BREAK_AT_ENTRY,
+};
+
+enum IgnoreBreakMode {
+  kIgnoreIfAllFramesBlackboxed,
+  kIgnoreIfTopFrameBlackboxed
 };
 
 class BreakLocation {
  public:
+  static BreakLocation Invalid() { return BreakLocation(-1, NOT_DEBUG_BREAK); }
   static BreakLocation FromFrame(Handle<DebugInfo> debug_info,
                                  JavaScriptFrame* frame);
 
   static void AllAtCurrentStatement(Handle<DebugInfo> debug_info,
                                     JavaScriptFrame* frame,
-                                    List<BreakLocation>* result_out);
+                                    std::vector<BreakLocation>* result_out);
 
+  inline bool IsSuspend() const { return type_ == DEBUG_BREAK_SLOT_AT_SUSPEND; }
   inline bool IsReturn() const { return type_ == DEBUG_BREAK_SLOT_AT_RETURN; }
-  inline bool IsCall() const { return type_ == DEBUG_BREAK_SLOT_AT_CALL; }
-  inline bool IsTailCall() const {
-    return type_ == DEBUG_BREAK_SLOT_AT_TAIL_CALL;
+  inline bool IsReturnOrSuspend() const {
+    return type_ >= DEBUG_BREAK_SLOT_AT_RETURN;
   }
+  inline bool IsCall() const { return type_ == DEBUG_BREAK_SLOT_AT_CALL; }
   inline bool IsDebugBreakSlot() const { return type_ >= DEBUG_BREAK_SLOT; }
   inline bool IsDebuggerStatement() const {
     return type_ == DEBUGGER_STATEMENT;
   }
+  inline bool IsDebugBreakAtEntry() const {
+    bool result = type_ == DEBUG_BREAK_AT_ENTRY;
+    return result;
+  }
 
-  bool HasBreakPoint(Handle<DebugInfo> debug_info) const;
+  bool HasBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info) const;
 
   inline int position() const { return position_; }
 
+  debug::BreakLocationType type() const;
+
+  JSGeneratorObject GetGeneratorObjectForSuspendedFrame(
+      JavaScriptFrame* frame) const;
+
  private:
   BreakLocation(Handle<AbstractCode> abstract_code, DebugBreakType type,
-                int code_offset, int position)
+                int code_offset, int position, int generator_obj_reg_index)
       : abstract_code_(abstract_code),
         code_offset_(code_offset),
         type_(type),
-        position_(position) {
+        position_(position),
+        generator_obj_reg_index_(generator_obj_reg_index) {
     DCHECK_NE(NOT_DEBUG_BREAK, type_);
   }
 
+  BreakLocation(int position, DebugBreakType type)
+      : code_offset_(0),
+        type_(type),
+        position_(position),
+        generator_obj_reg_index_(0) {}
+
   static int BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,
                                       Handle<AbstractCode> abstract_code,
                                       int offset);
@@ -109,113 +121,54 @@
   int code_offset_;
   DebugBreakType type_;
   int position_;
+  int generator_obj_reg_index_;
 
-  friend class CodeBreakIterator;
-  friend class BytecodeArrayBreakIterator;
+  friend class BreakIterator;
 };
 
-class BreakIterator {
+class V8_EXPORT_PRIVATE BreakIterator {
  public:
-  static std::unique_ptr<BreakIterator> GetIterator(
-      Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code);
+  explicit BreakIterator(Handle<DebugInfo> debug_info);
 
-  virtual ~BreakIterator() {}
+  BreakLocation GetBreakLocation();
+  bool Done() const { return source_position_iterator_.done(); }
+  void Next();
 
-  virtual BreakLocation GetBreakLocation() = 0;
-  virtual bool Done() const = 0;
-  virtual void Next() = 0;
-
+  void SkipToPosition(int position);
   void SkipTo(int count) {
     while (count-- > 0) Next();
   }
 
-  virtual int code_offset() = 0;
+  int code_offset() { return source_position_iterator_.code_offset(); }
   int break_index() const { return break_index_; }
   inline int position() const { return position_; }
   inline int statement_position() const { return statement_position_; }
 
-  virtual bool IsDebugBreak() = 0;
-  virtual void ClearDebugBreak() = 0;
-  virtual void SetDebugBreak() = 0;
+  void ClearDebugBreak();
+  void SetDebugBreak();
 
- protected:
-  explicit BreakIterator(Handle<DebugInfo> debug_info);
+ private:
+  int BreakIndexFromPosition(int position);
 
-  int BreakIndexFromPosition(int position, BreakPositionAlignment alignment);
+  Isolate* isolate();
 
-  Isolate* isolate() { return debug_info_->GetIsolate(); }
+  DebugBreakType GetDebugBreakType();
 
   Handle<DebugInfo> debug_info_;
   int break_index_;
   int position_;
   int statement_position_;
-
- private:
+  SourcePositionTableIterator source_position_iterator_;
   DisallowHeapAllocation no_gc_;
+
   DISALLOW_COPY_AND_ASSIGN(BreakIterator);
 };
 
-class CodeBreakIterator : public BreakIterator {
- public:
-  explicit CodeBreakIterator(Handle<DebugInfo> debug_info);
-  ~CodeBreakIterator() override {}
-
-  BreakLocation GetBreakLocation() override;
-  bool Done() const override { return reloc_iterator_.done(); }
-  void Next() override;
-
-  bool IsDebugBreak() override;
-  void ClearDebugBreak() override;
-  void SetDebugBreak() override;
-
-  void SkipToPosition(int position, BreakPositionAlignment alignment);
-
-  int code_offset() override {
-    return static_cast<int>(rinfo()->pc() -
-                            debug_info_->DebugCode()->instruction_start());
-  }
-
- private:
-  int GetModeMask();
-  DebugBreakType GetDebugBreakType();
-
-  RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); }
-  RelocInfo* rinfo() { return reloc_iterator_.rinfo(); }
-
-  RelocIterator reloc_iterator_;
-  SourcePositionTableIterator source_position_iterator_;
-  DISALLOW_COPY_AND_ASSIGN(CodeBreakIterator);
-};
-
-class BytecodeArrayBreakIterator : public BreakIterator {
- public:
-  explicit BytecodeArrayBreakIterator(Handle<DebugInfo> debug_info);
-  ~BytecodeArrayBreakIterator() override {}
-
-  BreakLocation GetBreakLocation() override;
-  bool Done() const override { return source_position_iterator_.done(); }
-  void Next() override;
-
-  bool IsDebugBreak() override;
-  void ClearDebugBreak() override;
-  void SetDebugBreak() override;
-
-  void SkipToPosition(int position, BreakPositionAlignment alignment);
-
-  int code_offset() override { return source_position_iterator_.code_offset(); }
-
- private:
-  DebugBreakType GetDebugBreakType();
-
-  SourcePositionTableIterator source_position_iterator_;
-  DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBreakIterator);
-};
-
 // Linked list holding debug info objects. The debug info objects are kept as
 // weak handles to avoid a debug info object to keep a function alive.
 class DebugInfoListNode {
  public:
-  explicit DebugInfoListNode(DebugInfo* debug_info);
+  DebugInfoListNode(Isolate* isolate, DebugInfo debug_info);
   ~DebugInfoListNode();
 
   DebugInfoListNode* next() { return next_; }
@@ -224,7 +177,7 @@
 
  private:
   // Global (weak) handle to the debug info object.
-  DebugInfo** debug_info_;
+  Address* debug_info_;
 
   // Next pointer for linked list.
   DebugInfoListNode* next_;
@@ -259,48 +212,49 @@
 // active breakpoints in them. This debug info is held in the heap root object
 // debug_info which is a FixedArray. Each entry in this list is of class
 // DebugInfo.
-class Debug {
+class V8_EXPORT_PRIVATE Debug {
  public:
   // Debug event triggers.
-  void OnDebugBreak(Handle<Object> break_points_hit);
+  void OnDebugBreak(Handle<FixedArray> break_points_hit, StepAction stepAction);
 
-  void OnThrow(Handle<Object> exception);
+  base::Optional<Object> OnThrow(Handle<Object> exception)
+      V8_WARN_UNUSED_RESULT;
   void OnPromiseReject(Handle<Object> promise, Handle<Object> value);
   void OnCompileError(Handle<Script> script);
   void OnAfterCompile(Handle<Script> script);
-  void OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
-                        int parent_id);
 
-  MUST_USE_RESULT MaybeHandle<Object> Call(Handle<Object> fun,
-                                           Handle<Object> data);
-  Handle<Context> GetDebugContext();
-  void HandleDebugBreak();
+  void HandleDebugBreak(IgnoreBreakMode ignore_break_mode);
 
-  // Internal logic
-  bool Load();
-  void Break(JavaScriptFrame* frame);
+  // The break target may not be the top-most frame, since we may be
+  // breaking before entering a function that cannot contain break points.
+  void Break(JavaScriptFrame* frame, Handle<JSFunction> break_target);
 
   // Scripts handling.
   Handle<FixedArray> GetLoadedScripts();
 
   // Break point handling.
-  bool SetBreakPoint(Handle<JSFunction> function,
-                     Handle<Object> break_point_object,
-                     int* source_position);
-  bool SetBreakPointForScript(Handle<Script> script,
-                              Handle<Object> break_point_object,
-                              int* source_position,
-                              BreakPositionAlignment alignment);
-  void ClearBreakPoint(Handle<Object> break_point_object);
+  bool SetBreakpoint(Handle<SharedFunctionInfo> shared,
+                     Handle<BreakPoint> break_point, int* source_position);
+  void ClearBreakPoint(Handle<BreakPoint> break_point);
   void ChangeBreakOnException(ExceptionBreakType type, bool enable);
   bool IsBreakOnException(ExceptionBreakType type);
 
-  // The parameter is either a BreakPointInfo object, or a FixedArray of
-  // BreakPointInfo objects.
-  // Returns an empty handle if no breakpoint is hit, or a FixedArray with all
-  // hit breakpoints.
-  MaybeHandle<FixedArray> GetHitBreakPointObjects(
-      Handle<Object> break_point_objects);
+  void SetTerminateOnResume();
+
+  bool SetBreakPointForScript(Handle<Script> script, Handle<String> condition,
+                              int* source_position, int* id);
+  bool SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
+                                Handle<String> condition, int* id);
+  void RemoveBreakpoint(int id);
+  void RemoveBreakpointForWasmScript(Handle<Script> script, int id);
+
+  void RecordWasmScriptWithBreakpoints(Handle<Script> script);
+
+  // Find breakpoints from the debug info and the break location and check
+  // whether they are hit. Return an empty handle if not, or a FixedArray with
+  // hit BreakPoint objects.
+  MaybeHandle<FixedArray> GetHitBreakPoints(Handle<DebugInfo> debug_info,
+                                            int position);
 
   // Stepping handling.
   void PrepareStep(StepAction step_action);
@@ -308,41 +262,39 @@
   void PrepareStepInSuspendedGenerator();
   void PrepareStepOnThrow();
   void ClearStepping();
-  void ClearStepOut();
 
-  bool PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared);
+  void SetBreakOnNextFunctionCall();
+  void ClearBreakOnNextFunctionCall();
+
+  void DeoptimizeFunction(Handle<SharedFunctionInfo> shared);
+  void PrepareFunctionForDebugExecution(Handle<SharedFunctionInfo> shared);
+  void InstallDebugBreakTrampoline();
   bool GetPossibleBreakpoints(Handle<Script> script, int start_position,
-                              int end_position, std::set<int>* positions);
-
-  void RecordGenerator(Handle<JSGeneratorObject> generator_object);
-
-  void RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
-                      Handle<Object> parent);
-
-  int NextAsyncTaskId(Handle<JSObject> promise);
+                              int end_position, bool restrict_to_function,
+                              std::vector<BreakLocation>* locations);
 
   bool IsBlackboxed(Handle<SharedFunctionInfo> shared);
+  bool ShouldBeSkipped();
 
-  void SetDebugDelegate(debug::DebugDelegate* delegate, bool pass_ownership);
+  bool CanBreakAtEntry(Handle<SharedFunctionInfo> shared);
+
+  void SetDebugDelegate(debug::DebugDelegate* delegate);
 
   // Returns whether the operation succeeded.
-  bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
-  void CreateDebugInfo(Handle<SharedFunctionInfo> shared);
-  static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
+  bool EnsureBreakInfo(Handle<SharedFunctionInfo> shared);
+  void CreateBreakInfo(Handle<SharedFunctionInfo> shared);
+  Handle<DebugInfo> GetOrCreateDebugInfo(Handle<SharedFunctionInfo> shared);
 
-  template <typename C>
-  bool CompileToRevealInnerFunctions(C* compilable);
+  void InstallCoverageInfo(Handle<SharedFunctionInfo> shared,
+                           Handle<CoverageInfo> coverage_info);
+  void RemoveAllCoverageInfos();
 
   // This function is used in FunctionNameUsing* tests.
   Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script,
                                                 int position);
 
   static Handle<Object> GetSourceBreakLocations(
-      Handle<SharedFunctionInfo> shared,
-      BreakPositionAlignment position_aligment);
-
-  // Check whether a global object is the debug global object.
-  bool IsDebugGlobal(JSGlobalObject* global);
+      Isolate* isolate, Handle<SharedFunctionInfo> shared);
 
   // Check whether this frame is just about to return.
   bool IsBreakAtReturn(JavaScriptFrame* frame);
@@ -350,54 +302,60 @@
   // Support for LiveEdit
   void ScheduleFrameRestart(StackFrame* frame);
 
-  bool IsFrameBlackboxed(JavaScriptFrame* frame);
+  bool AllFramesOnStackAreBlackboxed();
+
+  // Set new script source, throw an exception if error occurred. When preview
+  // is true: try to set source, throw exception if any without actual script
+  // change. stack_changed is true if after editing script on pause stack is
+  // changed and client should request stack trace again.
+  bool SetScriptSource(Handle<Script> script, Handle<String> source,
+                       bool preview, debug::LiveEditResult* result);
+
+  int GetFunctionDebuggingId(Handle<JSFunction> function);
 
   // Threading support.
   char* ArchiveDebug(char* to);
   char* RestoreDebug(char* from);
   static int ArchiveSpacePerThread();
   void FreeThreadResources() { }
-  void Iterate(ObjectVisitor* v);
+  void Iterate(RootVisitor* v);
+  void InitThread(const ExecutionAccess& lock) { ThreadInit(); }
 
-  bool CheckExecutionState(int id) {
-    return CheckExecutionState() && break_id() == id;
-  }
+  bool CheckExecutionState() { return is_active(); }
 
-  bool CheckExecutionState() {
-    return is_active() && !debug_context().is_null() && break_id() != 0;
-  }
+  void StartSideEffectCheckMode();
+  void StopSideEffectCheckMode();
 
-  bool PerformSideEffectCheck(Handle<JSFunction> function);
-  bool PerformSideEffectCheckForCallback(Address function);
+  void ApplySideEffectChecks(Handle<DebugInfo> debug_info);
+  void ClearSideEffectChecks(Handle<DebugInfo> debug_info);
+
+  bool PerformSideEffectCheck(Handle<JSFunction> function,
+                              Handle<Object> receiver);
+
+  enum AccessorKind { kNotAccessor, kGetter, kSetter };
+  bool PerformSideEffectCheckForCallback(Handle<Object> callback_info,
+                                         Handle<Object> receiver,
+                                         AccessorKind accessor_kind);
+  bool PerformSideEffectCheckAtBytecode(InterpretedFrame* frame);
+  bool PerformSideEffectCheckForObject(Handle<Object> object);
 
   // Flags and states.
-  DebugScope* debugger_entry() {
-    return reinterpret_cast<DebugScope*>(
-        base::NoBarrier_Load(&thread_local_.current_debug_scope_));
-  }
-  inline Handle<Context> debug_context() { return debug_context_; }
-
-  void set_live_edit_enabled(bool v) { live_edit_enabled_ = v; }
-  bool live_edit_enabled() const {
-    return FLAG_enable_liveedit && live_edit_enabled_;
-  }
-
   inline bool is_active() const { return is_active_; }
-  inline bool is_loaded() const { return !debug_context_.is_null(); }
   inline bool in_debug_scope() const {
-    return !!base::NoBarrier_Load(&thread_local_.current_debug_scope_);
+    return !!base::Relaxed_Load(&thread_local_.current_debug_scope_);
   }
+  inline bool needs_check_on_function_call() const {
+    return hook_on_function_call_;
+  }
+
   void set_break_points_active(bool v) { break_points_active_ = v; }
   bool break_points_active() const { return break_points_active_; }
 
-  StackFrame::Id break_frame_id() { return thread_local_.break_frame_id_; }
-  int break_id() { return thread_local_.break_id_; }
+  StackFrameId break_frame_id() { return thread_local_.break_frame_id_; }
 
-  Handle<Object> return_value_handle() {
-    return handle(thread_local_.return_value_, isolate_);
-  }
-  Object* return_value() { return thread_local_.return_value_; }
-  void set_return_value(Object* value) { thread_local_.return_value_ = value; }
+  Handle<Object> return_value_handle();
+  Object return_value() { return thread_local_.return_value_; }
+  void set_return_value(Object value) { thread_local_.return_value_ = value; }
 
   // Support for embedding into generated code.
   Address is_active_address() {
@@ -408,10 +366,6 @@
     return reinterpret_cast<Address>(&hook_on_function_call_);
   }
 
-  Address last_step_action_address() {
-    return reinterpret_cast<Address>(&thread_local_.last_step_action_);
-  }
-
   Address suspended_generator_address() {
     return reinterpret_cast<Address>(&thread_local_.suspended_generator_);
   }
@@ -419,62 +373,59 @@
   Address restart_fp_address() {
     return reinterpret_cast<Address>(&thread_local_.restart_fp_);
   }
+  bool will_restart() const {
+    return thread_local_.restart_fp_ != kNullAddress;
+  }
 
   StepAction last_step_action() { return thread_local_.last_step_action_; }
+  bool break_on_next_function_call() const {
+    return thread_local_.break_on_next_function_call_;
+  }
+
+  inline bool break_disabled() const { return break_disabled_; }
 
   DebugFeatureTracker* feature_tracker() { return &feature_tracker_; }
 
+  // For functions in which we cannot set a break point, use a canonical
+  // source position for break points.
+  static const int kBreakAtEntryPosition = 0;
+
+  void RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info);
+
  private:
   explicit Debug(Isolate* isolate);
-  ~Debug() { DCHECK_NULL(debug_delegate_); }
+  ~Debug();
 
+  void UpdateDebugInfosForExecutionMode();
   void UpdateState();
   void UpdateHookOnFunctionCall();
-  void RemoveDebugDelegate();
   void Unload();
-  void SetNextBreakId() {
-    thread_local_.break_id_ = ++thread_local_.break_count_;
-  }
 
   // Return the number of virtual frames below debugger entry.
   int CurrentFrameCount();
 
   inline bool ignore_events() const {
-    return is_suppressed_ || !is_active_ || isolate_->needs_side_effect_check();
+    return is_suppressed_ || !is_active_ ||
+           isolate_->debug_execution_mode() == DebugInfo::kSideEffects;
   }
-  inline bool break_disabled() const { return break_disabled_; }
 
   void clear_suspended_generator() {
-    thread_local_.suspended_generator_ = Smi::kZero;
+    thread_local_.suspended_generator_ = Smi::zero();
   }
 
   bool has_suspended_generator() const {
-    return thread_local_.suspended_generator_ != Smi::kZero;
+    return thread_local_.suspended_generator_ != Smi::zero();
   }
 
   bool IsExceptionBlackboxed(bool uncaught);
 
-  void OnException(Handle<Object> exception, Handle<Object> promise);
+  void OnException(Handle<Object> exception, Handle<Object> promise,
+                   v8::debug::ExceptionType exception_type);
 
-  // Constructors for debug event objects.
-  MUST_USE_RESULT MaybeHandle<Object> MakeExecutionState();
-  MUST_USE_RESULT MaybeHandle<Object> MakeBreakEvent(
-      Handle<Object> break_points_hit);
-  MUST_USE_RESULT MaybeHandle<Object> MakeExceptionEvent(
-      Handle<Object> exception,
-      bool uncaught,
-      Handle<Object> promise);
-  MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent(
-      Handle<Script> script, v8::DebugEvent type);
-  MUST_USE_RESULT MaybeHandle<Object> MakeAsyncTaskEvent(
-      v8::debug::PromiseDebugActionType type, int id);
-
-  void ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script);
-  void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data);
+  void ProcessCompileEvent(bool has_compile_error, Handle<Script> script);
 
   // Find the closest source position for a break point for a given position.
-  int FindBreakablePosition(Handle<DebugInfo> debug_info, int source_position,
-                            BreakPositionAlignment alignment);
+  int FindBreakablePosition(Handle<DebugInfo> debug_info, int source_position);
   // Instrument code to break at break points.
   void ApplyBreakPoints(Handle<DebugInfo> debug_info);
   // Clear code from instrumentation.
@@ -482,22 +433,23 @@
   // Clear all code from instrumentation.
   void ClearAllBreakPoints();
   // Instrument a function with one-shots.
-  void FloodWithOneShot(Handle<SharedFunctionInfo> function);
+  void FloodWithOneShot(Handle<SharedFunctionInfo> function,
+                        bool returns_only = false);
   // Clear all one-shot instrumentations, but restore break points.
   void ClearOneShot();
 
+  bool IsFrameBlackboxed(JavaScriptFrame* frame);
+
   void ActivateStepOut(StackFrame* frame);
-  void RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info);
   MaybeHandle<FixedArray> CheckBreakPoints(Handle<DebugInfo> debug_info,
                                            BreakLocation* location,
                                            bool* has_break_points = nullptr);
   bool IsMutedAtCurrentLocation(JavaScriptFrame* frame);
-  bool CheckBreakPoint(Handle<Object> break_point_object);
-  MaybeHandle<Object> CallFunction(const char* name, int argc,
-                                   Handle<Object> args[]);
+  // Check whether a BreakPoint object is hit. Evaluate condition depending
+  // on whether this is a regular break location or a break at function entry.
+  bool CheckBreakPoint(Handle<BreakPoint> break_point, bool is_break_at_entry);
 
   inline void AssertDebugContext() {
-    DCHECK(isolate_->context() == *debug_context());
     DCHECK(in_debug_scope());
   }
 
@@ -505,11 +457,17 @@
 
   void PrintBreakLocation();
 
-  // Global handles.
-  Handle<Context> debug_context_;
+  void ClearAllDebuggerHints();
+
+  // Wraps logic for clearing and maybe freeing all debug infos.
+  using DebugInfoClearFunction = std::function<void(Handle<DebugInfo>)>;
+  void ClearAllDebugInfos(const DebugInfoClearFunction& clear_function);
+
+  void FindDebugInfo(Handle<DebugInfo> debug_info, DebugInfoListNode** prev,
+                     DebugInfoListNode** curr);
+  void FreeDebugInfoListNode(DebugInfoListNode* prev, DebugInfoListNode* node);
 
   debug::DebugDelegate* debug_delegate_ = nullptr;
-  bool owns_debug_delegate_ = false;
 
   // Debugger is active, i.e. there is a debug event listener attached.
   bool is_active_;
@@ -518,8 +476,8 @@
   bool hook_on_function_call_;
   // Suppress debug events.
   bool is_suppressed_;
-  // LiveEdit is enabled.
-  bool live_edit_enabled_;
+  // Running liveedit.
+  bool running_live_edit_ = false;
   // Do not trigger debug break events.
   bool break_disabled_;
   // Do not break on break points.
@@ -534,6 +492,12 @@
   // List of active debug info objects.
   DebugInfoListNode* debug_info_list_;
 
+  // Used for side effect check to mark temporary objects.
+  class TemporaryObjectsTracker;
+  std::unique_ptr<TemporaryObjectsTracker> temporary_objects_;
+
+  Handle<RegExpMatchInfo> regexp_match_info_;
+
   // Used to collect histogram data on debugger feature usage.
   DebugFeatureTracker feature_tracker_;
 
@@ -543,18 +507,19 @@
     // Top debugger entry.
     base::AtomicWord current_debug_scope_;
 
-    // Counter for generating next break id.
-    int break_count_;
-
-    // Current break id.
-    int break_id_;
-
     // Frame id for the frame of the current break.
-    StackFrame::Id break_frame_id_;
+    StackFrameId break_frame_id_;
 
     // Step action for last step performed.
     StepAction last_step_action_;
 
+    // If set, next PrepareStepIn will ignore this function until stepped into
+    // another function, at which point this will be cleared.
+    Object ignore_step_into_function_;
+
+    // If set then we need to repeat StepOut action at return.
+    bool fast_forward_to_return_;
+
     // Source statement position from last step next action.
     int last_statement_position_;
 
@@ -565,20 +530,28 @@
     int target_frame_count_;
 
     // Value of the accumulator at the point of entering the debugger.
-    Object* return_value_;
+    Object return_value_;
 
     // The suspended generator object to track when stepping.
-    Object* suspended_generator_;
+    Object suspended_generator_;
 
     // The new frame pointer to drop to when restarting a frame.
     Address restart_fp_;
 
-    int async_task_count_;
+    // Last used inspector breakpoint id.
+    int last_breakpoint_id_;
+
+    // This flag is true when SetBreakOnNextFunctionCall is called and it forces
+    // debugger to break on next function call.
+    bool break_on_next_function_call_;
   };
 
   // Storage location for registers when handling debug break calls
   ThreadLocal thread_local_;
 
+  // This is a global handle, lazily initialized.
+  Handle<WeakArrayList> wasm_scripts_with_breakpoints_;
+
   Isolate* isolate_;
 
   friend class Isolate;
@@ -586,118 +559,31 @@
   friend class DisableBreak;
   friend class LiveEdit;
   friend class SuppressDebug;
-  friend class NoSideEffectScope;
-  friend class LegacyDebugDelegate;
 
   friend Handle<FixedArray> GetDebuggedFunctions();  // In test-debug.cc
-  friend void CheckDebuggerUnloaded(bool check_functions);  // In test-debug.cc
+  friend void CheckDebuggerUnloaded();               // In test-debug.cc
 
   DISALLOW_COPY_AND_ASSIGN(Debug);
 };
 
-class LegacyDebugDelegate : public v8::debug::DebugDelegate {
- public:
-  explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {}
-  void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
-                            int parent_id) override;
-  void ScriptCompiled(v8::Local<v8::debug::Script> script,
-                      bool has_compile_error) override;
-  void BreakProgramRequested(v8::Local<v8::Context> paused_context,
-                             v8::Local<v8::Object> exec_state,
-                             v8::Local<v8::Value> break_points_hit) override;
-  void ExceptionThrown(v8::Local<v8::Context> paused_context,
-                       v8::Local<v8::Object> exec_state,
-                       v8::Local<v8::Value> exception,
-                       v8::Local<v8::Value> promise, bool is_uncaught) override;
-  bool IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
-                            const v8::debug::Location& start,
-                            const v8::debug::Location& end) override {
-    return false;
-  }
-
- protected:
-  Isolate* isolate_;
-
- private:
-  void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data);
-  virtual void ProcessDebugEvent(v8::DebugEvent event,
-                                 Handle<JSObject> event_data,
-                                 Handle<JSObject> exec_state) = 0;
-};
-
-class JavaScriptDebugDelegate : public LegacyDebugDelegate {
- public:
-  JavaScriptDebugDelegate(Isolate* isolate, Handle<JSFunction> listener,
-                          Handle<Object> data);
-  virtual ~JavaScriptDebugDelegate();
-
- private:
-  void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data,
-                         Handle<JSObject> exec_state) override;
-
-  Handle<JSFunction> listener_;
-  Handle<Object> data_;
-};
-
-class NativeDebugDelegate : public LegacyDebugDelegate {
- public:
-  NativeDebugDelegate(Isolate* isolate, v8::Debug::EventCallback callback,
-                      Handle<Object> data);
-  virtual ~NativeDebugDelegate();
-
- private:
-  // Details of the debug event delivered to the debug event listener.
-  class EventDetails : public v8::Debug::EventDetails {
-   public:
-    EventDetails(DebugEvent event, Handle<JSObject> exec_state,
-                 Handle<JSObject> event_data, Handle<Object> callback_data);
-    virtual DebugEvent GetEvent() const;
-    virtual v8::Local<v8::Object> GetExecutionState() const;
-    virtual v8::Local<v8::Object> GetEventData() const;
-    virtual v8::Local<v8::Context> GetEventContext() const;
-    virtual v8::Local<v8::Value> GetCallbackData() const;
-    virtual v8::Debug::ClientData* GetClientData() const { return nullptr; }
-    virtual v8::Isolate* GetIsolate() const;
-
-   private:
-    DebugEvent event_;              // Debug event causing the break.
-    Handle<JSObject> exec_state_;   // Current execution state.
-    Handle<JSObject> event_data_;   // Data associated with the event.
-    Handle<Object> callback_data_;  // User data passed with the callback
-                                    // when it was registered.
-  };
-
-  void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data,
-                         Handle<JSObject> exec_state) override;
-
-  v8::Debug::EventCallback callback_;
-  Handle<Object> data_;
-};
-
 // This scope is used to load and enter the debug context and create a new
 // break state.  Leaving the scope will restore the previous state.
-// On failure to load, FailedToEnter returns true.
-class DebugScope BASE_EMBEDDED {
+class DebugScope {
  public:
   explicit DebugScope(Debug* debug);
   ~DebugScope();
 
-  // Check whether loading was successful.
-  inline bool failed() { return failed_; }
-
-  // Get the active context from before entering the debugger.
-  inline Handle<Context> GetContext() { return save_.context(); }
+  void set_terminate_on_resume();
 
  private:
   Isolate* isolate() { return debug_->isolate_; }
 
   Debug* debug_;
   DebugScope* prev_;               // Previous scope if entered recursively.
-  StackFrame::Id break_frame_id_;  // Previous break frame id.
-  int break_id_;                   // Previous break id.
-  bool failed_;                    // Did the debug context fail to load?
-  SaveContext save_;               // Saves previous context.
-  PostponeInterruptsScope no_termination_exceptons_;
+  StackFrameId break_frame_id_;    // Previous break frame id.
+  PostponeInterruptsScope no_interrupts_;
+  // This is used as a boolean.
+  bool terminate_on_resume_ = false;
 };
 
 // This scope is used to handle return values in nested debug break points.
@@ -715,11 +601,11 @@
 };
 
 // Stack allocated class for disabling break.
-class DisableBreak BASE_EMBEDDED {
+class DisableBreak {
  public:
-  explicit DisableBreak(Debug* debug)
+  explicit DisableBreak(Debug* debug, bool disable = true)
       : debug_(debug), previous_break_disabled_(debug->break_disabled_) {
-    debug_->break_disabled_ = true;
+    debug_->break_disabled_ = disable;
   }
   ~DisableBreak() {
     debug_->break_disabled_ = previous_break_disabled_;
@@ -731,8 +617,7 @@
   DISALLOW_COPY_AND_ASSIGN(DisableBreak);
 };
 
-
-class SuppressDebug BASE_EMBEDDED {
+class SuppressDebug {
  public:
   explicit SuppressDebug(Debug* debug)
       : debug_(debug), old_state_(debug->is_suppressed_) {
@@ -746,24 +631,6 @@
   DISALLOW_COPY_AND_ASSIGN(SuppressDebug);
 };
 
-class NoSideEffectScope {
- public:
-  NoSideEffectScope(Isolate* isolate, bool disallow_side_effects)
-      : isolate_(isolate),
-        old_needs_side_effect_check_(isolate->needs_side_effect_check()) {
-    isolate->set_needs_side_effect_check(old_needs_side_effect_check_ ||
-                                         disallow_side_effects);
-    isolate->debug()->UpdateHookOnFunctionCall();
-    isolate->debug()->side_effect_check_failed_ = false;
-  }
-  ~NoSideEffectScope();
-
- private:
-  Isolate* isolate_;
-  bool old_needs_side_effect_check_;
-  DISALLOW_COPY_AND_ASSIGN(NoSideEffectScope);
-};
-
 // Code generator routines.
 class DebugCodegen : public AllStatic {
  public:
@@ -772,11 +639,6 @@
     IGNORE_RESULT_REGISTER
   };
 
-  static void GenerateDebugBreakStub(MacroAssembler* masm,
-                                     DebugBreakCallHelperMode mode);
-
-  static void GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode);
-
   // Builtin to drop frames to restart function.
   static void GenerateFrameDropperTrampoline(MacroAssembler* masm);
 
@@ -784,10 +646,8 @@
   // drop frames to restart function if necessary.
   static void GenerateHandleDebuggerStatement(MacroAssembler* masm);
 
-  static void PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                  Handle<Code> code);
-  static bool DebugBreakSlotIsPatched(Address pc);
-  static void ClearDebugBreakSlot(Isolate* isolate, Address pc);
+  // Builtin to trigger a debug break before entering the function.
+  static void GenerateDebugBreakTrampoline(MacroAssembler* masm);
 };
 
 
diff --git a/src/debug/debug.js b/src/debug/debug.js
deleted file mode 100644
index 6993274..0000000
--- a/src/debug/debug.js
+++ /dev/null
@@ -1,1030 +0,0 @@
-// Copyright 2012 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(function (global, utils) {
-"use strict";
-
-// ----------------------------------------------------------------------------
-// Imports
-
-var FrameMirror = global.FrameMirror;
-var GlobalArray = global.Array;
-var GlobalRegExp = global.RegExp;
-var IsNaN = global.isNaN;
-var MakeMirror = global.MakeMirror;
-var MathMin = global.Math.min;
-var Mirror = global.Mirror;
-var ValueMirror = global.ValueMirror;
-
-//----------------------------------------------------------------------------
-
-// Default number of frames to include in the response to backtrace request.
-var kDefaultBacktraceLength = 10;
-
-var Debug = {};
-
-// Regular expression to skip "crud" at the beginning of a source line which is
-// not really code. Currently the regular expression matches whitespace and
-// comments.
-var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
-
-// Debug events which can occour in the V8 JavaScript engine. These originate
-// from the API include file debug.h.
-Debug.DebugEvent = { Break: 1,
-                     Exception: 2,
-                     AfterCompile: 3,
-                     CompileError: 4,
-                     AsyncTaskEvent: 5 };
-
-// Types of exceptions that can be broken upon.
-Debug.ExceptionBreak = { Caught : 0,
-                         Uncaught: 1 };
-
-// The different types of steps.
-Debug.StepAction = { StepOut: 0,
-                     StepNext: 1,
-                     StepIn: 2 };
-
-// The different types of scripts matching enum ScriptType in objects.h.
-Debug.ScriptType = { Native: 0,
-                     Extension: 1,
-                     Normal: 2,
-                     Wasm: 3};
-
-// The different types of script compilations matching enum
-// Script::CompilationType in objects.h.
-Debug.ScriptCompilationType = { Host: 0,
-                                Eval: 1,
-                                JSON: 2 };
-
-// The different script break point types.
-Debug.ScriptBreakPointType = { ScriptId: 0,
-                               ScriptName: 1,
-                               ScriptRegExp: 2 };
-
-// The different types of breakpoint position alignments.
-// Must match BreakPositionAlignment in debug.h.
-Debug.BreakPositionAlignment = {
-  Statement: 0,
-  BreakPosition: 1
-};
-
-function ScriptTypeFlag(type) {
-  return (1 << type);
-}
-
-// Globals.
-var next_response_seq = 0;
-var next_break_point_number = 1;
-var break_points = [];
-var script_break_points = [];
-var debugger_flags = {
-  breakPointsActive: {
-    value: true,
-    getValue: function() { return this.value; },
-    setValue: function(value) {
-      this.value = !!value;
-      %SetBreakPointsActive(this.value);
-    }
-  },
-  breakOnCaughtException: {
-    getValue: function() { return Debug.isBreakOnException(); },
-    setValue: function(value) {
-      if (value) {
-        Debug.setBreakOnException();
-      } else {
-        Debug.clearBreakOnException();
-      }
-    }
-  },
-  breakOnUncaughtException: {
-    getValue: function() { return Debug.isBreakOnUncaughtException(); },
-    setValue: function(value) {
-      if (value) {
-        Debug.setBreakOnUncaughtException();
-      } else {
-        Debug.clearBreakOnUncaughtException();
-      }
-    }
-  },
-};
-
-
-// Create a new break point object and add it to the list of break points.
-function MakeBreakPoint(source_position, opt_script_break_point) {
-  var break_point = new BreakPoint(source_position, opt_script_break_point);
-  break_points.push(break_point);
-  return break_point;
-}
-
-
-// Object representing a break point.
-// NOTE: This object does not have a reference to the function having break
-// point as this would cause function not to be garbage collected when it is
-// not used any more. We do not want break points to keep functions alive.
-function BreakPoint(source_position, opt_script_break_point) {
-  this.source_position_ = source_position;
-  if (opt_script_break_point) {
-    this.script_break_point_ = opt_script_break_point;
-  } else {
-    this.number_ = next_break_point_number++;
-  }
-  this.active_ = true;
-  this.condition_ = null;
-}
-
-
-BreakPoint.prototype.number = function() {
-  return this.number_;
-};
-
-
-BreakPoint.prototype.func = function() {
-  return this.func_;
-};
-
-
-BreakPoint.prototype.source_position = function() {
-  return this.source_position_;
-};
-
-
-BreakPoint.prototype.active = function() {
-  if (this.script_break_point()) {
-    return this.script_break_point().active();
-  }
-  return this.active_;
-};
-
-
-BreakPoint.prototype.condition = function() {
-  if (this.script_break_point() && this.script_break_point().condition()) {
-    return this.script_break_point().condition();
-  }
-  return this.condition_;
-};
-
-
-BreakPoint.prototype.script_break_point = function() {
-  return this.script_break_point_;
-};
-
-
-BreakPoint.prototype.enable = function() {
-  this.active_ = true;
-};
-
-
-BreakPoint.prototype.disable = function() {
-  this.active_ = false;
-};
-
-
-BreakPoint.prototype.setCondition = function(condition) {
-  this.condition_ = condition;
-};
-
-
-BreakPoint.prototype.isTriggered = function(exec_state) {
-  // Break point not active - not triggered.
-  if (!this.active()) return false;
-
-  // Check for conditional break point.
-  if (this.condition()) {
-    // If break point has condition try to evaluate it in the top frame.
-    try {
-      var mirror = exec_state.frame(0).evaluate(this.condition());
-      // If no sensible mirror or non true value break point not triggered.
-      if (!(mirror instanceof ValueMirror) || !mirror.value_) {
-        return false;
-      }
-    } catch (e) {
-      // Exception evaluating condition counts as not triggered.
-      return false;
-    }
-  }
-
-  // Break point triggered.
-  return true;
-};
-
-
-// Function called from the runtime when a break point is hit. Returns true if
-// the break point is triggered and supposed to break execution.
-function IsBreakPointTriggered(break_id, break_point) {
-  return break_point.isTriggered(MakeExecutionState(break_id));
-}
-
-
-// Object representing a script break point. The script is referenced by its
-// script name or script id and the break point is represented as line and
-// column.
-function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
-                          opt_groupId, opt_position_alignment) {
-  this.type_ = type;
-  if (type == Debug.ScriptBreakPointType.ScriptId) {
-    this.script_id_ = script_id_or_name;
-  } else if (type == Debug.ScriptBreakPointType.ScriptName) {
-    this.script_name_ = script_id_or_name;
-  } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
-    this.script_regexp_object_ = new GlobalRegExp(script_id_or_name);
-  } else {
-    throw %make_error(kDebugger, "Unexpected breakpoint type " + type);
-  }
-  this.line_ = opt_line || 0;
-  this.column_ = opt_column;
-  this.groupId_ = opt_groupId;
-  this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
-      ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
-  this.active_ = true;
-  this.condition_ = null;
-  this.break_points_ = [];
-}
-
-
-ScriptBreakPoint.prototype.number = function() {
-  return this.number_;
-};
-
-
-ScriptBreakPoint.prototype.groupId = function() {
-  return this.groupId_;
-};
-
-
-ScriptBreakPoint.prototype.type = function() {
-  return this.type_;
-};
-
-
-ScriptBreakPoint.prototype.script_id = function() {
-  return this.script_id_;
-};
-
-
-ScriptBreakPoint.prototype.script_name = function() {
-  return this.script_name_;
-};
-
-
-ScriptBreakPoint.prototype.script_regexp_object = function() {
-  return this.script_regexp_object_;
-};
-
-
-ScriptBreakPoint.prototype.line = function() {
-  return this.line_;
-};
-
-
-ScriptBreakPoint.prototype.column = function() {
-  return this.column_;
-};
-
-
-ScriptBreakPoint.prototype.actual_locations = function() {
-  var locations = [];
-  for (var i = 0; i < this.break_points_.length; i++) {
-    locations.push(this.break_points_[i].actual_location);
-  }
-  return locations;
-};
-
-
-ScriptBreakPoint.prototype.update_positions = function(line, column) {
-  this.line_ = line;
-  this.column_ = column;
-};
-
-
-ScriptBreakPoint.prototype.active = function() {
-  return this.active_;
-};
-
-
-ScriptBreakPoint.prototype.condition = function() {
-  return this.condition_;
-};
-
-
-ScriptBreakPoint.prototype.enable = function() {
-  this.active_ = true;
-};
-
-
-ScriptBreakPoint.prototype.disable = function() {
-  this.active_ = false;
-};
-
-
-ScriptBreakPoint.prototype.setCondition = function(condition) {
-  this.condition_ = condition;
-};
-
-
-// Check whether a script matches this script break point. Currently this is
-// only based on script name.
-ScriptBreakPoint.prototype.matchesScript = function(script) {
-  if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
-    return this.script_id_ == script.id;
-  } else {
-    // We might want to account columns here as well.
-    if (!(script.line_offset <= this.line_  &&
-          this.line_ < script.line_offset + %ScriptLineCount(script))) {
-      return false;
-    }
-    if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
-      return this.script_name_ == script.nameOrSourceURL();
-    } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
-      return this.script_regexp_object_.test(script.nameOrSourceURL());
-    } else {
-      throw %make_error(kDebugger, "Unexpected breakpoint type " + this.type_);
-    }
-  }
-};
-
-
-// Set the script break point in a script.
-ScriptBreakPoint.prototype.set = function (script) {
-  var column = this.column();
-  var line = this.line();
-  // If the column is undefined the break is on the line. To help locate the
-  // first piece of breakable code on the line try to find the column on the
-  // line which contains some source.
-  if (IS_UNDEFINED(column)) {
-    var source_line = %ScriptSourceLine(script, line || script.line_offset);
-
-    // Allocate array for caching the columns where the actual source starts.
-    if (!script.sourceColumnStart_) {
-      script.sourceColumnStart_ = new GlobalArray(%ScriptLineCount(script));
-    }
-
-    // Fill cache if needed and get column where the actual source starts.
-    if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
-      script.sourceColumnStart_[line] =
-          source_line.match(sourceLineBeginningSkip)[0].length;
-    }
-    column = script.sourceColumnStart_[line];
-  }
-
-  // Convert the line and column into an absolute position within the script.
-  var position = Debug.findScriptSourcePosition(script, this.line(), column);
-
-  // If the position is not found in the script (the script might be shorter
-  // than it used to be) just ignore it.
-  if (IS_NULL(position)) return;
-
-  // Create a break point object and set the break point.
-  var break_point = MakeBreakPoint(position, this);
-  var actual_position = %SetScriptBreakPoint(script, position,
-                                             this.position_alignment_,
-                                             break_point);
-  if (IS_UNDEFINED(actual_position)) {
-    actual_position = position;
-  }
-  var actual_location = script.locationFromPosition(actual_position, true);
-  break_point.actual_location = { line: actual_location.line,
-                                  column: actual_location.column,
-                                  script_id: script.id };
-  this.break_points_.push(break_point);
-  return break_point;
-};
-
-
-// Clear all the break points created from this script break point
-ScriptBreakPoint.prototype.clear = function () {
-  var remaining_break_points = [];
-  for (var i = 0; i < break_points.length; i++) {
-    if (break_points[i].script_break_point() &&
-        break_points[i].script_break_point() === this) {
-      %ClearBreakPoint(break_points[i]);
-    } else {
-      remaining_break_points.push(break_points[i]);
-    }
-  }
-  break_points = remaining_break_points;
-  this.break_points_ = [];
-};
-
-
-Debug.setListener = function(listener, opt_data) {
-  if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
-    throw %make_type_error(kDebuggerType);
-  }
-  %SetDebugEventListener(listener, opt_data);
-};
-
-
-// Returns a Script object. If the parameter is a function the return value
-// is the script in which the function is defined. If the parameter is a string
-// the return value is the script for which the script name has that string
-// value.  If it is a regexp and there is a unique script whose name matches
-// we return that, otherwise undefined.
-Debug.findScript = function(func_or_script_name) {
-  if (IS_FUNCTION(func_or_script_name)) {
-    return %FunctionGetScript(func_or_script_name);
-  } else if (%IsRegExp(func_or_script_name)) {
-    var scripts = this.scripts();
-    var last_result = null;
-    var result_count = 0;
-    for (var i in scripts) {
-      var script = scripts[i];
-      if (func_or_script_name.test(script.name)) {
-        last_result = script;
-        result_count++;
-      }
-    }
-    // Return the unique script matching the regexp.  If there are more
-    // than one we don't return a value since there is no good way to
-    // decide which one to return.  Returning a "random" one, say the
-    // first, would introduce nondeterminism (or something close to it)
-    // because the order is the heap iteration order.
-    if (result_count == 1) {
-      return last_result;
-    } else {
-      return UNDEFINED;
-    }
-  } else {
-    return %GetScript(func_or_script_name);
-  }
-};
-
-// Returns the script source. If the parameter is a function the return value
-// is the script source for the script in which the function is defined. If the
-// parameter is a string the return value is the script for which the script
-// name has that string value.
-Debug.scriptSource = function(func_or_script_name) {
-  return this.findScript(func_or_script_name).source;
-};
-
-
-Debug.source = function(f) {
-  if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType);
-  return %FunctionGetSourceCode(f);
-};
-
-
-Debug.sourcePosition = function(f) {
-  if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType);
-  return %FunctionGetScriptSourcePosition(f);
-};
-
-
-Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
-  var script = %FunctionGetScript(func);
-  var script_offset = %FunctionGetScriptSourcePosition(func);
-  return %ScriptLocationFromLine(script, opt_line, opt_column, script_offset);
-};
-
-
-// Returns the character position in a script based on a line number and an
-// optional position within that line.
-Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
-  var location = %ScriptLocationFromLine(script, opt_line, opt_column, 0);
-  return location ? location.position : null;
-};
-
-
-Debug.findBreakPoint = function(break_point_number, remove) {
-  var break_point;
-  for (var i = 0; i < break_points.length; i++) {
-    if (break_points[i].number() == break_point_number) {
-      break_point = break_points[i];
-      // Remove the break point from the list if requested.
-      if (remove) {
-        break_points.splice(i, 1);
-      }
-      break;
-    }
-  }
-  if (break_point) {
-    return break_point;
-  } else {
-    return this.findScriptBreakPoint(break_point_number, remove);
-  }
-};
-
-Debug.findBreakPointActualLocations = function(break_point_number) {
-  for (var i = 0; i < script_break_points.length; i++) {
-    if (script_break_points[i].number() == break_point_number) {
-      return script_break_points[i].actual_locations();
-    }
-  }
-  for (var i = 0; i < break_points.length; i++) {
-    if (break_points[i].number() == break_point_number) {
-      return [break_points[i].actual_location];
-    }
-  }
-  return [];
-};
-
-Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
-  if (!IS_FUNCTION(func)) throw %make_type_error(kDebuggerType);
-  // Break points in API functions are not supported.
-  if (%FunctionIsAPIFunction(func)) {
-    throw %make_error(kDebugger, 'Cannot set break point in native code.');
-  }
-  // Find source position.
-  var source_position =
-      this.findFunctionSourceLocation(func, opt_line, opt_column).position;
-  // Find the script for the function.
-  var script = %FunctionGetScript(func);
-  // Break in builtin JavaScript code is not supported.
-  if (script.type == Debug.ScriptType.Native) {
-    throw %make_error(kDebugger, 'Cannot set break point in native code.');
-  }
-  // If the script for the function has a name convert this to a script break
-  // point.
-  if (script && script.id) {
-    // Find line and column for the position in the script and set a script
-    // break point from that.
-    var location = script.locationFromPosition(source_position, false);
-    return this.setScriptBreakPointById(script.id,
-                                        location.line, location.column,
-                                        opt_condition);
-  } else {
-    // Set a break point directly on the function.
-    var break_point = MakeBreakPoint(source_position);
-    var actual_position =
-        %SetFunctionBreakPoint(func, source_position, break_point);
-    var actual_location = script.locationFromPosition(actual_position, true);
-    break_point.actual_location = { line: actual_location.line,
-                                    column: actual_location.column,
-                                    script_id: script.id };
-    break_point.setCondition(opt_condition);
-    return break_point.number();
-  }
-};
-
-
-Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
-                                                    condition, enabled,
-                                                    opt_position_alignment)
-{
-  var break_point = MakeBreakPoint(position);
-  break_point.setCondition(condition);
-  if (!enabled) {
-    break_point.disable();
-  }
-  var script = scriptById(script_id);
-  if (script) {
-    var position_alignment = IS_UNDEFINED(opt_position_alignment)
-        ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
-    break_point.actual_position = %SetScriptBreakPoint(script, position,
-        position_alignment, break_point);
-  }
-  return break_point;
-};
-
-
-Debug.enableBreakPoint = function(break_point_number) {
-  var break_point = this.findBreakPoint(break_point_number, false);
-  // Only enable if the breakpoint hasn't been deleted:
-  if (break_point) {
-    break_point.enable();
-  }
-};
-
-
-Debug.disableBreakPoint = function(break_point_number) {
-  var break_point = this.findBreakPoint(break_point_number, false);
-  // Only enable if the breakpoint hasn't been deleted:
-  if (break_point) {
-    break_point.disable();
-  }
-};
-
-
-Debug.changeBreakPointCondition = function(break_point_number, condition) {
-  var break_point = this.findBreakPoint(break_point_number, false);
-  break_point.setCondition(condition);
-};
-
-
-Debug.clearBreakPoint = function(break_point_number) {
-  var break_point = this.findBreakPoint(break_point_number, true);
-  if (break_point) {
-    return %ClearBreakPoint(break_point);
-  } else {
-    break_point = this.findScriptBreakPoint(break_point_number, true);
-    if (!break_point) throw %make_error(kDebugger, 'Invalid breakpoint');
-  }
-};
-
-
-Debug.clearAllBreakPoints = function() {
-  for (var i = 0; i < break_points.length; i++) {
-    var break_point = break_points[i];
-    %ClearBreakPoint(break_point);
-  }
-  break_points = [];
-};
-
-
-Debug.disableAllBreakPoints = function() {
-  // Disable all user defined breakpoints:
-  for (var i = 1; i < next_break_point_number; i++) {
-    Debug.disableBreakPoint(i);
-  }
-  // Disable all exception breakpoints:
-  %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
-  %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
-};
-
-
-Debug.findScriptBreakPoint = function(break_point_number, remove) {
-  var script_break_point;
-  for (var i = 0; i < script_break_points.length; i++) {
-    if (script_break_points[i].number() == break_point_number) {
-      script_break_point = script_break_points[i];
-      // Remove the break point from the list if requested.
-      if (remove) {
-        script_break_point.clear();
-        script_break_points.splice(i,1);
-      }
-      break;
-    }
-  }
-  return script_break_point;
-};
-
-
-// Sets a breakpoint in a script identified through id or name at the
-// specified source line and column within that line.
-Debug.setScriptBreakPoint = function(type, script_id_or_name,
-                                     opt_line, opt_column, opt_condition,
-                                     opt_groupId, opt_position_alignment) {
-  // Create script break point object.
-  var script_break_point =
-      new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
-                           opt_groupId, opt_position_alignment);
-
-  // Assign number to the new script break point and add it.
-  script_break_point.number_ = next_break_point_number++;
-  script_break_point.setCondition(opt_condition);
-  script_break_points.push(script_break_point);
-
-  // Run through all scripts to see if this script break point matches any
-  // loaded scripts.
-  var scripts = this.scripts();
-  for (var i = 0; i < scripts.length; i++) {
-    if (script_break_point.matchesScript(scripts[i])) {
-      script_break_point.set(scripts[i]);
-    }
-  }
-
-  return script_break_point.number();
-};
-
-
-Debug.setScriptBreakPointById = function(script_id,
-                                         opt_line, opt_column,
-                                         opt_condition, opt_groupId,
-                                         opt_position_alignment) {
-  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
-                                  script_id, opt_line, opt_column,
-                                  opt_condition, opt_groupId,
-                                  opt_position_alignment);
-};
-
-
-Debug.setScriptBreakPointByName = function(script_name,
-                                           opt_line, opt_column,
-                                           opt_condition, opt_groupId) {
-  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
-                                  script_name, opt_line, opt_column,
-                                  opt_condition, opt_groupId);
-};
-
-
-Debug.setScriptBreakPointByRegExp = function(script_regexp,
-                                             opt_line, opt_column,
-                                             opt_condition, opt_groupId) {
-  return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
-                                  script_regexp, opt_line, opt_column,
-                                  opt_condition, opt_groupId);
-};
-
-
-Debug.enableScriptBreakPoint = function(break_point_number) {
-  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
-  script_break_point.enable();
-};
-
-
-Debug.disableScriptBreakPoint = function(break_point_number) {
-  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
-  script_break_point.disable();
-};
-
-
-Debug.changeScriptBreakPointCondition = function(
-    break_point_number, condition) {
-  var script_break_point = this.findScriptBreakPoint(break_point_number, false);
-  script_break_point.setCondition(condition);
-};
-
-
-Debug.scriptBreakPoints = function() {
-  return script_break_points;
-};
-
-
-Debug.clearStepping = function() {
-  %ClearStepping();
-};
-
-Debug.setBreakOnException = function() {
-  return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
-};
-
-Debug.clearBreakOnException = function() {
-  return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
-};
-
-Debug.isBreakOnException = function() {
-  return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
-};
-
-Debug.setBreakOnUncaughtException = function() {
-  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
-};
-
-Debug.clearBreakOnUncaughtException = function() {
-  return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
-};
-
-Debug.isBreakOnUncaughtException = function() {
-  return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
-};
-
-Debug.showBreakPoints = function(f, full, opt_position_alignment) {
-  if (!IS_FUNCTION(f)) throw %make_error(kDebuggerType);
-  var source = full ? this.scriptSource(f) : this.source(f);
-  var offset = full ? 0 : this.sourcePosition(f);
-  var position_alignment = IS_UNDEFINED(opt_position_alignment)
-      ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
-  var locations = %GetBreakLocations(f, position_alignment);
-  if (!locations) return source;
-  locations.sort(function(x, y) { return x - y; });
-  var result = "";
-  var prev_pos = 0;
-  var pos;
-  for (var i = 0; i < locations.length; i++) {
-    pos = locations[i] - offset;
-    result += source.slice(prev_pos, pos);
-    result += "[B" + i + "]";
-    prev_pos = pos;
-  }
-  pos = source.length;
-  result += source.substring(prev_pos, pos);
-  return result;
-};
-
-
-// Get all the scripts currently loaded. Locating all the scripts is based on
-// scanning the heap.
-Debug.scripts = function() {
-  // Collect all scripts in the heap.
-  return %DebugGetLoadedScripts();
-};
-
-
-// Get a specific script currently loaded. This is based on scanning the heap.
-// TODO(clemensh): Create a runtime function for this.
-function scriptById(scriptId) {
-  var scripts = Debug.scripts();
-  for (var script of scripts) {
-    if (script.id == scriptId) return script;
-  }
-  return UNDEFINED;
-};
-
-
-Debug.debuggerFlags = function() {
-  return debugger_flags;
-};
-
-Debug.MakeMirror = MakeMirror;
-
-function MakeExecutionState(break_id) {
-  return new ExecutionState(break_id);
-}
-
-function ExecutionState(break_id) {
-  this.break_id = break_id;
-  this.selected_frame = 0;
-}
-
-ExecutionState.prototype.prepareStep = function(action) {
-  if (action === Debug.StepAction.StepIn ||
-      action === Debug.StepAction.StepOut ||
-      action === Debug.StepAction.StepNext) {
-    return %PrepareStep(this.break_id, action);
-  }
-  throw %make_type_error(kDebuggerType);
-};
-
-ExecutionState.prototype.evaluateGlobal = function(source) {
-  return MakeMirror(%DebugEvaluateGlobal(this.break_id, source));
-};
-
-ExecutionState.prototype.frameCount = function() {
-  return %GetFrameCount(this.break_id);
-};
-
-ExecutionState.prototype.frame = function(opt_index) {
-  // If no index supplied return the selected frame.
-  if (opt_index == null) opt_index = this.selected_frame;
-  if (opt_index < 0 || opt_index >= this.frameCount()) {
-    throw %make_type_error(kDebuggerFrame);
-  }
-  return new FrameMirror(this.break_id, opt_index);
-};
-
-ExecutionState.prototype.setSelectedFrame = function(index) {
-  var i = TO_NUMBER(index);
-  if (i < 0 || i >= this.frameCount()) {
-    throw %make_type_error(kDebuggerFrame);
-  }
-  this.selected_frame = i;
-};
-
-ExecutionState.prototype.selectedFrame = function() {
-  return this.selected_frame;
-};
-
-function MakeBreakEvent(break_id, break_points_hit) {
-  return new BreakEvent(break_id, break_points_hit);
-}
-
-
-function BreakEvent(break_id, break_points_hit) {
-  this.frame_ = new FrameMirror(break_id, 0);
-  this.break_points_hit_ = break_points_hit;
-}
-
-
-BreakEvent.prototype.eventType = function() {
-  return Debug.DebugEvent.Break;
-};
-
-
-BreakEvent.prototype.func = function() {
-  return this.frame_.func();
-};
-
-
-BreakEvent.prototype.sourceLine = function() {
-  return this.frame_.sourceLine();
-};
-
-
-BreakEvent.prototype.sourceColumn = function() {
-  return this.frame_.sourceColumn();
-};
-
-
-BreakEvent.prototype.sourceLineText = function() {
-  return this.frame_.sourceLineText();
-};
-
-
-BreakEvent.prototype.breakPointsHit = function() {
-  return this.break_points_hit_;
-};
-
-
-function MakeExceptionEvent(break_id, exception, uncaught, promise) {
-  return new ExceptionEvent(break_id, exception, uncaught, promise);
-}
-
-
-function ExceptionEvent(break_id, exception, uncaught, promise) {
-  this.exec_state_ = new ExecutionState(break_id);
-  this.exception_ = exception;
-  this.uncaught_ = uncaught;
-  this.promise_ = promise;
-}
-
-
-ExceptionEvent.prototype.eventType = function() {
-  return Debug.DebugEvent.Exception;
-};
-
-
-ExceptionEvent.prototype.exception = function() {
-  return this.exception_;
-};
-
-
-ExceptionEvent.prototype.uncaught = function() {
-  return this.uncaught_;
-};
-
-
-ExceptionEvent.prototype.promise = function() {
-  return this.promise_;
-};
-
-
-ExceptionEvent.prototype.func = function() {
-  return this.exec_state_.frame(0).func();
-};
-
-
-ExceptionEvent.prototype.sourceLine = function() {
-  return this.exec_state_.frame(0).sourceLine();
-};
-
-
-ExceptionEvent.prototype.sourceColumn = function() {
-  return this.exec_state_.frame(0).sourceColumn();
-};
-
-
-ExceptionEvent.prototype.sourceLineText = function() {
-  return this.exec_state_.frame(0).sourceLineText();
-};
-
-
-function MakeCompileEvent(script, type) {
-  return new CompileEvent(script, type);
-}
-
-
-function CompileEvent(script, type) {
-  this.script_ = MakeMirror(script);
-  this.type_ = type;
-}
-
-
-CompileEvent.prototype.eventType = function() {
-  return this.type_;
-};
-
-
-CompileEvent.prototype.script = function() {
-  return this.script_;
-};
-
-
-function MakeScriptObject_(script, include_source) {
-  var o = { id: script.id(),
-            name: script.name(),
-            lineOffset: script.lineOffset(),
-            columnOffset: script.columnOffset(),
-            lineCount: script.lineCount(),
-          };
-  if (!IS_UNDEFINED(script.data())) {
-    o.data = script.data();
-  }
-  if (include_source) {
-    o.source = script.source();
-  }
-  return o;
-}
-
-
-function MakeAsyncTaskEvent(type, id) {
-  return new AsyncTaskEvent(type, id);
-}
-
-
-function AsyncTaskEvent(type, id) {
-  this.type_ = type;
-  this.id_ = id;
-}
-
-
-AsyncTaskEvent.prototype.type = function() {
-  return this.type_;
-}
-
-
-AsyncTaskEvent.prototype.id = function() {
-  return this.id_;
-}
-
-// -------------------------------------------------------------------
-// Exports
-
-utils.InstallConstants(global, [
-  "Debug", Debug,
-  "BreakEvent", BreakEvent,
-  "CompileEvent", CompileEvent,
-  "BreakPoint", BreakPoint,
-]);
-
-// Functions needed by the debugger runtime.
-utils.InstallFunctions(utils, DONT_ENUM, [
-  "MakeExecutionState", MakeExecutionState,
-  "MakeExceptionEvent", MakeExceptionEvent,
-  "MakeBreakEvent", MakeBreakEvent,
-  "MakeCompileEvent", MakeCompileEvent,
-  "MakeAsyncTaskEvent", MakeAsyncTaskEvent,
-  "IsBreakPointTriggered", IsBreakPointTriggered,
-]);
-
-})
diff --git a/src/debug/ia32/debug-ia32.cc b/src/debug/ia32/debug-ia32.cc
index 0ce9874..309614c 100644
--- a/src/debug/ia32/debug-ia32.cc
+++ b/src/debug/ia32/debug-ia32.cc
@@ -6,99 +6,15 @@
 
 #include "src/debug/debug.h"
 
-#include "src/codegen.h"
+#include "src/codegen/macro-assembler.h"
 #include "src/debug/liveedit.h"
-#include "src/ia32/frames-ia32.h"
+#include "src/execution/frames-inl.h"
 
 namespace v8 {
 namespace internal {
 
 #define __ ACCESS_MASM(masm)
 
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_codesize;
-  __ bind(&check_codesize);
-  __ Nop(Assembler::kDebugBreakSlotLength);
-  DCHECK_EQ(Assembler::kDebugBreakSlotLength,
-            masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction.
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  static const int kSize = Assembler::kDebugBreakSlotLength;
-  CodePatcher patcher(isolate, pc, kSize);
-
-  // Add a label for checking the size of the code used for returning.
-  Label check_codesize;
-  patcher.masm()->bind(&check_codesize);
-  patcher.masm()->call(code->entry(), RelocInfo::NONE32);
-  // Check that the size of the code generated is as expected.
-  DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  return !Assembler::IsNop(pc);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-
-  // Enter an internal frame.
-  {
-    FrameScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ push(eax);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ Move(eax, Immediate(1));
-    __ mov(ebx,
-           Immediate(ExternalReference(
-               Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
-    CEntryStub ceb(masm->isolate(), 1);
-    __ CallStub(&ceb);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; ++i) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber eax if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function.
-        if (!(reg.is(eax) && (mode == SAVE_RESULT_REGISTER))) {
-          __ Move(reg, Immediate(kDebugZapValue));
-        }
-      }
-    }
-    // Get rid of the internal frame.
-  }
-
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ ret(0);
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -112,24 +28,24 @@
 
 void DebugCodegen::GenerateFrameDropperTrampoline(MacroAssembler* masm) {
   // Frame is being dropped:
-  // - Drop to the target frame specified by ebx.
+  // - Drop to the target frame specified by eax.
   // - Look up current function on the frame.
   // - Leave the frame.
   // - Restart the frame by calling the function.
-  __ mov(ebp, ebx);
-  __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+  __ mov(ebp, eax);
+  __ mov(edi, Operand(ebp, StandardFrameConstants::kFunctionOffset));
   __ leave();
 
-  __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
-  __ mov(ebx,
-         FieldOperand(ebx, SharedFunctionInfo::kFormalParameterCountOffset));
+  __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+  __ movzx_w(
+      eax, FieldOperand(eax, SharedFunctionInfo::kFormalParameterCountOffset));
 
-  ParameterCount dummy(ebx);
-  __ InvokeFunction(edi, dummy, dummy, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  // The expected and actual argument counts don't matter as long as they match
+  // and we don't enter the ArgumentsAdaptorTrampoline.
+  __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+  __ InvokeFunctionCode(edi, no_reg, eax, eax, JUMP_FUNCTION);
 }
 
-
 const bool LiveEdit::kFrameDropperSupported = true;
 
 #undef __
diff --git a/src/debug/interface-types.h b/src/debug/interface-types.h
index b86986d..a2645d3 100644
--- a/src/debug/interface-types.h
+++ b/src/debug/interface-types.h
@@ -9,9 +9,15 @@
 #include <string>
 #include <vector>
 
-#include "src/globals.h"
+#include "include/v8.h"
+#include "src/common/globals.h"
 
 namespace v8 {
+
+namespace internal {
+class BuiltinArguments;
+}  // namespace internal
+
 namespace debug {
 
 /**
@@ -33,44 +39,136 @@
  private:
   int line_number_;
   int column_number_;
+  bool is_empty_;
 };
 
-/**
- * The result of disassembling a wasm function.
- * Consists of the disassembly string and an offset table mapping wasm byte
- * offsets to line and column in the disassembly.
- * The offset table entries are ordered by the byte_offset.
- * All numbers are 0-based.
- */
-struct WasmDisassemblyOffsetTableEntry {
-  WasmDisassemblyOffsetTableEntry(uint32_t byte_offset, int line, int column)
-      : byte_offset(byte_offset), line(line), column(column) {}
-
-  uint32_t byte_offset;
-  int line;
-  int column;
-};
-struct WasmDisassembly {
-  using OffsetTable = std::vector<WasmDisassemblyOffsetTableEntry>;
-  WasmDisassembly() {}
-  WasmDisassembly(std::string disassembly, OffsetTable offset_table)
-      : disassembly(std::move(disassembly)),
-        offset_table(std::move(offset_table)) {}
-
-  std::string disassembly;
-  OffsetTable offset_table;
-};
-
-enum PromiseDebugActionType {
-  kDebugPromiseCreated,
-  kDebugEnqueueAsyncFunction,
-  kDebugEnqueuePromiseResolve,
-  kDebugEnqueuePromiseReject,
-  kDebugPromiseCollected,
+enum DebugAsyncActionType {
+  kDebugPromiseThen,
+  kDebugPromiseCatch,
+  kDebugPromiseFinally,
   kDebugWillHandle,
   kDebugDidHandle,
+  kAsyncFunctionSuspended,
+  kAsyncFunctionFinished
 };
 
+enum BreakLocationType {
+  kCallBreakLocation,
+  kReturnBreakLocation,
+  kDebuggerStatementBreakLocation,
+  kCommonBreakLocation
+};
+
+enum class CoverageMode {
+  // Make use of existing information in feedback vectors on the heap.
+  // Only return a yes/no result. Optimization and GC are not affected.
+  // Collecting best effort coverage does not reset counters.
+  kBestEffort,
+  // Disable optimization and prevent feedback vectors from being garbage
+  // collected in order to preserve precise invocation counts. Collecting
+  // precise count coverage resets counters to get incremental updates.
+  kPreciseCount,
+  // We are only interested in a yes/no result for the function. Optimization
+  // and GC can be allowed once a function has been invoked. Collecting
+  // precise binary coverage resets counters for incremental updates.
+  kPreciseBinary,
+  // Similar to the precise coverage modes but provides coverage at a
+  // lower granularity. Design doc: goo.gl/lA2swZ.
+  kBlockCount,
+  kBlockBinary,
+};
+
+enum class TypeProfileMode {
+  kNone,
+  kCollect,
+};
+
+class V8_EXPORT_PRIVATE BreakLocation : public Location {
+ public:
+  BreakLocation(int line_number, int column_number, BreakLocationType type)
+      : Location(line_number, column_number), type_(type) {}
+
+  BreakLocationType type() const { return type_; }
+
+ private:
+  BreakLocationType type_;
+};
+
+class ConsoleCallArguments : private v8::FunctionCallbackInfo<v8::Value> {
+ public:
+  int Length() const { return v8::FunctionCallbackInfo<v8::Value>::Length(); }
+  V8_INLINE Local<Value> operator[](int i) const {
+    return v8::FunctionCallbackInfo<v8::Value>::operator[](i);
+  }
+
+  explicit ConsoleCallArguments(const v8::FunctionCallbackInfo<v8::Value>&);
+  explicit ConsoleCallArguments(const internal::BuiltinArguments&);
+};
+
+class ConsoleContext {
+ public:
+  ConsoleContext(int id, v8::Local<v8::String> name) : id_(id), name_(name) {}
+  ConsoleContext() : id_(0) {}
+
+  int id() const { return id_; }
+  v8::Local<v8::String> name() const { return name_; }
+
+ private:
+  int id_;
+  v8::Local<v8::String> name_;
+};
+
+class ConsoleDelegate {
+ public:
+  virtual void Debug(const ConsoleCallArguments& args,
+                     const ConsoleContext& context) {}
+  virtual void Error(const ConsoleCallArguments& args,
+                     const ConsoleContext& context) {}
+  virtual void Info(const ConsoleCallArguments& args,
+                    const ConsoleContext& context) {}
+  virtual void Log(const ConsoleCallArguments& args,
+                   const ConsoleContext& context) {}
+  virtual void Warn(const ConsoleCallArguments& args,
+                    const ConsoleContext& context) {}
+  virtual void Dir(const ConsoleCallArguments& args,
+                   const ConsoleContext& context) {}
+  virtual void DirXml(const ConsoleCallArguments& args,
+                      const ConsoleContext& context) {}
+  virtual void Table(const ConsoleCallArguments& args,
+                     const ConsoleContext& context) {}
+  virtual void Trace(const ConsoleCallArguments& args,
+                     const ConsoleContext& context) {}
+  virtual void Group(const ConsoleCallArguments& args,
+                     const ConsoleContext& context) {}
+  virtual void GroupCollapsed(const ConsoleCallArguments& args,
+                              const ConsoleContext& context) {}
+  virtual void GroupEnd(const ConsoleCallArguments& args,
+                        const ConsoleContext& context) {}
+  virtual void Clear(const ConsoleCallArguments& args,
+                     const ConsoleContext& context) {}
+  virtual void Count(const ConsoleCallArguments& args,
+                     const ConsoleContext& context) {}
+  virtual void CountReset(const ConsoleCallArguments& args,
+                          const ConsoleContext& context) {}
+  virtual void Assert(const ConsoleCallArguments& args,
+                      const ConsoleContext& context) {}
+  virtual void Profile(const ConsoleCallArguments& args,
+                       const ConsoleContext& context) {}
+  virtual void ProfileEnd(const ConsoleCallArguments& args,
+                          const ConsoleContext& context) {}
+  virtual void Time(const ConsoleCallArguments& args,
+                    const ConsoleContext& context) {}
+  virtual void TimeLog(const ConsoleCallArguments& args,
+                       const ConsoleContext& context) {}
+  virtual void TimeEnd(const ConsoleCallArguments& args,
+                       const ConsoleContext& context) {}
+  virtual void TimeStamp(const ConsoleCallArguments& args,
+                         const ConsoleContext& context) {}
+  virtual ~ConsoleDelegate() = default;
+};
+
+using BreakpointId = int;
+
 }  // namespace debug
 }  // namespace v8
 
diff --git a/src/debug/liveedit.cc b/src/debug/liveedit.cc
index fa70e77..981692c 100644
--- a/src/debug/liveedit.cc
+++ b/src/debug/liveedit.cc
@@ -4,35 +4,59 @@
 
 #include "src/debug/liveedit.h"
 
-#include "src/assembler-inl.h"
+#include "src/api/api-inl.h"
+#include "src/ast/ast-traversal-visitor.h"
+#include "src/ast/ast.h"
 #include "src/ast/scopes.h"
-#include "src/code-stubs.h"
-#include "src/compilation-cache.h"
-#include "src/compiler.h"
+#include "src/codegen/compilation-cache.h"
+#include "src/codegen/compiler.h"
+#include "src/codegen/source-position-table.h"
+#include "src/common/globals.h"
+#include "src/debug/debug-interface.h"
 #include "src/debug/debug.h"
-#include "src/deoptimizer.h"
-#include "src/frames-inl.h"
-#include "src/global-handles.h"
-#include "src/isolate-inl.h"
-#include "src/messages.h"
-#include "src/objects-inl.h"
-#include "src/source-position-table.h"
-#include "src/v8.h"
-#include "src/v8memory.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/isolate-inl.h"
+#include "src/execution/v8threads.h"
+#include "src/init/v8.h"
+#include "src/logging/log.h"
+#include "src/objects/hash-table-inl.h"
+#include "src/objects/js-generator-inl.h"
+#include "src/objects/objects-inl.h"
+#include "src/parsing/parse-info.h"
+#include "src/parsing/parsing.h"
 
 namespace v8 {
 namespace internal {
+namespace {
+// A general-purpose comparator between 2 arrays.
+class Comparator {
+ public:
+  // Holds 2 arrays of some elements allowing to compare any pair of
+  // element from the first array and element from the second array.
+  class Input {
+   public:
+    virtual int GetLength1() = 0;
+    virtual int GetLength2() = 0;
+    virtual bool Equals(int index1, int index2) = 0;
 
-void SetElementSloppy(Handle<JSObject> object,
-                      uint32_t index,
-                      Handle<Object> value) {
-  // Ignore return value from SetElement. It can only be a failure if there
-  // are element setters causing exceptions and the debugger context has none
-  // of these.
-  Object::SetElement(object->GetIsolate(), object, index, value, SLOPPY)
-      .Assert();
-}
+   protected:
+    virtual ~Input() = default;
+  };
 
+  // Receives compare result as a series of chunks.
+  class Output {
+   public:
+    // Puts another chunk in result list. Note that technically speaking
+    // only 3 arguments actually needed with 4th being derivable.
+    virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
+
+   protected:
+    virtual ~Output() = default;
+  };
+
+  // Finds the difference between 2 arrays of elements.
+  static void CalculateDifference(Input* input, Output* result_writer);
+};
 
 // A simple implementation of dynamic programming algorithm. It solves
 // the problem of finding the difference of 2 arrays. It uses a table of results
@@ -163,7 +187,7 @@
 
   // Each cell keeps a value plus direction. Value is multiplied by 4.
   void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
-    DCHECK((value4 & kDirectionMask) == 0);
+    DCHECK_EQ(0, value4 & kDirectionMask);
     get_cell(i1, i2) = value4 | dir;
   }
 
@@ -233,7 +257,6 @@
   };
 };
 
-
 void Comparator::CalculateDifference(Comparator::Input* input,
                                      Comparator::Output* result_writer) {
   Differencer differencer(input);
@@ -242,18 +265,14 @@
   differencer.SaveResult(result_writer);
 }
 
-
-static bool CompareSubstrings(Handle<String> s1, int pos1,
-                              Handle<String> s2, int pos2, int len) {
+bool CompareSubstrings(Handle<String> s1, int pos1, Handle<String> s2, int pos2,
+                       int len) {
   for (int i = 0; i < len; i++) {
-    if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
-      return false;
-    }
+    if (s1->Get(i + pos1) != s2->Get(i + pos2)) return false;
   }
   return true;
 }
 
-
 // Additional to Input interface. Lets switch Input range to subrange.
 // More elegant way would be to wrap one Input as another Input object
 // and translate positions there, but that would cost us additional virtual
@@ -271,16 +290,9 @@
   virtual void SetSubrange2(int offset, int len) = 0;
 };
 
-
-static int min(int a, int b) {
-  return a < b ? a : b;
-}
-
-
 // Finds common prefix and suffix in input. This parts shouldn't take space in
 // linear programming table. Enable subranging in input and output.
-static void NarrowDownInput(SubrangableInput* input,
-    SubrangableOutput* output) {
+void NarrowDownInput(SubrangableInput* input, SubrangableOutput* output) {
   const int len1 = input->GetLength1();
   const int len2 = input->GetLength2();
 
@@ -289,14 +301,15 @@
 
   {
     common_prefix_len = 0;
-    int prefix_limit = min(len1, len2);
+    int prefix_limit = std::min(len1, len2);
     while (common_prefix_len < prefix_limit &&
         input->Equals(common_prefix_len, common_prefix_len)) {
       common_prefix_len++;
     }
 
     common_suffix_len = 0;
-    int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
+    int suffix_limit =
+        std::min(len1 - common_prefix_len, len2 - common_prefix_len);
 
     while (common_suffix_len < suffix_limit &&
         input->Equals(len1 - common_suffix_len - 1,
@@ -317,40 +330,6 @@
   }
 }
 
-
-// A helper class that writes chunk numbers into JSArray.
-// Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
-class CompareOutputArrayWriter {
- public:
-  explicit CompareOutputArrayWriter(Isolate* isolate)
-      : array_(isolate->factory()->NewJSArray(10)), current_size_(0) {}
-
-  Handle<JSArray> GetResult() {
-    return array_;
-  }
-
-  void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
-    Isolate* isolate = array_->GetIsolate();
-    SetElementSloppy(array_,
-                     current_size_,
-                     Handle<Object>(Smi::FromInt(char_pos1), isolate));
-    SetElementSloppy(array_,
-                     current_size_ + 1,
-                     Handle<Object>(Smi::FromInt(char_pos1 + char_len1),
-                                    isolate));
-    SetElementSloppy(array_,
-                     current_size_ + 2,
-                     Handle<Object>(Smi::FromInt(char_pos2 + char_len2),
-                                    isolate));
-    current_size_ += 3;
-  }
-
- private:
-  Handle<JSArray> array_;
-  int current_size_;
-};
-
-
 // Represents 2 strings as 2 arrays of tokens.
 // TODO(LiveEdit): Currently it's actually an array of charactres.
 //     Make array of tokens instead.
@@ -361,13 +340,9 @@
       : s1_(s1), offset1_(offset1), len1_(len1),
         s2_(s2), offset2_(offset2), len2_(len2) {
   }
-  virtual int GetLength1() {
-    return len1_;
-  }
-  virtual int GetLength2() {
-    return len2_;
-  }
-  bool Equals(int index1, int index2) {
+  int GetLength1() override { return len1_; }
+  int GetLength2() override { return len2_; }
+  bool Equals(int index1, int index2) override {
     return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
   }
 
@@ -380,47 +355,39 @@
   int len2_;
 };
 
-
-// Stores compare result in JSArray. Converts substring positions
+// Stores compare result in std::vector. Converts substring positions
 // to absolute positions.
 class TokensCompareOutput : public Comparator::Output {
  public:
-  TokensCompareOutput(CompareOutputArrayWriter* array_writer,
-                      int offset1, int offset2)
-        : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
-  }
+  TokensCompareOutput(int offset1, int offset2,
+                      std::vector<SourceChangeRange>* output)
+      : output_(output), offset1_(offset1), offset2_(offset2) {}
 
-  void AddChunk(int pos1, int pos2, int len1, int len2) {
-    array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
+  void AddChunk(int pos1, int pos2, int len1, int len2) override {
+    output_->emplace_back(
+        SourceChangeRange{pos1 + offset1_, pos1 + len1 + offset1_,
+                          pos2 + offset2_, pos2 + offset2_ + len2});
   }
 
  private:
-  CompareOutputArrayWriter* array_writer_;
+  std::vector<SourceChangeRange>* output_;
   int offset1_;
   int offset2_;
 };
 
-
 // Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
 // never has terminating new line character.
 class LineEndsWrapper {
  public:
-  explicit LineEndsWrapper(Handle<String> string)
-      : ends_array_(String::CalculateLineEnds(string, false)),
-        string_len_(string->length()) {
-  }
+  explicit LineEndsWrapper(Isolate* isolate, Handle<String> string)
+      : ends_array_(String::CalculateLineEnds(isolate, string, false)),
+        string_len_(string->length()) {}
   int length() {
     return ends_array_->length() + 1;
   }
   // Returns start for any line including start of the imaginary line after
   // the last line.
-  int GetLineStart(int index) {
-    if (index == 0) {
-      return 0;
-    } else {
-      return GetLineEnd(index - 1);
-    }
-  }
+  int GetLineStart(int index) { return index == 0 ? 0 : GetLineEnd(index - 1); }
   int GetLineEnd(int index) {
     if (index == ends_array_->length()) {
       // End of the last line is always an end of the whole string.
@@ -437,11 +404,10 @@
   int string_len_;
 
   int GetPosAfterNewLine(int index) {
-    return Smi::cast(ends_array_->get(index))->value() + 1;
+    return Smi::ToInt(ends_array_->get(index)) + 1;
   }
 };
 
-
 // Represents 2 strings as 2 arrays of lines.
 class LineArrayCompareInput : public SubrangableInput {
  public:
@@ -453,13 +419,9 @@
         subrange_len1_(line_ends1_.length()),
         subrange_len2_(line_ends2_.length()) {
   }
-  int GetLength1() {
-    return subrange_len1_;
-  }
-  int GetLength2() {
-    return subrange_len2_;
-  }
-  bool Equals(int index1, int index2) {
+  int GetLength1() override { return subrange_len1_; }
+  int GetLength2() override { return subrange_len2_; }
+  bool Equals(int index1, int index2) override {
     index1 += subrange_offset1_;
     index2 += subrange_offset2_;
 
@@ -475,11 +437,11 @@
     return CompareSubstrings(s1_, line_start1, s2_, line_start2,
                              len1);
   }
-  void SetSubrange1(int offset, int len) {
+  void SetSubrange1(int offset, int len) override {
     subrange_offset1_ = offset;
     subrange_len1_ = len;
   }
-  void SetSubrange2(int offset, int len) {
+  void SetSubrange2(int offset, int len) override {
     subrange_offset2_ = offset;
     subrange_len2_ = len;
   }
@@ -495,20 +457,25 @@
   int subrange_len2_;
 };
 
-
-// Stores compare result in JSArray. For each chunk tries to conduct
+// Stores compare result in std::vector. For each chunk tries to conduct
 // a fine-grained nested diff token-wise.
 class TokenizingLineArrayCompareOutput : public SubrangableOutput {
  public:
-  TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
+  TokenizingLineArrayCompareOutput(Isolate* isolate, LineEndsWrapper line_ends1,
                                    LineEndsWrapper line_ends2,
-                                   Handle<String> s1, Handle<String> s2)
-      : array_writer_(s1->GetIsolate()),
-        line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
-        subrange_offset1_(0), subrange_offset2_(0) {
-  }
+                                   Handle<String> s1, Handle<String> s2,
+                                   std::vector<SourceChangeRange>* output)
+      : isolate_(isolate),
+        line_ends1_(line_ends1),
+        line_ends2_(line_ends2),
+        s1_(s1),
+        s2_(s2),
+        subrange_offset1_(0),
+        subrange_offset2_(0),
+        output_(output) {}
 
-  void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
+  void AddChunk(int line_pos1, int line_pos2, int line_len1,
+                int line_len2) override {
     line_pos1 += subrange_offset1_;
     line_pos2 += subrange_offset2_;
 
@@ -519,1137 +486,870 @@
 
     if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
       // Chunk is small enough to conduct a nested token-level diff.
-      HandleScope subTaskScope(s1_->GetIsolate());
+      HandleScope subTaskScope(isolate_);
 
       TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
                                       s2_, char_pos2, char_len2);
-      TokensCompareOutput tokens_output(&array_writer_, char_pos1,
-                                          char_pos2);
+      TokensCompareOutput tokens_output(char_pos1, char_pos2, output_);
 
       Comparator::CalculateDifference(&tokens_input, &tokens_output);
     } else {
-      array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
+      output_->emplace_back(SourceChangeRange{
+          char_pos1, char_pos1 + char_len1, char_pos2, char_pos2 + char_len2});
     }
   }
-  void SetSubrange1(int offset, int len) {
+  void SetSubrange1(int offset, int len) override {
     subrange_offset1_ = offset;
   }
-  void SetSubrange2(int offset, int len) {
+  void SetSubrange2(int offset, int len) override {
     subrange_offset2_ = offset;
   }
 
-  Handle<JSArray> GetResult() {
-    return array_writer_.GetResult();
-  }
-
  private:
   static const int CHUNK_LEN_LIMIT = 800;
 
-  CompareOutputArrayWriter array_writer_;
+  Isolate* isolate_;
   LineEndsWrapper line_ends1_;
   LineEndsWrapper line_ends2_;
   Handle<String> s1_;
   Handle<String> s2_;
   int subrange_offset1_;
   int subrange_offset2_;
+  std::vector<SourceChangeRange>* output_;
 };
 
+struct SourcePositionEvent {
+  enum Type { LITERAL_STARTS, LITERAL_ENDS, DIFF_STARTS, DIFF_ENDS };
 
-Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
-                                         Handle<String> s2) {
-  s1 = String::Flatten(s1);
-  s2 = String::Flatten(s2);
+  int position;
+  Type type;
 
-  LineEndsWrapper line_ends1(s1);
-  LineEndsWrapper line_ends2(s2);
+  union {
+    FunctionLiteral* literal;
+    int pos_diff;
+  };
 
-  LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
-  TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
+  SourcePositionEvent(FunctionLiteral* literal, bool is_start)
+      : position(is_start ? literal->start_position()
+                          : literal->end_position()),
+        type(is_start ? LITERAL_STARTS : LITERAL_ENDS),
+        literal(literal) {}
+  SourcePositionEvent(const SourceChangeRange& change, bool is_start)
+      : position(is_start ? change.start_position : change.end_position),
+        type(is_start ? DIFF_STARTS : DIFF_ENDS),
+        pos_diff((change.new_end_position - change.new_start_position) -
+                 (change.end_position - change.start_position)) {}
 
-  NarrowDownInput(&input, &output);
-
-  Comparator::CalculateDifference(&input, &output);
-
-  return output.GetResult();
-}
-
-
-// Unwraps JSValue object, returning its field "value"
-static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
-  return Handle<Object>(jsValue->value(), jsValue->GetIsolate());
-}
-
-
-// Wraps any object into a OpaqueReference, that will hide the object
-// from JavaScript.
-static Handle<JSValue> WrapInJSValue(Handle<HeapObject> object) {
-  Isolate* isolate = object->GetIsolate();
-  Handle<JSFunction> constructor = isolate->opaque_reference_function();
-  Handle<JSValue> result =
-      Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
-  result->set_value(*object);
-  return result;
-}
-
-
-static Handle<SharedFunctionInfo> UnwrapSharedFunctionInfoFromJSValue(
-    Handle<JSValue> jsValue) {
-  Object* shared = jsValue->value();
-  CHECK(shared->IsSharedFunctionInfo());
-  return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(shared));
-}
-
-
-static int GetArrayLength(Handle<JSArray> array) {
-  Object* length = array->length();
-  CHECK(length->IsSmi());
-  return Smi::cast(length)->value();
-}
-
-void FunctionInfoWrapper::SetInitialProperties(Handle<String> name,
-                                               int start_position,
-                                               int end_position, int param_num,
-                                               int parent_index,
-                                               int function_literal_id) {
-  HandleScope scope(isolate());
-  this->SetField(kFunctionNameOffset_, name);
-  this->SetSmiValueField(kStartPositionOffset_, start_position);
-  this->SetSmiValueField(kEndPositionOffset_, end_position);
-  this->SetSmiValueField(kParamNumOffset_, param_num);
-  this->SetSmiValueField(kParentIndexOffset_, parent_index);
-  this->SetSmiValueField(kFunctionLiteralIdOffset_, function_literal_id);
-}
-
-void FunctionInfoWrapper::SetSharedFunctionInfo(
-    Handle<SharedFunctionInfo> info) {
-  Handle<JSValue> info_holder = WrapInJSValue(info);
-  this->SetField(kSharedFunctionInfoOffset_, info_holder);
-}
-
-Handle<SharedFunctionInfo> FunctionInfoWrapper::GetSharedFunctionInfo() {
-  Handle<Object> element = this->GetField(kSharedFunctionInfoOffset_);
-  Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
-  Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
-  CHECK(raw_result->IsSharedFunctionInfo());
-  return Handle<SharedFunctionInfo>::cast(raw_result);
-}
-
-void SharedInfoWrapper::SetProperties(Handle<String> name,
-                                      int start_position,
-                                      int end_position,
-                                      Handle<SharedFunctionInfo> info) {
-  HandleScope scope(isolate());
-  this->SetField(kFunctionNameOffset_, name);
-  Handle<JSValue> info_holder = WrapInJSValue(info);
-  this->SetField(kSharedInfoOffset_, info_holder);
-  this->SetSmiValueField(kStartPositionOffset_, start_position);
-  this->SetSmiValueField(kEndPositionOffset_, end_position);
-}
-
-
-Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() {
-  Handle<Object> element = this->GetField(kSharedInfoOffset_);
-  Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
-  return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
-}
-
-
-void LiveEdit::InitializeThreadLocal(Debug* debug) {
-  debug->thread_local_.restart_fp_ = 0;
-}
-
-
-MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script,
-                                                 Handle<String> source) {
-  Isolate* isolate = script->GetIsolate();
-
-  MaybeHandle<JSArray> infos;
-  Handle<Object> original_source =
-      Handle<Object>(script->source(), isolate);
-  script->set_source(*source);
-
-  {
-    // Creating verbose TryCatch from public API is currently the only way to
-    // force code save location. We do not use this the object directly.
-    v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
-    try_catch.SetVerbose(true);
-
-    // A logical 'try' section.
-    infos = Compiler::CompileForLiveEdit(script);
-  }
-
-  // A logical 'catch' section.
-  Handle<JSObject> rethrow_exception;
-  if (isolate->has_pending_exception()) {
-    Handle<Object> exception(isolate->pending_exception(), isolate);
-    MessageLocation message_location = isolate->GetMessageLocation();
-
-    isolate->clear_pending_message();
-    isolate->clear_pending_exception();
-
-    // If possible, copy positions from message object to exception object.
-    if (exception->IsJSObject() && !message_location.script().is_null()) {
-      rethrow_exception = Handle<JSObject>::cast(exception);
-
-      Factory* factory = isolate->factory();
-      Handle<String> start_pos_key = factory->InternalizeOneByteString(
-          STATIC_CHAR_VECTOR("startPosition"));
-      Handle<String> end_pos_key =
-          factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("endPosition"));
-      Handle<String> script_obj_key =
-          factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptObject"));
-      Handle<Smi> start_pos(
-          Smi::FromInt(message_location.start_pos()), isolate);
-      Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate);
-      Handle<JSObject> script_obj =
-          Script::GetWrapper(message_location.script());
-      Object::SetProperty(rethrow_exception, start_pos_key, start_pos, SLOPPY)
-          .Assert();
-      Object::SetProperty(rethrow_exception, end_pos_key, end_pos, SLOPPY)
-          .Assert();
-      Object::SetProperty(rethrow_exception, script_obj_key, script_obj, SLOPPY)
-          .Assert();
+  static bool LessThan(const SourcePositionEvent& a,
+                       const SourcePositionEvent& b) {
+    if (a.position != b.position) return a.position < b.position;
+    if (a.type != b.type) return a.type < b.type;
+    if (a.type == LITERAL_STARTS && b.type == LITERAL_STARTS) {
+      // If the literals start in the same position, we want the one with the
+      // furthest (i.e. largest) end position to be first.
+      if (a.literal->end_position() != b.literal->end_position()) {
+        return a.literal->end_position() > b.literal->end_position();
+      }
+      // If they also end in the same position, we want the first in order of
+      // literal ids to be first.
+      return a.literal->function_literal_id() <
+             b.literal->function_literal_id();
+    } else if (a.type == LITERAL_ENDS && b.type == LITERAL_ENDS) {
+      // If the literals end in the same position, we want the one with the
+      // nearest (i.e. largest) start position to be first.
+      if (a.literal->start_position() != b.literal->start_position()) {
+        return a.literal->start_position() > b.literal->start_position();
+      }
+      // If they also end in the same position, we want the last in order of
+      // literal ids to be first.
+      return a.literal->function_literal_id() >
+             b.literal->function_literal_id();
+    } else {
+      return a.pos_diff < b.pos_diff;
     }
   }
+};
 
-  // A logical 'finally' section.
-  script->set_source(*original_source);
+struct FunctionLiteralChange {
+  // If any of start/end position is kNoSourcePosition, this literal is
+  // considered damaged and will not be mapped and edited at all.
+  int new_start_position;
+  int new_end_position;
+  bool has_changes;
+  FunctionLiteral* outer_literal;
 
-  if (rethrow_exception.is_null()) {
-    return infos.ToHandleChecked();
-  } else {
-    return isolate->Throw<JSArray>(rethrow_exception);
+  explicit FunctionLiteralChange(int new_start_position, FunctionLiteral* outer)
+      : new_start_position(new_start_position),
+        new_end_position(kNoSourcePosition),
+        has_changes(false),
+        outer_literal(outer) {}
+};
+
+using FunctionLiteralChanges =
+    std::unordered_map<FunctionLiteral*, FunctionLiteralChange>;
+void CalculateFunctionLiteralChanges(
+    const std::vector<FunctionLiteral*>& literals,
+    const std::vector<SourceChangeRange>& diffs,
+    FunctionLiteralChanges* result) {
+  std::vector<SourcePositionEvent> events;
+  events.reserve(literals.size() * 2 + diffs.size() * 2);
+  for (FunctionLiteral* literal : literals) {
+    events.emplace_back(literal, true);
+    events.emplace_back(literal, false);
   }
-}
-
-// Finds all references to original and replaces them with substitution.
-static void ReplaceCodeObject(Handle<Code> original,
-                              Handle<Code> substitution) {
-  // Perform a full GC in order to ensure that we are not in the middle of an
-  // incremental marking phase when we are replacing the code object.
-  // Since we are not in an incremental marking phase we can write pointers
-  // to code objects (that are never in new space) without worrying about
-  // write barriers.
-  Heap* heap = original->GetHeap();
-  HeapIterator iterator(heap, HeapIterator::kFilterUnreachable);
-  // Now iterate over all pointers of all objects, including code_target
-  // implicit pointers.
-  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
-    if (obj->IsJSFunction()) {
-      JSFunction* fun = JSFunction::cast(obj);
-      if (fun->code() == *original) fun->ReplaceCode(*substitution);
-    } else if (obj->IsSharedFunctionInfo()) {
-      SharedFunctionInfo* info = SharedFunctionInfo::cast(obj);
-      if (info->code() == *original) info->set_code(*substitution);
+  for (const SourceChangeRange& diff : diffs) {
+    events.emplace_back(diff, true);
+    events.emplace_back(diff, false);
+  }
+  std::sort(events.begin(), events.end(), SourcePositionEvent::LessThan);
+  bool inside_diff = false;
+  int delta = 0;
+  std::stack<std::pair<FunctionLiteral*, FunctionLiteralChange>> literal_stack;
+  for (const SourcePositionEvent& event : events) {
+    switch (event.type) {
+      case SourcePositionEvent::DIFF_ENDS:
+        DCHECK(inside_diff);
+        inside_diff = false;
+        delta += event.pos_diff;
+        break;
+      case SourcePositionEvent::LITERAL_ENDS: {
+        DCHECK_EQ(literal_stack.top().first, event.literal);
+        FunctionLiteralChange& change = literal_stack.top().second;
+        change.new_end_position = inside_diff
+                                      ? kNoSourcePosition
+                                      : event.literal->end_position() + delta;
+        result->insert(literal_stack.top());
+        literal_stack.pop();
+        break;
+      }
+      case SourcePositionEvent::LITERAL_STARTS:
+        literal_stack.push(std::make_pair(
+            event.literal,
+            FunctionLiteralChange(
+                inside_diff ? kNoSourcePosition
+                            : event.literal->start_position() + delta,
+                literal_stack.empty() ? nullptr : literal_stack.top().first)));
+        break;
+      case SourcePositionEvent::DIFF_STARTS:
+        DCHECK(!inside_diff);
+        inside_diff = true;
+        if (!literal_stack.empty()) {
+          // Note that outer literal has not necessarily changed, unless the
+          // diff goes past the end of this literal. In this case, we'll mark
+          // this function as damaged and parent as changed later in
+          // MapLiterals.
+          literal_stack.top().second.has_changes = true;
+        }
+        break;
     }
   }
 }
 
-// Patch function feedback vector.
-// The feedback vector is a cache for complex object boilerplates and for a
-// native context. We must clean cached values, or if the structure of the
-// vector itself changes we need to allocate a new one.
-class FeedbackVectorFixer {
+// Function which has not changed itself, but if any variable in its
+// outer context has been added/removed, we must consider this function
+// as damaged and not update references to it.
+// This is because old compiled function has hardcoded references to
+// it's outer context.
+bool HasChangedScope(FunctionLiteral* a, FunctionLiteral* b) {
+  Scope* scope_a = a->scope()->outer_scope();
+  Scope* scope_b = b->scope()->outer_scope();
+  while (scope_a && scope_b) {
+    std::unordered_map<int, Handle<String>> vars;
+    for (Variable* var : *scope_a->locals()) {
+      if (!var->IsContextSlot()) continue;
+      vars[var->index()] = var->name();
+    }
+    for (Variable* var : *scope_b->locals()) {
+      if (!var->IsContextSlot()) continue;
+      auto it = vars.find(var->index());
+      if (it == vars.end()) return true;
+      if (*it->second != *var->name()) return true;
+    }
+    scope_a = scope_a->outer_scope();
+    scope_b = scope_b->outer_scope();
+  }
+  return scope_a != scope_b;
+}
+
+enum ChangeState { UNCHANGED, CHANGED, DAMAGED };
+
+using LiteralMap = std::unordered_map<FunctionLiteral*, FunctionLiteral*>;
+void MapLiterals(const FunctionLiteralChanges& changes,
+                 const std::vector<FunctionLiteral*>& new_literals,
+                 LiteralMap* unchanged, LiteralMap* changed) {
+  // Track the top-level script function separately as it can overlap fully with
+  // another function, e.g. the script "()=>42".
+  const std::pair<int, int> kTopLevelMarker = std::make_pair(-1, -1);
+  std::map<std::pair<int, int>, FunctionLiteral*> position_to_new_literal;
+  for (FunctionLiteral* literal : new_literals) {
+    DCHECK(literal->start_position() != kNoSourcePosition);
+    DCHECK(literal->end_position() != kNoSourcePosition);
+    std::pair<int, int> key =
+        literal->function_literal_id() == kFunctionLiteralIdTopLevel
+            ? kTopLevelMarker
+            : std::make_pair(literal->start_position(),
+                             literal->end_position());
+    // Make sure there are no duplicate keys.
+    DCHECK_EQ(position_to_new_literal.find(key), position_to_new_literal.end());
+    position_to_new_literal[key] = literal;
+  }
+  LiteralMap mappings;
+  std::unordered_map<FunctionLiteral*, ChangeState> change_state;
+  for (const auto& change_pair : changes) {
+    FunctionLiteral* literal = change_pair.first;
+    const FunctionLiteralChange& change = change_pair.second;
+    std::pair<int, int> key =
+        literal->function_literal_id() == kFunctionLiteralIdTopLevel
+            ? kTopLevelMarker
+            : std::make_pair(change.new_start_position,
+                             change.new_end_position);
+    auto it = position_to_new_literal.find(key);
+    if (it == position_to_new_literal.end() ||
+        HasChangedScope(literal, it->second)) {
+      change_state[literal] = ChangeState::DAMAGED;
+      if (!change.outer_literal) continue;
+      if (change_state[change.outer_literal] != ChangeState::DAMAGED) {
+        change_state[change.outer_literal] = ChangeState::CHANGED;
+      }
+    } else {
+      mappings[literal] = it->second;
+      if (change_state.find(literal) == change_state.end()) {
+        change_state[literal] =
+            change.has_changes ? ChangeState::CHANGED : ChangeState::UNCHANGED;
+      }
+    }
+  }
+  for (const auto& mapping : mappings) {
+    if (change_state[mapping.first] == ChangeState::UNCHANGED) {
+      (*unchanged)[mapping.first] = mapping.second;
+    } else if (change_state[mapping.first] == ChangeState::CHANGED) {
+      (*changed)[mapping.first] = mapping.second;
+    }
+  }
+}
+
+class CollectFunctionLiterals final
+    : public AstTraversalVisitor<CollectFunctionLiterals> {
  public:
-  static void PatchFeedbackVector(FunctionInfoWrapper* compile_info_wrapper,
-                                  Handle<SharedFunctionInfo> shared_info,
-                                  Isolate* isolate) {
-    // When feedback metadata changes, we have to create new array instances.
-    // Since we cannot create instances when iterating heap, we should first
-    // collect all functions and fix their literal arrays.
-    Handle<FixedArray> function_instances =
-        CollectJSFunctions(shared_info, isolate);
-
-    for (int i = 0; i < function_instances->length(); i++) {
-      Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
-      Handle<Cell> new_cell = isolate->factory()->NewManyClosuresCell(
-          isolate->factory()->undefined_value());
-      fun->set_feedback_vector_cell(*new_cell);
-      // Only create feedback vectors if we already have the metadata.
-      if (shared_info->is_compiled()) JSFunction::EnsureLiterals(fun);
-    }
+  CollectFunctionLiterals(Isolate* isolate, AstNode* root)
+      : AstTraversalVisitor<CollectFunctionLiterals>(isolate, root) {}
+  void VisitFunctionLiteral(FunctionLiteral* lit) {
+    AstTraversalVisitor::VisitFunctionLiteral(lit);
+    literals_->push_back(lit);
+  }
+  void Run(std::vector<FunctionLiteral*>* literals) {
+    literals_ = literals;
+    AstTraversalVisitor::Run();
+    literals_ = nullptr;
   }
 
  private:
-  // Iterates all function instances in the HEAP that refers to the
-  // provided shared_info.
-  template<typename Visitor>
-  static void IterateJSFunctions(Handle<SharedFunctionInfo> shared_info,
-                                 Visitor* visitor) {
-    HeapIterator iterator(shared_info->GetHeap());
-    for (HeapObject* obj = iterator.next(); obj != NULL;
-        obj = iterator.next()) {
-      if (obj->IsJSFunction()) {
-        JSFunction* function = JSFunction::cast(obj);
-        if (function->shared() == *shared_info) {
-          visitor->visit(function);
+  std::vector<FunctionLiteral*>* literals_;
+};
+
+bool ParseScript(Isolate* isolate, Handle<Script> script, ParseInfo* parse_info,
+                 bool compile_as_well, std::vector<FunctionLiteral*>* literals,
+                 debug::LiveEditResult* result) {
+  v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
+  Handle<SharedFunctionInfo> shared;
+  bool success = false;
+  if (compile_as_well) {
+    success = Compiler::CompileForLiveEdit(parse_info, script, isolate)
+                  .ToHandle(&shared);
+  } else {
+    success = parsing::ParseProgram(parse_info, script, isolate,
+                                    parsing::ReportStatisticsMode::kYes);
+    if (!success) {
+      // Throw the parser error.
+      parse_info->pending_error_handler()->PrepareErrors(
+          isolate, parse_info->ast_value_factory());
+      parse_info->pending_error_handler()->ReportErrors(isolate, script);
+    }
+  }
+  if (!success) {
+    isolate->OptionalRescheduleException(false);
+    DCHECK(try_catch.HasCaught());
+    result->message = try_catch.Message()->Get();
+    auto self = Utils::OpenHandle(*try_catch.Message());
+    auto msg = i::Handle<i::JSMessageObject>::cast(self);
+    result->line_number = msg->GetLineNumber();
+    result->column_number = msg->GetColumnNumber();
+    result->status = debug::LiveEditResult::COMPILE_ERROR;
+    return false;
+  }
+  CollectFunctionLiterals(isolate, parse_info->literal()).Run(literals);
+  return true;
+}
+
+struct FunctionData {
+  FunctionData(FunctionLiteral* literal, bool should_restart)
+      : literal(literal),
+        stack_position(NOT_ON_STACK),
+        should_restart(should_restart) {}
+
+  FunctionLiteral* literal;
+  MaybeHandle<SharedFunctionInfo> shared;
+  std::vector<Handle<JSFunction>> js_functions;
+  std::vector<Handle<JSGeneratorObject>> running_generators;
+  // In case of multiple functions with different stack position, the latest
+  // one (in the order below) is used, since it is the most restrictive.
+  // This is important only for functions to be restarted.
+  enum StackPosition {
+    NOT_ON_STACK,
+    ABOVE_BREAK_FRAME,
+    PATCHABLE,
+    BELOW_NON_DROPPABLE_FRAME,
+    ARCHIVED_THREAD,
+  };
+  StackPosition stack_position;
+  bool should_restart;
+};
+
+class FunctionDataMap : public ThreadVisitor {
+ public:
+  void AddInterestingLiteral(int script_id, FunctionLiteral* literal,
+                             bool should_restart) {
+    map_.emplace(GetFuncId(script_id, literal),
+                 FunctionData{literal, should_restart});
+  }
+
+  bool Lookup(SharedFunctionInfo sfi, FunctionData** data) {
+    int start_position = sfi.StartPosition();
+    if (!sfi.script().IsScript() || start_position == -1) {
+      return false;
+    }
+    Script script = Script::cast(sfi.script());
+    return Lookup(GetFuncId(script.id(), sfi), data);
+  }
+
+  bool Lookup(Handle<Script> script, FunctionLiteral* literal,
+              FunctionData** data) {
+    return Lookup(GetFuncId(script->id(), literal), data);
+  }
+
+  void Fill(Isolate* isolate, Address* restart_frame_fp) {
+    {
+      HeapObjectIterator iterator(isolate->heap(),
+                                  HeapObjectIterator::kFilterUnreachable);
+      for (HeapObject obj = iterator.Next(); !obj.is_null();
+           obj = iterator.Next()) {
+        if (obj.IsSharedFunctionInfo()) {
+          SharedFunctionInfo sfi = SharedFunctionInfo::cast(obj);
+          FunctionData* data = nullptr;
+          if (!Lookup(sfi, &data)) continue;
+          data->shared = handle(sfi, isolate);
+        } else if (obj.IsJSFunction()) {
+          JSFunction js_function = JSFunction::cast(obj);
+          SharedFunctionInfo sfi = js_function.shared();
+          FunctionData* data = nullptr;
+          if (!Lookup(sfi, &data)) continue;
+          data->js_functions.emplace_back(js_function, isolate);
+        } else if (obj.IsJSGeneratorObject()) {
+          JSGeneratorObject gen = JSGeneratorObject::cast(obj);
+          if (gen.is_closed()) continue;
+          SharedFunctionInfo sfi = gen.function().shared();
+          FunctionData* data = nullptr;
+          if (!Lookup(sfi, &data)) continue;
+          data->running_generators.emplace_back(gen, isolate);
         }
       }
     }
-  }
-
-  // Finds all instances of JSFunction that refers to the provided shared_info
-  // and returns array with them.
-  static Handle<FixedArray> CollectJSFunctions(
-      Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
-    CountVisitor count_visitor;
-    count_visitor.count = 0;
-    IterateJSFunctions(shared_info, &count_visitor);
-    int size = count_visitor.count;
-
-    Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
-    if (size > 0) {
-      CollectVisitor collect_visitor(result);
-      IterateJSFunctions(shared_info, &collect_visitor);
-    }
-    return result;
-  }
-
-  class CountVisitor {
-   public:
-    void visit(JSFunction* fun) {
-      count++;
-    }
-    int count;
-  };
-
-  class CollectVisitor {
-   public:
-    explicit CollectVisitor(Handle<FixedArray> output)
-        : m_output(output), m_pos(0) {}
-
-    void visit(JSFunction* fun) {
-      m_output->set(m_pos, fun);
-      m_pos++;
-    }
-   private:
-    Handle<FixedArray> m_output;
-    int m_pos;
-  };
-};
-
-
-// Marks code that shares the same shared function info or has inlined
-// code that shares the same function info.
-class DependentFunctionMarker: public OptimizedFunctionVisitor {
- public:
-  SharedFunctionInfo* shared_info_;
-  bool found_;
-
-  explicit DependentFunctionMarker(SharedFunctionInfo* shared_info)
-    : shared_info_(shared_info), found_(false) { }
-
-  virtual void EnterContext(Context* context) { }  // Don't care.
-  virtual void LeaveContext(Context* context)  { }  // Don't care.
-  virtual void VisitFunction(JSFunction* function) {
-    // It should be guaranteed by the iterator that everything is optimized.
-    DCHECK(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
-    if (function->Inlines(shared_info_)) {
-      // Mark the code for deoptimization.
-      function->code()->set_marked_for_deoptimization(true);
-      found_ = true;
-    }
-  }
-};
-
-
-static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
-  DisallowHeapAllocation no_allocation;
-  DependentFunctionMarker marker(function_info);
-  // TODO(titzer): need to traverse all optimized code to find OSR code here.
-  Deoptimizer::VisitAllOptimizedFunctions(function_info->GetIsolate(), &marker);
-
-  if (marker.found_) {
-    // Only go through with the deoptimization if something was found.
-    Deoptimizer::DeoptimizeMarkedCode(function_info->GetIsolate());
-  }
-}
-
-
-void LiveEdit::ReplaceFunctionCode(
-    Handle<JSArray> new_compile_info_array,
-    Handle<JSArray> shared_info_array) {
-  Isolate* isolate = new_compile_info_array->GetIsolate();
-
-  FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
-  SharedInfoWrapper shared_info_wrapper(shared_info_array);
-
-  Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
-  Handle<SharedFunctionInfo> new_shared_info =
-      compile_info_wrapper.GetSharedFunctionInfo();
-
-  if (shared_info->is_compiled()) {
-    // Take whatever code we can get from the new shared function info. We
-    // expect activations of neither the old bytecode nor old FCG code, since
-    // the lowest activation is going to be restarted.
-    Handle<Code> old_code(shared_info->code());
-    Handle<Code> new_code(new_shared_info->code());
-    // Clear old bytecode. This will trigger self-healing if we do not install
-    // new bytecode.
-    shared_info->ClearBytecodeArray();
-    if (!shared_info->HasBaselineCode()) {
-      // Every function from this SFI is interpreted.
-      if (!new_shared_info->HasBaselineCode()) {
-        // We have newly compiled bytecode. Simply replace the old one.
-        shared_info->set_bytecode_array(new_shared_info->bytecode_array());
-      } else {
-        // Rely on self-healing for places that used to run bytecode.
-        shared_info->ReplaceCode(*new_code);
+    FunctionData::StackPosition stack_position =
+        isolate->debug()->break_frame_id() == StackFrameId::NO_ID
+            ? FunctionData::PATCHABLE
+            : FunctionData::ABOVE_BREAK_FRAME;
+    for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
+      StackFrame* frame = it.frame();
+      if (stack_position == FunctionData::ABOVE_BREAK_FRAME) {
+        if (frame->id() == isolate->debug()->break_frame_id()) {
+          stack_position = FunctionData::PATCHABLE;
+        }
       }
-    } else {
-      // Functions from this SFI can be either interpreted or running FCG.
-      DCHECK(old_code->kind() == Code::FUNCTION);
-      if (new_shared_info->HasBytecodeArray()) {
-        // Start using new bytecode everywhere.
-        shared_info->set_bytecode_array(new_shared_info->bytecode_array());
-        ReplaceCodeObject(old_code,
-                          isolate->builtins()->InterpreterEntryTrampoline());
-      } else {
-        // Start using new FCG code everywhere.
-        // Rely on self-healing for places that used to run bytecode.
-        DCHECK(new_code->kind() == Code::FUNCTION);
-        ReplaceCodeObject(old_code, new_code);
+      if (stack_position == FunctionData::PATCHABLE &&
+          (frame->is_exit() || frame->is_builtin_exit())) {
+        stack_position = FunctionData::BELOW_NON_DROPPABLE_FRAME;
+        continue;
+      }
+      if (!frame->is_java_script()) continue;
+      std::vector<Handle<SharedFunctionInfo>> sfis;
+      JavaScriptFrame::cast(frame)->GetFunctions(&sfis);
+      for (auto& sfi : sfis) {
+        if (stack_position == FunctionData::PATCHABLE &&
+            IsResumableFunction(sfi->kind())) {
+          stack_position = FunctionData::BELOW_NON_DROPPABLE_FRAME;
+        }
+        FunctionData* data = nullptr;
+        if (!Lookup(*sfi, &data)) continue;
+        if (!data->should_restart) continue;
+        data->stack_position = stack_position;
+        *restart_frame_fp = frame->fp();
       }
     }
 
-    if (shared_info->HasDebugInfo()) {
-      // Existing break points will be re-applied. Reset the debug info here.
-      isolate->debug()->RemoveDebugInfoAndClearFromShared(
-          handle(shared_info->GetDebugInfo()));
+    isolate->thread_manager()->IterateArchivedThreads(this);
+  }
+
+ private:
+  // Unique id for a function: script_id + start_position, where start_position
+  // is special cased to -1 for top-level so that it does not overlap with a
+  // function whose start position is 0.
+  using FuncId = std::pair<int, int>;
+
+  FuncId GetFuncId(int script_id, FunctionLiteral* literal) {
+    int start_position = literal->start_position();
+    if (literal->function_literal_id() == 0) {
+      // This is the top-level script function literal, so special case its
+      // start position
+      DCHECK_EQ(start_position, 0);
+      start_position = -1;
     }
-    shared_info->set_scope_info(new_shared_info->scope_info());
-    shared_info->set_outer_scope_info(new_shared_info->outer_scope_info());
-    shared_info->DisableOptimization(kLiveEdit);
-    // Update the type feedback vector, if needed.
-    Handle<FeedbackMetadata> new_feedback_metadata(
-        new_shared_info->feedback_metadata());
-    shared_info->set_feedback_metadata(*new_feedback_metadata);
-  } else {
-    shared_info->set_feedback_metadata(
-        FeedbackMetadata::cast(isolate->heap()->empty_fixed_array()));
+    return FuncId(script_id, start_position);
   }
 
-  int start_position = compile_info_wrapper.GetStartPosition();
-  int end_position = compile_info_wrapper.GetEndPosition();
-  shared_info->set_start_position(start_position);
-  shared_info->set_end_position(end_position);
-
-  FeedbackVectorFixer::PatchFeedbackVector(&compile_info_wrapper, shared_info,
-                                           isolate);
-
-  DeoptimizeDependentFunctions(*shared_info);
-  isolate->compilation_cache()->Remove(shared_info);
-}
-
-void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array,
-                                     int new_function_literal_id) {
-  SharedInfoWrapper shared_info_wrapper(shared_info_array);
-  Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
-
-  shared_info->set_function_literal_id(new_function_literal_id);
-  DeoptimizeDependentFunctions(*shared_info);
-  shared_info_array->GetIsolate()->compilation_cache()->Remove(shared_info);
-}
-
-void LiveEdit::FixupScript(Handle<Script> script, int max_function_literal_id) {
-  Isolate* isolate = script->GetIsolate();
-  Handle<FixedArray> old_infos(script->shared_function_infos(), isolate);
-  Handle<FixedArray> new_infos(
-      isolate->factory()->NewFixedArray(max_function_literal_id + 1));
-  script->set_shared_function_infos(*new_infos);
-  SharedFunctionInfo::ScriptIterator iterator(isolate, old_infos);
-  while (SharedFunctionInfo* shared = iterator.Next()) {
-    // We can't use SharedFunctionInfo::SetScript(info, undefined_value()) here,
-    // as we severed the link from the Script to the SharedFunctionInfo above.
-    Handle<SharedFunctionInfo> info(shared, isolate);
-    info->set_script(isolate->heap()->undefined_value());
-    Handle<Object> new_noscript_list = WeakFixedArray::Add(
-        isolate->factory()->noscript_shared_function_infos(), info);
-    isolate->heap()->SetRootNoScriptSharedFunctionInfos(*new_noscript_list);
-
-    // Put the SharedFunctionInfo at its new, correct location.
-    SharedFunctionInfo::SetScript(info, script);
+  FuncId GetFuncId(int script_id, SharedFunctionInfo sfi) {
+    DCHECK_EQ(script_id, Script::cast(sfi.script()).id());
+    int start_position = sfi.StartPosition();
+    DCHECK_NE(start_position, -1);
+    if (sfi.is_toplevel()) {
+      // This is the top-level function, so special case its start position
+      DCHECK_EQ(start_position, 0);
+      start_position = -1;
+    }
+    return FuncId(script_id, start_position);
   }
+
+  bool Lookup(FuncId id, FunctionData** data) {
+    auto it = map_.find(id);
+    if (it == map_.end()) return false;
+    *data = &it->second;
+    return true;
+  }
+
+  void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
+    for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+      std::vector<Handle<SharedFunctionInfo>> sfis;
+      it.frame()->GetFunctions(&sfis);
+      for (auto& sfi : sfis) {
+        FunctionData* data = nullptr;
+        if (!Lookup(*sfi, &data)) continue;
+        data->stack_position = FunctionData::ARCHIVED_THREAD;
+      }
+    }
+  }
+
+  std::map<FuncId, FunctionData> map_;
+};
+
+bool CanPatchScript(
+    const LiteralMap& changed, Handle<Script> script, Handle<Script> new_script,
+    FunctionDataMap& function_data_map,  // NOLINT(runtime/references)
+    debug::LiveEditResult* result) {
+  debug::LiveEditResult::Status status = debug::LiveEditResult::OK;
+  for (const auto& mapping : changed) {
+    FunctionData* data = nullptr;
+    function_data_map.Lookup(script, mapping.first, &data);
+    FunctionData* new_data = nullptr;
+    function_data_map.Lookup(new_script, mapping.second, &new_data);
+    Handle<SharedFunctionInfo> sfi;
+    if (!data->shared.ToHandle(&sfi)) {
+      continue;
+    } else if (!data->should_restart) {
+      UNREACHABLE();
+    } else if (data->stack_position == FunctionData::ABOVE_BREAK_FRAME) {
+      status = debug::LiveEditResult::BLOCKED_BY_FUNCTION_ABOVE_BREAK_FRAME;
+    } else if (data->stack_position ==
+               FunctionData::BELOW_NON_DROPPABLE_FRAME) {
+      status =
+          debug::LiveEditResult::BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME;
+    } else if (!data->running_generators.empty()) {
+      status = debug::LiveEditResult::BLOCKED_BY_RUNNING_GENERATOR;
+    } else if (data->stack_position == FunctionData::ARCHIVED_THREAD) {
+      status = debug::LiveEditResult::BLOCKED_BY_ACTIVE_FUNCTION;
+    }
+    if (status != debug::LiveEditResult::OK) {
+      result->status = status;
+      return false;
+    }
+  }
+  return true;
 }
 
-void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
-                                 Handle<Object> script_handle) {
-  Handle<SharedFunctionInfo> shared_info =
-      UnwrapSharedFunctionInfoFromJSValue(function_wrapper);
-  Isolate* isolate = function_wrapper->GetIsolate();
-  CHECK(script_handle->IsScript() || script_handle->IsUndefined(isolate));
-  SharedFunctionInfo::SetScript(shared_info, script_handle);
-  shared_info->DisableOptimization(kLiveEdit);
-
-  function_wrapper->GetIsolate()->compilation_cache()->Remove(shared_info);
-}
-
-namespace {
-// For a script text change (defined as position_change_array), translates
-// position in unchanged text to position in changed text.
-// Text change is a set of non-overlapping regions in text, that have changed
-// their contents and length. It is specified as array of groups of 3 numbers:
-// (change_begin, change_end, change_end_new_position).
-// Each group describes a change in text; groups are sorted by change_begin.
-// Only position in text beyond any changes may be successfully translated.
-// If a positions is inside some region that changed, result is currently
-// undefined.
-static int TranslatePosition(int original_position,
-                             Handle<JSArray> position_change_array) {
-  int position_diff = 0;
-  int array_len = GetArrayLength(position_change_array);
-  Isolate* isolate = position_change_array->GetIsolate();
-  // TODO(635): binary search may be used here
-  for (int i = 0; i < array_len; i += 3) {
-    HandleScope scope(isolate);
-    Handle<Object> element =
-        JSReceiver::GetElement(isolate, position_change_array, i)
-            .ToHandleChecked();
-    CHECK(element->IsSmi());
-    int chunk_start = Handle<Smi>::cast(element)->value();
-    if (original_position < chunk_start) {
+bool CanRestartFrame(
+    Isolate* isolate, Address fp,
+    FunctionDataMap& function_data_map,  // NOLINT(runtime/references)
+    const LiteralMap& changed, debug::LiveEditResult* result) {
+  DCHECK_GT(fp, 0);
+  StackFrame* restart_frame = nullptr;
+  StackFrameIterator it(isolate);
+  for (; !it.done(); it.Advance()) {
+    if (it.frame()->fp() == fp) {
+      restart_frame = it.frame();
       break;
     }
-    element = JSReceiver::GetElement(isolate, position_change_array, i + 1)
-                  .ToHandleChecked();
-    CHECK(element->IsSmi());
-    int chunk_end = Handle<Smi>::cast(element)->value();
-    // Position mustn't be inside a chunk.
-    DCHECK(original_position >= chunk_end);
-    element = JSReceiver::GetElement(isolate, position_change_array, i + 2)
-                  .ToHandleChecked();
-    CHECK(element->IsSmi());
-    int chunk_changed_end = Handle<Smi>::cast(element)->value();
-    position_diff = chunk_changed_end - chunk_end;
   }
-
-  return original_position + position_diff;
+  DCHECK(restart_frame && restart_frame->is_java_script());
+  if (!LiveEdit::kFrameDropperSupported) {
+    result->status = debug::LiveEditResult::FRAME_RESTART_IS_NOT_SUPPORTED;
+    return false;
+  }
+  std::vector<Handle<SharedFunctionInfo>> sfis;
+  JavaScriptFrame::cast(restart_frame)->GetFunctions(&sfis);
+  for (auto& sfi : sfis) {
+    FunctionData* data = nullptr;
+    if (!function_data_map.Lookup(*sfi, &data)) continue;
+    auto new_literal_it = changed.find(data->literal);
+    if (new_literal_it == changed.end()) continue;
+    if (new_literal_it->second->scope()->new_target_var()) {
+      result->status =
+          debug::LiveEditResult::BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME;
+      return false;
+    }
+  }
+  return true;
 }
 
-void TranslateSourcePositionTable(Handle<AbstractCode> code,
-                                  Handle<JSArray> position_change_array) {
-  Isolate* isolate = code->GetIsolate();
+void TranslateSourcePositionTable(Isolate* isolate, Handle<BytecodeArray> code,
+                                  const std::vector<SourceChangeRange>& diffs) {
   Zone zone(isolate->allocator(), ZONE_NAME);
   SourcePositionTableBuilder builder(&zone);
 
-  Handle<ByteArray> source_position_table(code->source_position_table());
+  Handle<ByteArray> source_position_table(code->SourcePositionTable(), isolate);
   for (SourcePositionTableIterator iterator(*source_position_table);
        !iterator.done(); iterator.Advance()) {
     SourcePosition position = iterator.source_position();
     position.SetScriptOffset(
-        TranslatePosition(position.ScriptOffset(), position_change_array));
+        LiveEdit::TranslatePosition(diffs, position.ScriptOffset()));
     builder.AddPosition(iterator.code_offset(), position,
                         iterator.is_statement());
   }
 
   Handle<ByteArray> new_source_position_table(
-      builder.ToSourcePositionTable(isolate, code));
-  code->set_source_position_table(*new_source_position_table);
+      builder.ToSourcePositionTable(isolate));
+  code->set_source_position_table(*new_source_position_table, kReleaseStore);
+  LOG_CODE_EVENT(isolate,
+                 CodeLinePosInfoRecordEvent(code->GetFirstBytecodeAddress(),
+                                            *new_source_position_table));
 }
-}  // namespace
 
-void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
-                                      Handle<JSArray> position_change_array) {
-  SharedInfoWrapper shared_info_wrapper(shared_info_array);
-  Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
-
-  int old_function_start = info->start_position();
-  int new_function_start = TranslatePosition(old_function_start,
-                                             position_change_array);
-  int new_function_end = TranslatePosition(info->end_position(),
-                                           position_change_array);
-  int new_function_token_pos =
-      TranslatePosition(info->function_token_position(), position_change_array);
-
-  info->set_start_position(new_function_start);
-  info->set_end_position(new_function_end);
-  info->set_function_token_position(new_function_token_pos);
-
-  if (info->HasBytecodeArray()) {
+void UpdatePositions(Isolate* isolate, Handle<SharedFunctionInfo> sfi,
+                     const std::vector<SourceChangeRange>& diffs) {
+  int old_start_position = sfi->StartPosition();
+  int new_start_position =
+      LiveEdit::TranslatePosition(diffs, old_start_position);
+  int new_end_position = LiveEdit::TranslatePosition(diffs, sfi->EndPosition());
+  int new_function_token_position =
+      LiveEdit::TranslatePosition(diffs, sfi->function_token_position());
+  sfi->SetPosition(new_start_position, new_end_position);
+  sfi->SetFunctionTokenPosition(new_function_token_position,
+                                new_start_position);
+  if (sfi->HasBytecodeArray()) {
     TranslateSourcePositionTable(
-        Handle<AbstractCode>(AbstractCode::cast(info->bytecode_array())),
-        position_change_array);
-  }
-  if (info->code()->kind() == Code::FUNCTION) {
-    TranslateSourcePositionTable(
-        Handle<AbstractCode>(AbstractCode::cast(info->code())),
-        position_change_array);
-  }
-  if (info->HasDebugInfo()) {
-    // Existing break points will be re-applied. Reset the debug info here.
-    info->GetIsolate()->debug()->RemoveDebugInfoAndClearFromShared(
-        handle(info->GetDebugInfo()));
+        isolate, handle(sfi->GetBytecodeArray(), isolate), diffs);
   }
 }
+}  // anonymous namespace
 
-
-static Handle<Script> CreateScriptCopy(Handle<Script> original) {
-  Isolate* isolate = original->GetIsolate();
-
-  Handle<String> original_source(String::cast(original->source()));
-  Handle<Script> copy = isolate->factory()->NewScript(original_source);
-
-  copy->set_name(original->name());
-  copy->set_line_offset(original->line_offset());
-  copy->set_column_offset(original->column_offset());
-  copy->set_type(original->type());
-  copy->set_context_data(original->context_data());
-  copy->set_eval_from_shared(original->eval_from_shared());
-  copy->set_eval_from_position(original->eval_from_position());
-
-  Handle<FixedArray> infos(isolate->factory()->NewFixedArray(
-      original->shared_function_infos()->length()));
-  copy->set_shared_function_infos(*infos);
-
-  // Copy all the flags, but clear compilation state.
-  copy->set_flags(original->flags());
-  copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL);
-
-  return copy;
-}
-
-Handle<Object> LiveEdit::ChangeScriptSource(Handle<Script> original_script,
-                                            Handle<String> new_source,
-                                            Handle<Object> old_script_name) {
-  Isolate* isolate = original_script->GetIsolate();
-  Handle<Object> old_script_object;
-  if (old_script_name->IsString()) {
-    Handle<Script> old_script = CreateScriptCopy(original_script);
-    old_script->set_name(String::cast(*old_script_name));
-    old_script_object = old_script;
-    isolate->debug()->OnAfterCompile(old_script);
-  } else {
-    old_script_object = isolate->factory()->null_value();
+void LiveEdit::PatchScript(Isolate* isolate, Handle<Script> script,
+                           Handle<String> new_source, bool preview,
+                           debug::LiveEditResult* result) {
+  std::vector<SourceChangeRange> diffs;
+  LiveEdit::CompareStrings(isolate,
+                           handle(String::cast(script->source()), isolate),
+                           new_source, &diffs);
+  if (diffs.empty()) {
+    result->status = debug::LiveEditResult::OK;
+    return;
   }
 
-  original_script->set_source(*new_source);
+  UnoptimizedCompileState compile_state(isolate);
+  UnoptimizedCompileFlags flags =
+      UnoptimizedCompileFlags::ForScriptCompile(isolate, *script);
+  flags.set_is_eager(true);
+  ParseInfo parse_info(isolate, flags, &compile_state);
+  std::vector<FunctionLiteral*> literals;
+  if (!ParseScript(isolate, script, &parse_info, false, &literals, result))
+    return;
 
-  // Drop line ends so that they will be recalculated.
-  original_script->set_line_ends(isolate->heap()->undefined_value());
+  Handle<Script> new_script = isolate->factory()->CloneScript(script);
+  new_script->set_source(*new_source);
+  UnoptimizedCompileState new_compile_state(isolate);
+  UnoptimizedCompileFlags new_flags =
+      UnoptimizedCompileFlags::ForScriptCompile(isolate, *new_script);
+  new_flags.set_is_eager(true);
+  ParseInfo new_parse_info(isolate, new_flags, &new_compile_state);
+  std::vector<FunctionLiteral*> new_literals;
+  if (!ParseScript(isolate, new_script, &new_parse_info, true, &new_literals,
+                   result)) {
+    return;
+  }
 
-  return old_script_object;
-}
+  FunctionLiteralChanges literal_changes;
+  CalculateFunctionLiteralChanges(literals, diffs, &literal_changes);
 
+  LiteralMap changed;
+  LiteralMap unchanged;
+  MapLiterals(literal_changes, new_literals, &unchanged, &changed);
 
+  FunctionDataMap function_data_map;
+  for (const auto& mapping : changed) {
+    function_data_map.AddInterestingLiteral(script->id(), mapping.first, true);
+    function_data_map.AddInterestingLiteral(new_script->id(), mapping.second,
+                                            false);
+  }
+  for (const auto& mapping : unchanged) {
+    function_data_map.AddInterestingLiteral(script->id(), mapping.first, false);
+  }
+  Address restart_frame_fp = 0;
+  function_data_map.Fill(isolate, &restart_frame_fp);
 
-void LiveEdit::ReplaceRefToNestedFunction(
-    Handle<JSValue> parent_function_wrapper,
-    Handle<JSValue> orig_function_wrapper,
-    Handle<JSValue> subst_function_wrapper) {
+  if (!CanPatchScript(changed, script, new_script, function_data_map, result)) {
+    return;
+  }
+  if (restart_frame_fp &&
+      !CanRestartFrame(isolate, restart_frame_fp, function_data_map, changed,
+                       result)) {
+    return;
+  }
 
-  Handle<SharedFunctionInfo> parent_shared =
-      UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper);
-  Handle<SharedFunctionInfo> orig_shared =
-      UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper);
-  Handle<SharedFunctionInfo> subst_shared =
-      UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper);
+  if (preview) {
+    result->status = debug::LiveEditResult::OK;
+    return;
+  }
 
-  for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
-    if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
-      if (it.rinfo()->target_object() == *orig_shared) {
-        it.rinfo()->set_target_object(*subst_shared);
+  std::map<int, int> start_position_to_unchanged_id;
+  for (const auto& mapping : unchanged) {
+    FunctionData* data = nullptr;
+    if (!function_data_map.Lookup(script, mapping.first, &data)) continue;
+    Handle<SharedFunctionInfo> sfi;
+    if (!data->shared.ToHandle(&sfi)) continue;
+    DCHECK_EQ(sfi->script(), *script);
+
+    isolate->compilation_cache()->Remove(sfi);
+    isolate->debug()->DeoptimizeFunction(sfi);
+    if (sfi->HasDebugInfo()) {
+      Handle<DebugInfo> debug_info(sfi->GetDebugInfo(), isolate);
+      isolate->debug()->RemoveBreakInfoAndMaybeFree(debug_info);
+    }
+    SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, sfi);
+    UpdatePositions(isolate, sfi, diffs);
+
+    sfi->set_script(*new_script);
+    sfi->set_function_literal_id(mapping.second->function_literal_id());
+    new_script->shared_function_infos().Set(
+        mapping.second->function_literal_id(), HeapObjectReference::Weak(*sfi));
+    DCHECK_EQ(sfi->function_literal_id(),
+              mapping.second->function_literal_id());
+
+    // Save the new start_position -> id mapping, so that we can recover it when
+    // iterating over changed functions' constant pools.
+    start_position_to_unchanged_id[mapping.second->start_position()] =
+        mapping.second->function_literal_id();
+
+    if (sfi->HasUncompiledDataWithPreparseData()) {
+      sfi->ClearPreparseData();
+    }
+
+    for (auto& js_function : data->js_functions) {
+      js_function->set_raw_feedback_cell(
+          *isolate->factory()->many_closures_cell());
+      if (!js_function->is_compiled()) continue;
+      IsCompiledScope is_compiled_scope(
+          js_function->shared().is_compiled_scope(isolate));
+      JSFunction::EnsureFeedbackVector(js_function, &is_compiled_scope);
+    }
+
+    if (!sfi->HasBytecodeArray()) continue;
+    FixedArray constants = sfi->GetBytecodeArray().constant_pool();
+    for (int i = 0; i < constants.length(); ++i) {
+      if (!constants.get(i).IsSharedFunctionInfo()) continue;
+      FunctionData* data = nullptr;
+      if (!function_data_map.Lookup(SharedFunctionInfo::cast(constants.get(i)),
+                                    &data)) {
+        continue;
+      }
+      auto change_it = changed.find(data->literal);
+      if (change_it == changed.end()) continue;
+      if (!function_data_map.Lookup(new_script, change_it->second, &data)) {
+        continue;
+      }
+      Handle<SharedFunctionInfo> new_sfi;
+      if (!data->shared.ToHandle(&new_sfi)) continue;
+      constants.set(i, *new_sfi);
+    }
+  }
+  for (const auto& mapping : changed) {
+    FunctionData* data = nullptr;
+    if (!function_data_map.Lookup(new_script, mapping.second, &data)) continue;
+    Handle<SharedFunctionInfo> new_sfi = data->shared.ToHandleChecked();
+    DCHECK_EQ(new_sfi->script(), *new_script);
+
+    if (!function_data_map.Lookup(script, mapping.first, &data)) continue;
+    Handle<SharedFunctionInfo> sfi;
+    if (!data->shared.ToHandle(&sfi)) continue;
+
+    isolate->debug()->DeoptimizeFunction(sfi);
+    isolate->compilation_cache()->Remove(sfi);
+    for (auto& js_function : data->js_functions) {
+      js_function->set_shared(*new_sfi);
+      js_function->set_code(js_function->shared().GetCode());
+
+      js_function->set_raw_feedback_cell(
+          *isolate->factory()->many_closures_cell());
+      if (!js_function->is_compiled()) continue;
+      IsCompiledScope is_compiled_scope(
+          js_function->shared().is_compiled_scope(isolate));
+      JSFunction::EnsureFeedbackVector(js_function, &is_compiled_scope);
+    }
+  }
+  SharedFunctionInfo::ScriptIterator it(isolate, *new_script);
+  for (SharedFunctionInfo sfi = it.Next(); !sfi.is_null(); sfi = it.Next()) {
+    if (!sfi.HasBytecodeArray()) continue;
+    FixedArray constants = sfi.GetBytecodeArray().constant_pool();
+    for (int i = 0; i < constants.length(); ++i) {
+      if (!constants.get(i).IsSharedFunctionInfo()) continue;
+      SharedFunctionInfo inner_sfi = SharedFunctionInfo::cast(constants.get(i));
+      // See if there is a mapping from this function's start position to a
+      // unchanged function's id.
+      auto unchanged_it =
+          start_position_to_unchanged_id.find(inner_sfi.StartPosition());
+      if (unchanged_it == start_position_to_unchanged_id.end()) continue;
+
+      // Grab that function id from the new script's SFI list, which should have
+      // already been updated in in the unchanged pass.
+      SharedFunctionInfo old_unchanged_inner_sfi =
+          SharedFunctionInfo::cast(new_script->shared_function_infos()
+                                       .Get(unchanged_it->second)
+                                       ->GetHeapObject());
+      if (old_unchanged_inner_sfi == inner_sfi) continue;
+      DCHECK_NE(old_unchanged_inner_sfi, inner_sfi);
+      // Now some sanity checks. Make sure that the unchanged SFI has already
+      // been processed and patched to be on the new script ...
+      DCHECK_EQ(old_unchanged_inner_sfi.script(), *new_script);
+      constants.set(i, old_unchanged_inner_sfi);
+    }
+  }
+#ifdef DEBUG
+  {
+    // Check that all the functions in the new script are valid, that their
+    // function literals match what is expected, and that start positions are
+    // unique.
+    DisallowHeapAllocation no_gc;
+
+    SharedFunctionInfo::ScriptIterator it(isolate, *new_script);
+    std::set<int> start_positions;
+    for (SharedFunctionInfo sfi = it.Next(); !sfi.is_null(); sfi = it.Next()) {
+      DCHECK_EQ(sfi.script(), *new_script);
+      DCHECK_EQ(sfi.function_literal_id(), it.CurrentIndex());
+      // Don't check the start position of the top-level function, as it can
+      // overlap with a function in the script.
+      if (sfi.is_toplevel()) {
+        DCHECK_EQ(start_positions.find(sfi.StartPosition()),
+                  start_positions.end());
+        start_positions.insert(sfi.StartPosition());
+      }
+
+      if (!sfi.HasBytecodeArray()) continue;
+      // Check that all the functions in this function's constant pool are also
+      // on the new script, and that their id matches their index in the new
+      // scripts function list.
+      FixedArray constants = sfi.GetBytecodeArray().constant_pool();
+      for (int i = 0; i < constants.length(); ++i) {
+        if (!constants.get(i).IsSharedFunctionInfo()) continue;
+        SharedFunctionInfo inner_sfi =
+            SharedFunctionInfo::cast(constants.get(i));
+        DCHECK_EQ(inner_sfi.script(), *new_script);
+        DCHECK_EQ(inner_sfi, new_script->shared_function_infos()
+                                 .Get(inner_sfi.function_literal_id())
+                                 ->GetHeapObject());
       }
     }
   }
+#endif
+
+  if (restart_frame_fp) {
+    for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
+      if (it.frame()->fp() == restart_frame_fp) {
+        isolate->debug()->ScheduleFrameRestart(it.frame());
+        result->stack_changed = true;
+        break;
+      }
+    }
+  }
+
+  int script_id = script->id();
+  script->set_id(new_script->id());
+  new_script->set_id(script_id);
+  result->status = debug::LiveEditResult::OK;
+  result->script = ToApiHandle<v8::debug::Script>(new_script);
 }
 
+void LiveEdit::InitializeThreadLocal(Debug* debug) {
+  debug->thread_local_.restart_fp_ = 0;
+}
 
-// Check an activation against list of functions. If there is a function
-// that matches, its status in result array is changed to status argument value.
-static bool CheckActivation(Handle<JSArray> shared_info_array,
-                            Handle<JSArray> result,
-                            StackFrame* frame,
-                            LiveEdit::FunctionPatchabilityStatus status) {
-  if (!frame->is_java_script()) return false;
-
-  Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function());
-
-  Isolate* isolate = shared_info_array->GetIsolate();
-  int len = GetArrayLength(shared_info_array);
-  for (int i = 0; i < len; i++) {
-    HandleScope scope(isolate);
-    Handle<Object> element =
-        JSReceiver::GetElement(isolate, shared_info_array, i).ToHandleChecked();
-    Handle<JSValue> jsvalue = Handle<JSValue>::cast(element);
-    Handle<SharedFunctionInfo> shared =
-        UnwrapSharedFunctionInfoFromJSValue(jsvalue);
-
-    if (function->Inlines(*shared)) {
-      SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
-      return true;
+bool LiveEdit::RestartFrame(JavaScriptFrame* frame) {
+  if (!LiveEdit::kFrameDropperSupported) return false;
+  Isolate* isolate = frame->isolate();
+  StackFrameId break_frame_id = isolate->debug()->break_frame_id();
+  bool break_frame_found = break_frame_id == StackFrameId::NO_ID;
+  for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
+    StackFrame* current = it.frame();
+    break_frame_found = break_frame_found || break_frame_id == current->id();
+    if (current->fp() == frame->fp()) {
+      if (break_frame_found) {
+        isolate->debug()->ScheduleFrameRestart(current);
+        return true;
+      } else {
+        return false;
+      }
+    }
+    if (!break_frame_found) continue;
+    if (current->is_exit() || current->is_builtin_exit()) {
+      return false;
+    }
+    if (!current->is_java_script()) continue;
+    std::vector<Handle<SharedFunctionInfo>> shareds;
+    JavaScriptFrame::cast(current)->GetFunctions(&shareds);
+    for (auto& shared : shareds) {
+      if (IsResumableFunction(shared->kind())) {
+        return false;
+      }
     }
   }
   return false;
 }
 
-// Describes a set of call frames that execute any of listed functions.
-// Finding no such frames does not mean error.
-class MultipleFunctionTarget {
- public:
-  MultipleFunctionTarget(Handle<JSArray> old_shared_array,
-                         Handle<JSArray> new_shared_array,
-                         Handle<JSArray> result)
-      : old_shared_array_(old_shared_array),
-        new_shared_array_(new_shared_array),
-        result_(result) {}
-  bool MatchActivation(StackFrame* frame,
-      LiveEdit::FunctionPatchabilityStatus status) {
-    return CheckActivation(old_shared_array_, result_, frame, status);
-  }
-  const char* GetNotFoundMessage() const {
-    return NULL;
-  }
-  bool FrameUsesNewTarget(StackFrame* frame) {
-    if (!frame->is_java_script()) return false;
-    JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame);
-    Handle<SharedFunctionInfo> old_shared(jsframe->function()->shared());
-    Isolate* isolate = old_shared->GetIsolate();
-    int len = GetArrayLength(old_shared_array_);
-    // Find corresponding new shared function info and return whether it
-    // references new.target.
-    for (int i = 0; i < len; i++) {
-      HandleScope scope(isolate);
-      Handle<Object> old_element =
-          JSReceiver::GetElement(isolate, old_shared_array_, i)
-              .ToHandleChecked();
-      if (!old_shared.is_identical_to(UnwrapSharedFunctionInfoFromJSValue(
-              Handle<JSValue>::cast(old_element)))) {
-        continue;
-      }
+void LiveEdit::CompareStrings(Isolate* isolate, Handle<String> s1,
+                              Handle<String> s2,
+                              std::vector<SourceChangeRange>* diffs) {
+  s1 = String::Flatten(isolate, s1);
+  s2 = String::Flatten(isolate, s2);
 
-      Handle<Object> new_element =
-          JSReceiver::GetElement(isolate, new_shared_array_, i)
-              .ToHandleChecked();
-      if (new_element->IsUndefined(isolate)) return false;
-      Handle<SharedFunctionInfo> new_shared =
-          UnwrapSharedFunctionInfoFromJSValue(
-              Handle<JSValue>::cast(new_element));
-      if (new_shared->scope_info()->HasNewTarget()) {
-        SetElementSloppy(
-            result_, i,
-            Handle<Smi>(
-                Smi::FromInt(
-                    LiveEdit::FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART),
-                isolate));
-        return true;
-      }
-      return false;
-    }
-    return false;
-  }
+  LineEndsWrapper line_ends1(isolate, s1);
+  LineEndsWrapper line_ends2(isolate, s2);
 
-  void set_status(LiveEdit::FunctionPatchabilityStatus status) {
-    Isolate* isolate = old_shared_array_->GetIsolate();
-    int len = GetArrayLength(old_shared_array_);
-    for (int i = 0; i < len; ++i) {
-      Handle<Object> old_element =
-          JSReceiver::GetElement(isolate, result_, i).ToHandleChecked();
-      if (!old_element->IsSmi() ||
-          Smi::cast(*old_element)->value() ==
-              LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {
-        SetElementSloppy(result_, i,
-                         Handle<Smi>(Smi::FromInt(status), isolate));
-      }
-    }
-  }
+  LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
+  TokenizingLineArrayCompareOutput output(isolate, line_ends1, line_ends2, s1,
+                                          s2, diffs);
 
- private:
-  Handle<JSArray> old_shared_array_;
-  Handle<JSArray> new_shared_array_;
-  Handle<JSArray> result_;
-};
+  NarrowDownInput(&input, &output);
 
-
-// Drops all call frame matched by target and all frames above them.
-template <typename TARGET>
-static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
-                                                     TARGET& target,  // NOLINT
-                                                     bool do_drop) {
-  Debug* debug = isolate->debug();
-  Zone zone(isolate->allocator(), ZONE_NAME);
-  Vector<StackFrame*> frames = CreateStackMap(isolate, &zone);
-
-  int top_frame_index = -1;
-  int frame_index = 0;
-  for (; frame_index < frames.length(); frame_index++) {
-    StackFrame* frame = frames[frame_index];
-    if (frame->id() == debug->break_frame_id()) {
-      top_frame_index = frame_index;
-      break;
-    }
-    if (target.MatchActivation(
-            frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
-      // We are still above break_frame. It is not a target frame,
-      // it is a problem.
-      return "Debugger mark-up on stack is not found";
-    }
-  }
-
-  if (top_frame_index == -1) {
-    // We haven't found break frame, but no function is blocking us anyway.
-    return target.GetNotFoundMessage();
-  }
-
-  bool target_frame_found = false;
-  int bottom_js_frame_index = top_frame_index;
-  bool non_droppable_frame_found = false;
-  LiveEdit::FunctionPatchabilityStatus non_droppable_reason;
-
-  for (; frame_index < frames.length(); frame_index++) {
-    StackFrame* frame = frames[frame_index];
-    if (frame->is_exit() || frame->is_builtin_exit()) {
-      non_droppable_frame_found = true;
-      non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE;
-      break;
-    }
-    if (frame->is_java_script()) {
-      SharedFunctionInfo* shared =
-          JavaScriptFrame::cast(frame)->function()->shared();
-      if (IsResumableFunction(shared->kind())) {
-        non_droppable_frame_found = true;
-        non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR;
-        break;
-      }
-    }
-    if (target.MatchActivation(
-            frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
-      target_frame_found = true;
-      bottom_js_frame_index = frame_index;
-    }
-  }
-
-  if (non_droppable_frame_found) {
-    // There is a C or generator frame on stack.  We can't drop C frames, and we
-    // can't restart generators.  Check that there are no target frames below
-    // them.
-    for (; frame_index < frames.length(); frame_index++) {
-      StackFrame* frame = frames[frame_index];
-      if (frame->is_java_script()) {
-        if (target.MatchActivation(frame, non_droppable_reason)) {
-          // Fail.
-          return NULL;
-        }
-        if (non_droppable_reason ==
-                LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR &&
-            !target_frame_found) {
-          // Fail.
-          target.set_status(non_droppable_reason);
-          return NULL;
-        }
-      }
-    }
-  }
-
-  // We cannot restart a frame that uses new.target.
-  if (target.FrameUsesNewTarget(frames[bottom_js_frame_index])) return NULL;
-
-  if (!do_drop) {
-    // We are in check-only mode.
-    return NULL;
-  }
-
-  if (!target_frame_found) {
-    // Nothing to drop.
-    return target.GetNotFoundMessage();
-  }
-
-  if (!LiveEdit::kFrameDropperSupported) {
-    return "Stack manipulations are not supported in this architecture.";
-  }
-
-  debug->ScheduleFrameRestart(frames[bottom_js_frame_index]);
-  return NULL;
+  Comparator::CalculateDifference(&input, &output);
 }
 
-
-// Fills result array with statuses of functions. Modifies the stack
-// removing all listed function if possible and if do_drop is true.
-static const char* DropActivationsInActiveThread(
-    Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
-    Handle<JSArray> result, bool do_drop) {
-  MultipleFunctionTarget target(old_shared_array, new_shared_array, result);
-  Isolate* isolate = old_shared_array->GetIsolate();
-
-  const char* message =
-      DropActivationsInActiveThreadImpl(isolate, target, do_drop);
-  if (message) {
-    return message;
+int LiveEdit::TranslatePosition(const std::vector<SourceChangeRange>& diffs,
+                                int position) {
+  auto it = std::lower_bound(diffs.begin(), diffs.end(), position,
+                             [](const SourceChangeRange& change, int position) {
+                               return change.end_position < position;
+                             });
+  if (it != diffs.end() && position == it->end_position) {
+    return it->new_end_position;
   }
-
-  int array_len = GetArrayLength(old_shared_array);
-
-  // Replace "blocked on active" with "replaced on active" status.
-  for (int i = 0; i < array_len; i++) {
-    Handle<Object> obj =
-        JSReceiver::GetElement(isolate, result, i).ToHandleChecked();
-    if (*obj == Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
-      Handle<Object> replaced(
-          Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate);
-      SetElementSloppy(result, i, replaced);
-    }
-  }
-  return NULL;
+  if (it == diffs.begin()) return position;
+  DCHECK(it == diffs.end() || position <= it->start_position);
+  it = std::prev(it);
+  return position + (it->new_end_position - it->end_position);
 }
-
-
-bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array,
-                                    Handle<FixedArray> result,
-                                    int len) {
-  Isolate* isolate = shared_info_array->GetIsolate();
-  bool found_suspended_activations = false;
-
-  DCHECK_LE(len, result->length());
-
-  FunctionPatchabilityStatus active = FUNCTION_BLOCKED_ACTIVE_GENERATOR;
-
-  Heap* heap = isolate->heap();
-  HeapIterator iterator(heap, HeapIterator::kFilterUnreachable);
-  HeapObject* obj = NULL;
-  while ((obj = iterator.next()) != NULL) {
-    if (!obj->IsJSGeneratorObject()) continue;
-
-    JSGeneratorObject* gen = JSGeneratorObject::cast(obj);
-    if (gen->is_closed()) continue;
-
-    HandleScope scope(isolate);
-
-    for (int i = 0; i < len; i++) {
-      Handle<JSValue> jsvalue = Handle<JSValue>::cast(
-          FixedArray::get(*shared_info_array, i, isolate));
-      Handle<SharedFunctionInfo> shared =
-          UnwrapSharedFunctionInfoFromJSValue(jsvalue);
-
-      if (gen->function()->shared() == *shared) {
-        result->set(i, Smi::FromInt(active));
-        found_suspended_activations = true;
-      }
-    }
-  }
-
-  return found_suspended_activations;
-}
-
-
-class InactiveThreadActivationsChecker : public ThreadVisitor {
- public:
-  InactiveThreadActivationsChecker(Handle<JSArray> old_shared_array,
-                                   Handle<JSArray> result)
-      : old_shared_array_(old_shared_array),
-        result_(result),
-        has_blocked_functions_(false) {}
-  void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
-    for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
-      has_blocked_functions_ |=
-          CheckActivation(old_shared_array_, result_, it.frame(),
-                          LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
-    }
-  }
-  bool HasBlockedFunctions() {
-    return has_blocked_functions_;
-  }
-
- private:
-  Handle<JSArray> old_shared_array_;
-  Handle<JSArray> result_;
-  bool has_blocked_functions_;
-};
-
-
-Handle<JSArray> LiveEdit::CheckAndDropActivations(
-    Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
-    bool do_drop) {
-  Isolate* isolate = old_shared_array->GetIsolate();
-  int len = GetArrayLength(old_shared_array);
-
-  DCHECK(old_shared_array->HasFastElements());
-  Handle<FixedArray> old_shared_array_elements(
-      FixedArray::cast(old_shared_array->elements()));
-
-  Handle<JSArray> result = isolate->factory()->NewJSArray(len);
-  result->set_length(Smi::FromInt(len));
-  JSObject::EnsureWritableFastElements(result);
-  Handle<FixedArray> result_elements =
-      handle(FixedArray::cast(result->elements()), isolate);
-
-  // Fill the default values.
-  for (int i = 0; i < len; i++) {
-    FunctionPatchabilityStatus status = FUNCTION_AVAILABLE_FOR_PATCH;
-    result_elements->set(i, Smi::FromInt(status));
-  }
-
-  // Scan the heap for active generators -- those that are either currently
-  // running (as we wouldn't want to restart them, because we don't know where
-  // to restart them from) or suspended.  Fail if any one corresponds to the set
-  // of functions being edited.
-  if (FindActiveGenerators(old_shared_array_elements, result_elements, len)) {
-    return result;
-  }
-
-  // Check inactive threads. Fail if some functions are blocked there.
-  InactiveThreadActivationsChecker inactive_threads_checker(old_shared_array,
-                                                            result);
-  isolate->thread_manager()->IterateArchivedThreads(
-      &inactive_threads_checker);
-  if (inactive_threads_checker.HasBlockedFunctions()) {
-    return result;
-  }
-
-  // Try to drop activations from the current stack.
-  const char* error_message = DropActivationsInActiveThread(
-      old_shared_array, new_shared_array, result, do_drop);
-  if (error_message != NULL) {
-    // Add error message as an array extra element.
-    Handle<String> str =
-        isolate->factory()->NewStringFromAsciiChecked(error_message);
-    SetElementSloppy(result, len, str);
-  }
-  return result;
-}
-
-
-// Describes a single callframe a target. Not finding this frame
-// means an error.
-class SingleFrameTarget {
- public:
-  explicit SingleFrameTarget(JavaScriptFrame* frame)
-      : m_frame(frame),
-        m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {}
-
-  bool MatchActivation(StackFrame* frame,
-      LiveEdit::FunctionPatchabilityStatus status) {
-    if (frame->fp() == m_frame->fp()) {
-      m_saved_status = status;
-      return true;
-    }
-    return false;
-  }
-  const char* GetNotFoundMessage() const {
-    return "Failed to found requested frame";
-  }
-  LiveEdit::FunctionPatchabilityStatus saved_status() {
-    return m_saved_status;
-  }
-  void set_status(LiveEdit::FunctionPatchabilityStatus status) {
-    m_saved_status = status;
-  }
-
-  bool FrameUsesNewTarget(StackFrame* frame) {
-    if (!frame->is_java_script()) return false;
-    JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame);
-    Handle<SharedFunctionInfo> shared(jsframe->function()->shared());
-    return shared->scope_info()->HasNewTarget();
-  }
-
- private:
-  JavaScriptFrame* m_frame;
-  LiveEdit::FunctionPatchabilityStatus m_saved_status;
-};
-
-
-// Finds a drops required frame and all frames above.
-// Returns error message or NULL.
-const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) {
-  SingleFrameTarget target(frame);
-
-  const char* result =
-      DropActivationsInActiveThreadImpl(frame->isolate(), target, true);
-  if (result != NULL) {
-    return result;
-  }
-  if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) {
-    return "Function is blocked under native code";
-  }
-  if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR) {
-    return "Function is blocked under a generator activation";
-  }
-  return NULL;
-}
-
-Handle<JSArray> LiveEditFunctionTracker::Collect(FunctionLiteral* node,
-                                                 Handle<Script> script,
-                                                 Zone* zone, Isolate* isolate) {
-  LiveEditFunctionTracker visitor(script, zone, isolate);
-  visitor.VisitFunctionLiteral(node);
-  return visitor.result_;
-}
-
-LiveEditFunctionTracker::LiveEditFunctionTracker(Handle<Script> script,
-                                                 Zone* zone, Isolate* isolate)
-    : AstTraversalVisitor<LiveEditFunctionTracker>(isolate) {
-  current_parent_index_ = -1;
-  isolate_ = isolate;
-  len_ = 0;
-  result_ = isolate->factory()->NewJSArray(10);
-  script_ = script;
-  zone_ = zone;
-}
-
-void LiveEditFunctionTracker::VisitFunctionLiteral(FunctionLiteral* node) {
-  // FunctionStarted is called in pre-order.
-  FunctionStarted(node);
-  // Recurse using the regular traversal.
-  AstTraversalVisitor::VisitFunctionLiteral(node);
-  // FunctionDone are called in post-order.
-  Handle<SharedFunctionInfo> info =
-      script_->FindSharedFunctionInfo(isolate_, node).ToHandleChecked();
-  FunctionDone(info, node->scope());
-}
-
-void LiveEditFunctionTracker::FunctionStarted(FunctionLiteral* fun) {
-  HandleScope handle_scope(isolate_);
-  FunctionInfoWrapper info = FunctionInfoWrapper::Create(isolate_);
-  info.SetInitialProperties(fun->name(), fun->start_position(),
-                            fun->end_position(), fun->parameter_count(),
-                            current_parent_index_, fun->function_literal_id());
-  current_parent_index_ = len_;
-  SetElementSloppy(result_, len_, info.GetJSArray());
-  len_++;
-}
-
-// Saves full information about a function: its code, its scope info
-// and a SharedFunctionInfo object.
-void LiveEditFunctionTracker::FunctionDone(Handle<SharedFunctionInfo> shared,
-                                           Scope* scope) {
-  HandleScope handle_scope(isolate_);
-  FunctionInfoWrapper info = FunctionInfoWrapper::cast(
-      *JSReceiver::GetElement(isolate_, result_, current_parent_index_)
-           .ToHandleChecked());
-  info.SetSharedFunctionInfo(shared);
-
-  Handle<Object> scope_info_list = SerializeFunctionScope(scope);
-  info.SetFunctionScopeInfo(scope_info_list);
-
-  current_parent_index_ = info.GetParentIndex();
-}
-
-Handle<Object> LiveEditFunctionTracker::SerializeFunctionScope(Scope* scope) {
-  Handle<JSArray> scope_info_list = isolate_->factory()->NewJSArray(10);
-  int scope_info_length = 0;
-
-  // Saves some description of scope. It stores name and indexes of
-  // variables in the whole scope chain. Null-named slots delimit
-  // scopes of this chain.
-  Scope* current_scope = scope;
-  while (current_scope != NULL) {
-    HandleScope handle_scope(isolate_);
-    for (Variable* var : *current_scope->locals()) {
-      if (!var->IsContextSlot()) continue;
-      int context_index = var->index() - Context::MIN_CONTEXT_SLOTS;
-      int location = scope_info_length + context_index * 2;
-      SetElementSloppy(scope_info_list, location, var->name());
-      SetElementSloppy(scope_info_list, location + 1,
-                       handle(Smi::FromInt(var->index()), isolate_));
-    }
-    scope_info_length += current_scope->ContextLocalCount() * 2;
-    SetElementSloppy(scope_info_list, scope_info_length,
-                     isolate_->factory()->null_value());
-    scope_info_length++;
-
-    current_scope = current_scope->outer_scope();
-  }
-
-  return scope_info_list;
-}
-
 }  // namespace internal
 }  // namespace v8
diff --git a/src/debug/liveedit.h b/src/debug/liveedit.h
index 4ad1bc5..4291efb 100644
--- a/src/debug/liveedit.h
+++ b/src/debug/liveedit.h
@@ -5,317 +5,74 @@
 #ifndef V8_DEBUG_LIVEEDIT_H_
 #define V8_DEBUG_LIVEEDIT_H_
 
+#include <vector>
 
-// Live Edit feature implementation.
-// User should be able to change script on already running VM. This feature
-// matches hot swap features in other frameworks.
-//
-// The basic use-case is when user spots some mistake in function body
-// from debugger and wishes to change the algorithm without restart.
-//
-// A single change always has a form of a simple replacement (in pseudo-code):
-//   script.source[positions, positions+length] = new_string;
-// Implementation first determines, which function's body includes this
-// change area. Then both old and new versions of script are fully compiled
-// in order to analyze, whether the function changed its outer scope
-// expectations (or number of parameters). If it didn't, function's code is
-// patched with a newly compiled code. If it did change, enclosing function
-// gets patched. All inner functions are left untouched, whatever happened
-// to them in a new script version. However, new version of code will
-// instantiate newly compiled functions.
-
-
-#include "src/allocation.h"
-#include "src/ast/ast-traversal-visitor.h"
+#include "src/common/globals.h"
+#include "src/handles/handles.h"
 
 namespace v8 {
+namespace debug {
+struct LiveEditResult;
+}  // namespace debug
 namespace internal {
 
-// This class collects some specific information on structure of functions
-// in a particular script.
-//
-// The primary interest of the Tracker is to record function scope structures
-// in order to analyze whether function code may be safely patched (with new
-// code successfully reading existing data from function scopes). The Tracker
-// also collects compiled function codes.
-class LiveEditFunctionTracker
-    : public AstTraversalVisitor<LiveEditFunctionTracker> {
- public:
-  // Traverses the entire AST, and records information about all
-  // FunctionLiterals for further use by LiveEdit code patching. The collected
-  // information is returned as a serialized array.
-  static Handle<JSArray> Collect(FunctionLiteral* node, Handle<Script> script,
-                                 Zone* zone, Isolate* isolate);
+class Script;
+class String;
+class Debug;
+class JavaScriptFrame;
 
- protected:
-  friend AstTraversalVisitor<LiveEditFunctionTracker>;
-  void VisitFunctionLiteral(FunctionLiteral* node);
-
- private:
-  LiveEditFunctionTracker(Handle<Script> script, Zone* zone, Isolate* isolate);
-
-  void FunctionStarted(FunctionLiteral* fun);
-  void FunctionDone(Handle<SharedFunctionInfo> shared, Scope* scope);
-  Handle<Object> SerializeFunctionScope(Scope* scope);
-
-  Handle<Script> script_;
-  Zone* zone_;
-  Isolate* isolate_;
-
-  Handle<JSArray> result_;
-  int len_;
-  int current_parent_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(LiveEditFunctionTracker);
+struct SourceChangeRange {
+  int start_position;
+  int end_position;
+  int new_start_position;
+  int new_end_position;
 };
 
+/**
+  Liveedit step-by-step:
+  1. calculate diff between old source and new source,
+  2. map function literals from old source to new source,
+  3. create new script for new_source,
+  4. mark literals with changed code as changed, all others as unchanged,
+  5. check that for changed literals there are no:
+    - running generators in the heap,
+    - non droppable frames (e.g. running generator) above them on stack.
+  6. mark the bottom most frame with changed function as scheduled for restart
+     if any,
+  7. for unchanged functions:
+    - deoptimize,
+    - remove from cache,
+    - update source positions,
+    - move to new script,
+    - reset feedback information and preparsed scope information if any,
+    - replace any sfi in constant pool with changed one if any.
+  8. for changed functions:
+    - deoptimize
+    - remove from cache,
+    - reset feedback information,
+    - update all links from js functions to old shared with new one.
+  9. swap scripts.
+ */
 
-class LiveEdit : AllStatic {
+class V8_EXPORT_PRIVATE LiveEdit : AllStatic {
  public:
   static void InitializeThreadLocal(Debug* debug);
 
-  MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo(
-      Handle<Script> script,
-      Handle<String> source);
-
-  static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
-                                  Handle<JSArray> shared_info_array);
-
-  static void FixupScript(Handle<Script> script, int max_function_literal_id);
-
-  static void FunctionSourceUpdated(Handle<JSArray> shared_info_array,
-                                    int new_function_literal_id);
-
-  // Updates script field in FunctionSharedInfo.
-  static void SetFunctionScript(Handle<JSValue> function_wrapper,
-                                Handle<Object> script_handle);
-
-  static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
-                                     Handle<JSArray> position_change_array);
-
-  // For a script updates its source field. If old_script_name is provided
-  // (i.e. is a String), also creates a copy of the script with its original
-  // source and sends notification to debugger.
-  static Handle<Object> ChangeScriptSource(Handle<Script> original_script,
-                                           Handle<String> new_source,
-                                           Handle<Object> old_script_name);
-
-  // In a code of a parent function replaces original function as embedded
-  // object with a substitution one.
-  static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
-                                         Handle<JSValue> orig_function_shared,
-                                         Handle<JSValue> subst_function_shared);
-
-  // Find open generator activations, and set corresponding "result" elements to
-  // FUNCTION_BLOCKED_ACTIVE_GENERATOR.
-  static bool FindActiveGenerators(Handle<FixedArray> shared_info_array,
-                                   Handle<FixedArray> result, int len);
-
-  // Checks listed functions on stack and return array with corresponding
-  // FunctionPatchabilityStatus statuses; extra array element may
-  // contain general error message. Modifies the current stack and
-  // has restart the lowest found frames and drops all other frames above
-  // if possible and if do_drop is true.
-  static Handle<JSArray> CheckAndDropActivations(
-      Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
-      bool do_drop);
-
   // Restarts the call frame and completely drops all frames above it.
-  // Return error message or NULL.
-  static const char* RestartFrame(JavaScriptFrame* frame);
+  static bool RestartFrame(JavaScriptFrame* frame);
 
-  // A copy of this is in liveedit.js.
-  enum FunctionPatchabilityStatus {
-    FUNCTION_AVAILABLE_FOR_PATCH = 1,
-    FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
-    FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
-    FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
-    FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
-    FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
-    FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7,
-    FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART = 8
-  };
-
-  // Compares 2 strings line-by-line, then token-wise and returns diff in form
-  // of array of triplets (pos1, pos1_end, pos2_end) describing list
-  // of diff chunks.
-  static Handle<JSArray> CompareStrings(Handle<String> s1,
-                                        Handle<String> s2);
-
+  static void CompareStrings(Isolate* isolate, Handle<String> a,
+                             Handle<String> b,
+                             std::vector<SourceChangeRange>* diffs);
+  static int TranslatePosition(const std::vector<SourceChangeRange>& changed,
+                               int position);
+  static void PatchScript(Isolate* isolate, Handle<Script> script,
+                          Handle<String> source, bool preview,
+                          debug::LiveEditResult* result);
   // Architecture-specific constant.
   static const bool kFrameDropperSupported;
 };
-
-
-// A general-purpose comparator between 2 arrays.
-class Comparator {
- public:
-  // Holds 2 arrays of some elements allowing to compare any pair of
-  // element from the first array and element from the second array.
-  class Input {
-   public:
-    virtual int GetLength1() = 0;
-    virtual int GetLength2() = 0;
-    virtual bool Equals(int index1, int index2) = 0;
-
-   protected:
-    virtual ~Input() {}
-  };
-
-  // Receives compare result as a series of chunks.
-  class Output {
-   public:
-    // Puts another chunk in result list. Note that technically speaking
-    // only 3 arguments actually needed with 4th being derivable.
-    virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
-
-   protected:
-    virtual ~Output() {}
-  };
-
-  // Finds the difference between 2 arrays of elements.
-  static void CalculateDifference(Input* input,
-                                  Output* result_writer);
-};
-
-
-
-// Simple helper class that creates more or less typed structures over
-// JSArray object. This is an adhoc method of passing structures from C++
-// to JavaScript.
-template<typename S>
-class JSArrayBasedStruct {
- public:
-  static S Create(Isolate* isolate) {
-    Factory* factory = isolate->factory();
-    Handle<JSArray> array = factory->NewJSArray(S::kSize_);
-    return S(array);
-  }
-
-  static S cast(Object* object) {
-    JSArray* array = JSArray::cast(object);
-    Handle<JSArray> array_handle(array);
-    return S(array_handle);
-  }
-
-  explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
-  }
-
-  Handle<JSArray> GetJSArray() {
-    return array_;
-  }
-
-  Isolate* isolate() const {
-    return array_->GetIsolate();
-  }
-
- protected:
-  void SetField(int field_position, Handle<Object> value) {
-    Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
-        .Assert();
-  }
-
-  void SetSmiValueField(int field_position, int value) {
-    SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
-  }
-
-  Handle<Object> GetField(int field_position) {
-    return JSReceiver::GetElement(isolate(), array_, field_position)
-        .ToHandleChecked();
-  }
-
-  int GetSmiValueField(int field_position) {
-    Handle<Object> res = GetField(field_position);
-    return Handle<Smi>::cast(res)->value();
-  }
-
- private:
-  Handle<JSArray> array_;
-};
-
-
-// Represents some function compilation details. This structure will be used
-// from JavaScript. It contains Code object, which is kept wrapped
-// into a BlindReference for sanitizing reasons.
-class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
- public:
-  explicit FunctionInfoWrapper(Handle<JSArray> array)
-      : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
-  }
-
-  void SetInitialProperties(Handle<String> name, int start_position,
-                            int end_position, int param_num, int parent_index,
-                            int function_literal_id);
-
-  void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
-    this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
-  }
-
-  void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
-
-  Handle<SharedFunctionInfo> GetSharedFunctionInfo();
-
-  int GetParentIndex() {
-    return this->GetSmiValueField(kParentIndexOffset_);
-  }
-
-  int GetStartPosition() {
-    return this->GetSmiValueField(kStartPositionOffset_);
-  }
-
-  int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); }
-
- private:
-  static const int kFunctionNameOffset_ = 0;
-  static const int kStartPositionOffset_ = 1;
-  static const int kEndPositionOffset_ = 2;
-  static const int kParamNumOffset_ = 3;
-  static const int kFunctionScopeInfoOffset_ = 4;
-  static const int kParentIndexOffset_ = 5;
-  static const int kSharedFunctionInfoOffset_ = 6;
-  static const int kFunctionLiteralIdOffset_ = 7;
-  static const int kSize_ = 8;
-
-  friend class JSArrayBasedStruct<FunctionInfoWrapper>;
-};
-
-
-// Wraps SharedFunctionInfo along with some of its fields for passing it
-// back to JavaScript. SharedFunctionInfo object itself is additionally
-// wrapped into BlindReference for sanitizing reasons.
-class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
- public:
-  static bool IsInstance(Handle<JSArray> array) {
-    if (array->length() != Smi::FromInt(kSize_)) return false;
-    Handle<Object> element(
-        JSReceiver::GetElement(array->GetIsolate(), array, kSharedInfoOffset_)
-            .ToHandleChecked());
-    if (!element->IsJSValue()) return false;
-    return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
-  }
-
-  explicit SharedInfoWrapper(Handle<JSArray> array)
-      : JSArrayBasedStruct<SharedInfoWrapper>(array) {
-  }
-
-  void SetProperties(Handle<String> name,
-                     int start_position,
-                     int end_position,
-                     Handle<SharedFunctionInfo> info);
-
-  Handle<SharedFunctionInfo> GetInfo();
-
- private:
-  static const int kFunctionNameOffset_ = 0;
-  static const int kStartPositionOffset_ = 1;
-  static const int kEndPositionOffset_ = 2;
-  static const int kSharedInfoOffset_ = 3;
-  static const int kSize_ = 4;
-
-  friend class JSArrayBasedStruct<SharedInfoWrapper>;
-};
-
 }  // namespace internal
 }  // namespace v8
 
-#endif /* V8_DEBUG_LIVEEDIT_H_ */
+#endif  // V8_DEBUG_LIVEEDIT_H_
diff --git a/src/debug/liveedit.js b/src/debug/liveedit.js
deleted file mode 100644
index 8e20654..0000000
--- a/src/debug/liveedit.js
+++ /dev/null
@@ -1,1053 +0,0 @@
-// Copyright 2012 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// LiveEdit feature implementation. The script should be executed after
-// debug.js.
-
-// A LiveEdit namespace. It contains functions that modifies JavaScript code
-// according to changes of script source (if possible).
-//
-// When new script source is put in, the difference is calculated textually,
-// in form of list of delete/add/change chunks. The functions that include
-// change chunk(s) get recompiled, or their enclosing functions are
-// recompiled instead.
-// If the function may not be recompiled (e.g. it was completely erased in new
-// version of the script) it remains unchanged, but the code that could
-// create a new instance of this function goes away. An old version of script
-// is created to back up this obsolete function.
-// All unchanged functions have their positions updated accordingly.
-//
-// LiveEdit namespace is declared inside a single function constructor.
-
-(function(global, utils) {
-  "use strict";
-
-  // -------------------------------------------------------------------
-  // Imports
-
-  var FindScriptSourcePosition = global.Debug.findScriptSourcePosition;
-  var GlobalArray = global.Array;
-  var MathFloor = global.Math.floor;
-  var MathMax = global.Math.max;
-  var SyntaxError = global.SyntaxError;
-
-  // -------------------------------------------------------------------
-
-  // Forward declaration for minifier.
-  var FunctionStatus;
-
-  // Applies the change to the script.
-  // The change is in form of list of chunks encoded in a single array as
-  // a series of triplets (pos1_start, pos1_end, pos2_end)
-  function ApplyPatchMultiChunk(script, diff_array, new_source, preview_only,
-      change_log) {
-
-    var old_source = script.source;
-
-    // Gather compile information about old version of script.
-    var old_compile_info = GatherCompileInfo(old_source, script);
-
-    // Build tree structures for old and new versions of the script.
-    var root_old_node = BuildCodeInfoTree(old_compile_info);
-
-    var pos_translator = new PosTranslator(diff_array);
-
-    // Analyze changes.
-    MarkChangedFunctions(root_old_node, pos_translator.GetChunks());
-
-    // Find all SharedFunctionInfo's that were compiled from this script.
-    FindLiveSharedInfos(root_old_node, script);
-
-    // Gather compile information about new version of script.
-    var new_compile_info;
-    try {
-      new_compile_info = GatherCompileInfo(new_source, script);
-    } catch (e) {
-      var failure =
-          new Failure("Failed to compile new version of script: " + e);
-      if (e instanceof SyntaxError) {
-        var details = {
-          type: "liveedit_compile_error",
-          syntaxErrorMessage: e.message
-        };
-        CopyErrorPositionToDetails(e, details);
-        failure.details = details;
-      }
-      throw failure;
-    }
-
-    var max_function_literal_id = new_compile_info.reduce(
-        (max, info) => MathMax(max, info.function_literal_id), 0);
-
-    var root_new_node = BuildCodeInfoTree(new_compile_info);
-
-    // Link recompiled script data with other data.
-    FindCorrespondingFunctions(root_old_node, root_new_node);
-
-    // Prepare to-do lists.
-    var replace_code_list = new GlobalArray();
-    var link_to_old_script_list = new GlobalArray();
-    var link_to_original_script_list = new GlobalArray();
-    var update_positions_list = new GlobalArray();
-
-    function HarvestTodo(old_node) {
-      function CollectDamaged(node) {
-        link_to_old_script_list.push(node);
-        for (var i = 0; i < node.children.length; i++) {
-          CollectDamaged(node.children[i]);
-        }
-      }
-
-      // Recursively collects all newly compiled functions that are going into
-      // business and should have link to the actual script updated.
-      function CollectNew(node_list) {
-        for (var i = 0; i < node_list.length; i++) {
-          link_to_original_script_list.push(node_list[i]);
-          CollectNew(node_list[i].children);
-        }
-      }
-
-      if (old_node.status == FunctionStatus.DAMAGED) {
-        CollectDamaged(old_node);
-        return;
-      }
-      if (old_node.status == FunctionStatus.UNCHANGED) {
-        update_positions_list.push(old_node);
-      } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) {
-        update_positions_list.push(old_node);
-      } else if (old_node.status == FunctionStatus.CHANGED) {
-        replace_code_list.push(old_node);
-        CollectNew(old_node.unmatched_new_nodes);
-      }
-      for (var i = 0; i < old_node.children.length; i++) {
-        HarvestTodo(old_node.children[i]);
-      }
-    }
-
-    var preview_description = {
-        change_tree: DescribeChangeTree(root_old_node),
-        textual_diff: {
-          old_len: old_source.length,
-          new_len: new_source.length,
-          chunks: diff_array
-        },
-        updated: false
-    };
-
-    if (preview_only) {
-      return preview_description;
-    }
-
-    HarvestTodo(root_old_node);
-
-    // Collect shared infos for functions whose code need to be patched.
-    var replaced_function_old_infos = new GlobalArray();
-    var replaced_function_new_infos = new GlobalArray();
-    for (var i = 0; i < replace_code_list.length; i++) {
-      var old_infos = replace_code_list[i].live_shared_function_infos;
-      var new_info =
-          replace_code_list[i].corresponding_node.info.shared_function_info;
-
-      if (old_infos) {
-        for (var j = 0; j < old_infos.length; j++) {
-          replaced_function_old_infos.push(old_infos[j]);
-          replaced_function_new_infos.push(new_info);
-        }
-      }
-    }
-
-    // We haven't changed anything before this line yet.
-    // Committing all changes.
-
-    // Check that function being patched is not currently on stack or drop them.
-    var dropped_functions_number =
-        CheckStackActivations(replaced_function_old_infos,
-                              replaced_function_new_infos,
-                              change_log);
-
-    // Our current implementation requires client to manually issue "step in"
-    // command for correct stack state if the stack was modified.
-    preview_description.stack_modified = dropped_functions_number != 0;
-
-    var old_script;
-
-    // Create an old script only if there are function that should be linked
-    // to old version.
-    if (link_to_old_script_list.length == 0) {
-      %LiveEditReplaceScript(script, new_source, null);
-      old_script = UNDEFINED;
-    } else {
-      var old_script_name = CreateNameForOldScript(script);
-
-      // Update the script text and create a new script representing an old
-      // version of the script.
-      old_script = %LiveEditReplaceScript(script, new_source, old_script_name);
-
-      var link_to_old_script_report = new GlobalArray();
-      change_log.push( { linked_to_old_script: link_to_old_script_report } );
-
-      // We need to link to old script all former nested functions.
-      for (var i = 0; i < link_to_old_script_list.length; i++) {
-        LinkToOldScript(link_to_old_script_list[i], old_script,
-            link_to_old_script_report);
-      }
-
-      preview_description.created_script_name = old_script_name;
-    }
-
-    for (var i = 0; i < replace_code_list.length; i++) {
-      PatchFunctionCode(replace_code_list[i], change_log);
-    }
-
-    var position_patch_report = new GlobalArray();
-    change_log.push( {position_patched: position_patch_report} );
-
-    for (var i = 0; i < update_positions_list.length; i++) {
-      // TODO(LiveEdit): take into account whether it's source_changed or
-      // unchanged and whether positions changed at all.
-      PatchPositions(update_positions_list[i], diff_array,
-          position_patch_report);
-
-      if (update_positions_list[i].live_shared_function_infos) {
-        var new_function_literal_id =
-            update_positions_list[i]
-                .corresponding_node.info.function_literal_id;
-        update_positions_list[i].live_shared_function_infos.forEach(function(
-            info) {
-          %LiveEditFunctionSourceUpdated(
-              info.raw_array, new_function_literal_id);
-        });
-      }
-    }
-
-    %LiveEditFixupScript(script, max_function_literal_id);
-
-    // Link all the functions we're going to use to an actual script.
-    for (var i = 0; i < link_to_original_script_list.length; i++) {
-      %LiveEditFunctionSetScript(
-          link_to_original_script_list[i].info.shared_function_info, script);
-    }
-
-    preview_description.updated = true;
-    return preview_description;
-  }
-
-  // Fully compiles source string as a script. Returns Array of
-  // FunctionCompileInfo -- a descriptions of all functions of the script.
-  // Elements of array are ordered by start positions of functions (from top
-  // to bottom) in the source. Fields outer_index and next_sibling_index help
-  // to navigate the nesting structure of functions.
-  //
-  // All functions get compiled linked to script provided as parameter script.
-  // TODO(LiveEdit): consider not using actual scripts as script, because
-  // we have to manually erase all links right after compile.
-  function GatherCompileInfo(source, script) {
-    // Get function info, elements are partially sorted (it is a tree of
-    // nested functions serialized as parent followed by serialized children.
-    var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
-
-    // Sort function infos by start position field.
-    var compile_info = new GlobalArray();
-    var old_index_map = new GlobalArray();
-    for (var i = 0; i < raw_compile_info.length; i++) {
-      var info = new FunctionCompileInfo(raw_compile_info[i]);
-      // Remove all links to the actual script. Breakpoints system and
-      // LiveEdit itself believe that any function in heap that points to a
-      // particular script is a regular function.
-      // For some functions we will restore this link later.
-      %LiveEditFunctionSetScript(info.shared_function_info, UNDEFINED);
-      compile_info.push(info);
-      old_index_map.push(i);
-    }
-
-    for (var i = 0; i < compile_info.length; i++) {
-      var k = i;
-      for (var j = i + 1; j < compile_info.length; j++) {
-        if (compile_info[k].start_position > compile_info[j].start_position) {
-          k = j;
-        }
-      }
-      if (k != i) {
-        var temp_info = compile_info[k];
-        var temp_index = old_index_map[k];
-        compile_info[k] = compile_info[i];
-        old_index_map[k] = old_index_map[i];
-        compile_info[i] = temp_info;
-        old_index_map[i] = temp_index;
-      }
-    }
-
-    // After sorting update outer_index field using old_index_map. Also
-    // set next_sibling_index field.
-    var current_index = 0;
-
-    // The recursive function, that goes over all children of a particular
-    // node (i.e. function info).
-    function ResetIndexes(new_parent_index, old_parent_index) {
-      var previous_sibling = -1;
-      while (current_index < compile_info.length &&
-          compile_info[current_index].outer_index == old_parent_index) {
-        var saved_index = current_index;
-        compile_info[saved_index].outer_index = new_parent_index;
-        if (previous_sibling != -1) {
-          compile_info[previous_sibling].next_sibling_index = saved_index;
-        }
-        previous_sibling = saved_index;
-        current_index++;
-        ResetIndexes(saved_index, old_index_map[saved_index]);
-      }
-      if (previous_sibling != -1) {
-        compile_info[previous_sibling].next_sibling_index = -1;
-      }
-    }
-
-    ResetIndexes(-1, -1);
-    Assert(current_index == compile_info.length);
-
-    return compile_info;
-  }
-
-
-  // Replaces function's Code.
-  function PatchFunctionCode(old_node, change_log) {
-    var new_info = old_node.corresponding_node.info;
-    if (old_node.live_shared_function_infos) {
-      old_node.live_shared_function_infos.forEach(function (old_info) {
-        %LiveEditReplaceFunctionCode(new_info.raw_array,
-                                     old_info.raw_array);
-
-        // The function got a new code. However, this new code brings all new
-        // instances of SharedFunctionInfo for nested functions. However,
-        // we want the original instances to be used wherever possible.
-        // (This is because old instances and new instances will be both
-        // linked to a script and breakpoints subsystem does not really
-        // expects this; neither does LiveEdit subsystem on next call).
-        for (var i = 0; i < old_node.children.length; i++) {
-          if (old_node.children[i].corresponding_node) {
-            var corresponding_child_info =
-                old_node.children[i].corresponding_node.info.
-                    shared_function_info;
-
-            if (old_node.children[i].live_shared_function_infos) {
-              old_node.children[i].live_shared_function_infos.
-                  forEach(function (old_child_info) {
-                    %LiveEditReplaceRefToNestedFunction(
-                        old_info.info,
-                        corresponding_child_info,
-                        old_child_info.info);
-                  });
-            }
-          }
-        }
-      });
-
-      change_log.push( {function_patched: new_info.function_name} );
-    } else {
-      change_log.push( {function_patched: new_info.function_name,
-          function_info_not_found: true} );
-    }
-  }
-
-
-  // Makes a function associated with another instance of a script (the
-  // one representing its old version). This way the function still
-  // may access its own text.
-  function LinkToOldScript(old_info_node, old_script, report_array) {
-    if (old_info_node.live_shared_function_infos) {
-      old_info_node.live_shared_function_infos.
-          forEach(function (info) {
-            %LiveEditFunctionSetScript(info.info, old_script);
-          });
-
-      report_array.push( { name: old_info_node.info.function_name } );
-    } else {
-      report_array.push(
-          { name: old_info_node.info.function_name, not_found: true } );
-    }
-  }
-
-  function Assert(condition, message) {
-    if (!condition) {
-      if (message) {
-        throw "Assert " + message;
-      } else {
-        throw "Assert";
-      }
-    }
-  }
-
-  function DiffChunk(pos1, pos2, len1, len2) {
-    this.pos1 = pos1;
-    this.pos2 = pos2;
-    this.len1 = len1;
-    this.len2 = len2;
-  }
-
-  function PosTranslator(diff_array) {
-    var chunks = new GlobalArray();
-    var current_diff = 0;
-    for (var i = 0; i < diff_array.length; i += 3) {
-      var pos1_begin = diff_array[i];
-      var pos2_begin = pos1_begin + current_diff;
-      var pos1_end = diff_array[i + 1];
-      var pos2_end = diff_array[i + 2];
-      chunks.push(new DiffChunk(pos1_begin, pos2_begin, pos1_end - pos1_begin,
-          pos2_end - pos2_begin));
-      current_diff = pos2_end - pos1_end;
-    }
-    this.chunks = chunks;
-  }
-  PosTranslator.prototype.GetChunks = function() {
-    return this.chunks;
-  };
-
-  PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) {
-    var array = this.chunks;
-    if (array.length == 0 || pos < array[0].pos1) {
-      return pos;
-    }
-    var chunk_index1 = 0;
-    var chunk_index2 = array.length - 1;
-
-    while (chunk_index1 < chunk_index2) {
-      var middle_index = MathFloor((chunk_index1 + chunk_index2) / 2);
-      if (pos < array[middle_index + 1].pos1) {
-        chunk_index2 = middle_index;
-      } else {
-        chunk_index1 = middle_index + 1;
-      }
-    }
-    var chunk = array[chunk_index1];
-    if (pos >= chunk.pos1 + chunk.len1) {
-      return pos + chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1;
-    }
-
-    if (!inside_chunk_handler) {
-      inside_chunk_handler = PosTranslator.DefaultInsideChunkHandler;
-    }
-    return inside_chunk_handler(pos, chunk);
-  };
-
-  PosTranslator.DefaultInsideChunkHandler = function(pos, diff_chunk) {
-    Assert(false, "Cannot translate position in changed area");
-  };
-
-  PosTranslator.ShiftWithTopInsideChunkHandler =
-      function(pos, diff_chunk) {
-    // We carelessly do not check whether we stay inside the chunk after
-    // translation.
-    return pos - diff_chunk.pos1 + diff_chunk.pos2;
-  };
-
-  var FunctionStatus = {
-      // No change to function or its inner functions; however its positions
-      // in script may have been shifted.
-      UNCHANGED: "unchanged",
-      // The code of a function remains unchanged, but something happened inside
-      // some inner functions.
-      SOURCE_CHANGED: "source changed",
-      // The code of a function is changed or some nested function cannot be
-      // properly patched so this function must be recompiled.
-      CHANGED: "changed",
-      // Function is changed but cannot be patched.
-      DAMAGED: "damaged"
-  };
-
-  function CodeInfoTreeNode(code_info, children, array_index) {
-    this.info = code_info;
-    this.children = children;
-    // an index in array of compile_info
-    this.array_index = array_index;
-    this.parent = UNDEFINED;
-
-    this.status = FunctionStatus.UNCHANGED;
-    // Status explanation is used for debugging purposes and will be shown
-    // in user UI if some explanations are needed.
-    this.status_explanation = UNDEFINED;
-    this.new_start_pos = UNDEFINED;
-    this.new_end_pos = UNDEFINED;
-    this.corresponding_node = UNDEFINED;
-    this.unmatched_new_nodes = UNDEFINED;
-
-    // 'Textual' correspondence/matching is weaker than 'pure'
-    // correspondence/matching. We need 'textual' level for visual presentation
-    // in UI, we use 'pure' level for actual code manipulation.
-    // Sometimes only function body is changed (functions in old and new script
-    // textually correspond), but we cannot patch the code, so we see them
-    // as an old function deleted and new function created.
-    this.textual_corresponding_node = UNDEFINED;
-    this.textually_unmatched_new_nodes = UNDEFINED;
-
-    this.live_shared_function_infos = UNDEFINED;
-  }
-
-  // From array of function infos that is implicitly a tree creates
-  // an actual tree of functions in script.
-  function BuildCodeInfoTree(code_info_array) {
-    // Throughtout all function we iterate over input array.
-    var index = 0;
-
-    // Recursive function that builds a branch of tree.
-    function BuildNode() {
-      var my_index = index;
-      index++;
-      var child_array = new GlobalArray();
-      while (index < code_info_array.length &&
-          code_info_array[index].outer_index == my_index) {
-        child_array.push(BuildNode());
-      }
-      var node = new CodeInfoTreeNode(code_info_array[my_index], child_array,
-          my_index);
-      for (var i = 0; i < child_array.length; i++) {
-        child_array[i].parent = node;
-      }
-      return node;
-    }
-
-    var root = BuildNode();
-    Assert(index == code_info_array.length);
-    return root;
-  }
-
-  // Applies a list of the textual diff chunks onto the tree of functions.
-  // Determines status of each function (from unchanged to damaged). However
-  // children of unchanged functions are ignored.
-  function MarkChangedFunctions(code_info_tree, chunks) {
-
-    // A convenient iterator over diff chunks that also translates
-    // positions from old to new in a current non-changed part of script.
-    var chunk_it = new function() {
-      var chunk_index = 0;
-      var pos_diff = 0;
-      this.current = function() { return chunks[chunk_index]; };
-      this.next = function() {
-        var chunk = chunks[chunk_index];
-        pos_diff = chunk.pos2 + chunk.len2 - (chunk.pos1 + chunk.len1);
-        chunk_index++;
-      };
-      this.done = function() { return chunk_index >= chunks.length; };
-      this.TranslatePos = function(pos) { return pos + pos_diff; };
-    };
-
-    // A recursive function that processes internals of a function and all its
-    // inner functions. Iterator chunk_it initially points to a chunk that is
-    // below function start.
-    function ProcessInternals(info_node) {
-      info_node.new_start_pos = chunk_it.TranslatePos(
-          info_node.info.start_position);
-      var child_index = 0;
-      var code_changed = false;
-      var source_changed = false;
-      // Simultaneously iterates over child functions and over chunks.
-      while (!chunk_it.done() &&
-          chunk_it.current().pos1 < info_node.info.end_position) {
-        if (child_index < info_node.children.length) {
-          var child = info_node.children[child_index];
-
-          if (child.info.end_position <= chunk_it.current().pos1) {
-            ProcessUnchangedChild(child);
-            child_index++;
-            continue;
-          } else if (child.info.start_position >=
-              chunk_it.current().pos1 + chunk_it.current().len1) {
-            code_changed = true;
-            chunk_it.next();
-            continue;
-          } else if (child.info.start_position <= chunk_it.current().pos1 &&
-              child.info.end_position >= chunk_it.current().pos1 +
-              chunk_it.current().len1) {
-            ProcessInternals(child);
-            source_changed = source_changed ||
-                ( child.status != FunctionStatus.UNCHANGED );
-            code_changed = code_changed ||
-                ( child.status == FunctionStatus.DAMAGED );
-            child_index++;
-            continue;
-          } else {
-            code_changed = true;
-            child.status = FunctionStatus.DAMAGED;
-            child.status_explanation =
-                "Text diff overlaps with function boundary";
-            child_index++;
-            continue;
-          }
-        } else {
-          if (chunk_it.current().pos1 + chunk_it.current().len1 <=
-              info_node.info.end_position) {
-            info_node.status = FunctionStatus.CHANGED;
-            chunk_it.next();
-            continue;
-          } else {
-            info_node.status = FunctionStatus.DAMAGED;
-            info_node.status_explanation =
-                "Text diff overlaps with function boundary";
-            return;
-          }
-        }
-        Assert("Unreachable", false);
-      }
-      while (child_index < info_node.children.length) {
-        var child = info_node.children[child_index];
-        ProcessUnchangedChild(child);
-        child_index++;
-      }
-      if (code_changed) {
-        info_node.status = FunctionStatus.CHANGED;
-      } else if (source_changed) {
-        info_node.status = FunctionStatus.SOURCE_CHANGED;
-      }
-      info_node.new_end_pos =
-          chunk_it.TranslatePos(info_node.info.end_position);
-    }
-
-    function ProcessUnchangedChild(node) {
-      node.new_start_pos = chunk_it.TranslatePos(node.info.start_position);
-      node.new_end_pos = chunk_it.TranslatePos(node.info.end_position);
-    }
-
-    ProcessInternals(code_info_tree);
-  }
-
-  // For each old function (if it is not damaged) tries to find a corresponding
-  // function in new script. Typically it should succeed (non-damaged functions
-  // by definition may only have changes inside their bodies). However there are
-  // reasons for correspondence not to be found; function with unmodified text
-  // in new script may become enclosed into other function; the innocent change
-  // inside function body may in fact be something like "} function B() {" that
-  // splits a function into 2 functions.
-  function FindCorrespondingFunctions(old_code_tree, new_code_tree) {
-
-    // A recursive function that tries to find a correspondence for all
-    // child functions and for their inner functions.
-    function ProcessNode(old_node, new_node) {
-      var scope_change_description =
-          IsFunctionContextLocalsChanged(old_node.info, new_node.info);
-      if (scope_change_description) {
-        old_node.status = FunctionStatus.CHANGED;
-      }
-
-      var old_children = old_node.children;
-      var new_children = new_node.children;
-
-      var unmatched_new_nodes_list = [];
-      var textually_unmatched_new_nodes_list = [];
-
-      var old_index = 0;
-      var new_index = 0;
-      while (old_index < old_children.length) {
-        if (old_children[old_index].status == FunctionStatus.DAMAGED) {
-          old_index++;
-        } else if (new_index < new_children.length) {
-          if (new_children[new_index].info.start_position <
-              old_children[old_index].new_start_pos) {
-            unmatched_new_nodes_list.push(new_children[new_index]);
-            textually_unmatched_new_nodes_list.push(new_children[new_index]);
-            new_index++;
-          } else if (new_children[new_index].info.start_position ==
-              old_children[old_index].new_start_pos) {
-            if (new_children[new_index].info.end_position ==
-                old_children[old_index].new_end_pos) {
-              old_children[old_index].corresponding_node =
-                  new_children[new_index];
-              old_children[old_index].textual_corresponding_node =
-                  new_children[new_index];
-              if (scope_change_description) {
-                old_children[old_index].status = FunctionStatus.DAMAGED;
-                old_children[old_index].status_explanation =
-                    "Enclosing function is now incompatible. " +
-                    scope_change_description;
-                old_children[old_index].corresponding_node = UNDEFINED;
-              } else if (old_children[old_index].status !=
-                  FunctionStatus.UNCHANGED) {
-                ProcessNode(old_children[old_index],
-                    new_children[new_index]);
-                if (old_children[old_index].status == FunctionStatus.DAMAGED) {
-                  unmatched_new_nodes_list.push(
-                      old_children[old_index].corresponding_node);
-                  old_children[old_index].corresponding_node = UNDEFINED;
-                  old_node.status = FunctionStatus.CHANGED;
-                }
-              } else {
-                ProcessNode(old_children[old_index], new_children[new_index]);
-              }
-            } else {
-              old_children[old_index].status = FunctionStatus.DAMAGED;
-              old_children[old_index].status_explanation =
-                  "No corresponding function in new script found";
-              old_node.status = FunctionStatus.CHANGED;
-              unmatched_new_nodes_list.push(new_children[new_index]);
-              textually_unmatched_new_nodes_list.push(new_children[new_index]);
-            }
-            new_index++;
-            old_index++;
-          } else {
-            old_children[old_index].status = FunctionStatus.DAMAGED;
-            old_children[old_index].status_explanation =
-                "No corresponding function in new script found";
-            old_node.status = FunctionStatus.CHANGED;
-            old_index++;
-          }
-        } else {
-          old_children[old_index].status = FunctionStatus.DAMAGED;
-          old_children[old_index].status_explanation =
-              "No corresponding function in new script found";
-          old_node.status = FunctionStatus.CHANGED;
-          old_index++;
-        }
-      }
-
-      while (new_index < new_children.length) {
-        unmatched_new_nodes_list.push(new_children[new_index]);
-        textually_unmatched_new_nodes_list.push(new_children[new_index]);
-        new_index++;
-      }
-
-      if (old_node.status == FunctionStatus.CHANGED) {
-        if (old_node.info.param_num != new_node.info.param_num) {
-          old_node.status = FunctionStatus.DAMAGED;
-          old_node.status_explanation = "Changed parameter number: " +
-              old_node.info.param_num + " and " + new_node.info.param_num;
-        }
-      }
-      old_node.unmatched_new_nodes = unmatched_new_nodes_list;
-      old_node.textually_unmatched_new_nodes =
-          textually_unmatched_new_nodes_list;
-    }
-
-    ProcessNode(old_code_tree, new_code_tree);
-
-    old_code_tree.corresponding_node = new_code_tree;
-    old_code_tree.textual_corresponding_node = new_code_tree;
-
-    Assert(old_code_tree.status != FunctionStatus.DAMAGED,
-        "Script became damaged");
-  }
-
-  function FindLiveSharedInfos(old_code_tree, script) {
-    var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script);
-
-    var shared_infos = new GlobalArray();
-
-    for (var i = 0; i < shared_raw_list.length; i++) {
-      shared_infos.push(new SharedInfoWrapper(shared_raw_list[i]));
-    }
-
-    // Finds all SharedFunctionInfos that corresponds to compile info
-    // in old version of the script.
-    function FindFunctionInfos(compile_info) {
-      var wrappers = [];
-
-      for (var i = 0; i < shared_infos.length; i++) {
-        var wrapper = shared_infos[i];
-        if (wrapper.start_position == compile_info.start_position &&
-            wrapper.end_position == compile_info.end_position) {
-          wrappers.push(wrapper);
-        }
-      }
-
-      if (wrappers.length > 0) {
-        return wrappers;
-      }
-    }
-
-    function TraverseTree(node) {
-      node.live_shared_function_infos = FindFunctionInfos(node.info);
-
-      for (var i = 0; i < node.children.length; i++) {
-        TraverseTree(node.children[i]);
-      }
-    }
-
-    TraverseTree(old_code_tree);
-  }
-
-
-  // An object describing function compilation details. Its index fields
-  // apply to indexes inside array that stores these objects.
-  function FunctionCompileInfo(raw_array) {
-    this.function_name = raw_array[0];
-    this.start_position = raw_array[1];
-    this.end_position = raw_array[2];
-    this.param_num = raw_array[3];
-    this.scope_info = raw_array[4];
-    this.outer_index = raw_array[5];
-    this.shared_function_info = raw_array[6];
-    this.function_literal_id = raw_array[7];
-    this.next_sibling_index = null;
-    this.raw_array = raw_array;
-  }
-
-  function SharedInfoWrapper(raw_array) {
-    this.function_name = raw_array[0];
-    this.start_position = raw_array[1];
-    this.end_position = raw_array[2];
-    this.info = raw_array[3];
-    this.raw_array = raw_array;
-  }
-
-  // Changes positions (including all statements) in function.
-  function PatchPositions(old_info_node, diff_array, report_array) {
-    if (old_info_node.live_shared_function_infos) {
-      old_info_node.live_shared_function_infos.forEach(function (info) {
-          %LiveEditPatchFunctionPositions(info.raw_array,
-                                          diff_array);
-      });
-
-      report_array.push( { name: old_info_node.info.function_name } );
-    } else {
-      // TODO(LiveEdit): function is not compiled yet or is already collected.
-      report_array.push(
-          { name: old_info_node.info.function_name, info_not_found: true } );
-    }
-  }
-
-  // Adds a suffix to script name to mark that it is old version.
-  function CreateNameForOldScript(script) {
-    // TODO(635): try better than this; support several changes.
-    return script.name + " (old)";
-  }
-
-  // Compares a function scope heap structure, old and new version, whether it
-  // changed or not. Returns explanation if they differ.
-  function IsFunctionContextLocalsChanged(function_info1, function_info2) {
-    var scope_info1 = function_info1.scope_info;
-    var scope_info2 = function_info2.scope_info;
-
-    var scope_info1_text;
-    var scope_info2_text;
-
-    if (scope_info1) {
-      scope_info1_text = scope_info1.toString();
-    } else {
-      scope_info1_text = "";
-    }
-    if (scope_info2) {
-      scope_info2_text = scope_info2.toString();
-    } else {
-      scope_info2_text = "";
-    }
-
-    if (scope_info1_text != scope_info2_text) {
-      return "Variable map changed: [" + scope_info1_text +
-          "] => [" + scope_info2_text + "]";
-    }
-    // No differences. Return undefined.
-    return;
-  }
-
-  // Minifier forward declaration.
-  var FunctionPatchabilityStatus;
-
-  // For array of wrapped shared function infos checks that none of them
-  // have activations on stack (of any thread). Throws a Failure exception
-  // if this proves to be false.
-  function CheckStackActivations(old_shared_wrapper_list,
-                                 new_shared_list,
-                                 change_log) {
-    var old_shared_list = new GlobalArray();
-    for (var i = 0; i < old_shared_wrapper_list.length; i++) {
-      old_shared_list[i] = old_shared_wrapper_list[i].info;
-    }
-    var result = %LiveEditCheckAndDropActivations(
-                     old_shared_list, new_shared_list, true);
-    if (result[old_shared_wrapper_list.length]) {
-      // Extra array element may contain error message.
-      throw new Failure(result[old_shared_wrapper_list.length]);
-    }
-
-    var problems = new GlobalArray();
-    var dropped = new GlobalArray();
-    for (var i = 0; i < old_shared_list.length; i++) {
-      var shared = old_shared_wrapper_list[i];
-      if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
-        dropped.push({ name: shared.function_name } );
-      } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
-        var description = {
-            name: shared.function_name,
-            start_pos: shared.start_position,
-            end_pos: shared.end_position,
-            replace_problem:
-                FunctionPatchabilityStatus.SymbolName(result[i])
-        };
-        problems.push(description);
-      }
-    }
-    if (dropped.length > 0) {
-      change_log.push({ dropped_from_stack: dropped });
-    }
-    if (problems.length > 0) {
-      change_log.push( { functions_on_stack: problems } );
-      throw new Failure("Blocked by functions on stack");
-    }
-
-    return dropped.length;
-  }
-
-  // A copy of the FunctionPatchabilityStatus enum from liveedit.h
-  var FunctionPatchabilityStatus = {
-      AVAILABLE_FOR_PATCH: 1,
-      BLOCKED_ON_ACTIVE_STACK: 2,
-      BLOCKED_ON_OTHER_STACK: 3,
-      BLOCKED_UNDER_NATIVE_CODE: 4,
-      REPLACED_ON_ACTIVE_STACK: 5,
-      BLOCKED_UNDER_GENERATOR: 6,
-      BLOCKED_ACTIVE_GENERATOR: 7,
-      BLOCKED_NO_NEW_TARGET_ON_RESTART: 8
-  };
-
-  FunctionPatchabilityStatus.SymbolName = function(code) {
-    var enumeration = FunctionPatchabilityStatus;
-    for (var name in enumeration) {
-      if (enumeration[name] == code) {
-        return name;
-      }
-    }
-  };
-
-
-  // A logical failure in liveedit process. This means that change_log
-  // is valid and consistent description of what happened.
-  function Failure(message) {
-    this.message = message;
-  }
-
-  Failure.prototype.toString = function() {
-    return "LiveEdit Failure: " + this.message;
-  };
-
-  function CopyErrorPositionToDetails(e, details) {
-    function createPositionStruct(script, position) {
-      if (position == -1) return;
-      var location = script.locationFromPosition(position, true);
-      if (location == null) return;
-      return {
-        line: location.line + 1,
-        column: location.column + 1,
-        position: position
-      };
-    }
-
-    if (!("scriptObject" in e) || !("startPosition" in e)) {
-      return;
-    }
-
-    var script = e.scriptObject;
-
-    var position_struct = {
-      start: createPositionStruct(script, e.startPosition),
-      end: createPositionStruct(script, e.endPosition)
-    };
-    details.position = position_struct;
-  }
-
-  // LiveEdit main entry point: changes a script text to a new string.
-  function SetScriptSource(script, new_source, preview_only, change_log) {
-    var old_source = script.source;
-    var diff = CompareStrings(old_source, new_source);
-    return ApplyPatchMultiChunk(script, diff, new_source, preview_only,
-        change_log);
-  }
-
-  function CompareStrings(s1, s2) {
-    return %LiveEditCompareStrings(s1, s2);
-  }
-
-  // Applies the change to the script.
-  // The change is always a substring (change_pos, change_pos + change_len)
-  // being replaced with a completely different string new_str.
-  // This API is a legacy and is obsolete.
-  //
-  // @param {Script} script that is being changed
-  // @param {Array} change_log a list that collects engineer-readable
-  //     description of what happened.
-  function ApplySingleChunkPatch(script, change_pos, change_len, new_str,
-      change_log) {
-    var old_source = script.source;
-
-    // Prepare new source string.
-    var new_source = old_source.substring(0, change_pos) +
-        new_str + old_source.substring(change_pos + change_len);
-
-    return ApplyPatchMultiChunk(script,
-        [ change_pos, change_pos + change_len, change_pos + new_str.length],
-        new_source, false, change_log);
-  }
-
-  // Creates JSON description for a change tree.
-  function DescribeChangeTree(old_code_tree) {
-
-    function ProcessOldNode(node) {
-      var child_infos = [];
-      for (var i = 0; i < node.children.length; i++) {
-        var child = node.children[i];
-        if (child.status != FunctionStatus.UNCHANGED) {
-          child_infos.push(ProcessOldNode(child));
-        }
-      }
-      var new_child_infos = [];
-      if (node.textually_unmatched_new_nodes) {
-        for (var i = 0; i < node.textually_unmatched_new_nodes.length; i++) {
-          var child = node.textually_unmatched_new_nodes[i];
-          new_child_infos.push(ProcessNewNode(child));
-        }
-      }
-      var res = {
-        name: node.info.function_name,
-        positions: DescribePositions(node),
-        status: node.status,
-        children: child_infos,
-        new_children: new_child_infos
-      };
-      if (node.status_explanation) {
-        res.status_explanation = node.status_explanation;
-      }
-      if (node.textual_corresponding_node) {
-        res.new_positions = DescribePositions(node.textual_corresponding_node);
-      }
-      return res;
-    }
-
-    function ProcessNewNode(node) {
-      var child_infos = [];
-      // Do not list ancestors.
-      if (false) {
-        for (var i = 0; i < node.children.length; i++) {
-          child_infos.push(ProcessNewNode(node.children[i]));
-        }
-      }
-      var res = {
-        name: node.info.function_name,
-        positions: DescribePositions(node),
-        children: child_infos,
-      };
-      return res;
-    }
-
-    function DescribePositions(node) {
-      return {
-        start_position: node.info.start_position,
-        end_position: node.info.end_position
-      };
-    }
-
-    return ProcessOldNode(old_code_tree);
-  }
-
-  // -------------------------------------------------------------------
-  // Exports
-
-  var LiveEdit = {};
-  LiveEdit.SetScriptSource = SetScriptSource;
-  LiveEdit.ApplyPatchMultiChunk = ApplyPatchMultiChunk;
-  LiveEdit.Failure = Failure;
-
-  LiveEdit.TestApi = {
-    PosTranslator: PosTranslator,
-    CompareStrings: CompareStrings,
-    ApplySingleChunkPatch: ApplySingleChunkPatch
-  };
-
-  global.Debug.LiveEdit = LiveEdit;
-
-})
diff --git a/src/debug/mips/OWNERS b/src/debug/mips/OWNERS
deleted file mode 100644
index 89455a4..0000000
--- a/src/debug/mips/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-paul.lind@imgtec.com
-gergely.kis@imgtec.com
-akos.palfi@imgtec.com
-balazs.kilvady@imgtec.com
-dusan.milosavljevic@imgtec.com
-ivica.bogosavljevic@imgtec.com
diff --git a/src/debug/mips/debug-mips.cc b/src/debug/mips/debug-mips.cc
index 5b809e6..30bf215 100644
--- a/src/debug/mips/debug-mips.cc
+++ b/src/debug/mips/debug-mips.cc
@@ -6,103 +6,15 @@
 
 #include "src/debug/debug.h"
 
-#include "src/codegen.h"
+#include "src/codegen/macro-assembler.h"
 #include "src/debug/liveedit.h"
+#include "src/execution/frames-inl.h"
 
 namespace v8 {
 namespace internal {
 
 #define __ ACCESS_MASM(masm)
 
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_size;
-  __ bind(&check_size);
-  for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
-    __ nop(MacroAssembler::DEBUG_BREAK_NOP);
-  }
-  DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
-            masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction. Avoid emitting
-  // the trampoline pool in the debug break slot code.
-  Assembler::BlockTrampolinePoolScope block_pool(masm);
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  // Patch the code changing the debug break slot code from:
-  //   nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
-  //   nop(DEBUG_BREAK_NOP)
-  //   nop(DEBUG_BREAK_NOP)
-  //   nop(DEBUG_BREAK_NOP)
-  // to a call to the debug break slot code.
-  //   li t9, address   (lui t9 / ori t9 instruction pair)
-  //   call t9          (jalr t9 / nop instruction pair)
-  patcher.masm()->li(v8::internal::t9,
-                     Operand(reinterpret_cast<int32_t>(code->entry())));
-  patcher.masm()->Call(v8::internal::t9);
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  Instr current_instr = Assembler::instr_at(pc);
-  return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-  {
-    FrameScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ push(v0);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ PrepareCEntryArgs(1);
-    __ PrepareCEntryFunction(ExternalReference(
-        Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
-
-    CEntryStub ceb(masm->isolate(), 1);
-    __ CallStub(&ceb);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; i++) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber v0 if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function returned by DebugBreak.
-        if (!(reg.is(v0) && (mode == SAVE_RESULT_REGISTER))) {
-          __ li(reg, kDebugZapValue);
-        }
-      }
-    }
-    // Leave the internal frame.
-  }
-
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ Ret();
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -121,20 +33,17 @@
   // - Leave the frame.
   // - Restart the frame by calling the function.
   __ mov(fp, a1);
-  __ lw(a1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+  __ lw(a1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
 
   // Pop return address and frame.
   __ LeaveFrame(StackFrame::INTERNAL);
 
   __ lw(a0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
-  __ lw(a0,
-        FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset));
+  __ lhu(a0,
+         FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset));
   __ mov(a2, a0);
 
-  ParameterCount dummy1(a2);
-  ParameterCount dummy2(a0);
-  __ InvokeFunction(a1, dummy1, dummy2, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  __ InvokeFunction(a1, a2, a0, JUMP_FUNCTION);
 }
 
 
diff --git a/src/debug/mips64/OWNERS b/src/debug/mips64/OWNERS
deleted file mode 100644
index 89455a4..0000000
--- a/src/debug/mips64/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-paul.lind@imgtec.com
-gergely.kis@imgtec.com
-akos.palfi@imgtec.com
-balazs.kilvady@imgtec.com
-dusan.milosavljevic@imgtec.com
-ivica.bogosavljevic@imgtec.com
diff --git a/src/debug/mips64/debug-mips64.cc b/src/debug/mips64/debug-mips64.cc
index b8dbbfb..f677a38 100644
--- a/src/debug/mips64/debug-mips64.cc
+++ b/src/debug/mips64/debug-mips64.cc
@@ -6,65 +6,15 @@
 
 #include "src/debug/debug.h"
 
-#include "src/codegen.h"
+#include "src/codegen/macro-assembler.h"
 #include "src/debug/liveedit.h"
+#include "src/execution/frames-inl.h"
 
 namespace v8 {
 namespace internal {
 
 #define __ ACCESS_MASM(masm)
 
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_size;
-  __ bind(&check_size);
-  for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
-    __ nop(MacroAssembler::DEBUG_BREAK_NOP);
-  }
-  DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
-            masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction. Avoid emitting
-  // the trampoline pool in the debug break slot code.
-  Assembler::BlockTrampolinePoolScope block_pool(masm);
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  // Patch the code changing the debug break slot code from:
-  //   nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
-  //   nop(DEBUG_BREAK_NOP)
-  //   nop(DEBUG_BREAK_NOP)
-  //   nop(DEBUG_BREAK_NOP)
-  //   nop(DEBUG_BREAK_NOP)
-  //   nop(DEBUG_BREAK_NOP)
-  // to a call to the debug break slot code.
-  //   li t9, address   (4-instruction sequence on mips64)
-  //   call t9          (jalr t9 / nop instruction pair)
-  patcher.masm()->li(v8::internal::t9,
-                     Operand(reinterpret_cast<int64_t>(code->entry())),
-                     ADDRESS_LOAD);
-  patcher.masm()->Call(v8::internal::t9);
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  Instr current_instr = Assembler::instr_at(pc);
-  return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -76,47 +26,6 @@
   __ Ret();
 }
 
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-  {
-    FrameScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ push(v0);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ PrepareCEntryArgs(1);
-    __ PrepareCEntryFunction(ExternalReference(
-        Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
-
-    CEntryStub ceb(masm->isolate(), 1);
-    __ CallStub(&ceb);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; i++) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber v0 if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function returned by DebugBreak.
-        if (!(reg.is(v0) && (mode == SAVE_RESULT_REGISTER))) {
-          __ li(reg, kDebugZapValue);
-        }
-      }
-    }
-
-    // Leave the internal frame.
-  }
-
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ Ret();
-}
-
 void DebugCodegen::GenerateFrameDropperTrampoline(MacroAssembler* masm) {
   // Frame is being dropped:
   // - Drop to the target frame specified by a1.
@@ -124,20 +33,17 @@
   // - Leave the frame.
   // - Restart the frame by calling the function.
   __ mov(fp, a1);
-  __ ld(a1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+  __ Ld(a1, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
 
   // Pop return address and frame.
   __ LeaveFrame(StackFrame::INTERNAL);
 
-  __ ld(a0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
-  __ ld(a0,
-        FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset));
+  __ Ld(a0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+  __ Lhu(a0,
+         FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset));
   __ mov(a2, a0);
 
-  ParameterCount dummy1(a2);
-  ParameterCount dummy2(a0);
-  __ InvokeFunction(a1, dummy1, dummy2, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  __ InvokeFunction(a1, a2, a0, JUMP_FUNCTION);
 }
 
 
diff --git a/src/debug/mirrors.js b/src/debug/mirrors.js
deleted file mode 100644
index b534fec..0000000
--- a/src/debug/mirrors.js
+++ /dev/null
@@ -1,2410 +0,0 @@
-// Copyright 2006-2012 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(function(global, utils) {
-"use strict";
-
-// ----------------------------------------------------------------------------
-// Imports
-
-var GlobalArray = global.Array;
-var IsNaN = global.isNaN;
-var JSONStringify = global.JSON.stringify;
-var MapEntries;
-var MapIteratorNext;
-var SetIteratorNext;
-var SetValues;
-
-utils.Import(function(from) {
-  MapEntries = from.MapEntries;
-  MapIteratorNext = from.MapIteratorNext;
-  SetIteratorNext = from.SetIteratorNext;
-  SetValues = from.SetValues;
-});
-
-// ----------------------------------------------------------------------------
-
-// Mirror hierarchy:
-// - Mirror
-//   - ValueMirror
-//     - UndefinedMirror
-//     - NullMirror
-//     - BooleanMirror
-//     - NumberMirror
-//     - StringMirror
-//     - SymbolMirror
-//     - ObjectMirror
-//       - FunctionMirror
-//         - UnresolvedFunctionMirror
-//       - ArrayMirror
-//       - DateMirror
-//       - RegExpMirror
-//       - ErrorMirror
-//       - PromiseMirror
-//       - MapMirror
-//       - SetMirror
-//       - IteratorMirror
-//       - GeneratorMirror
-//   - PropertyMirror
-//   - InternalPropertyMirror
-//   - FrameMirror
-//   - ScriptMirror
-//   - ScopeMirror
-
-// Type names of the different mirrors.
-var MirrorType = {
-  UNDEFINED_TYPE : 'undefined',
-  NULL_TYPE : 'null',
-  BOOLEAN_TYPE : 'boolean',
-  NUMBER_TYPE : 'number',
-  STRING_TYPE : 'string',
-  SYMBOL_TYPE : 'symbol',
-  OBJECT_TYPE : 'object',
-  FUNCTION_TYPE : 'function',
-  REGEXP_TYPE : 'regexp',
-  ERROR_TYPE : 'error',
-  PROPERTY_TYPE : 'property',
-  INTERNAL_PROPERTY_TYPE : 'internalProperty',
-  FRAME_TYPE : 'frame',
-  SCRIPT_TYPE : 'script',
-  CONTEXT_TYPE : 'context',
-  SCOPE_TYPE : 'scope',
-  PROMISE_TYPE : 'promise',
-  MAP_TYPE : 'map',
-  SET_TYPE : 'set',
-  ITERATOR_TYPE : 'iterator',
-  GENERATOR_TYPE : 'generator',
-}
-
-/**
- * Returns the mirror for a specified value or object.
- *
- * @param {value or Object} value the value or object to retrieve the mirror for
- * @returns {Mirror} the mirror reflects the passed value or object
- */
-function MakeMirror(value) {
-  var mirror;
-
-  if (IS_UNDEFINED(value)) {
-    mirror = new UndefinedMirror();
-  } else if (IS_NULL(value)) {
-    mirror = new NullMirror();
-  } else if (IS_BOOLEAN(value)) {
-    mirror = new BooleanMirror(value);
-  } else if (IS_NUMBER(value)) {
-    mirror = new NumberMirror(value);
-  } else if (IS_STRING(value)) {
-    mirror = new StringMirror(value);
-  } else if (IS_SYMBOL(value)) {
-    mirror = new SymbolMirror(value);
-  } else if (IS_ARRAY(value)) {
-    mirror = new ArrayMirror(value);
-  } else if (IS_DATE(value)) {
-    mirror = new DateMirror(value);
-  } else if (IS_FUNCTION(value)) {
-    mirror = new FunctionMirror(value);
-  } else if (%IsRegExp(value)) {
-    mirror = new RegExpMirror(value);
-  } else if (IS_ERROR(value)) {
-    mirror = new ErrorMirror(value);
-  } else if (IS_SCRIPT(value)) {
-    mirror = new ScriptMirror(value);
-  } else if (IS_MAP(value) || IS_WEAKMAP(value)) {
-    mirror = new MapMirror(value);
-  } else if (IS_SET(value) || IS_WEAKSET(value)) {
-    mirror = new SetMirror(value);
-  } else if (IS_MAP_ITERATOR(value) || IS_SET_ITERATOR(value)) {
-    mirror = new IteratorMirror(value);
-  } else if (%is_promise(value)) {
-    mirror = new PromiseMirror(value);
-  } else if (IS_GENERATOR(value)) {
-    mirror = new GeneratorMirror(value);
-  } else {
-    mirror = new ObjectMirror(value, MirrorType.OBJECT_TYPE);
-  }
-
-  return mirror;
-}
-
-
-/**
- * Returns the mirror for the undefined value.
- *
- * @returns {Mirror} the mirror reflects the undefined value
- */
-function GetUndefinedMirror() {
-  return MakeMirror(UNDEFINED);
-}
-
-
-/**
- * Inherit the prototype methods from one constructor into another.
- *
- * The Function.prototype.inherits from lang.js rewritten as a standalone
- * function (not on Function.prototype). NOTE: If this file is to be loaded
- * during bootstrapping this function needs to be revritten using some native
- * functions as prototype setup using normal JavaScript does not work as
- * expected during bootstrapping (see mirror.js in r114903).
- *
- * @param {function} ctor Constructor function which needs to inherit the
- *     prototype
- * @param {function} superCtor Constructor function to inherit prototype from
- */
-function inherits(ctor, superCtor) {
-  var tempCtor = function(){};
-  tempCtor.prototype = superCtor.prototype;
-  ctor.super_ = superCtor.prototype;
-  ctor.prototype = new tempCtor();
-  ctor.prototype.constructor = ctor;
-}
-
-// Maximum length when sending strings through the JSON protocol.
-var kMaxProtocolStringLength = 80;
-
-
-// A copy of the PropertyKind enum from property-details.h
-var PropertyType = {};
-PropertyType.Data     = 0;
-PropertyType.Accessor = 1;
-
-
-// Different attributes for a property.
-var PropertyAttribute = {};
-PropertyAttribute.None       = NONE;
-PropertyAttribute.ReadOnly   = READ_ONLY;
-PropertyAttribute.DontEnum   = DONT_ENUM;
-PropertyAttribute.DontDelete = DONT_DELETE;
-
-
-// A copy of the scope types from runtime-debug.cc.
-// NOTE: these constants should be backward-compatible, so
-// add new ones to the end of this list.
-var ScopeType = { Global:  0,
-                  Local:   1,
-                  With:    2,
-                  Closure: 3,
-                  Catch:   4,
-                  Block:   5,
-                  Script:  6,
-                  Eval:    7,
-                  Module:  8,
-                };
-
-/**
- * Base class for all mirror objects.
- * @param {string} type The type of the mirror
- * @constructor
- */
-function Mirror(type) {
-  this.type_ = type;
-}
-
-
-Mirror.prototype.type = function() {
-  return this.type_;
-};
-
-
-/**
- * Check whether the mirror reflects a value.
- * @returns {boolean} True if the mirror reflects a value.
- */
-Mirror.prototype.isValue = function() {
-  return this instanceof ValueMirror;
-};
-
-
-/**
- * Check whether the mirror reflects the undefined value.
- * @returns {boolean} True if the mirror reflects the undefined value.
- */
-Mirror.prototype.isUndefined = function() {
-  return this instanceof UndefinedMirror;
-};
-
-
-/**
- * Check whether the mirror reflects the null value.
- * @returns {boolean} True if the mirror reflects the null value
- */
-Mirror.prototype.isNull = function() {
-  return this instanceof NullMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a boolean value.
- * @returns {boolean} True if the mirror reflects a boolean value
- */
-Mirror.prototype.isBoolean = function() {
-  return this instanceof BooleanMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a number value.
- * @returns {boolean} True if the mirror reflects a number value
- */
-Mirror.prototype.isNumber = function() {
-  return this instanceof NumberMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a string value.
- * @returns {boolean} True if the mirror reflects a string value
- */
-Mirror.prototype.isString = function() {
-  return this instanceof StringMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a symbol.
- * @returns {boolean} True if the mirror reflects a symbol
- */
-Mirror.prototype.isSymbol = function() {
-  return this instanceof SymbolMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an object.
- * @returns {boolean} True if the mirror reflects an object
- */
-Mirror.prototype.isObject = function() {
-  return this instanceof ObjectMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a function.
- * @returns {boolean} True if the mirror reflects a function
- */
-Mirror.prototype.isFunction = function() {
-  return this instanceof FunctionMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an unresolved function.
- * @returns {boolean} True if the mirror reflects an unresolved function
- */
-Mirror.prototype.isUnresolvedFunction = function() {
-  return this instanceof UnresolvedFunctionMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an array.
- * @returns {boolean} True if the mirror reflects an array
- */
-Mirror.prototype.isArray = function() {
-  return this instanceof ArrayMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a date.
- * @returns {boolean} True if the mirror reflects a date
- */
-Mirror.prototype.isDate = function() {
-  return this instanceof DateMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a regular expression.
- * @returns {boolean} True if the mirror reflects a regular expression
- */
-Mirror.prototype.isRegExp = function() {
-  return this instanceof RegExpMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an error.
- * @returns {boolean} True if the mirror reflects an error
- */
-Mirror.prototype.isError = function() {
-  return this instanceof ErrorMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a promise.
- * @returns {boolean} True if the mirror reflects a promise
- */
-Mirror.prototype.isPromise = function() {
-  return this instanceof PromiseMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a generator object.
- * @returns {boolean} True if the mirror reflects a generator object
- */
-Mirror.prototype.isGenerator = function() {
-  return this instanceof GeneratorMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a property.
- * @returns {boolean} True if the mirror reflects a property
- */
-Mirror.prototype.isProperty = function() {
-  return this instanceof PropertyMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an internal property.
- * @returns {boolean} True if the mirror reflects an internal property
- */
-Mirror.prototype.isInternalProperty = function() {
-  return this instanceof InternalPropertyMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a stack frame.
- * @returns {boolean} True if the mirror reflects a stack frame
- */
-Mirror.prototype.isFrame = function() {
-  return this instanceof FrameMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a script.
- * @returns {boolean} True if the mirror reflects a script
- */
-Mirror.prototype.isScript = function() {
-  return this instanceof ScriptMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a context.
- * @returns {boolean} True if the mirror reflects a context
- */
-Mirror.prototype.isContext = function() {
-  return this instanceof ContextMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a scope.
- * @returns {boolean} True if the mirror reflects a scope
- */
-Mirror.prototype.isScope = function() {
-  return this instanceof ScopeMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a map.
- * @returns {boolean} True if the mirror reflects a map
- */
-Mirror.prototype.isMap = function() {
-  return this instanceof MapMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a set.
- * @returns {boolean} True if the mirror reflects a set
- */
-Mirror.prototype.isSet = function() {
-  return this instanceof SetMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an iterator.
- * @returns {boolean} True if the mirror reflects an iterator
- */
-Mirror.prototype.isIterator = function() {
-  return this instanceof IteratorMirror;
-};
-
-
-Mirror.prototype.toText = function() {
-  // Simpel to text which is used when on specialization in subclass.
-  return "#<" + this.constructor.name + ">";
-};
-
-
-/**
- * Base class for all value mirror objects.
- * @param {string} type The type of the mirror
- * @param {value} value The value reflected by this mirror
- * @constructor
- * @extends Mirror
- */
-function ValueMirror(type, value) {
-  %_Call(Mirror, this, type);
-  this.value_ = value;
-}
-inherits(ValueMirror, Mirror);
-
-
-/**
- * Check whether this is a primitive value.
- * @return {boolean} True if the mirror reflects a primitive value
- */
-ValueMirror.prototype.isPrimitive = function() {
-  var type = this.type();
-  return type === 'undefined' ||
-         type === 'null' ||
-         type === 'boolean' ||
-         type === 'number' ||
-         type === 'string' ||
-         type === 'symbol';
-};
-
-
-/**
- * Get the actual value reflected by this mirror.
- * @return {value} The value reflected by this mirror
- */
-ValueMirror.prototype.value = function() {
-  return this.value_;
-};
-
-
-/**
- * Mirror object for Undefined.
- * @constructor
- * @extends ValueMirror
- */
-function UndefinedMirror() {
-  %_Call(ValueMirror, this, MirrorType.UNDEFINED_TYPE, UNDEFINED);
-}
-inherits(UndefinedMirror, ValueMirror);
-
-
-UndefinedMirror.prototype.toText = function() {
-  return 'undefined';
-};
-
-
-/**
- * Mirror object for null.
- * @constructor
- * @extends ValueMirror
- */
-function NullMirror() {
-  %_Call(ValueMirror, this, MirrorType.NULL_TYPE, null);
-}
-inherits(NullMirror, ValueMirror);
-
-
-NullMirror.prototype.toText = function() {
-  return 'null';
-};
-
-
-/**
- * Mirror object for boolean values.
- * @param {boolean} value The boolean value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function BooleanMirror(value) {
-  %_Call(ValueMirror, this, MirrorType.BOOLEAN_TYPE, value);
-}
-inherits(BooleanMirror, ValueMirror);
-
-
-BooleanMirror.prototype.toText = function() {
-  return this.value_ ? 'true' : 'false';
-};
-
-
-/**
- * Mirror object for number values.
- * @param {number} value The number value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function NumberMirror(value) {
-  %_Call(ValueMirror, this, MirrorType.NUMBER_TYPE, value);
-}
-inherits(NumberMirror, ValueMirror);
-
-
-NumberMirror.prototype.toText = function() {
-  return %NumberToString(this.value_);
-};
-
-
-/**
- * Mirror object for string values.
- * @param {string} value The string value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function StringMirror(value) {
-  %_Call(ValueMirror, this, MirrorType.STRING_TYPE, value);
-}
-inherits(StringMirror, ValueMirror);
-
-
-StringMirror.prototype.length = function() {
-  return this.value_.length;
-};
-
-StringMirror.prototype.getTruncatedValue = function(maxLength) {
-  if (maxLength != -1 && this.length() > maxLength) {
-    return this.value_.substring(0, maxLength) +
-           '... (length: ' + this.length() + ')';
-  }
-  return this.value_;
-};
-
-StringMirror.prototype.toText = function() {
-  return this.getTruncatedValue(kMaxProtocolStringLength);
-};
-
-
-/**
- * Mirror object for a Symbol
- * @param {Object} value The Symbol
- * @constructor
- * @extends Mirror
- */
-function SymbolMirror(value) {
-  %_Call(ValueMirror, this, MirrorType.SYMBOL_TYPE, value);
-}
-inherits(SymbolMirror, ValueMirror);
-
-
-SymbolMirror.prototype.description = function() {
-  return %SymbolDescription(%ValueOf(this.value_));
-}
-
-
-SymbolMirror.prototype.toText = function() {
-  return %SymbolDescriptiveString(%ValueOf(this.value_));
-}
-
-
-/**
- * Mirror object for objects.
- * @param {object} value The object reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function ObjectMirror(value, type) {
-  type = type || MirrorType.OBJECT_TYPE;
-  %_Call(ValueMirror, this, type, value);
-}
-inherits(ObjectMirror, ValueMirror);
-
-
-ObjectMirror.prototype.className = function() {
-  return %_ClassOf(this.value_);
-};
-
-
-ObjectMirror.prototype.constructorFunction = function() {
-  return MakeMirror(%DebugGetProperty(this.value_, 'constructor'));
-};
-
-
-ObjectMirror.prototype.prototypeObject = function() {
-  return MakeMirror(%DebugGetProperty(this.value_, 'prototype'));
-};
-
-
-ObjectMirror.prototype.protoObject = function() {
-  return MakeMirror(%DebugGetPrototype(this.value_));
-};
-
-
-ObjectMirror.prototype.hasNamedInterceptor = function() {
-  // Get information on interceptors for this object.
-  var x = %GetInterceptorInfo(this.value_);
-  return (x & 2) != 0;
-};
-
-
-ObjectMirror.prototype.hasIndexedInterceptor = function() {
-  // Get information on interceptors for this object.
-  var x = %GetInterceptorInfo(this.value_);
-  return (x & 1) != 0;
-};
-
-
-/**
- * Return the property names for this object.
- * @param {number} kind Indicate whether named, indexed or both kinds of
- *     properties are requested
- * @param {number} limit Limit the number of names returend to the specified
-       value
- * @return {Array} Property names for this object
- */
-ObjectMirror.prototype.propertyNames = function() {
-  return %GetOwnPropertyKeys(this.value_, PROPERTY_FILTER_NONE);
-};
-
-
-/**
- * Return the properties for this object as an array of PropertyMirror objects.
- * @param {number} kind Indicate whether named, indexed or both kinds of
- *     properties are requested
- * @param {number} limit Limit the number of properties returned to the
-       specified value
- * @return {Array} Property mirrors for this object
- */
-ObjectMirror.prototype.properties = function() {
-  var names = this.propertyNames();
-  var properties = new GlobalArray(names.length);
-  for (var i = 0; i < names.length; i++) {
-    properties[i] = this.property(names[i]);
-  }
-
-  return properties;
-};
-
-
-/**
- * Return the internal properties for this object as an array of
- * InternalPropertyMirror objects.
- * @return {Array} Property mirrors for this object
- */
-ObjectMirror.prototype.internalProperties = function() {
-  return ObjectMirror.GetInternalProperties(this.value_);
-}
-
-
-ObjectMirror.prototype.property = function(name) {
-  var details = %DebugGetPropertyDetails(this.value_, name);
-  if (details) {
-    return new PropertyMirror(this, name, details);
-  }
-
-  // Nothing found.
-  return GetUndefinedMirror();
-};
-
-
-
-/**
- * Try to find a property from its value.
- * @param {Mirror} value The property value to look for
- * @return {PropertyMirror} The property with the specified value. If no
- *     property was found with the specified value UndefinedMirror is returned
- */
-ObjectMirror.prototype.lookupProperty = function(value) {
-  var properties = this.properties();
-
-  // Look for property value in properties.
-  for (var i = 0; i < properties.length; i++) {
-
-    // Skip properties which are defined through accessors.
-    var property = properties[i];
-    if (property.propertyType() == PropertyType.Data) {
-      if (property.value_ === value.value_) {
-        return property;
-      }
-    }
-  }
-
-  // Nothing found.
-  return GetUndefinedMirror();
-};
-
-
-/**
- * Returns objects which has direct references to this object
- * @param {number} opt_max_objects Optional parameter specifying the maximum
- *     number of referencing objects to return.
- * @return {Array} The objects which has direct references to this object.
- */
-ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
-  // Find all objects with direct references to this object.
-  var result = %DebugReferencedBy(this.value_,
-                                  Mirror.prototype, opt_max_objects || 0);
-
-  // Make mirrors for all the references found.
-  for (var i = 0; i < result.length; i++) {
-    result[i] = MakeMirror(result[i]);
-  }
-
-  return result;
-};
-
-
-ObjectMirror.prototype.toText = function() {
-  var name;
-  var ctor = this.constructorFunction();
-  if (!ctor.isFunction()) {
-    name = this.className();
-  } else {
-    name = ctor.name();
-    if (!name) {
-      name = this.className();
-    }
-  }
-  return '#<' + name + '>';
-};
-
-
-/**
- * Return the internal properties of the value, such as [[PrimitiveValue]] of
- * scalar wrapper objects, properties of the bound function and properties of
- * the promise.
- * This method is done static to be accessible from Debug API with the bare
- * values without mirrors.
- * @return {Array} array (possibly empty) of InternalProperty instances
- */
-ObjectMirror.GetInternalProperties = function(value) {
-  var properties = %DebugGetInternalProperties(value);
-  var result = [];
-  for (var i = 0; i < properties.length; i += 2) {
-    result.push(new InternalPropertyMirror(properties[i], properties[i + 1]));
-  }
-  return result;
-}
-
-
-/**
- * Mirror object for functions.
- * @param {function} value The function object reflected by this mirror.
- * @constructor
- * @extends ObjectMirror
- */
-function FunctionMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.FUNCTION_TYPE);
-  this.resolved_ = true;
-}
-inherits(FunctionMirror, ObjectMirror);
-
-
-/**
- * Returns whether the function is resolved.
- * @return {boolean} True if the function is resolved. Unresolved functions can
- *     only originate as functions from stack frames
- */
-FunctionMirror.prototype.resolved = function() {
-  return this.resolved_;
-};
-
-
-/**
- * Returns the name of the function.
- * @return {string} Name of the function
- */
-FunctionMirror.prototype.name = function() {
-  return %FunctionGetName(this.value_);
-};
-
-
-/**
- * Returns the displayName if it is set, otherwise name, otherwise inferred
- * name.
- * @return {string} Name of the function
- */
-FunctionMirror.prototype.debugName = function() {
-  return %FunctionGetDebugName(this.value_);
-}
-
-
-/**
- * Returns the inferred name of the function.
- * @return {string} Name of the function
- */
-FunctionMirror.prototype.inferredName = function() {
-  return %FunctionGetInferredName(this.value_);
-};
-
-
-/**
- * Returns the source code for the function.
- * @return {string or undefined} The source code for the function. If the
- *     function is not resolved undefined will be returned.
- */
-FunctionMirror.prototype.source = function() {
-  // Return source if function is resolved. Otherwise just fall through to
-  // return undefined.
-  if (this.resolved()) {
-    return %FunctionToString(this.value_);
-  }
-};
-
-
-/**
- * Returns the script object for the function.
- * @return {ScriptMirror or undefined} Script object for the function or
- *     undefined if the function has no script
- */
-FunctionMirror.prototype.script = function() {
-  // Return script if function is resolved. Otherwise just fall through
-  // to return undefined.
-  if (this.resolved()) {
-    if (this.script_) {
-      return this.script_;
-    }
-    var script = %FunctionGetScript(this.value_);
-    if (script) {
-      return this.script_ = MakeMirror(script);
-    }
-  }
-};
-
-
-/**
- * Returns the script source position for the function. Only makes sense
- * for functions which has a script defined.
- * @return {Number or undefined} in-script position for the function
- */
-FunctionMirror.prototype.sourcePosition_ = function() {
-  // Return position if function is resolved. Otherwise just fall
-  // through to return undefined.
-  if (this.resolved()) {
-    return %FunctionGetScriptSourcePosition(this.value_);
-  }
-};
-
-
-/**
- * Returns the script source location object for the function. Only makes sense
- * for functions which has a script defined.
- * @return {Location or undefined} in-script location for the function begin
- */
-FunctionMirror.prototype.sourceLocation = function() {
-  if (this.resolved()) {
-    var script = this.script();
-    if (script) {
-      return script.locationFromPosition(this.sourcePosition_(), true);
-    }
-  }
-};
-
-
-/**
- * Returns objects constructed by this function.
- * @param {number} opt_max_instances Optional parameter specifying the maximum
- *     number of instances to return.
- * @return {Array or undefined} The objects constructed by this function.
- */
-FunctionMirror.prototype.constructedBy = function(opt_max_instances) {
-  if (this.resolved()) {
-    // Find all objects constructed from this function.
-    var result = %DebugConstructedBy(this.value_, opt_max_instances || 0);
-
-    // Make mirrors for all the instances found.
-    for (var i = 0; i < result.length; i++) {
-      result[i] = MakeMirror(result[i]);
-    }
-
-    return result;
-  } else {
-    return [];
-  }
-};
-
-
-FunctionMirror.prototype.scopeCount = function() {
-  if (this.resolved()) {
-    if (IS_UNDEFINED(this.scopeCount_)) {
-      this.scopeCount_ = %GetFunctionScopeCount(this.value());
-    }
-    return this.scopeCount_;
-  } else {
-    return 0;
-  }
-};
-
-
-FunctionMirror.prototype.scope = function(index) {
-  if (this.resolved()) {
-    return new ScopeMirror(UNDEFINED, this, UNDEFINED, index);
-  }
-};
-
-
-FunctionMirror.prototype.toText = function() {
-  return this.source();
-};
-
-
-FunctionMirror.prototype.context = function() {
-  if (this.resolved()) {
-    if (!this._context)
-      this._context = new ContextMirror(%FunctionGetContextData(this.value_));
-    return this._context;
-  }
-};
-
-
-/**
- * Mirror object for unresolved functions.
- * @param {string} value The name for the unresolved function reflected by this
- *     mirror.
- * @constructor
- * @extends ObjectMirror
- */
-function UnresolvedFunctionMirror(value) {
-  // Construct this using the ValueMirror as an unresolved function is not a
-  // real object but just a string.
-  %_Call(ValueMirror, this, MirrorType.FUNCTION_TYPE, value);
-  this.propertyCount_ = 0;
-  this.elementCount_ = 0;
-  this.resolved_ = false;
-}
-inherits(UnresolvedFunctionMirror, FunctionMirror);
-
-
-UnresolvedFunctionMirror.prototype.className = function() {
-  return 'Function';
-};
-
-
-UnresolvedFunctionMirror.prototype.constructorFunction = function() {
-  return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.prototypeObject = function() {
-  return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.protoObject = function() {
-  return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.name = function() {
-  return this.value_;
-};
-
-
-UnresolvedFunctionMirror.prototype.debugName = function() {
-  return this.value_;
-};
-
-
-UnresolvedFunctionMirror.prototype.inferredName = function() {
-  return UNDEFINED;
-};
-
-
-UnresolvedFunctionMirror.prototype.propertyNames = function(kind, limit) {
-  return [];
-};
-
-
-/**
- * Mirror object for arrays.
- * @param {Array} value The Array object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function ArrayMirror(value) {
-  %_Call(ObjectMirror, this, value);
-}
-inherits(ArrayMirror, ObjectMirror);
-
-
-ArrayMirror.prototype.length = function() {
-  return this.value_.length;
-};
-
-
-ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index,
-                                                            opt_to_index) {
-  var from_index = opt_from_index || 0;
-  var to_index = opt_to_index || this.length() - 1;
-  if (from_index > to_index) return new GlobalArray();
-  var values = new GlobalArray(to_index - from_index + 1);
-  for (var i = from_index; i <= to_index; i++) {
-    var details = %DebugGetPropertyDetails(this.value_, TO_STRING(i));
-    var value;
-    if (details) {
-      value = new PropertyMirror(this, i, details);
-    } else {
-      value = GetUndefinedMirror();
-    }
-    values[i - from_index] = value;
-  }
-  return values;
-};
-
-
-/**
- * Mirror object for dates.
- * @param {Date} value The Date object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function DateMirror(value) {
-  %_Call(ObjectMirror, this, value);
-}
-inherits(DateMirror, ObjectMirror);
-
-
-DateMirror.prototype.toText = function() {
-  var s = JSONStringify(this.value_);
-  return s.substring(1, s.length - 1);  // cut quotes
-};
-
-
-/**
- * Mirror object for regular expressions.
- * @param {RegExp} value The RegExp object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function RegExpMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.REGEXP_TYPE);
-}
-inherits(RegExpMirror, ObjectMirror);
-
-
-/**
- * Returns the source to the regular expression.
- * @return {string or undefined} The source to the regular expression
- */
-RegExpMirror.prototype.source = function() {
-  return this.value_.source;
-};
-
-
-/**
- * Returns whether this regular expression has the global (g) flag set.
- * @return {boolean} Value of the global flag
- */
-RegExpMirror.prototype.global = function() {
-  return this.value_.global;
-};
-
-
-/**
- * Returns whether this regular expression has the ignore case (i) flag set.
- * @return {boolean} Value of the ignore case flag
- */
-RegExpMirror.prototype.ignoreCase = function() {
-  return this.value_.ignoreCase;
-};
-
-
-/**
- * Returns whether this regular expression has the multiline (m) flag set.
- * @return {boolean} Value of the multiline flag
- */
-RegExpMirror.prototype.multiline = function() {
-  return this.value_.multiline;
-};
-
-
-/**
- * Returns whether this regular expression has the sticky (y) flag set.
- * @return {boolean} Value of the sticky flag
- */
-RegExpMirror.prototype.sticky = function() {
-  return this.value_.sticky;
-};
-
-
-/**
- * Returns whether this regular expression has the unicode (u) flag set.
- * @return {boolean} Value of the unicode flag
- */
-RegExpMirror.prototype.unicode = function() {
-  return this.value_.unicode;
-};
-
-
-RegExpMirror.prototype.toText = function() {
-  // Simpel to text which is used when on specialization in subclass.
-  return "/" + this.source() + "/";
-};
-
-
-/**
- * Mirror object for error objects.
- * @param {Error} value The error object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function ErrorMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.ERROR_TYPE);
-}
-inherits(ErrorMirror, ObjectMirror);
-
-
-/**
- * Returns the message for this eror object.
- * @return {string or undefined} The message for this eror object
- */
-ErrorMirror.prototype.message = function() {
-  return this.value_.message;
-};
-
-
-ErrorMirror.prototype.toText = function() {
-  // Use the same text representation as in messages.js.
-  var text;
-  try {
-    text = %ErrorToString(this.value_);
-  } catch (e) {
-    text = '#<Error>';
-  }
-  return text;
-};
-
-
-/**
- * Mirror object for a Promise object.
- * @param {Object} value The Promise object
- * @constructor
- * @extends ObjectMirror
- */
-function PromiseMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.PROMISE_TYPE);
-}
-inherits(PromiseMirror, ObjectMirror);
-
-
-function PromiseGetStatus_(value) {
-  var status = %PromiseStatus(value);
-  if (status == 0) return "pending";
-  if (status == 1) return "resolved";
-  return "rejected";
-}
-
-
-function PromiseGetValue_(value) {
-  return %PromiseResult(value);
-}
-
-
-PromiseMirror.prototype.status = function() {
-  return PromiseGetStatus_(this.value_);
-};
-
-
-PromiseMirror.prototype.promiseValue = function() {
-  return MakeMirror(PromiseGetValue_(this.value_));
-};
-
-
-function MapMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.MAP_TYPE);
-}
-inherits(MapMirror, ObjectMirror);
-
-
-/**
- * Returns an array of key/value pairs of a map.
- * This will keep keys alive for WeakMaps.
- *
- * @param {number=} opt_limit Max elements to return.
- * @returns {Array.<Object>} Array of key/value pairs of a map.
- */
-MapMirror.prototype.entries = function(opt_limit) {
-  var result = [];
-
-  if (IS_WEAKMAP(this.value_)) {
-    var entries = %GetWeakMapEntries(this.value_, opt_limit || 0);
-    for (var i = 0; i < entries.length; i += 2) {
-      result.push({
-        key: entries[i],
-        value: entries[i + 1]
-      });
-    }
-    return result;
-  }
-
-  var iter = %_Call(MapEntries, this.value_);
-  var next;
-  while ((!opt_limit || result.length < opt_limit) &&
-         !(next = iter.next()).done) {
-    result.push({
-      key: next.value[0],
-      value: next.value[1]
-    });
-  }
-  return result;
-};
-
-
-function SetMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.SET_TYPE);
-}
-inherits(SetMirror, ObjectMirror);
-
-
-function IteratorGetValues_(iter, next_function, opt_limit) {
-  var result = [];
-  var next;
-  while ((!opt_limit || result.length < opt_limit) &&
-         !(next = %_Call(next_function, iter)).done) {
-    result.push(next.value);
-  }
-  return result;
-}
-
-
-/**
- * Returns an array of elements of a set.
- * This will keep elements alive for WeakSets.
- *
- * @param {number=} opt_limit Max elements to return.
- * @returns {Array.<Object>} Array of elements of a set.
- */
-SetMirror.prototype.values = function(opt_limit) {
-  if (IS_WEAKSET(this.value_)) {
-    return %GetWeakSetValues(this.value_, opt_limit || 0);
-  }
-
-  var iter = %_Call(SetValues, this.value_);
-  return IteratorGetValues_(iter, SetIteratorNext, opt_limit);
-};
-
-
-function IteratorMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.ITERATOR_TYPE);
-}
-inherits(IteratorMirror, ObjectMirror);
-
-
-/**
- * Returns a preview of elements of an iterator.
- * Does not change the backing iterator state.
- *
- * @param {number=} opt_limit Max elements to return.
- * @returns {Array.<Object>} Array of elements of an iterator.
- */
-IteratorMirror.prototype.preview = function(opt_limit) {
-  if (IS_MAP_ITERATOR(this.value_)) {
-    return IteratorGetValues_(%MapIteratorClone(this.value_),
-                              MapIteratorNext,
-                              opt_limit);
-  } else if (IS_SET_ITERATOR(this.value_)) {
-    return IteratorGetValues_(%SetIteratorClone(this.value_),
-                              SetIteratorNext,
-                              opt_limit);
-  }
-};
-
-
-/**
- * Mirror object for a Generator object.
- * @param {Object} data The Generator object
- * @constructor
- * @extends Mirror
- */
-function GeneratorMirror(value) {
-  %_Call(ObjectMirror, this, value, MirrorType.GENERATOR_TYPE);
-}
-inherits(GeneratorMirror, ObjectMirror);
-
-
-function GeneratorGetStatus_(value) {
-  var continuation = %GeneratorGetContinuation(value);
-  if (continuation < -1) return "running";
-  if (continuation == -1) return "closed";
-  return "suspended";
-}
-
-
-GeneratorMirror.prototype.status = function() {
-  return GeneratorGetStatus_(this.value_);
-};
-
-
-GeneratorMirror.prototype.sourcePosition_ = function() {
-  return %GeneratorGetSourcePosition(this.value_);
-};
-
-
-GeneratorMirror.prototype.sourceLocation = function() {
-  var pos = this.sourcePosition_();
-  if (!IS_UNDEFINED(pos)) {
-    var script = this.func().script();
-    if (script) {
-      return script.locationFromPosition(pos, true);
-    }
-  }
-};
-
-
-GeneratorMirror.prototype.func = function() {
-  if (!this.func_) {
-    this.func_ = MakeMirror(%GeneratorGetFunction(this.value_));
-  }
-  return this.func_;
-};
-
-
-GeneratorMirror.prototype.receiver = function() {
-  if (!this.receiver_) {
-    this.receiver_ = MakeMirror(%GeneratorGetReceiver(this.value_));
-  }
-  return this.receiver_;
-};
-
-
-GeneratorMirror.prototype.scopeCount = function() {
-  // This value can change over time as the underlying generator is suspended
-  // at different locations.
-  return %GetGeneratorScopeCount(this.value());
-};
-
-
-GeneratorMirror.prototype.scope = function(index) {
-  return new ScopeMirror(UNDEFINED, UNDEFINED, this, index);
-};
-
-
-GeneratorMirror.prototype.allScopes = function() {
-  var scopes = [];
-  for (let i = 0; i < this.scopeCount(); i++) {
-    scopes.push(this.scope(i));
-  }
-  return scopes;
-};
-
-
-/**
- * Base mirror object for properties.
- * @param {ObjectMirror} mirror The mirror object having this property
- * @param {string} name The name of the property
- * @param {Array} details Details about the property
- * @constructor
- * @extends Mirror
- */
-function PropertyMirror(mirror, name, details) {
-  %_Call(Mirror, this, MirrorType.PROPERTY_TYPE);
-  this.mirror_ = mirror;
-  this.name_ = name;
-  this.value_ = details[0];
-  this.details_ = details[1];
-  this.is_interceptor_ = details[2];
-  if (details.length > 3) {
-    this.exception_ = details[3];
-    this.getter_ = details[4];
-    this.setter_ = details[5];
-  }
-}
-inherits(PropertyMirror, Mirror);
-
-
-PropertyMirror.prototype.isReadOnly = function() {
-  return (this.attributes() & PropertyAttribute.ReadOnly) != 0;
-};
-
-
-PropertyMirror.prototype.isEnum = function() {
-  return (this.attributes() & PropertyAttribute.DontEnum) == 0;
-};
-
-
-PropertyMirror.prototype.canDelete = function() {
-  return (this.attributes() & PropertyAttribute.DontDelete) == 0;
-};
-
-
-PropertyMirror.prototype.name = function() {
-  return this.name_;
-};
-
-
-PropertyMirror.prototype.toText = function() {
-  if (IS_SYMBOL(this.name_)) return %SymbolDescriptiveString(this.name_);
-  return this.name_;
-};
-
-
-PropertyMirror.prototype.isIndexed = function() {
-  for (var i = 0; i < this.name_.length; i++) {
-    if (this.name_[i] < '0' || '9' < this.name_[i]) {
-      return false;
-    }
-  }
-  return true;
-};
-
-
-PropertyMirror.prototype.value = function() {
-  return MakeMirror(this.value_, false);
-};
-
-
-/**
- * Returns whether this property value is an exception.
- * @return {boolean} True if this property value is an exception
- */
-PropertyMirror.prototype.isException = function() {
-  return this.exception_ ? true : false;
-};
-
-
-PropertyMirror.prototype.attributes = function() {
-  return %DebugPropertyAttributesFromDetails(this.details_);
-};
-
-
-PropertyMirror.prototype.propertyType = function() {
-  return %DebugPropertyKindFromDetails(this.details_);
-};
-
-
-/**
- * Returns whether this property has a getter defined through __defineGetter__.
- * @return {boolean} True if this property has a getter
- */
-PropertyMirror.prototype.hasGetter = function() {
-  return this.getter_ ? true : false;
-};
-
-
-/**
- * Returns whether this property has a setter defined through __defineSetter__.
- * @return {boolean} True if this property has a setter
- */
-PropertyMirror.prototype.hasSetter = function() {
-  return this.setter_ ? true : false;
-};
-
-
-/**
- * Returns the getter for this property defined through __defineGetter__.
- * @return {Mirror} FunctionMirror reflecting the getter function or
- *     UndefinedMirror if there is no getter for this property
- */
-PropertyMirror.prototype.getter = function() {
-  if (this.hasGetter()) {
-    return MakeMirror(this.getter_);
-  } else {
-    return GetUndefinedMirror();
-  }
-};
-
-
-/**
- * Returns the setter for this property defined through __defineSetter__.
- * @return {Mirror} FunctionMirror reflecting the setter function or
- *     UndefinedMirror if there is no setter for this property
- */
-PropertyMirror.prototype.setter = function() {
-  if (this.hasSetter()) {
-    return MakeMirror(this.setter_);
-  } else {
-    return GetUndefinedMirror();
-  }
-};
-
-
-/**
- * Returns whether this property is natively implemented by the host or a set
- * through JavaScript code.
- * @return {boolean} True if the property is
- *     UndefinedMirror if there is no setter for this property
- */
-PropertyMirror.prototype.isNative = function() {
-  return this.is_interceptor_ ||
-         ((this.propertyType() == PropertyType.Accessor) &&
-          !this.hasGetter() && !this.hasSetter());
-};
-
-
-/**
- * Mirror object for internal properties. Internal property reflects properties
- * not accessible from user code such as [[BoundThis]] in bound function.
- * Their names are merely symbolic.
- * @param {string} name The name of the property
- * @param {value} property value
- * @constructor
- * @extends Mirror
- */
-function InternalPropertyMirror(name, value) {
-  %_Call(Mirror, this, MirrorType.INTERNAL_PROPERTY_TYPE);
-  this.name_ = name;
-  this.value_ = value;
-}
-inherits(InternalPropertyMirror, Mirror);
-
-
-InternalPropertyMirror.prototype.name = function() {
-  return this.name_;
-};
-
-
-InternalPropertyMirror.prototype.value = function() {
-  return MakeMirror(this.value_, false);
-};
-
-
-var kFrameDetailsFrameIdIndex = 0;
-var kFrameDetailsReceiverIndex = 1;
-var kFrameDetailsFunctionIndex = 2;
-var kFrameDetailsScriptIndex = 3;
-var kFrameDetailsArgumentCountIndex = 4;
-var kFrameDetailsLocalCountIndex = 5;
-var kFrameDetailsSourcePositionIndex = 6;
-var kFrameDetailsConstructCallIndex = 7;
-var kFrameDetailsAtReturnIndex = 8;
-var kFrameDetailsFlagsIndex = 9;
-var kFrameDetailsFirstDynamicIndex = 10;
-
-var kFrameDetailsNameIndex = 0;
-var kFrameDetailsValueIndex = 1;
-var kFrameDetailsNameValueSize = 2;
-
-var kFrameDetailsFlagDebuggerFrameMask = 1 << 0;
-var kFrameDetailsFlagOptimizedFrameMask = 1 << 1;
-var kFrameDetailsFlagInlinedFrameIndexMask = 7 << 2;
-
-/**
- * Wrapper for the frame details information retreived from the VM. The frame
- * details from the VM is an array with the following content. See runtime.cc
- * Runtime_GetFrameDetails.
- *     0: Id
- *     1: Receiver
- *     2: Function
- *     3: Script
- *     4: Argument count
- *     5: Local count
- *     6: Source position
- *     7: Construct call
- *     8: Is at return
- *     9: Flags (debugger frame, optimized frame, inlined frame index)
- *     Arguments name, value
- *     Locals name, value
- *     Return value if any
- * @param {number} break_id Current break id
- * @param {number} index Frame number
- * @constructor
- */
-function FrameDetails(break_id, index) {
-  this.break_id_ = break_id;
-  this.details_ = %GetFrameDetails(break_id, index);
-}
-
-
-FrameDetails.prototype.frameId = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsFrameIdIndex];
-};
-
-
-FrameDetails.prototype.receiver = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsReceiverIndex];
-};
-
-
-FrameDetails.prototype.func = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsFunctionIndex];
-};
-
-
-FrameDetails.prototype.script = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsScriptIndex];
-};
-
-
-FrameDetails.prototype.isConstructCall = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsConstructCallIndex];
-};
-
-
-FrameDetails.prototype.isAtReturn = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsAtReturnIndex];
-};
-
-
-FrameDetails.prototype.isDebuggerFrame = function() {
-  %CheckExecutionState(this.break_id_);
-  var f = kFrameDetailsFlagDebuggerFrameMask;
-  return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
-};
-
-
-FrameDetails.prototype.isOptimizedFrame = function() {
-  %CheckExecutionState(this.break_id_);
-  var f = kFrameDetailsFlagOptimizedFrameMask;
-  return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
-};
-
-
-FrameDetails.prototype.isInlinedFrame = function() {
-  return this.inlinedFrameIndex() > 0;
-};
-
-
-FrameDetails.prototype.inlinedFrameIndex = function() {
-  %CheckExecutionState(this.break_id_);
-  var f = kFrameDetailsFlagInlinedFrameIndexMask;
-  return (this.details_[kFrameDetailsFlagsIndex] & f) >> 2;
-};
-
-
-FrameDetails.prototype.argumentCount = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsArgumentCountIndex];
-};
-
-
-FrameDetails.prototype.argumentName = function(index) {
-  %CheckExecutionState(this.break_id_);
-  if (index >= 0 && index < this.argumentCount()) {
-    return this.details_[kFrameDetailsFirstDynamicIndex +
-                         index * kFrameDetailsNameValueSize +
-                         kFrameDetailsNameIndex];
-  }
-};
-
-
-FrameDetails.prototype.argumentValue = function(index) {
-  %CheckExecutionState(this.break_id_);
-  if (index >= 0 && index < this.argumentCount()) {
-    return this.details_[kFrameDetailsFirstDynamicIndex +
-                         index * kFrameDetailsNameValueSize +
-                         kFrameDetailsValueIndex];
-  }
-};
-
-
-FrameDetails.prototype.localCount = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsLocalCountIndex];
-};
-
-
-FrameDetails.prototype.sourcePosition = function() {
-  %CheckExecutionState(this.break_id_);
-  return this.details_[kFrameDetailsSourcePositionIndex];
-};
-
-
-FrameDetails.prototype.localName = function(index) {
-  %CheckExecutionState(this.break_id_);
-  if (index >= 0 && index < this.localCount()) {
-    var locals_offset = kFrameDetailsFirstDynamicIndex +
-                        this.argumentCount() * kFrameDetailsNameValueSize;
-    return this.details_[locals_offset +
-                         index * kFrameDetailsNameValueSize +
-                         kFrameDetailsNameIndex];
-  }
-};
-
-
-FrameDetails.prototype.localValue = function(index) {
-  %CheckExecutionState(this.break_id_);
-  if (index >= 0 && index < this.localCount()) {
-    var locals_offset = kFrameDetailsFirstDynamicIndex +
-                        this.argumentCount() * kFrameDetailsNameValueSize;
-    return this.details_[locals_offset +
-                         index * kFrameDetailsNameValueSize +
-                         kFrameDetailsValueIndex];
-  }
-};
-
-
-FrameDetails.prototype.returnValue = function() {
-  %CheckExecutionState(this.break_id_);
-  var return_value_offset =
-      kFrameDetailsFirstDynamicIndex +
-      (this.argumentCount() + this.localCount()) * kFrameDetailsNameValueSize;
-  if (this.details_[kFrameDetailsAtReturnIndex]) {
-    return this.details_[return_value_offset];
-  }
-};
-
-
-FrameDetails.prototype.scopeCount = function() {
-  if (IS_UNDEFINED(this.scopeCount_)) {
-    this.scopeCount_ = %GetScopeCount(this.break_id_, this.frameId());
-  }
-  return this.scopeCount_;
-};
-
-
-/**
- * Mirror object for stack frames.
- * @param {number} break_id The break id in the VM for which this frame is
-       valid
- * @param {number} index The frame index (top frame is index 0)
- * @constructor
- * @extends Mirror
- */
-function FrameMirror(break_id, index) {
-  %_Call(Mirror, this, MirrorType.FRAME_TYPE);
-  this.break_id_ = break_id;
-  this.index_ = index;
-  this.details_ = new FrameDetails(break_id, index);
-}
-inherits(FrameMirror, Mirror);
-
-
-FrameMirror.prototype.details = function() {
-  return this.details_;
-};
-
-
-FrameMirror.prototype.index = function() {
-  return this.index_;
-};
-
-
-FrameMirror.prototype.func = function() {
-  if (this.func_) {
-    return this.func_;
-  }
-
-  // Get the function for this frame from the VM.
-  var f = this.details_.func();
-
-  // Create a function mirror. NOTE: MakeMirror cannot be used here as the
-  // value returned from the VM might be a string if the function for the
-  // frame is unresolved.
-  if (IS_FUNCTION(f)) {
-    return this.func_ = MakeMirror(f);
-  } else {
-    return new UnresolvedFunctionMirror(f);
-  }
-};
-
-
-FrameMirror.prototype.script = function() {
-  if (!this.script_) {
-    this.script_ = MakeMirror(this.details_.script());
-  }
-
-  return this.script_;
-}
-
-
-FrameMirror.prototype.receiver = function() {
-  return MakeMirror(this.details_.receiver());
-};
-
-
-FrameMirror.prototype.isConstructCall = function() {
-  return this.details_.isConstructCall();
-};
-
-
-FrameMirror.prototype.isAtReturn = function() {
-  return this.details_.isAtReturn();
-};
-
-
-FrameMirror.prototype.isDebuggerFrame = function() {
-  return this.details_.isDebuggerFrame();
-};
-
-
-FrameMirror.prototype.isOptimizedFrame = function() {
-  return this.details_.isOptimizedFrame();
-};
-
-
-FrameMirror.prototype.isInlinedFrame = function() {
-  return this.details_.isInlinedFrame();
-};
-
-
-FrameMirror.prototype.inlinedFrameIndex = function() {
-  return this.details_.inlinedFrameIndex();
-};
-
-
-FrameMirror.prototype.argumentCount = function() {
-  return this.details_.argumentCount();
-};
-
-
-FrameMirror.prototype.argumentName = function(index) {
-  return this.details_.argumentName(index);
-};
-
-
-FrameMirror.prototype.argumentValue = function(index) {
-  return MakeMirror(this.details_.argumentValue(index));
-};
-
-
-FrameMirror.prototype.localCount = function() {
-  return this.details_.localCount();
-};
-
-
-FrameMirror.prototype.localName = function(index) {
-  return this.details_.localName(index);
-};
-
-
-FrameMirror.prototype.localValue = function(index) {
-  return MakeMirror(this.details_.localValue(index));
-};
-
-
-FrameMirror.prototype.returnValue = function() {
-  return MakeMirror(this.details_.returnValue());
-};
-
-
-FrameMirror.prototype.sourcePosition = function() {
-  return this.details_.sourcePosition();
-};
-
-
-FrameMirror.prototype.sourceLocation = function() {
-  var script = this.script();
-  if (script) {
-    return script.locationFromPosition(this.sourcePosition(), true);
-  }
-};
-
-
-FrameMirror.prototype.sourceLine = function() {
-  var location = this.sourceLocation();
-  if (location) {
-    return location.line;
-  }
-};
-
-
-FrameMirror.prototype.sourceColumn = function() {
-  var location = this.sourceLocation();
-  if (location) {
-    return location.column;
-  }
-};
-
-
-FrameMirror.prototype.sourceLineText = function() {
-  var location = this.sourceLocation();
-  if (location) {
-    return location.sourceText;
-  }
-};
-
-
-FrameMirror.prototype.scopeCount = function() {
-  return this.details_.scopeCount();
-};
-
-
-FrameMirror.prototype.scope = function(index) {
-  return new ScopeMirror(this, UNDEFINED, UNDEFINED, index);
-};
-
-
-FrameMirror.prototype.allScopes = function(opt_ignore_nested_scopes) {
-  var scopeDetails = %GetAllScopesDetails(this.break_id_,
-                                          this.details_.frameId(),
-                                          this.details_.inlinedFrameIndex(),
-                                          !!opt_ignore_nested_scopes);
-  var result = [];
-  for (var i = 0; i < scopeDetails.length; ++i) {
-    result.push(new ScopeMirror(this, UNDEFINED, UNDEFINED, i,
-                                scopeDetails[i]));
-  }
-  return result;
-};
-
-
-FrameMirror.prototype.evaluate = function(source, throw_on_side_effect = false) {
-  return MakeMirror(%DebugEvaluate(this.break_id_,
-                                   this.details_.frameId(),
-                                   this.details_.inlinedFrameIndex(),
-                                   source,
-                                   throw_on_side_effect));
-};
-
-
-FrameMirror.prototype.invocationText = function() {
-  // Format frame invoaction (receiver, function and arguments).
-  var result = '';
-  var func = this.func();
-  var receiver = this.receiver();
-  if (this.isConstructCall()) {
-    // For constructor frames display new followed by the function name.
-    result += 'new ';
-    result += func.name() ? func.name() : '[anonymous]';
-  } else if (this.isDebuggerFrame()) {
-    result += '[debugger]';
-  } else {
-    // If the receiver has a className which is 'global' don't display it.
-    var display_receiver =
-      !receiver.className || (receiver.className() != 'global');
-    if (display_receiver) {
-      result += receiver.toText();
-    }
-    // Try to find the function as a property in the receiver. Include the
-    // prototype chain in the lookup.
-    var property = GetUndefinedMirror();
-    if (receiver.isObject()) {
-      for (var r = receiver;
-           !r.isNull() && property.isUndefined();
-           r = r.protoObject()) {
-        property = r.lookupProperty(func);
-      }
-    }
-    if (!property.isUndefined()) {
-      // The function invoked was found on the receiver. Use the property name
-      // for the backtrace.
-      if (!property.isIndexed()) {
-        if (display_receiver) {
-          result += '.';
-        }
-        result += property.toText();
-      } else {
-        result += '[';
-        result += property.toText();
-        result += ']';
-      }
-      // Also known as - if the name in the function doesn't match the name
-      // under which it was looked up.
-      if (func.name() && func.name() != property.name()) {
-        result += '(aka ' + func.name() + ')';
-      }
-    } else {
-      // The function invoked was not found on the receiver. Use the function
-      // name if available for the backtrace.
-      if (display_receiver) {
-        result += '.';
-      }
-      result += func.name() ? func.name() : '[anonymous]';
-    }
-  }
-
-  // Render arguments for normal frames.
-  if (!this.isDebuggerFrame()) {
-    result += '(';
-    for (var i = 0; i < this.argumentCount(); i++) {
-      if (i != 0) result += ', ';
-      if (this.argumentName(i)) {
-        result += this.argumentName(i);
-        result += '=';
-      }
-      result += this.argumentValue(i).toText();
-    }
-    result += ')';
-  }
-
-  if (this.isAtReturn()) {
-    result += ' returning ';
-    result += this.returnValue().toText();
-  }
-
-  return result;
-};
-
-
-FrameMirror.prototype.sourceAndPositionText = function() {
-  // Format source and position.
-  var result = '';
-  var func = this.func();
-  if (func.resolved()) {
-    var script = func.script();
-    if (script) {
-      if (script.name()) {
-        result += script.name();
-      } else {
-        result += '[unnamed]';
-      }
-      if (!this.isDebuggerFrame()) {
-        var location = this.sourceLocation();
-        result += ' line ';
-        result += !IS_UNDEFINED(location) ? (location.line + 1) : '?';
-        result += ' column ';
-        result += !IS_UNDEFINED(location) ? (location.column + 1) : '?';
-        if (!IS_UNDEFINED(this.sourcePosition())) {
-          result += ' (position ' + (this.sourcePosition() + 1) + ')';
-        }
-      }
-    } else {
-      result += '[no source]';
-    }
-  } else {
-    result += '[unresolved]';
-  }
-
-  return result;
-};
-
-
-FrameMirror.prototype.localsText = function() {
-  // Format local variables.
-  var result = '';
-  var locals_count = this.localCount();
-  if (locals_count > 0) {
-    for (var i = 0; i < locals_count; ++i) {
-      result += '      var ';
-      result += this.localName(i);
-      result += ' = ';
-      result += this.localValue(i).toText();
-      if (i < locals_count - 1) result += '\n';
-    }
-  }
-
-  return result;
-};
-
-
-FrameMirror.prototype.restart = function() {
-  var result = %LiveEditRestartFrame(this.break_id_, this.index_);
-  if (IS_UNDEFINED(result)) {
-    result = "Failed to find requested frame";
-  }
-  return result;
-};
-
-
-FrameMirror.prototype.toText = function(opt_locals) {
-  var result = '';
-  result += '#' + (this.index() <= 9 ? '0' : '') + this.index();
-  result += ' ';
-  result += this.invocationText();
-  result += ' ';
-  result += this.sourceAndPositionText();
-  if (opt_locals) {
-    result += '\n';
-    result += this.localsText();
-  }
-  return result;
-};
-
-
-// This indexes correspond definitions in debug-scopes.h.
-var kScopeDetailsTypeIndex = 0;
-var kScopeDetailsObjectIndex = 1;
-var kScopeDetailsNameIndex = 2;
-var kScopeDetailsStartPositionIndex = 3;
-var kScopeDetailsEndPositionIndex = 4;
-var kScopeDetailsFunctionIndex = 5;
-
-function ScopeDetails(frame, fun, gen, index, opt_details) {
-  if (frame) {
-    this.break_id_ = frame.break_id_;
-    this.details_ = opt_details ||
-                    %GetScopeDetails(frame.break_id_,
-                                     frame.details_.frameId(),
-                                     frame.details_.inlinedFrameIndex(),
-                                     index);
-    this.frame_id_ = frame.details_.frameId();
-    this.inlined_frame_id_ = frame.details_.inlinedFrameIndex();
-  } else if (fun) {
-    this.details_ = opt_details || %GetFunctionScopeDetails(fun.value(), index);
-    this.fun_value_ = fun.value();
-    this.break_id_ = UNDEFINED;
-  } else {
-    this.details_ =
-      opt_details || %GetGeneratorScopeDetails(gen.value(), index);
-    this.gen_value_ = gen.value();
-    this.break_id_ = UNDEFINED;
-  }
-  this.index_ = index;
-}
-
-
-ScopeDetails.prototype.type = function() {
-  if (!IS_UNDEFINED(this.break_id_)) {
-    %CheckExecutionState(this.break_id_);
-  }
-  return this.details_[kScopeDetailsTypeIndex];
-};
-
-
-ScopeDetails.prototype.object = function() {
-  if (!IS_UNDEFINED(this.break_id_)) {
-    %CheckExecutionState(this.break_id_);
-  }
-  return this.details_[kScopeDetailsObjectIndex];
-};
-
-
-ScopeDetails.prototype.name = function() {
-  if (!IS_UNDEFINED(this.break_id_)) {
-    %CheckExecutionState(this.break_id_);
-  }
-  return this.details_[kScopeDetailsNameIndex];
-};
-
-
-ScopeDetails.prototype.startPosition = function() {
-  if (!IS_UNDEFINED(this.break_id_)) {
-    %CheckExecutionState(this.break_id_);
-  }
-  return this.details_[kScopeDetailsStartPositionIndex];
-}
-
-
-ScopeDetails.prototype.endPosition = function() {
-  if (!IS_UNDEFINED(this.break_id_)) {
-    %CheckExecutionState(this.break_id_);
-  }
-  return this.details_[kScopeDetailsEndPositionIndex];
-}
-
-ScopeDetails.prototype.func = function() {
-  if (!IS_UNDEFINED(this.break_id_)) {
-    %CheckExecutionState(this.break_id_);
-  }
-  return this.details_[kScopeDetailsFunctionIndex];
-}
-
-
-ScopeDetails.prototype.setVariableValueImpl = function(name, new_value) {
-  var raw_res;
-  if (!IS_UNDEFINED(this.break_id_)) {
-    %CheckExecutionState(this.break_id_);
-    raw_res = %SetScopeVariableValue(this.break_id_, this.frame_id_,
-        this.inlined_frame_id_, this.index_, name, new_value);
-  } else if (!IS_UNDEFINED(this.fun_value_)) {
-    raw_res = %SetScopeVariableValue(this.fun_value_, null, null, this.index_,
-        name, new_value);
-  } else {
-    raw_res = %SetScopeVariableValue(this.gen_value_, null, null, this.index_,
-        name, new_value);
-  }
-  if (!raw_res) throw %make_error(kDebugger, "Failed to set variable value");
-};
-
-
-/**
- * Mirror object for scope of frame or function. Either frame or function must
- * be specified.
- * @param {FrameMirror} frame The frame this scope is a part of
- * @param {FunctionMirror} function The function this scope is a part of
- * @param {GeneratorMirror} gen The generator this scope is a part of
- * @param {number} index The scope index in the frame
- * @param {Array=} opt_details Raw scope details data
- * @constructor
- * @extends Mirror
- */
-function ScopeMirror(frame, fun, gen, index, opt_details) {
-  %_Call(Mirror, this, MirrorType.SCOPE_TYPE);
-  if (frame) {
-    this.frame_index_ = frame.index_;
-  } else {
-    this.frame_index_ = UNDEFINED;
-  }
-  this.scope_index_ = index;
-  this.details_ = new ScopeDetails(frame, fun, gen, index, opt_details);
-}
-inherits(ScopeMirror, Mirror);
-
-
-ScopeMirror.prototype.details = function() {
-  return this.details_;
-};
-
-
-ScopeMirror.prototype.frameIndex = function() {
-  return this.frame_index_;
-};
-
-
-ScopeMirror.prototype.scopeIndex = function() {
-  return this.scope_index_;
-};
-
-
-ScopeMirror.prototype.scopeType = function() {
-  return this.details_.type();
-};
-
-
-ScopeMirror.prototype.scopeObject = function() {
-  // For local, closure and script scopes create a mirror
-  // as these objects are created on the fly materializing the local
-  // or closure scopes and therefore will not preserve identity.
-  return MakeMirror(this.details_.object());
-};
-
-
-ScopeMirror.prototype.setVariableValue = function(name, new_value) {
-  this.details_.setVariableValueImpl(name, new_value);
-};
-
-
-/**
- * Mirror object for script source.
- * @param {Script} script The script object
- * @constructor
- * @extends Mirror
- */
-function ScriptMirror(script) {
-  %_Call(Mirror, this, MirrorType.SCRIPT_TYPE);
-  this.script_ = script;
-  this.context_ = new ContextMirror(script.context_data);
-}
-inherits(ScriptMirror, Mirror);
-
-
-ScriptMirror.prototype.value = function() {
-  return this.script_;
-};
-
-
-ScriptMirror.prototype.name = function() {
-  return this.script_.name || this.script_.nameOrSourceURL();
-};
-
-
-ScriptMirror.prototype.id = function() {
-  return this.script_.id;
-};
-
-
-ScriptMirror.prototype.source = function() {
-  return this.script_.source;
-};
-
-
-ScriptMirror.prototype.setSource = function(source) {
-  if (!IS_STRING(source)) throw %make_error(kDebugger, "Source is not a string");
-  %DebugSetScriptSource(this.script_, source);
-};
-
-
-ScriptMirror.prototype.lineOffset = function() {
-  return this.script_.line_offset;
-};
-
-
-ScriptMirror.prototype.columnOffset = function() {
-  return this.script_.column_offset;
-};
-
-
-ScriptMirror.prototype.data = function() {
-  return this.script_.data;
-};
-
-
-ScriptMirror.prototype.scriptType = function() {
-  return this.script_.type;
-};
-
-
-ScriptMirror.prototype.compilationType = function() {
-  return this.script_.compilation_type;
-};
-
-
-ScriptMirror.prototype.lineCount = function() {
-  return %ScriptLineCount(this.script_);
-};
-
-
-ScriptMirror.prototype.locationFromPosition = function(
-    position, include_resource_offset) {
-  return this.script_.locationFromPosition(position, include_resource_offset);
-};
-
-
-ScriptMirror.prototype.context = function() {
-  return this.context_;
-};
-
-
-ScriptMirror.prototype.evalFromScript = function() {
-  return MakeMirror(this.script_.eval_from_script);
-};
-
-
-ScriptMirror.prototype.evalFromFunctionName = function() {
-  return MakeMirror(this.script_.eval_from_function_name);
-};
-
-
-ScriptMirror.prototype.evalFromLocation = function() {
-  var eval_from_script = this.evalFromScript();
-  if (!eval_from_script.isUndefined()) {
-    var position = this.script_.eval_from_script_position;
-    return eval_from_script.locationFromPosition(position, true);
-  }
-};
-
-
-ScriptMirror.prototype.toText = function() {
-  var result = '';
-  result += this.name();
-  result += ' (lines: ';
-  if (this.lineOffset() > 0) {
-    result += this.lineOffset();
-    result += '-';
-    result += this.lineOffset() + this.lineCount() - 1;
-  } else {
-    result += this.lineCount();
-  }
-  result += ')';
-  return result;
-};
-
-
-/**
- * Mirror object for context.
- * @param {Object} data The context data
- * @constructor
- * @extends Mirror
- */
-function ContextMirror(data) {
-  %_Call(Mirror, this, MirrorType.CONTEXT_TYPE);
-  this.data_ = data;
-}
-inherits(ContextMirror, Mirror);
-
-
-ContextMirror.prototype.data = function() {
-  return this.data_;
-};
-
-// ----------------------------------------------------------------------------
-// Exports
-
-utils.InstallFunctions(global, DONT_ENUM, [
-  "MakeMirror", MakeMirror,
-]);
-
-utils.InstallConstants(global, [
-  "ScopeType", ScopeType,
-  "PropertyType", PropertyType,
-  "PropertyAttribute", PropertyAttribute,
-  "Mirror", Mirror,
-  "ValueMirror", ValueMirror,
-  "UndefinedMirror", UndefinedMirror,
-  "NullMirror", NullMirror,
-  "BooleanMirror", BooleanMirror,
-  "NumberMirror", NumberMirror,
-  "StringMirror", StringMirror,
-  "SymbolMirror", SymbolMirror,
-  "ObjectMirror", ObjectMirror,
-  "FunctionMirror", FunctionMirror,
-  "UnresolvedFunctionMirror", UnresolvedFunctionMirror,
-  "ArrayMirror", ArrayMirror,
-  "DateMirror", DateMirror,
-  "RegExpMirror", RegExpMirror,
-  "ErrorMirror", ErrorMirror,
-  "PromiseMirror", PromiseMirror,
-  "MapMirror", MapMirror,
-  "SetMirror", SetMirror,
-  "IteratorMirror", IteratorMirror,
-  "GeneratorMirror", GeneratorMirror,
-  "PropertyMirror", PropertyMirror,
-  "InternalPropertyMirror", InternalPropertyMirror,
-  "FrameMirror", FrameMirror,
-  "ScriptMirror", ScriptMirror,
-  "ScopeMirror", ScopeMirror,
-  "FrameDetails", FrameDetails,
-]);
-
-})
diff --git a/src/debug/ppc/OWNERS b/src/debug/ppc/OWNERS
index 752e8e3..02c2cd7 100644
--- a/src/debug/ppc/OWNERS
+++ b/src/debug/ppc/OWNERS
@@ -1,6 +1,5 @@
-jyan@ca.ibm.com
-dstence@us.ibm.com
+junyan@redhat.com
 joransiu@ca.ibm.com
-mbrandy@us.ibm.com
-michael_dawson@ca.ibm.com
-bjaideep@ca.ibm.com
+midawson@redhat.com
+mfarazma@redhat.com
+vasili.skurydzin@ibm.com
diff --git a/src/debug/ppc/debug-ppc.cc b/src/debug/ppc/debug-ppc.cc
index 42be185..eeed2d8 100644
--- a/src/debug/ppc/debug-ppc.cc
+++ b/src/debug/ppc/debug-ppc.cc
@@ -2,116 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#if V8_TARGET_ARCH_PPC
+#if V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
 
 #include "src/debug/debug.h"
 
-#include "src/codegen.h"
+#include "src/codegen/macro-assembler.h"
 #include "src/debug/liveedit.h"
+#include "src/execution/frames-inl.h"
 
 namespace v8 {
 namespace internal {
 
 #define __ ACCESS_MASM(masm)
 
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_size;
-  __ bind(&check_size);
-  for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
-    __ nop(MacroAssembler::DEBUG_BREAK_NOP);
-  }
-  DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
-            masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction. Avoid emitting
-  // the trampoline pool in the debug break slot code.
-  Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotInstructions);
-  // Patch the code changing the debug break slot code from
-  //
-  //   ori r3, r3, 0
-  //   ori r3, r3, 0
-  //   ori r3, r3, 0
-  //   ori r3, r3, 0
-  //   ori r3, r3, 0
-  //
-  // to a call to the debug break code, using a FIXED_SEQUENCE.
-  //
-  //   mov r0, <address>
-  //   mtlr r0
-  //   blrl
-  //
-  Assembler::BlockTrampolinePoolScope block_trampoline_pool(patcher.masm());
-  patcher.masm()->mov(v8::internal::r0,
-                      Operand(reinterpret_cast<intptr_t>(code->entry())));
-  patcher.masm()->mtctr(v8::internal::r0);
-  patcher.masm()->bctrl();
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  Instr current_instr = Assembler::instr_at(pc);
-  return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-  {
-    FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ push(r3);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ mov(r3, Operand(1));
-    __ mov(r4,
-           Operand(ExternalReference(
-               Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
-    CEntryStub ceb(masm->isolate(), 1);
-    __ CallStub(&ceb);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; i++) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber r3 if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function.
-        if (!(reg.is(r3) && (mode == SAVE_RESULT_REGISTER))) {
-          __ mov(reg, Operand(kDebugZapValue));
-        }
-      }
-    }
-    // Leave the internal frame.
-  }
-
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ Ret();
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -131,17 +34,15 @@
   // - Restart the frame by calling the function.
 
   __ mr(fp, r4);
-  __ LoadP(r4, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+  __ LoadP(r4, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
   __ LeaveFrame(StackFrame::INTERNAL);
-  __ LoadP(r3, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
-  __ LoadP(
-      r3, FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
+  __ LoadTaggedPointerField(
+      r3, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
+  __ lhz(r3,
+         FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
   __ mr(r5, r3);
 
-  ParameterCount dummy1(r5);
-  ParameterCount dummy2(r3);
-  __ InvokeFunction(r4, dummy1, dummy2, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  __ InvokeFunction(r4, r5, r3, JUMP_FUNCTION);
 }
 
 const bool LiveEdit::kFrameDropperSupported = true;
@@ -150,4 +51,4 @@
 }  // namespace internal
 }  // namespace v8
 
-#endif  // V8_TARGET_ARCH_PPC
+#endif  // V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
diff --git a/src/debug/s390/OWNERS b/src/debug/s390/OWNERS
deleted file mode 100644
index 752e8e3..0000000
--- a/src/debug/s390/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-jyan@ca.ibm.com
-dstence@us.ibm.com
-joransiu@ca.ibm.com
-mbrandy@us.ibm.com
-michael_dawson@ca.ibm.com
-bjaideep@ca.ibm.com
diff --git a/src/debug/s390/debug-s390.cc b/src/debug/s390/debug-s390.cc
index 5ef6a60..7d08e13 100644
--- a/src/debug/s390/debug-s390.cc
+++ b/src/debug/s390/debug-s390.cc
@@ -2,120 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "src/v8.h"
+#include "src/init/v8.h"
 
 #if V8_TARGET_ARCH_S390
 
 #include "src/debug/debug.h"
 
-#include "src/codegen.h"
+#include "src/codegen/macro-assembler.h"
 #include "src/debug/liveedit.h"
+#include "src/execution/frames-inl.h"
 
 namespace v8 {
 namespace internal {
 
 #define __ ACCESS_MASM(masm)
 
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_size;
-  __ bind(&check_size);
-  //   oill r3, 0
-  //   oill r3, 0
-  __ nop(Assembler::DEBUG_BREAK_NOP);
-  __ nop(Assembler::DEBUG_BREAK_NOP);
-
-  //   lr r0, r0    64-bit only
-  //   lr r0, r0    64-bit only
-  //   lr r0, r0    64-bit only
-  for (int i = 8; i < Assembler::kDebugBreakSlotLength; i += 2) {
-    __ nop();
-  }
-  DCHECK_EQ(Assembler::kDebugBreakSlotLength,
-            masm->SizeOfCodeGeneratedSince(&check_size));
-}
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction.
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength);
-  // Patch the code changing the debug break slot code from
-  //
-  //   oill r3, 0
-  //   oill r3, 0
-  //   oill r3, 0   64-bit only
-  //   lr r0, r0    64-bit only
-  //
-  // to a call to the debug break code, using a FIXED_SEQUENCE.
-  //
-  //   iilf r14, <address>   6-bytes
-  //   basr r14, r14A        2-bytes
-  //
-  // The 64bit sequence has an extra iihf.
-  //
-  //   iihf r14, <high 32-bits address>    6-bytes
-  //   iilf r14, <lower 32-bits address>   6-bytes
-  //   basr r14, r14                       2-bytes
-  patcher.masm()->mov(v8::internal::r14,
-                      Operand(reinterpret_cast<intptr_t>(code->entry())));
-  patcher.masm()->basr(v8::internal::r14, v8::internal::r14);
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  Instr current_instr = Assembler::instr_at(pc);
-  return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-  {
-    FrameScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ push(r2);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ mov(r2, Operand(1));
-    __ mov(r3,
-           Operand(ExternalReference(
-               Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
-    CEntryStub ceb(masm->isolate(), 1);
-    __ CallStub(&ceb);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; i++) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber r2 if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function.
-        if (!(reg.is(r2) && (mode == SAVE_RESULT_REGISTER))) {
-          __ mov(reg, Operand(kDebugZapValue));
-        }
-      }
-    }
-    // Leave the internal frame.
-  }
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ Ret();
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -135,17 +36,15 @@
   // - Restart the frame by calling the function.
 
   __ LoadRR(fp, r3);
-  __ LoadP(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+  __ LoadP(r3, MemOperand(fp, StandardFrameConstants::kFunctionOffset));
   __ LeaveFrame(StackFrame::INTERNAL);
-  __ LoadP(r2, FieldMemOperand(r3, JSFunction::kSharedFunctionInfoOffset));
-  __ LoadP(
+  __ LoadTaggedPointerField(
+      r2, FieldMemOperand(r3, JSFunction::kSharedFunctionInfoOffset));
+  __ LoadLogicalHalfWordP(
       r2, FieldMemOperand(r2, SharedFunctionInfo::kFormalParameterCountOffset));
   __ LoadRR(r4, r2);
 
-  ParameterCount dummy1(r4);
-  ParameterCount dummy2(r2);
-  __ InvokeFunction(r3, dummy1, dummy2, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  __ InvokeFunction(r3, r4, r2, JUMP_FUNCTION);
 }
 
 const bool LiveEdit::kFrameDropperSupported = true;
diff --git a/src/debug/wasm/gdb-server/DIR_METADATA b/src/debug/wasm/gdb-server/DIR_METADATA
new file mode 100644
index 0000000..3b428d9
--- /dev/null
+++ b/src/debug/wasm/gdb-server/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>JavaScript>WebAssembly"
+}
\ No newline at end of file
diff --git a/src/debug/wasm/gdb-server/OWNERS b/src/debug/wasm/gdb-server/OWNERS
new file mode 100644
index 0000000..e2c94e8
--- /dev/null
+++ b/src/debug/wasm/gdb-server/OWNERS
@@ -0,0 +1 @@
+paolosev@microsoft.com
diff --git a/src/debug/wasm/gdb-server/gdb-remote-util.cc b/src/debug/wasm/gdb-server/gdb-remote-util.cc
new file mode 100644
index 0000000..b4880ed
--- /dev/null
+++ b/src/debug/wasm/gdb-server/gdb-remote-util.cc
@@ -0,0 +1,104 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
+using std::string;
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+// GDB expects lower case values.
+static const char kHexChars[] = "0123456789abcdef";
+
+void UInt8ToHex(uint8_t byte, char chars[2]) {
+  DCHECK(chars);
+  chars[0] = kHexChars[byte >> 4];
+  chars[1] = kHexChars[byte & 0xF];
+}
+
+bool HexToUInt8(const char chars[2], uint8_t* byte) {
+  uint8_t o1, o2;
+  if (NibbleToUInt8(chars[0], &o1) && NibbleToUInt8(chars[1], &o2)) {
+    *byte = (o1 << 4) + o2;
+    return true;
+  }
+
+  return false;
+}
+
+bool NibbleToUInt8(char ch, uint8_t* byte) {
+  DCHECK(byte);
+
+  // Check for nibble of a-f
+  if ((ch >= 'a') && (ch <= 'f')) {
+    *byte = (ch - 'a' + 10);
+    return true;
+  }
+
+  // Check for nibble of A-F
+  if ((ch >= 'A') && (ch <= 'F')) {
+    *byte = (ch - 'A' + 10);
+    return true;
+  }
+
+  // Check for nibble of 0-9
+  if ((ch >= '0') && (ch <= '9')) {
+    *byte = (ch - '0');
+    return true;
+  }
+
+  // Not a valid nibble representation
+  return false;
+}
+
+std::vector<std::string> StringSplit(const string& instr, const char* delim) {
+  std::vector<std::string> result;
+
+  const char* in = instr.data();
+  if (nullptr == in) return result;
+
+  // Check if we have nothing to do
+  if (nullptr == delim) {
+    result.push_back(string(in));
+    return result;
+  }
+
+  while (*in) {
+    // Toss all preceeding delimiters
+    while (*in && strchr(delim, *in)) in++;
+
+    // If we still have something to process
+    if (*in) {
+      const char* start = in;
+      size_t len = 0;
+      // Keep moving forward for all valid chars
+      while (*in && (strchr(delim, *in) == nullptr)) {
+        len++;
+        in++;
+      }
+
+      // Build this token and add it to the array.
+      result.push_back(string{start, len});
+    }
+  }
+  return result;
+}
+
+std::string Mem2Hex(const uint8_t* mem, size_t count) {
+  std::vector<char> result(count * 2 + 1);
+  for (size_t i = 0; i < count; i++) UInt8ToHex(*mem++, &result[i * 2]);
+  result[count * 2] = '\0';
+  return result.data();
+}
+
+std::string Mem2Hex(const std::string& str) {
+  return Mem2Hex(reinterpret_cast<const uint8_t*>(str.data()), str.size());
+}
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/wasm/gdb-server/gdb-remote-util.h b/src/debug/wasm/gdb-server/gdb-remote-util.h
new file mode 100644
index 0000000..88a2715
--- /dev/null
+++ b/src/debug/wasm/gdb-server/gdb-remote-util.h
@@ -0,0 +1,72 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_GDB_REMOTE_UTIL_H_
+#define V8_DEBUG_WASM_GDB_SERVER_GDB_REMOTE_UTIL_H_
+
+#include <string>
+#include <vector>
+#include "src/flags/flags.h"
+#include "src/utils/utils.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+#define TRACE_GDB_REMOTE(...)                                            \
+  do {                                                                   \
+    if (FLAG_trace_wasm_gdb_remote) PrintF("[gdb-remote] " __VA_ARGS__); \
+  } while (false)
+
+// Convert from 0-255 to a pair of ASCII chars (0-9,a-f).
+void UInt8ToHex(uint8_t byte, char chars[2]);
+
+// Convert a pair of hex chars into a value 0-255 or return false if either
+// input character is not a valid nibble.
+bool HexToUInt8(const char chars[2], uint8_t* byte);
+
+// Convert from ASCII (0-9,a-f,A-F) to 4b unsigned or return false if the
+// input char is unexpected.
+bool NibbleToUInt8(char ch, uint8_t* byte);
+
+std::vector<std::string> V8_EXPORT_PRIVATE StringSplit(const std::string& instr,
+                                                       const char* delim);
+
+// Convert the memory pointed to by {mem} into a hex string in GDB-remote
+// format.
+std::string Mem2Hex(const uint8_t* mem, size_t count);
+std::string Mem2Hex(const std::string& str);
+
+// For LLDB debugging, an address in a Wasm module code space is represented
+// with 64 bits, where the first 32 bits identify the module id:
+// +--------------------+--------------------+
+// |     module_id      |       offset       |
+// +--------------------+--------------------+
+//  <----- 32 bit -----> <----- 32 bit ----->
+class wasm_addr_t {
+ public:
+  wasm_addr_t(uint32_t module_id, uint32_t offset)
+      : module_id_(module_id), offset_(offset) {}
+  explicit wasm_addr_t(uint64_t address)
+      : module_id_(address >> 32), offset_(address & 0xffffffff) {}
+
+  inline uint32_t ModuleId() const { return module_id_; }
+  inline uint32_t Offset() const { return offset_; }
+
+  inline operator uint64_t() const {
+    return static_cast<uint64_t>(module_id_) << 32 | offset_;
+  }
+
+ private:
+  uint32_t module_id_;
+  uint32_t offset_;
+};
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_GDB_REMOTE_UTIL_H_
diff --git a/src/debug/wasm/gdb-server/gdb-server-thread.cc b/src/debug/wasm/gdb-server/gdb-server-thread.cc
new file mode 100644
index 0000000..03b9b8f
--- /dev/null
+++ b/src/debug/wasm/gdb-server/gdb-server-thread.cc
@@ -0,0 +1,118 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/gdb-server-thread.h"
+#include "src/debug/wasm/gdb-server/gdb-server.h"
+#include "src/debug/wasm/gdb-server/session.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+GdbServerThread::GdbServerThread(GdbServer* gdb_server)
+    : Thread(v8::base::Thread::Options("GdbServerThread")),
+      gdb_server_(gdb_server),
+      start_semaphore_(0) {}
+
+bool GdbServerThread::StartAndInitialize() {
+  // Executed in the Isolate thread.
+  if (!Start()) {
+    return false;
+  }
+
+  // We need to make sure that {Stop} is never called before the thread has
+  // completely initialized {transport_} and {target_}. Otherwise there could be
+  // a race condition where in the main thread {Stop} might get called before
+  // the transport is created, and then in the GDBServer thread we may have time
+  // to setup the transport and block on accept() before the main thread blocks
+  // on joining the thread.
+  // The small performance hit caused by this Wait should be negligeable because
+  // this operation happensat most once per process and only when the
+  // --wasm-gdb-remote flag is set.
+  start_semaphore_.Wait();
+  return !!target_;
+}
+
+void GdbServerThread::CleanupThread() {
+  // Executed in the GdbServer thread.
+  v8::base::MutexGuard guard(&mutex_);
+
+  target_ = nullptr;
+  transport_ = nullptr;
+
+#if _WIN32
+  ::WSACleanup();
+#endif
+}
+
+void GdbServerThread::Run() {
+  // Executed in the GdbServer thread.
+#ifdef _WIN32
+  // Initialize Winsock
+  WSADATA wsaData;
+  int iResult = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
+  if (iResult != 0) {
+    TRACE_GDB_REMOTE("GdbServerThread::Run: WSAStartup failed\n");
+    return;
+  }
+#endif
+
+  // If the default port is not available, try any port.
+  SocketBinding socket_binding = SocketBinding::Bind(FLAG_wasm_gdb_remote_port);
+  if (!socket_binding.IsValid()) {
+    socket_binding = SocketBinding::Bind(0);
+  }
+  if (!socket_binding.IsValid()) {
+    TRACE_GDB_REMOTE("GdbServerThread::Run: Failed to bind any TCP port\n");
+    return;
+  }
+  TRACE_GDB_REMOTE("gdb-remote(%d) : Connect GDB with 'target remote :%d\n",
+                   __LINE__, socket_binding.GetBoundPort());
+
+  transport_ = socket_binding.CreateTransport();
+  target_ = std::make_unique<Target>(gdb_server_);
+
+  // Here we have completed the initialization, and the thread that called
+  // {StartAndInitialize} may resume execution.
+  start_semaphore_.Signal();
+
+  while (!target_->IsTerminated()) {
+    // Wait for incoming connections.
+    if (!transport_->AcceptConnection()) {
+      continue;
+    }
+
+    // Create a new session for this connection
+    Session session(transport_.get());
+    TRACE_GDB_REMOTE("GdbServerThread: Connected\n");
+
+    // Run this session for as long as it lasts
+    target_->Run(&session);
+  }
+  CleanupThread();
+}
+
+void GdbServerThread::Stop() {
+  // Executed in the Isolate thread.
+
+  // Synchronized, becauses {Stop} might be called while {Run} is still
+  // initializing {transport_} and {target_}. If this happens and the thread is
+  // blocked waiting for an incoming connection or GdbServer for incoming
+  // packets, it will unblocked when {transport_} is closed.
+  v8::base::MutexGuard guard(&mutex_);
+
+  if (target_) {
+    target_->Terminate();
+  }
+
+  if (transport_) {
+    transport_->Close();
+  }
+}
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/wasm/gdb-server/gdb-server-thread.h b/src/debug/wasm/gdb-server/gdb-server-thread.h
new file mode 100644
index 0000000..cca1e4a
--- /dev/null
+++ b/src/debug/wasm/gdb-server/gdb-server-thread.h
@@ -0,0 +1,63 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_THREAD_H_
+#define V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_THREAD_H_
+
+#include "src/base/platform/platform.h"
+#include "src/base/platform/semaphore.h"
+#include "src/debug/wasm/gdb-server/target.h"
+#include "src/debug/wasm/gdb-server/transport.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+class GdbServer;
+
+// class GdbServerThread spawns a thread where all communication with a debugger
+// happens.
+class GdbServerThread : public v8::base::Thread {
+ public:
+  explicit GdbServerThread(GdbServer* gdb_server);
+
+  // base::Thread
+  void Run() override;
+
+  // Starts the GDB-server thread and waits Run() method is called on the new
+  // thread and the initialization completes.
+  bool StartAndInitialize();
+
+  // Stops the GDB-server thread when the V8 process shuts down; gracefully
+  // closes any active debugging session.
+  void Stop();
+
+  Target& GetTarget() { return *target_; }
+
+ private:
+  void CleanupThread();
+
+  GdbServer* gdb_server_;
+
+  // Used to block the caller on StartAndInitialize() waiting for the new thread
+  // to have completed its initialization.
+  // (Note that Thread::StartSynchronously() wouldn't work in this case because
+  // it returns as soon as the new thread starts, but before Run() is called).
+  base::Semaphore start_semaphore_;
+
+  base::Mutex mutex_;
+  // Protected by {mutex_}:
+  std::unique_ptr<TransportBase> transport_;
+  std::unique_ptr<Target> target_;
+
+  DISALLOW_COPY_AND_ASSIGN(GdbServerThread);
+};
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_THREAD_H_
diff --git a/src/debug/wasm/gdb-server/gdb-server.cc b/src/debug/wasm/gdb-server/gdb-server.cc
new file mode 100644
index 0000000..96e2308
--- /dev/null
+++ b/src/debug/wasm/gdb-server/gdb-server.cc
@@ -0,0 +1,424 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/gdb-server.h"
+
+#include <inttypes.h>
+#include <functional>
+#include "src/api/api-inl.h"
+#include "src/api/api.h"
+#include "src/debug/debug.h"
+#include "src/debug/wasm/gdb-server/gdb-server-thread.h"
+#include "src/utils/locked-queue-inl.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+static const uint32_t kMaxWasmCallStack = 20;
+
+// A TaskRunner is an object that runs posted tasks (in the form of closure
+// objects). Tasks are queued and run, in order, in the thread where the
+// TaskRunner::RunMessageLoop() is called.
+class TaskRunner {
+ public:
+  // Class Task wraps a std::function with a semaphore to signal its completion.
+  // This logic would be neatly implemented with std::packaged_tasks but we
+  // cannot use <future> in V8.
+  class Task {
+   public:
+    Task(base::Semaphore* ready_semaphore, std::function<void()> func)
+        : ready_semaphore_(ready_semaphore), func_(func) {}
+
+    void Run() {
+      func_();
+      ready_semaphore_->Signal();
+    }
+
+    // A semaphore object passed by the thread that posts a task.
+    // The sender can Wait on this semaphore to block until the task has
+    // completed execution in the TaskRunner thread.
+    base::Semaphore* ready_semaphore_;
+
+    // The function to run.
+    std::function<void()> func_;
+  };
+
+  TaskRunner()
+      : process_queue_semaphore_(0),
+        nested_loop_count_(0),
+        is_terminated_(false) {}
+
+  // Starts the task runner. All tasks posted are run, in order, in the thread
+  // that calls this function.
+  void Run() {
+    is_terminated_ = false;
+    int loop_number = ++nested_loop_count_;
+    while (nested_loop_count_ == loop_number && !is_terminated_) {
+      std::shared_ptr<Task> task = GetNext();
+      if (task) {
+        task->Run();
+      }
+    }
+  }
+
+  // Terminates the task runner. Tasks that are still pending in the queue are
+  // not discarded and will be executed when the task runner is restarted.
+  void Terminate() {
+    DCHECK_LT(0, nested_loop_count_);
+    --nested_loop_count_;
+
+    is_terminated_ = true;
+    process_queue_semaphore_.Signal();
+  }
+
+  // Posts a task to the task runner, to be executed in the task runner thread.
+  template <typename Functor>
+  auto Append(base::Semaphore* ready_semaphore, Functor&& task) {
+    queue_.Enqueue(std::make_shared<Task>(ready_semaphore, task));
+    process_queue_semaphore_.Signal();
+  }
+
+ private:
+  std::shared_ptr<Task> GetNext() {
+    while (!is_terminated_) {
+      if (queue_.IsEmpty()) {
+        process_queue_semaphore_.Wait();
+      }
+
+      std::shared_ptr<Task> task;
+      if (queue_.Dequeue(&task)) {
+        return task;
+      }
+    }
+    return nullptr;
+  }
+
+  LockedQueue<std::shared_ptr<Task>> queue_;
+  v8::base::Semaphore process_queue_semaphore_;
+  int nested_loop_count_;
+  std::atomic<bool> is_terminated_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskRunner);
+};
+
+GdbServer::GdbServer() { task_runner_ = std::make_unique<TaskRunner>(); }
+
+template <typename Functor>
+auto GdbServer::RunSyncTask(Functor&& callback) const {
+  // Executed in the GDBServerThread.
+  v8::base::Semaphore ready_semaphore(0);
+  task_runner_->Append(&ready_semaphore, callback);
+  ready_semaphore.Wait();
+}
+
+// static
+std::unique_ptr<GdbServer> GdbServer::Create() {
+  DCHECK(FLAG_wasm_gdb_remote);
+
+  std::unique_ptr<GdbServer> gdb_server(new GdbServer());
+
+  // Spawns the GDB-stub thread where all the communication with the debugger
+  // happens.
+  gdb_server->thread_ = std::make_unique<GdbServerThread>(gdb_server.get());
+  if (!gdb_server->thread_->StartAndInitialize()) {
+    TRACE_GDB_REMOTE(
+        "Cannot initialize thread, GDB-remote debugging will be disabled.\n");
+    return nullptr;
+  }
+  return gdb_server;
+}
+
+GdbServer::~GdbServer() {
+  // All Isolates have been deregistered.
+  DCHECK(isolate_delegates_.empty());
+
+  if (thread_) {
+    // Waits for the GDB-stub thread to terminate.
+    thread_->Stop();
+    thread_->Join();
+  }
+}
+
+void GdbServer::RunMessageLoopOnPause() { task_runner_->Run(); }
+
+void GdbServer::QuitMessageLoopOnPause() { task_runner_->Terminate(); }
+
+std::vector<GdbServer::WasmModuleInfo> GdbServer::GetLoadedModules() {
+  // Executed in the GDBServerThread.
+  std::vector<GdbServer::WasmModuleInfo> modules;
+
+  RunSyncTask([this, &modules]() {
+    // Executed in the isolate thread.
+    for (const auto& pair : scripts_) {
+      uint32_t module_id = pair.first;
+      const WasmModuleDebug& module_debug = pair.second;
+      modules.push_back({module_id, module_debug.GetModuleName()});
+    }
+  });
+  return modules;
+}
+
+bool GdbServer::GetModuleDebugHandler(uint32_t module_id,
+                                      WasmModuleDebug** wasm_module_debug) {
+  // Always executed in the isolate thread.
+  ScriptsMap::iterator scriptIterator = scripts_.find(module_id);
+  if (scriptIterator != scripts_.end()) {
+    *wasm_module_debug = &scriptIterator->second;
+    return true;
+  }
+  wasm_module_debug = nullptr;
+  return false;
+}
+
+bool GdbServer::GetWasmGlobal(uint32_t frame_index, uint32_t index,
+                              uint8_t* buffer, uint32_t buffer_size,
+                              uint32_t* size) {
+  // Executed in the GDBServerThread.
+  bool result = false;
+  RunSyncTask([this, &result, frame_index, index, buffer, buffer_size, size]() {
+    // Executed in the isolate thread.
+    result = WasmModuleDebug::GetWasmGlobal(GetTarget().GetCurrentIsolate(),
+                                            frame_index, index, buffer,
+                                            buffer_size, size);
+  });
+  return result;
+}
+
+bool GdbServer::GetWasmLocal(uint32_t frame_index, uint32_t index,
+                             uint8_t* buffer, uint32_t buffer_size,
+                             uint32_t* size) {
+  // Executed in the GDBServerThread.
+  bool result = false;
+  RunSyncTask([this, &result, frame_index, index, buffer, buffer_size, size]() {
+    // Executed in the isolate thread.
+    result = WasmModuleDebug::GetWasmLocal(GetTarget().GetCurrentIsolate(),
+                                           frame_index, index, buffer,
+                                           buffer_size, size);
+  });
+  return result;
+}
+
+bool GdbServer::GetWasmStackValue(uint32_t frame_index, uint32_t index,
+                                  uint8_t* buffer, uint32_t buffer_size,
+                                  uint32_t* size) {
+  // Executed in the GDBServerThread.
+  bool result = false;
+  RunSyncTask([this, &result, frame_index, index, buffer, buffer_size, size]() {
+    // Executed in the isolate thread.
+    result = WasmModuleDebug::GetWasmStackValue(GetTarget().GetCurrentIsolate(),
+                                                frame_index, index, buffer,
+                                                buffer_size, size);
+  });
+  return result;
+}
+
+uint32_t GdbServer::GetWasmMemory(uint32_t frame_index, uint32_t offset,
+                                  uint8_t* buffer, uint32_t size) {
+  // Executed in the GDBServerThread.
+  uint32_t bytes_read = 0;
+  RunSyncTask([this, &bytes_read, frame_index, offset, buffer, size]() {
+    // Executed in the isolate thread.
+    bytes_read = WasmModuleDebug::GetWasmMemory(
+        GetTarget().GetCurrentIsolate(), frame_index, offset, buffer, size);
+  });
+  return bytes_read;
+}
+
+uint32_t GdbServer::GetWasmModuleBytes(wasm_addr_t wasm_addr, uint8_t* buffer,
+                                       uint32_t size) {
+  // Executed in the GDBServerThread.
+  uint32_t bytes_read = 0;
+  RunSyncTask([this, &bytes_read, wasm_addr, buffer, size]() {
+    // Executed in the isolate thread.
+    WasmModuleDebug* module_debug;
+    if (GetModuleDebugHandler(wasm_addr.ModuleId(), &module_debug)) {
+      bytes_read = module_debug->GetWasmModuleBytes(wasm_addr, buffer, size);
+    }
+  });
+  return bytes_read;
+}
+
+bool GdbServer::AddBreakpoint(uint32_t wasm_module_id, uint32_t offset) {
+  // Executed in the GDBServerThread.
+  bool result = false;
+  RunSyncTask([this, &result, wasm_module_id, offset]() {
+    // Executed in the isolate thread.
+    WasmModuleDebug* module_debug;
+    if (GetModuleDebugHandler(wasm_module_id, &module_debug)) {
+      int breakpoint_id = 0;
+      if (module_debug->AddBreakpoint(offset, &breakpoint_id)) {
+        breakpoints_[wasm_addr_t(wasm_module_id, offset)] = breakpoint_id;
+        result = true;
+      }
+    }
+  });
+  return result;
+}
+
+bool GdbServer::RemoveBreakpoint(uint32_t wasm_module_id, uint32_t offset) {
+  // Executed in the GDBServerThread.
+  bool result = false;
+  RunSyncTask([this, &result, wasm_module_id, offset]() {
+    // Executed in the isolate thread.
+    BreakpointsMap::iterator it =
+        breakpoints_.find(wasm_addr_t(wasm_module_id, offset));
+    if (it != breakpoints_.end()) {
+      int breakpoint_id = it->second;
+      breakpoints_.erase(it);
+
+      WasmModuleDebug* module_debug;
+      if (GetModuleDebugHandler(wasm_module_id, &module_debug)) {
+        module_debug->RemoveBreakpoint(offset, breakpoint_id);
+        result = true;
+      }
+    }
+  });
+  return result;
+}
+
+std::vector<wasm_addr_t> GdbServer::GetWasmCallStack() const {
+  // Executed in the GDBServerThread.
+  std::vector<wasm_addr_t> result;
+  RunSyncTask([this, &result]() {
+    // Executed in the isolate thread.
+    result = GetTarget().GetCallStack();
+  });
+  return result;
+}
+
+void GdbServer::AddIsolate(Isolate* isolate) {
+  // Executed in the isolate thread.
+  if (isolate_delegates_.find(isolate) == isolate_delegates_.end()) {
+    isolate_delegates_[isolate] =
+        std::make_unique<DebugDelegate>(isolate, this);
+  }
+}
+
+void GdbServer::RemoveIsolate(Isolate* isolate) {
+  // Executed in the isolate thread.
+  auto it = isolate_delegates_.find(isolate);
+  if (it != isolate_delegates_.end()) {
+    for (auto it = scripts_.begin(); it != scripts_.end();) {
+      if (it->second.GetIsolate() == isolate) {
+        it = scripts_.erase(it);
+      } else {
+        ++it;
+      }
+    }
+    isolate_delegates_.erase(it);
+  }
+}
+
+void GdbServer::Suspend() {
+  // Executed in the GDBServerThread.
+  auto it = isolate_delegates_.begin();
+  if (it != isolate_delegates_.end()) {
+    Isolate* isolate = it->first;
+    v8::Isolate* v8Isolate = (v8::Isolate*)isolate;
+    v8Isolate->RequestInterrupt(
+        // Executed in the isolate thread.
+        [](v8::Isolate* isolate, void*) {
+          if (v8::debug::AllFramesOnStackAreBlackboxed(isolate)) {
+            v8::debug::SetBreakOnNextFunctionCall(isolate);
+          } else {
+            v8::debug::BreakRightNow(isolate);
+          }
+        },
+        this);
+  }
+}
+
+void GdbServer::PrepareStep() {
+  // Executed in the GDBServerThread.
+  wasm_addr_t pc = GetTarget().GetCurrentPc();
+  RunSyncTask([this, pc]() {
+    // Executed in the isolate thread.
+    WasmModuleDebug* module_debug;
+    if (GetModuleDebugHandler(pc.ModuleId(), &module_debug)) {
+      module_debug->PrepareStep();
+    }
+  });
+}
+
+void GdbServer::AddWasmModule(uint32_t module_id,
+                              Local<debug::WasmScript> wasm_script) {
+  // Executed in the isolate thread.
+  DCHECK_EQ(Script::TYPE_WASM, Utils::OpenHandle(*wasm_script)->type());
+  v8::Isolate* isolate = wasm_script->GetIsolate();
+  scripts_.insert(
+      std::make_pair(module_id, WasmModuleDebug(isolate, wasm_script)));
+
+  if (FLAG_wasm_pause_waiting_for_debugger && scripts_.size() == 1) {
+    TRACE_GDB_REMOTE("Paused, waiting for a debugger to attach...\n");
+    Suspend();
+  }
+}
+
+Target& GdbServer::GetTarget() const { return thread_->GetTarget(); }
+
+// static
+std::atomic<uint32_t> GdbServer::DebugDelegate::id_s;
+
+GdbServer::DebugDelegate::DebugDelegate(Isolate* isolate, GdbServer* gdb_server)
+    : isolate_(isolate), id_(id_s++), gdb_server_(gdb_server) {
+  isolate_->SetCaptureStackTraceForUncaughtExceptions(
+      true, kMaxWasmCallStack, v8::StackTrace::kOverview);
+
+  // Register the delegate
+  isolate_->debug()->SetDebugDelegate(this);
+  v8::debug::TierDownAllModulesPerIsolate((v8::Isolate*)isolate_);
+  v8::debug::ChangeBreakOnException((v8::Isolate*)isolate_,
+                                    v8::debug::BreakOnUncaughtException);
+}
+
+GdbServer::DebugDelegate::~DebugDelegate() {
+  // Deregister the delegate
+  isolate_->debug()->SetDebugDelegate(nullptr);
+}
+
+void GdbServer::DebugDelegate::ScriptCompiled(Local<debug::Script> script,
+                                              bool is_live_edited,
+                                              bool has_compile_error) {
+  // Executed in the isolate thread.
+  if (script->IsWasm()) {
+    DCHECK_EQ(reinterpret_cast<v8::Isolate*>(isolate_), script->GetIsolate());
+    gdb_server_->AddWasmModule(GetModuleId(script->Id()),
+                               script.As<debug::WasmScript>());
+  }
+}
+
+void GdbServer::DebugDelegate::BreakProgramRequested(
+    // Executed in the isolate thread.
+    Local<v8::Context> paused_context,
+    const std::vector<debug::BreakpointId>& inspector_break_points_hit) {
+  gdb_server_->GetTarget().OnProgramBreak(
+      isolate_, WasmModuleDebug::GetCallStack(id_, isolate_));
+  gdb_server_->RunMessageLoopOnPause();
+}
+
+void GdbServer::DebugDelegate::ExceptionThrown(
+    // Executed in the isolate thread.
+    Local<v8::Context> paused_context, Local<Value> exception,
+    Local<Value> promise, bool is_uncaught,
+    debug::ExceptionType exception_type) {
+  if (exception_type == v8::debug::kException && is_uncaught) {
+    gdb_server_->GetTarget().OnException(
+        isolate_, WasmModuleDebug::GetCallStack(id_, isolate_));
+    gdb_server_->RunMessageLoopOnPause();
+  }
+}
+
+bool GdbServer::DebugDelegate::IsFunctionBlackboxed(
+    // Executed in the isolate thread.
+    Local<debug::Script> script, const debug::Location& start,
+    const debug::Location& end) {
+  return false;
+}
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/wasm/gdb-server/gdb-server.h b/src/debug/wasm/gdb-server/gdb-server.h
new file mode 100644
index 0000000..50939af
--- /dev/null
+++ b/src/debug/wasm/gdb-server/gdb-server.h
@@ -0,0 +1,201 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
+#define V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
+
+#include <map>
+#include <memory>
+#include "src/debug/wasm/gdb-server/gdb-server-thread.h"
+#include "src/debug/wasm/gdb-server/wasm-module-debug.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+class TaskRunner;
+
+// class GdbServer acts as a manager for the GDB-remote stub. It is instantiated
+// as soon as the first Wasm module is loaded in the Wasm engine and spawns a
+// separate thread to accept connections and exchange messages with a debugger.
+// It will contain the logic to serve debugger queries and access the state of
+// the Wasm engine.
+class GdbServer {
+ public:
+  // Factory method: creates and returns a GdbServer. Spawns a "GDB-remote"
+  // thread that will be used to communicate with the debugger.
+  // May return null on failure.
+  // This should be called once, the first time a Wasm module is loaded in the
+  // Wasm engine.
+  static std::unique_ptr<GdbServer> Create();
+
+  // Stops the "GDB-remote" thread and waits for it to complete. This should be
+  // called once, when the Wasm engine shuts down.
+  ~GdbServer();
+
+  // Queries the set of the Wasm modules currently loaded. Each module is
+  // identified by a unique integer module id.
+  struct WasmModuleInfo {
+    uint32_t module_id;
+    std::string module_name;
+  };
+  std::vector<WasmModuleInfo> GetLoadedModules();
+
+  // Queries the value of the {index} global value in the Wasm module identified
+  // by {frame_index}.
+  //
+  bool GetWasmGlobal(uint32_t frame_index, uint32_t index, uint8_t* buffer,
+                     uint32_t buffer_size, uint32_t* size);
+
+  // Queries the value of the {index} local value in the {frame_index}th stack
+  // frame in the Wasm module identified by {frame_index}.
+  //
+  bool GetWasmLocal(uint32_t frame_index, uint32_t index, uint8_t* buffer,
+                    uint32_t buffer_size, uint32_t* size);
+
+  // Queries the value of the {index} value in the operand stack.
+  //
+  bool GetWasmStackValue(uint32_t frame_index, uint32_t index, uint8_t* buffer,
+                         uint32_t buffer_size, uint32_t* size);
+
+  // Reads {size} bytes, starting from {offset}, from the Memory instance
+  // associated to the Wasm module identified by {frame_index}.
+  // Returns the number of bytes copied to {buffer}, or 0 is case of error.
+  // Note: only one Memory for Module is currently supported.
+  //
+  uint32_t GetWasmMemory(uint32_t frame_index, uint32_t offset, uint8_t* buffer,
+                         uint32_t size);
+
+  // Reads {size} bytes, starting from the low dword of {address}, from the Code
+  // space of th Wasm module identified by high dword of {address}.
+  // Returns the number of bytes copied to {buffer}, or 0 is case of error.
+  uint32_t GetWasmModuleBytes(wasm_addr_t address, uint8_t* buffer,
+                              uint32_t size);
+
+  // Inserts a breakpoint at the offset {offset} of the Wasm module identified
+  // by {wasm_module_id}.
+  // Returns true if the breakpoint was successfully added.
+  bool AddBreakpoint(uint32_t wasm_module_id, uint32_t offset);
+
+  // Removes a breakpoint at the offset {offset} of the Wasm module identified
+  // by {wasm_module_id}.
+  // Returns true if the breakpoint was successfully removed.
+  bool RemoveBreakpoint(uint32_t wasm_module_id, uint32_t offset);
+
+  // Returns the current call stack as a vector of program counters.
+  std::vector<wasm_addr_t> GetWasmCallStack() const;
+
+  // Manage the set of Isolates for this GdbServer.
+  void AddIsolate(Isolate* isolate);
+  void RemoveIsolate(Isolate* isolate);
+
+  // Requests that the thread suspend execution at the next Wasm instruction.
+  void Suspend();
+
+  // Handle stepping in wasm functions via the wasm interpreter.
+  void PrepareStep();
+
+  // Called when the target debuggee can resume execution (for example after
+  // having been suspended on a breakpoint). Terminates the task runner leaving
+  // all pending tasks in the queue.
+  void QuitMessageLoopOnPause();
+
+ private:
+  GdbServer();
+
+  // When the target debuggee is suspended for a breakpoint or exception, blocks
+  // the main (isolate) thread and enters in a message loop. Here it waits on a
+  // queue of Task objects that are posted by the GDB-stub thread and that
+  // represent queries received from the debugger via the GDB-remote protocol.
+  void RunMessageLoopOnPause();
+
+  // Post a task to run a callback in the isolate thread.
+  template <typename Callback>
+  auto RunSyncTask(Callback&& callback) const;
+
+  void AddWasmModule(uint32_t module_id, Local<debug::WasmScript> wasm_script);
+
+  // Given a Wasm module id, retrieves the corresponding debugging WasmScript
+  // object.
+  bool GetModuleDebugHandler(uint32_t module_id,
+                             WasmModuleDebug** wasm_module_debug);
+
+  // Returns the debugging target.
+  Target& GetTarget() const;
+
+  // Class DebugDelegate implements the debug::DebugDelegate interface to
+  // receive notifications when debug events happen in a given isolate, like a
+  // script being loaded, a breakpoint being hit, an exception being thrown.
+  class DebugDelegate : public debug::DebugDelegate {
+   public:
+    DebugDelegate(Isolate* isolate, GdbServer* gdb_server);
+    ~DebugDelegate();
+
+    // debug::DebugDelegate
+    void ScriptCompiled(Local<debug::Script> script, bool is_live_edited,
+                        bool has_compile_error) override;
+    void BreakProgramRequested(Local<v8::Context> paused_context,
+                               const std::vector<debug::BreakpointId>&
+                                   inspector_break_points_hit) override;
+    void ExceptionThrown(Local<v8::Context> paused_context,
+                         Local<Value> exception, Local<Value> promise,
+                         bool is_uncaught,
+                         debug::ExceptionType exception_type) override;
+    bool IsFunctionBlackboxed(Local<debug::Script> script,
+                              const debug::Location& start,
+                              const debug::Location& end) override;
+
+   private:
+    // Calculates module_id as:
+    // +--------------------+------------------- +
+    // | DebugDelegate::id_ |    Script::Id()    |
+    // +--------------------+------------------- +
+    //  <----- 16 bit -----> <----- 16 bit ----->
+    uint32_t GetModuleId(uint32_t script_id) const {
+      DCHECK_LT(script_id, 0x10000);
+      DCHECK_LT(id_, 0x10000);
+      return id_ << 16 | script_id;
+    }
+
+    Isolate* isolate_;
+    uint32_t id_;
+    GdbServer* gdb_server_;
+
+    static std::atomic<uint32_t> id_s;
+  };
+
+  // The GDB-stub thread where all the communication with the debugger happens.
+  std::unique_ptr<GdbServerThread> thread_;
+
+  // Used to transform the queries that arrive in the GDB-stub thread into
+  // tasks executed in the main (isolate) thread.
+  std::unique_ptr<TaskRunner> task_runner_;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Always accessed in the isolate thread.
+
+  // Set of breakpoints currently defines in Wasm code.
+  typedef std::map<uint64_t, int> BreakpointsMap;
+  BreakpointsMap breakpoints_;
+
+  typedef std::map<uint32_t, WasmModuleDebug> ScriptsMap;
+  ScriptsMap scripts_;
+
+  typedef std::map<Isolate*, std::unique_ptr<DebugDelegate>>
+      IsolateDebugDelegateMap;
+  IsolateDebugDelegateMap isolate_delegates_;
+
+  // End of fields always accessed in the isolate thread.
+  //////////////////////////////////////////////////////////////////////////////
+
+  DISALLOW_COPY_AND_ASSIGN(GdbServer);
+};
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_GDB_SERVER_H_
diff --git a/src/debug/wasm/gdb-server/packet.cc b/src/debug/wasm/gdb-server/packet.cc
new file mode 100644
index 0000000..f8306c4
--- /dev/null
+++ b/src/debug/wasm/gdb-server/packet.cc
@@ -0,0 +1,364 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/packet.h"
+#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+Packet::Packet() {
+  seq_ = -1;
+  Clear();
+}
+
+void Packet::Clear() {
+  data_.clear();
+  read_index_ = 0;
+}
+
+void Packet::Rewind() { read_index_ = 0; }
+
+bool Packet::EndOfPacket() const { return (read_index_ >= GetPayloadSize()); }
+
+void Packet::AddRawChar(char ch) { data_.push_back(ch); }
+
+void Packet::AddWord8(uint8_t byte) {
+  char seq[2];
+  UInt8ToHex(byte, seq);
+  AddRawChar(seq[0]);
+  AddRawChar(seq[1]);
+}
+
+void Packet::AddBlock(const void* ptr, uint32_t len) {
+  DCHECK(ptr);
+
+  const char* p = (const char*)ptr;
+
+  for (uint32_t offs = 0; offs < len; offs++) {
+    AddWord8(p[offs]);
+  }
+}
+
+void Packet::AddString(const char* str) {
+  DCHECK(str);
+
+  while (*str) {
+    AddRawChar(*str);
+    str++;
+  }
+}
+
+void Packet::AddHexString(const char* str) {
+  DCHECK(str);
+
+  while (*str) {
+    AddWord8(*str);
+    str++;
+  }
+}
+
+void Packet::AddNumberSep(uint64_t val, char sep) {
+  char out[sizeof(val) * 2];
+  char temp[2];
+
+  // Check for -1 optimization
+  if (val == static_cast<uint64_t>(-1)) {
+    AddRawChar('-');
+    AddRawChar('1');
+  } else {
+    int nibbles = 0;
+
+    // In the GDB remote protocol numbers are formatted as big-endian hex
+    // strings. Leading zeros can be skipped.
+    // For example the value 0x00001234 is formatted as "1234".
+    for (size_t a = 0; a < sizeof(val); a++) {
+      uint8_t byte = static_cast<uint8_t>(val & 0xFF);
+
+      // Stream in with bytes reversed, starting with the least significant.
+      // So if we have the value 0x00001234, we store 4, then 3, 2, 1.
+      // Note that the characters are later reversed to be in big-endian order.
+      UInt8ToHex(byte, temp);
+      out[nibbles++] = temp[1];
+      out[nibbles++] = temp[0];
+
+      // Get the next 8 bits;
+      val >>= 8;
+
+      // Suppress leading zeros, so we are done when val hits zero
+      if (val == 0) {
+        break;
+      }
+    }
+
+    // Strip the high zero for this byte if present.
+    if ((nibbles > 1) && (out[nibbles - 1] == '0')) nibbles--;
+
+    // Now write it out reverse to correct the order
+    while (nibbles) {
+      nibbles--;
+      AddRawChar(out[nibbles]);
+    }
+  }
+
+  // If we asked for a separator, insert it
+  if (sep) AddRawChar(sep);
+}
+
+bool Packet::GetNumberSep(uint64_t* val, char* sep) {
+  uint64_t out = 0;
+  char ch;
+  if (!GetRawChar(&ch)) {
+    return false;
+  }
+
+  // Numbers are formatted as a big-endian hex strings.
+  // The literals "0" and "-1" as special cases.
+
+  // Check for -1
+  if (ch == '-') {
+    if (!GetRawChar(&ch)) {
+      return false;
+    }
+
+    if (ch == '1') {
+      *val = (uint64_t)-1;
+
+      ch = 0;
+      GetRawChar(&ch);
+      if (sep) {
+        *sep = ch;
+      }
+      return true;
+    }
+    return false;
+  }
+
+  do {
+    uint8_t nib;
+
+    // Check for separator
+    if (!NibbleToUInt8(ch, &nib)) {
+      break;
+    }
+
+    // Add this nibble.
+    out = (out << 4) + nib;
+
+    // Get the next character (if availible)
+    ch = 0;
+    if (!GetRawChar(&ch)) {
+      break;
+    }
+  } while (1);
+
+  // Set the value;
+  *val = out;
+
+  // Add the separator if the user wants it...
+  if (sep != nullptr) *sep = ch;
+
+  return true;
+}
+
+bool Packet::GetRawChar(char* ch) {
+  DCHECK(ch != nullptr);
+
+  if (read_index_ >= GetPayloadSize()) return false;
+
+  *ch = data_[read_index_++];
+
+  // Check for RLE X*N, where X is the value, N is the reps.
+  if (*ch == '*') {
+    if (read_index_ < 2) {
+      TRACE_GDB_REMOTE("Unexpected RLE at start of packet.\n");
+      return false;
+    }
+
+    if (read_index_ >= GetPayloadSize()) {
+      TRACE_GDB_REMOTE("Unexpected EoP during RLE.\n");
+      return false;
+    }
+
+    // GDB does not use "CTRL" characters in the stream, so the
+    // number of reps is encoded as the ASCII value beyond 28
+    // (which when you add a min rep size of 4, forces the rep
+    // character to be ' ' (32) or greater).
+    int32_t cnt = (data_[read_index_] - 28);
+    if (cnt < 3) {
+      TRACE_GDB_REMOTE("Unexpected RLE length.\n");
+      return false;
+    }
+
+    // We have just read '*' and incremented the read pointer,
+    // so here is the old state, and expected new state.
+    //
+    //   Assume N = 5, we grow by N - size of encoding (3).
+    //
+    // OldP:       R  W
+    // OldD:  012X*N89 = 8 chars
+    // Size:  012X*N89__ = 10 chars
+    // Move:  012X*__N89 = 10 chars
+    // Fill:  012XXXXX89 = 10 chars
+    // NewP:       R    W  (shifted 5 - 3)
+
+    // First, store the remaining characters to the right into a temp string.
+    std::string right = data_.substr(read_index_ + 1);
+    // Discard the '*' we just read
+    data_.erase(read_index_ - 1);
+    // Append (N-1) 'X' chars
+    *ch = data_[read_index_ - 2];
+    data_.append(cnt - 1, *ch);
+    // Finally, append the remaining characters
+    data_.append(right);
+  }
+  return true;
+}
+
+bool Packet::GetWord8(uint8_t* value) {
+  DCHECK(value);
+
+  // Get two ASCII hex values and convert them to ints
+  char seq[2];
+  if (!GetRawChar(&seq[0]) || !GetRawChar(&seq[1])) {
+    return false;
+  }
+  return HexToUInt8(seq, value);
+}
+
+bool Packet::GetBlock(void* ptr, uint32_t len) {
+  DCHECK(ptr);
+
+  uint8_t* p = reinterpret_cast<uint8_t*>(ptr);
+  bool res = true;
+
+  for (uint32_t offs = 0; offs < len; offs++) {
+    res = GetWord8(&p[offs]);
+    if (false == res) {
+      break;
+    }
+  }
+
+  return res;
+}
+
+bool Packet::GetString(std::string* str) {
+  if (EndOfPacket()) {
+    return false;
+  }
+
+  *str = data_.substr(read_index_);
+  read_index_ = GetPayloadSize();
+  return true;
+}
+
+bool Packet::GetHexString(std::string* str) {
+  // Decode a string encoded as a series of 2-hex digit pairs.
+
+  if (EndOfPacket()) {
+    return false;
+  }
+
+  // Pull values until we hit a separator
+  str->clear();
+  char ch1;
+  while (GetRawChar(&ch1)) {
+    uint8_t nib1;
+    if (!NibbleToUInt8(ch1, &nib1)) {
+      read_index_--;
+      break;
+    }
+    char ch2;
+    uint8_t nib2;
+    if (!GetRawChar(&ch2) || !NibbleToUInt8(ch2, &nib2)) {
+      return false;
+    }
+    *str += static_cast<char>((nib1 << 4) + nib2);
+  }
+  return true;
+}
+
+const char* Packet::GetPayload() const { return data_.c_str(); }
+
+size_t Packet::GetPayloadSize() const { return data_.size(); }
+
+bool Packet::GetSequence(int32_t* ch) const {
+  DCHECK(ch);
+
+  if (seq_ != -1) {
+    *ch = seq_;
+    return true;
+  }
+
+  return false;
+}
+
+void Packet::ParseSequence() {
+  size_t saved_read_index = read_index_;
+  unsigned char seq;
+  char ch;
+  if (GetWord8(&seq) && GetRawChar(&ch)) {
+    if (ch == ':') {
+      SetSequence(seq);
+      return;
+    }
+  }
+  // No sequence number present, so reset to original position.
+  read_index_ = saved_read_index;
+}
+
+void Packet::SetSequence(int32_t val) { seq_ = val; }
+
+void Packet::SetError(ErrDef error) {
+  Clear();
+  AddRawChar('E');
+  AddWord8(static_cast<uint8_t>(error));
+}
+
+std::string Packet::GetPacketData() const {
+  char chars[2];
+  const char* ptr = GetPayload();
+  size_t size = GetPayloadSize();
+
+  std::stringstream outstr;
+
+  // Signal start of response
+  outstr << '$';
+
+  char run_xsum = 0;
+
+  // If there is a sequence, send as two nibble 8bit value + ':'
+  int32_t seq;
+  if (GetSequence(&seq)) {
+    UInt8ToHex(seq, chars);
+    outstr << chars[0];
+    run_xsum += chars[0];
+    outstr << chars[1];
+    run_xsum += chars[1];
+
+    outstr << ':';
+    run_xsum += ':';
+  }
+
+  // Send the main payload
+  for (size_t offs = 0; offs < size; ++offs) {
+    outstr << ptr[offs];
+    run_xsum += ptr[offs];
+  }
+
+  // Send XSUM as two nibble 8bit value preceeded by '#'
+  outstr << '#';
+  UInt8ToHex(run_xsum, chars);
+  outstr << chars[0];
+  outstr << chars[1];
+
+  return outstr.str();
+}
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/wasm/gdb-server/packet.h b/src/debug/wasm/gdb-server/packet.h
new file mode 100644
index 0000000..4308081
--- /dev/null
+++ b/src/debug/wasm/gdb-server/packet.h
@@ -0,0 +1,105 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_PACKET_H_
+#define V8_DEBUG_WASM_GDB_SERVER_PACKET_H_
+
+#include <string>
+#include <vector>
+#include "src/base/macros.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+class V8_EXPORT_PRIVATE Packet {
+ public:
+  Packet();
+
+  // Empty the vector and reset the read/write pointers.
+  void Clear();
+
+  // Reset the read pointer, allowing the packet to be re-read.
+  void Rewind();
+
+  // Return true of the read pointer has reached the write pointer.
+  bool EndOfPacket() const;
+
+  // Store a single raw 8 bit value
+  void AddRawChar(char ch);
+
+  // Store a block of data as hex pairs per byte
+  void AddBlock(const void* ptr, uint32_t len);
+
+  // Store a byte as a 2 chars block.
+  void AddWord8(uint8_t val);
+
+  // Store a number up to 64 bits, formatted as a big-endian hex string with
+  // preceeding zeros removed.  Since zeros can be removed, the width of this
+  // number is unknown, and the number is always followed by a NULL or a
+  // separator (non hex digit).
+  void AddNumberSep(uint64_t val, char sep);
+
+  // Add a raw string.
+  void AddString(const char* str);
+
+  // Add a string stored as a stream of ASCII hex digit pairs.  It is safe
+  // to use any non-null character in this stream.  If this does not terminate
+  // the packet, there should be a separator (non hex digit) immediately
+  // following.
+  void AddHexString(const char* str);
+
+  // Retrieve a single character if available
+  bool GetRawChar(char* ch);
+
+  // Retrieve "len" ASCII character pairs.
+  bool GetBlock(void* ptr, uint32_t len);
+
+  // Retrieve a 8, 16, 32, or 64 bit word as pairs of hex digits.  These
+  // functions will always consume bits/4 characters from the stream.
+  bool GetWord8(uint8_t* val);
+
+  // Retrieve a number (formatted as a big-endian hex string) and a separator.
+  // If 'sep' is null, the separator is consumed but thrown away.
+  bool GetNumberSep(uint64_t* val, char* sep);
+
+  // Get a string from the stream
+  bool GetString(std::string* str);
+  bool GetHexString(std::string* str);
+
+  // Return a pointer to the entire packet payload
+  const char* GetPayload() const;
+  size_t GetPayloadSize() const;
+
+  // Returns true and the sequence number, or false if it is unset.
+  bool GetSequence(int32_t* seq) const;
+
+  // Parses sequence number in package data and moves read pointer past it.
+  void ParseSequence();
+
+  // Set the sequence number.
+  void SetSequence(int32_t seq);
+
+  enum class ErrDef { None = 0, BadFormat = 1, BadArgs = 2, Failed = 3 };
+  void SetError(ErrDef);
+
+  // Returns the full content of a GDB-remote packet, in the format:
+  //    $payload#checksum
+  // where the two-digit checksum is computed as the modulo 256 sum of all
+  // characters between the leading ‘$’ and the trailing ‘#’.
+  std::string GetPacketData() const;
+
+ private:
+  int32_t seq_;
+  std::string data_;
+  size_t read_index_;
+};
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_PACKET_H_
diff --git a/src/debug/wasm/gdb-server/session.cc b/src/debug/wasm/gdb-server/session.cc
new file mode 100644
index 0000000..b052934
--- /dev/null
+++ b/src/debug/wasm/gdb-server/session.cc
@@ -0,0 +1,148 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/session.h"
+#include "src/debug/wasm/gdb-server/packet.h"
+#include "src/debug/wasm/gdb-server/transport.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+Session::Session(TransportBase* transport)
+    : io_(transport), connected_(true), ack_enabled_(true) {}
+
+void Session::WaitForDebugStubEvent() { io_->WaitForDebugStubEvent(); }
+
+bool Session::SignalThreadEvent() { return io_->SignalThreadEvent(); }
+
+bool Session::IsDataAvailable() const { return io_->IsDataAvailable(); }
+
+bool Session::IsConnected() const { return connected_; }
+
+void Session::Disconnect() {
+  io_->Disconnect();
+  connected_ = false;
+}
+
+bool Session::GetChar(char* ch) {
+  if (!io_->Read(ch, 1)) {
+    Disconnect();
+    return false;
+  }
+
+  return true;
+}
+
+bool Session::SendPacket(Packet* pkt, bool expect_ack) {
+  char ch;
+  do {
+    std::string data = pkt->GetPacketData();
+
+    TRACE_GDB_REMOTE("TX %s\n", data.size() < 160
+                                    ? data.c_str()
+                                    : (data.substr(0, 160) + "...").c_str());
+    if (!io_->Write(data.data(), static_cast<int32_t>(data.length()))) {
+      return false;
+    }
+
+    // If ACKs are off, we are done.
+    if (!expect_ack || !ack_enabled_) {
+      break;
+    }
+
+    // Otherwise, poll for '+'
+    if (!GetChar(&ch)) {
+      return false;
+    }
+
+    // Retry if we didn't get a '+'
+  } while (ch != '+');
+
+  return true;
+}
+
+bool Session::GetPayload(Packet* pkt, uint8_t* checksum) {
+  pkt->Clear();
+  *checksum = 0;
+
+  // Stream in the characters
+  char ch;
+  while (GetChar(&ch)) {
+    if (ch == '#') {
+      // If we see a '#' we must be done with the data.
+      return true;
+    } else if (ch == '$') {
+      // If we see a '$' we must have missed the last cmd, let's retry.
+      TRACE_GDB_REMOTE("RX Missing $, retry.\n");
+      *checksum = 0;
+      pkt->Clear();
+    } else {
+      // Keep a running XSUM.
+      *checksum += ch;
+      pkt->AddRawChar(ch);
+    }
+  }
+  return false;
+}
+
+bool Session::GetPacket(Packet* pkt) {
+  while (true) {
+    // Toss characters until we see a start of command
+    char ch;
+    do {
+      if (!GetChar(&ch)) {
+        return false;
+      }
+    } while (ch != '$');
+
+    uint8_t running_checksum = 0;
+    if (!GetPayload(pkt, &running_checksum)) {
+      return false;
+    }
+
+    // Get two nibble checksum
+    uint8_t trailing_checksum = 0;
+    char chars[2];
+    if (!GetChar(&chars[0]) || !GetChar(&chars[1]) ||
+        !HexToUInt8(chars, &trailing_checksum)) {
+      return false;
+    }
+
+    TRACE_GDB_REMOTE("RX $%s#%c%c\n", pkt->GetPayload(), chars[0], chars[1]);
+
+    pkt->ParseSequence();
+
+    // If ACKs are off, we are done.
+    if (!ack_enabled_) {
+      return true;
+    }
+
+    // If the XSUMs don't match, signal bad packet
+    if (trailing_checksum == running_checksum) {
+      char out[3] = {'+', 0, 0};
+
+      // If we have a sequence number
+      int32_t seq;
+      if (pkt->GetSequence(&seq)) {
+        // Respond with sequence number
+        UInt8ToHex(seq, &out[1]);
+        return io_->Write(out, 3);
+      } else {
+        return io_->Write(out, 1);
+      }
+    } else {
+      // Resend a bad XSUM and look for retransmit
+      TRACE_GDB_REMOTE("RX Bad XSUM, retry\n");
+      io_->Write("-", 1);
+      // retry...
+    }
+  }
+}
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/wasm/gdb-server/session.h b/src/debug/wasm/gdb-server/session.h
new file mode 100644
index 0000000..d7c2263
--- /dev/null
+++ b/src/debug/wasm/gdb-server/session.h
@@ -0,0 +1,73 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_SESSION_H_
+#define V8_DEBUG_WASM_GDB_SERVER_SESSION_H_
+
+#include "src/base/macros.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+class Packet;
+class TransportBase;
+
+// Represents a gdb-remote debugging session.
+class V8_EXPORT_PRIVATE Session {
+ public:
+  explicit Session(TransportBase* transport);
+
+  // Attempt to send a packet and optionally wait for an ACK from the receiver.
+  bool SendPacket(Packet* packet, bool expect_ack = true);
+
+  // Attempt to receive a packet.
+  bool GetPacket(Packet* packet);
+
+  // Return true if there is data to read.
+  bool IsDataAvailable() const;
+
+  // Return true if the connection is still valid.
+  bool IsConnected() const;
+
+  // Shutdown the connection.
+  void Disconnect();
+
+  // When a debugging session is active, the GDB-remote thread can block waiting
+  // for events and it will resume execution when one of these two events arise:
+  // - A network event (a new packet arrives, or the connection is dropped)
+  // - A thread event (the execution stopped because of a trap or breakpoint).
+  void WaitForDebugStubEvent();
+
+  // Signal that the debuggee execution stopped because of a trap or breakpoint.
+  bool SignalThreadEvent();
+
+  // By default, when either the debugger or the GDB-stub sends a packet,
+  // the first response expected is an acknowledgment: either '+' (to indicate
+  // the packet was received correctly) or '-' (to request retransmission).
+  // When a transport is reliable, the debugger may request that acknowledgement
+  // be disabled by means of the 'QStartNoAckMode' packet.
+  void EnableAck(bool ack_enabled) { ack_enabled_ = ack_enabled; }
+
+ private:
+  // Read a single character from the transport.
+  bool GetChar(char* ch);
+
+  // Read the content of a packet, from a leading '$' to a trailing '#'.
+  bool GetPayload(Packet* pkt, uint8_t* checksum);
+
+  TransportBase* io_;  // Transport object not owned by the Session.
+  bool connected_;     // Is the connection still valid.
+  bool ack_enabled_;   // If true, emit or wait for '+' from RSP stream.
+
+  DISALLOW_COPY_AND_ASSIGN(Session);
+};
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_SESSION_H_
diff --git a/src/debug/wasm/gdb-server/target.cc b/src/debug/wasm/gdb-server/target.cc
new file mode 100644
index 0000000..6992fd1
--- /dev/null
+++ b/src/debug/wasm/gdb-server/target.cc
@@ -0,0 +1,679 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/target.h"
+
+#include <inttypes.h>
+#include "src/base/platform/time.h"
+#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
+#include "src/debug/wasm/gdb-server/gdb-server.h"
+#include "src/debug/wasm/gdb-server/packet.h"
+#include "src/debug/wasm/gdb-server/session.h"
+#include "src/debug/wasm/gdb-server/transport.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+static const int kThreadId = 1;
+
+// Signals.
+static const int kSigTrace = 5;
+static const int kSigSegv = 11;
+
+Target::Target(GdbServer* gdb_server)
+    : gdb_server_(gdb_server),
+      status_(Status::Running),
+      cur_signal_(0),
+      session_(nullptr),
+      debugger_initial_suspension_(true),
+      semaphore_(0),
+      current_isolate_(nullptr) {
+  InitQueryPropertyMap();
+}
+
+void Target::InitQueryPropertyMap() {
+  // Request LLDB to send packets up to 4000 bytes for bulk transfers.
+  query_properties_["Supported"] =
+      "PacketSize=1000;vContSupported-;qXfer:libraries:read+;";
+
+  query_properties_["Attached"] = "1";
+
+  // There is only one register, named 'pc', in this architecture
+  query_properties_["RegisterInfo0"] =
+      "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;set:"
+      "General Purpose Registers;gcc:16;dwarf:16;generic:pc;";
+  query_properties_["RegisterInfo1"] = "E45";
+
+  // ProcessInfo for wasm32
+  query_properties_["ProcessInfo"] =
+      "pid:1;ppid:1;uid:1;gid:1;euid:1;egid:1;name:6c6c6462;triple:" +
+      Mem2Hex("wasm32-unknown-unknown-wasm") + ";ptrsize:4;";
+  query_properties_["Symbol"] = "OK";
+
+  // Current thread info
+  char buff[16];
+  snprintf(buff, sizeof(buff), "QC%x", kThreadId);
+  query_properties_["C"] = buff;
+}
+
+void Target::Terminate() {
+  // Executed in the Isolate thread, when the process shuts down.
+  SetStatus(Status::Terminated);
+}
+
+void Target::OnProgramBreak(Isolate* isolate,
+                            const std::vector<wasm_addr_t>& call_frames) {
+  OnSuspended(isolate, kSigTrace, call_frames);
+}
+void Target::OnException(Isolate* isolate,
+                         const std::vector<wasm_addr_t>& call_frames) {
+  OnSuspended(isolate, kSigSegv, call_frames);
+}
+void Target::OnSuspended(Isolate* isolate, int signal,
+                         const std::vector<wasm_addr_t>& call_frames) {
+  // This function will be called in the isolate thread, when the wasm
+  // interpreter gets suspended.
+
+  bool isWaitingForSuspension = (status_ == Status::WaitingForSuspension);
+  SetStatus(Status::Suspended, signal, call_frames, isolate);
+  if (isWaitingForSuspension) {
+    // Wake the GdbServer thread that was blocked waiting for the Target
+    // to suspend.
+    semaphore_.Signal();
+  } else if (session_) {
+    session_->SignalThreadEvent();
+  }
+}
+
+void Target::Run(Session* session) {
+  // Executed in the GdbServer thread.
+  session_ = session;
+  do {
+    WaitForDebugEvent();
+    ProcessDebugEvent();
+    ProcessCommands();
+  } while (!IsTerminated() && session_->IsConnected());
+  session_ = nullptr;
+}
+
+void Target::WaitForDebugEvent() {
+  // Executed in the GdbServer thread.
+
+  if (status_ == Status::Running) {
+    // Wait for either:
+    //   * the thread to fault (or single-step)
+    //   * an interrupt from LLDB
+    session_->WaitForDebugStubEvent();
+  }
+}
+
+void Target::ProcessDebugEvent() {
+  // Executed in the GdbServer thread
+
+  if (status_ == Status::Running) {
+    // Blocks, waiting for the engine to suspend.
+    Suspend();
+  }
+
+  // Here, the wasm interpreter has suspended and we have updated the current
+  // thread info.
+
+  if (debugger_initial_suspension_) {
+    // First time on a connection, we don't send the signal.
+    // All other times, send the signal that triggered us.
+    debugger_initial_suspension_ = false;
+  } else {
+    Packet pktOut;
+    SetStopReply(&pktOut);
+    session_->SendPacket(&pktOut, false);
+  }
+}
+
+void Target::Suspend() {
+  // Executed in the GdbServer thread
+  if (status_ == Status::Running) {
+    // TODO(paolosev) - this only suspends the wasm interpreter.
+    gdb_server_->Suspend();
+
+    status_ = Status::WaitingForSuspension;
+  }
+
+  while (status_ == Status::WaitingForSuspension) {
+    if (semaphore_.WaitFor(base::TimeDelta::FromMilliseconds(500))) {
+      // Here the wasm interpreter is suspended.
+      return;
+    }
+  }
+}
+
+void Target::ProcessCommands() {
+  // GDB-remote messages are processed in the GDBServer thread.
+
+  if (IsTerminated()) {
+    return;
+  } else if (status_ != Status::Suspended) {
+    // Don't process commands if we haven't stopped.
+    return;
+  }
+
+  // Now we are ready to process commands.
+  // Loop through packets until we process a continue packet or a detach.
+  Packet recv, reply;
+  while (session_->IsConnected()) {
+    if (!session_->GetPacket(&recv)) {
+      continue;
+    }
+
+    reply.Clear();
+    ProcessPacketResult result = ProcessPacket(&recv, &reply);
+    switch (result) {
+      case ProcessPacketResult::Paused:
+        session_->SendPacket(&reply);
+        break;
+
+      case ProcessPacketResult::Continue:
+        DCHECK_EQ(status_, Status::Running);
+        // If this is a continue type command, break out of this loop.
+        gdb_server_->QuitMessageLoopOnPause();
+        return;
+
+      case ProcessPacketResult::Detach:
+        SetStatus(Status::Running);
+        session_->SendPacket(&reply);
+        session_->Disconnect();
+        gdb_server_->QuitMessageLoopOnPause();
+        return;
+
+      case ProcessPacketResult::Kill:
+        session_->SendPacket(&reply);
+        exit(-9);
+
+      default:
+        UNREACHABLE();
+    }
+  }
+
+  if (!session_->IsConnected()) {
+    debugger_initial_suspension_ = true;
+  }
+}
+
+Target::ProcessPacketResult Target::ProcessPacket(Packet* pkt_in,
+                                                  Packet* pkt_out) {
+  ErrorCode err = ErrorCode::None;
+
+  // Clear the outbound message.
+  pkt_out->Clear();
+
+  // Set the sequence number, if present.
+  int32_t seq = -1;
+  if (pkt_in->GetSequence(&seq)) {
+    pkt_out->SetSequence(seq);
+  }
+
+  // A GDB-remote packet begins with an upper- or lower-case letter, which
+  // generally represents a single command.
+  // The letters 'q' and 'Q' introduce a "General query packets" and are used
+  // to extend the protocol with custom commands.
+  // The format of GDB-remote commands is documented here:
+  // https://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview.
+  char cmd;
+  pkt_in->GetRawChar(&cmd);
+
+  switch (cmd) {
+    // Queries the reason the target halted.
+    // IN : $?
+    // OUT: A Stop-reply packet
+    case '?':
+      SetStopReply(pkt_out);
+      break;
+
+    // Resumes execution
+    // IN : $c
+    // OUT: A Stop-reply packet is sent later, when the execution halts.
+    case 'c':
+      SetStatus(Status::Running);
+      return ProcessPacketResult::Continue;
+
+    // Detaches the debugger from this target
+    // IN : $D
+    // OUT: $OK
+    case 'D':
+      TRACE_GDB_REMOTE("Requested Detach.\n");
+      pkt_out->AddString("OK");
+      return ProcessPacketResult::Detach;
+
+    // Read general registers (We only support register 'pc' that contains
+    // the current instruction pointer).
+    // IN : $g
+    // OUT: $xx...xx
+    case 'g': {
+      uint64_t pc = GetCurrentPc();
+      pkt_out->AddBlock(&pc, sizeof(pc));
+      break;
+    }
+
+    // Write general registers - NOT SUPPORTED
+    // IN : $Gxx..xx
+    // OUT: $ (empty string)
+    case 'G': {
+      break;
+    }
+
+    // Set thread for subsequent operations. For Wasm targets, we currently
+    // assume that there is only one thread with id = kThreadId (= 1).
+    // IN : $H(c/g)(-1,0,xxxx)
+    // OUT: $OK
+    case 'H': {
+      // Type of the operation (‘m’, ‘M’, ‘g’, ‘G’, ...)
+      char operation;
+      if (!pkt_in->GetRawChar(&operation)) {
+        err = ErrorCode::BadFormat;
+        break;
+      }
+
+      uint64_t thread_id;
+      if (!pkt_in->GetNumberSep(&thread_id, 0)) {
+        err = ErrorCode::BadFormat;
+        break;
+      }
+
+      // Ignore, only one thread supported for now.
+      pkt_out->AddString("OK");
+      break;
+    }
+
+    // Kills the debuggee.
+    // IN : $k
+    // OUT: $OK
+    case 'k':
+      TRACE_GDB_REMOTE("Requested Kill.\n");
+      pkt_out->AddString("OK");
+      return ProcessPacketResult::Kill;
+
+    // Reads {llll} addressable memory units starting at address {aaaa}.
+    // IN : $maaaa,llll
+    // OUT: $xx..xx
+    case 'm': {
+      uint64_t address;
+      if (!pkt_in->GetNumberSep(&address, 0)) {
+        err = ErrorCode::BadFormat;
+        break;
+      }
+      wasm_addr_t wasm_addr(address);
+
+      uint64_t len;
+      if (!pkt_in->GetNumberSep(&len, 0)) {
+        err = ErrorCode::BadFormat;
+        break;
+      }
+
+      if (len > Transport::kBufSize / 2) {
+        err = ErrorCode::BadArgs;
+        break;
+      }
+
+      uint32_t length = static_cast<uint32_t>(len);
+      uint8_t buff[Transport::kBufSize];
+      if (wasm_addr.ModuleId() > 0) {
+        uint32_t read =
+            gdb_server_->GetWasmModuleBytes(wasm_addr, buff, length);
+        if (read > 0) {
+          pkt_out->AddBlock(buff, read);
+        } else {
+          err = ErrorCode::Failed;
+        }
+      } else {
+        err = ErrorCode::BadArgs;
+      }
+      break;
+    }
+
+    // Writes {llll} addressable memory units starting at address {aaaa}.
+    // IN : $Maaaa,llll:xx..xx
+    // OUT: $OK
+    case 'M': {
+      // Writing to memory not supported for Wasm.
+      err = ErrorCode::Failed;
+      break;
+    }
+
+    // pN: Reads the value of register N.
+    // IN : $pxx
+    // OUT: $xx..xx
+    case 'p': {
+      uint64_t pc = GetCurrentPc();
+      pkt_out->AddBlock(&pc, sizeof(pc));
+    } break;
+
+    case 'q': {
+      err = ProcessQueryPacket(pkt_in, pkt_out);
+      break;
+    }
+
+    // Single step
+    // IN : $s
+    // OUT: A Stop-reply packet is sent later, when the execution halts.
+    case 's': {
+      if (status_ == Status::Suspended) {
+        gdb_server_->PrepareStep();
+        SetStatus(Status::Running);
+      }
+      return ProcessPacketResult::Continue;
+    }
+
+    // Find out if the thread 'id' is alive.
+    // IN : $T
+    // OUT: $OK if alive, $Enn if thread is dead.
+    case 'T': {
+      uint64_t id;
+      if (!pkt_in->GetNumberSep(&id, 0)) {
+        err = ErrorCode::BadFormat;
+        break;
+      }
+      if (id != kThreadId) {
+        err = ErrorCode::BadArgs;
+        break;
+      }
+      pkt_out->AddString("OK");
+      break;
+    }
+
+    // Z: Adds a breakpoint
+    // IN : $Z<type>,<addr>,<kind>
+    //      <type>: 0: sw breakpoint, 1: hw breakpoint, 2: watchpoint
+    // OUT: $OK (success) or $Enn (error)
+    case 'Z': {
+      uint64_t breakpoint_type;
+      uint64_t breakpoint_address;
+      uint64_t breakpoint_kind;
+      // Only software breakpoints are supported.
+      if (!pkt_in->GetNumberSep(&breakpoint_type, 0) || breakpoint_type != 0 ||
+          !pkt_in->GetNumberSep(&breakpoint_address, 0) ||
+          !pkt_in->GetNumberSep(&breakpoint_kind, 0)) {
+        err = ErrorCode::BadFormat;
+        break;
+      }
+
+      wasm_addr_t wasm_breakpoint_addr(breakpoint_address);
+      if (!gdb_server_->AddBreakpoint(wasm_breakpoint_addr.ModuleId(),
+                                      wasm_breakpoint_addr.Offset())) {
+        err = ErrorCode::Failed;
+        break;
+      }
+
+      pkt_out->AddString("OK");
+      break;
+    }
+
+    // z: Removes a breakpoint
+    // IN : $z<type>,<addr>,<kind>
+    //      <type>: 0: sw breakpoint, 1: hw breakpoint, 2: watchpoint
+    // OUT: $OK (success) or $Enn (error)
+    case 'z': {
+      uint64_t breakpoint_type;
+      uint64_t breakpoint_address;
+      uint64_t breakpoint_kind;
+      if (!pkt_in->GetNumberSep(&breakpoint_type, 0) || breakpoint_type != 0 ||
+          !pkt_in->GetNumberSep(&breakpoint_address, 0) ||
+          !pkt_in->GetNumberSep(&breakpoint_kind, 0)) {
+        err = ErrorCode::BadFormat;
+        break;
+      }
+
+      wasm_addr_t wasm_breakpoint_addr(breakpoint_address);
+      if (!gdb_server_->RemoveBreakpoint(wasm_breakpoint_addr.ModuleId(),
+                                         wasm_breakpoint_addr.Offset())) {
+        err = ErrorCode::Failed;
+        break;
+      }
+
+      pkt_out->AddString("OK");
+      break;
+    }
+
+    // If the command is not recognized, ignore it by sending an empty reply.
+    default: {
+      TRACE_GDB_REMOTE("Unknown command: %s\n", pkt_in->GetPayload());
+    }
+  }
+
+  // If there is an error, return the error code instead of a payload
+  if (err != ErrorCode::None) {
+    pkt_out->Clear();
+    pkt_out->AddRawChar('E');
+    pkt_out->AddWord8(static_cast<uint8_t>(err));
+  }
+  return ProcessPacketResult::Paused;
+}
+
+Target::ErrorCode Target::ProcessQueryPacket(const Packet* pkt_in,
+                                             Packet* pkt_out) {
+  const char* str = &pkt_in->GetPayload()[1];
+
+  // Get first thread query
+  // IN : $qfThreadInfo
+  // OUT: $m<tid>
+  //
+  // Get next thread query
+  // IN : $qsThreadInfo
+  // OUT: $m<tid> or l to denote end of list.
+  if (!strcmp(str, "fThreadInfo") || !strcmp(str, "sThreadInfo")) {
+    if (str[0] == 'f') {
+      pkt_out->AddString("m");
+      pkt_out->AddNumberSep(kThreadId, 0);
+    } else {
+      pkt_out->AddString("l");
+    }
+    return ErrorCode::None;
+  }
+
+  // Get a list of loaded libraries
+  // IN : $qXfer:libraries:read
+  // OUT: an XML document which lists loaded libraries, with this format:
+  // <library-list>
+  //   <library name="foo.wasm">
+  //     <section address="0x100000000"/>
+  //   </library>
+  //   <library name="bar.wasm">
+  //     <section address="0x200000000"/>
+  //   </library>
+  // </library-list>
+  // Note that LLDB must be compiled with libxml2 support to handle this packet.
+  std::string tmp = "Xfer:libraries:read";
+  if (!strncmp(str, tmp.data(), tmp.length())) {
+    std::vector<GdbServer::WasmModuleInfo> modules =
+        gdb_server_->GetLoadedModules();
+    std::string result("l<library-list>");
+    for (const auto& module : modules) {
+      wasm_addr_t address(module.module_id, 0);
+      char address_string[32];
+      snprintf(address_string, sizeof(address_string), "%" PRIu64,
+               static_cast<uint64_t>(address));
+      result += "<library name=\"";
+      result += module.module_name;
+      result += "\"><section address=\"";
+      result += address_string;
+      result += "\"/></library>";
+    }
+    result += "</library-list>";
+    pkt_out->AddString(result.c_str());
+    return ErrorCode::None;
+  }
+
+  // Get the current call stack.
+  // IN : $qWasmCallStack
+  // OUT: $xx..xxyy..yyzz..zz (A sequence of uint64_t values represented as
+  //                           consecutive 8-bytes blocks).
+  std::vector<std::string> toks = StringSplit(str, ":;");
+  if (toks[0] == "WasmCallStack") {
+    std::vector<wasm_addr_t> call_stack_pcs = gdb_server_->GetWasmCallStack();
+    std::vector<uint64_t> buffer;
+    for (wasm_addr_t pc : call_stack_pcs) {
+      buffer.push_back(pc);
+    }
+    pkt_out->AddBlock(buffer.data(),
+                      static_cast<uint32_t>(sizeof(uint64_t) * buffer.size()));
+    return ErrorCode::None;
+  }
+
+  // Get a Wasm global value in the Wasm module specified.
+  // IN : $qWasmGlobal:frame_index;index
+  // OUT: $xx..xx
+  if (toks[0] == "WasmGlobal") {
+    if (toks.size() == 3) {
+      uint32_t frame_index =
+          static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
+      uint32_t index =
+          static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
+      uint8_t buff[16];
+      uint32_t size = 0;
+      if (gdb_server_->GetWasmGlobal(frame_index, index, buff, 16, &size)) {
+        pkt_out->AddBlock(buff, size);
+        return ErrorCode::None;
+      } else {
+        return ErrorCode::Failed;
+      }
+    }
+    return ErrorCode::BadFormat;
+  }
+
+  // Get a Wasm local value in the stack frame specified.
+  // IN : $qWasmLocal:frame_index;index
+  // OUT: $xx..xx
+  if (toks[0] == "WasmLocal") {
+    if (toks.size() == 3) {
+      uint32_t frame_index =
+          static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
+      uint32_t index =
+          static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
+      uint8_t buff[16];
+      uint32_t size = 0;
+      if (gdb_server_->GetWasmLocal(frame_index, index, buff, 16, &size)) {
+        pkt_out->AddBlock(buff, size);
+        return ErrorCode::None;
+      } else {
+        return ErrorCode::Failed;
+      }
+    }
+    return ErrorCode::BadFormat;
+  }
+
+  // Get a Wasm local from the operand stack at the index specified.
+  // IN : qWasmStackValue:frame_index;index
+  // OUT: $xx..xx
+  if (toks[0] == "WasmStackValue") {
+    if (toks.size() == 3) {
+      uint32_t frame_index =
+          static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
+      uint32_t index =
+          static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
+      uint8_t buff[16];
+      uint32_t size = 0;
+      if (gdb_server_->GetWasmStackValue(frame_index, index, buff, 16, &size)) {
+        pkt_out->AddBlock(buff, size);
+        return ErrorCode::None;
+      } else {
+        return ErrorCode::Failed;
+      }
+    }
+    return ErrorCode::BadFormat;
+  }
+
+  // Read Wasm memory.
+  // IN : $qWasmMem:frame_index;addr;len
+  // OUT: $xx..xx
+  if (toks[0] == "WasmMem") {
+    if (toks.size() == 4) {
+      uint32_t frame_index =
+          static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
+      uint32_t address =
+          static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 16));
+      uint32_t length =
+          static_cast<uint32_t>(strtol(toks[3].data(), nullptr, 16));
+      if (length > Transport::kBufSize / 2) {
+        return ErrorCode::BadArgs;
+      }
+      uint8_t buff[Transport::kBufSize];
+      uint32_t read =
+          gdb_server_->GetWasmMemory(frame_index, address, buff, length);
+      if (read > 0) {
+        pkt_out->AddBlock(buff, read);
+        return ErrorCode::None;
+      } else {
+        return ErrorCode::Failed;
+      }
+    }
+    return ErrorCode::BadFormat;
+  }
+
+  // No match so far, check the property cache.
+  QueryPropertyMap::const_iterator it = query_properties_.find(toks[0]);
+  if (it != query_properties_.end()) {
+    pkt_out->AddString(it->second.data());
+  }
+  // If not found, just send an empty response.
+  return ErrorCode::None;
+}
+
+// A Stop-reply packet has the format:
+//   Sxx
+// or:
+//   Txx<name1>:<value1>;...;<nameN>:<valueN>
+// where 'xx' is a two-digit hex number that represents the stop signal
+// and the <name>:<value> pairs are used to report additional information,
+// like the thread id.
+void Target::SetStopReply(Packet* pkt_out) const {
+  pkt_out->AddRawChar('T');
+  pkt_out->AddWord8(cur_signal_);
+
+  // Adds 'thread-pcs:<pc1>,...,<pcN>;' A list of pc values for all threads that
+  // currently exist in the process.
+  char buff[64];
+  snprintf(buff, sizeof(buff), "thread-pcs:%" PRIx64 ";",
+           static_cast<uint64_t>(GetCurrentPc()));
+  pkt_out->AddString(buff);
+
+  // Adds 'thread:<tid>;' pair. Note that a terminating ';' is required.
+  pkt_out->AddString("thread:");
+  pkt_out->AddNumberSep(kThreadId, ';');
+}
+
+void Target::SetStatus(Status status, int8_t signal,
+                       std::vector<wasm_addr_t> call_frames, Isolate* isolate) {
+  v8::base::MutexGuard guard(&mutex_);
+
+  DCHECK((status == Status::Suspended && signal != 0 &&
+          call_frames.size() > 0 && isolate != nullptr) ||
+         (status != Status::Suspended && signal == 0 &&
+          call_frames.size() == 0 && isolate == nullptr));
+
+  current_isolate_ = isolate;
+  status_ = status;
+  cur_signal_ = signal;
+  call_frames_ = call_frames;
+}
+
+const std::vector<wasm_addr_t> Target::GetCallStack() const {
+  v8::base::MutexGuard guard(&mutex_);
+
+  return call_frames_;
+}
+
+wasm_addr_t Target::GetCurrentPc() const {
+  v8::base::MutexGuard guard(&mutex_);
+
+  wasm_addr_t pc{0};
+  if (call_frames_.size() > 0) {
+    pc = call_frames_[0];
+  }
+  return pc;
+}
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/wasm/gdb-server/target.h b/src/debug/wasm/gdb-server/target.h
new file mode 100644
index 0000000..1af81d3
--- /dev/null
+++ b/src/debug/wasm/gdb-server/target.h
@@ -0,0 +1,140 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_TARGET_H_
+#define V8_DEBUG_WASM_GDB_SERVER_TARGET_H_
+
+#include <atomic>
+#include <map>
+#include "src/base/macros.h"
+#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+class GdbServer;
+class Packet;
+class Session;
+
+// Class Target represents a debugging target. It contains the logic to decode
+// incoming GDB-remote packets, execute them forwarding the debugger commands
+// and queries to the Wasm engine, and send back GDB-remote packets.
+class Target {
+ public:
+  // Contruct a Target object.
+  explicit Target(GdbServer* gdb_server);
+
+  // This function spin on a debugging session, until it closes.
+  void Run(Session* ses);
+
+  void Terminate();
+  bool IsTerminated() const { return status_ == Status::Terminated; }
+
+  // Notifies that the debuggee thread suspended at a breakpoint.
+  void OnProgramBreak(Isolate* isolate,
+                      const std::vector<wasm_addr_t>& call_frames);
+  // Notifies that the debuggee thread suspended because of an unhandled
+  // exception.
+  void OnException(Isolate* isolate,
+                   const std::vector<wasm_addr_t>& call_frames);
+
+  // Returns the state at the moment of the thread suspension.
+  const std::vector<wasm_addr_t> GetCallStack() const;
+  wasm_addr_t GetCurrentPc() const;
+  Isolate* GetCurrentIsolate() const { return current_isolate_; }
+
+ private:
+  void OnSuspended(Isolate* isolate, int signal,
+                   const std::vector<wasm_addr_t>& call_frames);
+
+  // Initializes a map used to make fast lookups when handling query packets
+  // that have a constant response.
+  void InitQueryPropertyMap();
+
+  // Blocks waiting for one of these two events to occur:
+  // - A network packet arrives from the debugger, or the debugger connection is
+  //   closed;
+  // - The debuggee suspends execution because of a trap or breakpoint.
+  void WaitForDebugEvent();
+  void ProcessDebugEvent();
+
+  // Processes GDB-remote packets that arrive from the debugger.
+  // This method should be called when the debuggee has suspended its execution.
+  void ProcessCommands();
+
+  // Requests that the thread suspends execution at the next Wasm instruction.
+  void Suspend();
+
+  enum class ErrorCode { None = 0, BadFormat = 1, BadArgs = 2, Failed = 3 };
+
+  enum class ProcessPacketResult {
+    Paused,    // The command was processed, debuggee still paused.
+    Continue,  // The debuggee should resume execution.
+    Detach,    // Request to detach from the debugger.
+    Kill       // Request to terminate the debuggee process.
+  };
+  // This function always succeedes, since all errors are reported as an error
+  // string "Exx" where xx is a two digit number.
+  // The return value indicates if the target can resume execution or it is
+  // still paused.
+  ProcessPacketResult ProcessPacket(Packet* pkt_in, Packet* pkt_out);
+
+  // Processes a general query packet
+  ErrorCode ProcessQueryPacket(const Packet* pkt_in, Packet* pkt_out);
+
+  // Formats a 'Stop-reply' packet, which is sent in response of a 'c'
+  // (continue), 's' (step) and '?' (query halt reason) commands.
+  void SetStopReply(Packet* pkt_out) const;
+
+  enum class Status { Running, WaitingForSuspension, Suspended, Terminated };
+
+  void SetStatus(Status status, int8_t signal = 0,
+                 std::vector<wasm_addr_t> call_frames_ = {},
+                 Isolate* isolate = nullptr);
+
+  GdbServer* gdb_server_;
+
+  std::atomic<Status> status_;
+
+  // Signal being processed.
+  std::atomic<int8_t> cur_signal_;
+
+  // Session object not owned by the Target.
+  Session* session_;
+
+  // Map used to make fast lookups when handling query packets.
+  typedef std::map<std::string, std::string> QueryPropertyMap;
+  QueryPropertyMap query_properties_;
+
+  bool debugger_initial_suspension_;
+
+  // Used to block waiting for suspension
+  v8::base::Semaphore semaphore_;
+
+  mutable v8::base::Mutex mutex_;
+  //////////////////////////////////////////////////////////////////////////////
+  // Protected by {mutex_}:
+
+  // Current isolate. This is not null only when the target is in a Suspended
+  // state and it is the isolate associated to the current call stack and used
+  // for all debugging activities.
+  Isolate* current_isolate_;
+
+  // Call stack when the execution is suspended.
+  std::vector<wasm_addr_t> call_frames_;
+
+  // End of fields protected by {mutex_}.
+  //////////////////////////////////////////////////////////////////////////////
+
+  DISALLOW_COPY_AND_ASSIGN(Target);
+};
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_TARGET_H_
diff --git a/src/debug/wasm/gdb-server/transport.cc b/src/debug/wasm/gdb-server/transport.cc
new file mode 100644
index 0000000..f1aed96
--- /dev/null
+++ b/src/debug/wasm/gdb-server/transport.cc
@@ -0,0 +1,457 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/transport.h"
+#include <fcntl.h>
+
+#ifndef SD_BOTH
+#define SD_BOTH 2
+#endif
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+SocketBinding::SocketBinding(SocketHandle socket_handle)
+    : socket_handle_(socket_handle) {}
+
+// static
+SocketBinding SocketBinding::Bind(uint16_t tcp_port) {
+  SocketHandle socket_handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (socket_handle == InvalidSocket) {
+    TRACE_GDB_REMOTE("Failed to create socket.\n");
+    return SocketBinding(InvalidSocket);
+  }
+  struct sockaddr_in sockaddr;
+  // Clearing sockaddr_in first appears to be necessary on Mac OS X.
+  memset(&sockaddr, 0, sizeof(sockaddr));
+  socklen_t addrlen = static_cast<socklen_t>(sizeof(sockaddr));
+  sockaddr.sin_family = AF_INET;
+  sockaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  sockaddr.sin_port = htons(tcp_port);
+
+#if _WIN32
+  // On Windows, SO_REUSEADDR has a different meaning than on POSIX systems.
+  // SO_REUSEADDR allows hijacking of an open socket by another process.
+  // The SO_EXCLUSIVEADDRUSE flag prevents this behavior.
+  // See:
+  // http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx
+  //
+  // Additionally, unlike POSIX, TCP server sockets can be bound to
+  // ports in the TIME_WAIT state, without setting SO_REUSEADDR.
+  int exclusive_address = 1;
+  if (setsockopt(socket_handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+                 reinterpret_cast<char*>(&exclusive_address),
+                 sizeof(exclusive_address))) {
+    TRACE_GDB_REMOTE("Failed to set SO_EXCLUSIVEADDRUSE option.\n");
+  }
+#else
+  // On POSIX, this is necessary to ensure that the TCP port is released
+  // promptly when sel_ldr exits.  Without this, the TCP port might
+  // only be released after a timeout, and later processes can fail
+  // to bind it.
+  int reuse_address = 1;
+  if (setsockopt(socket_handle, SOL_SOCKET, SO_REUSEADDR,
+                 reinterpret_cast<char*>(&reuse_address),
+                 sizeof(reuse_address))) {
+    TRACE_GDB_REMOTE("Failed to set SO_REUSEADDR option.\n");
+  }
+#endif
+
+  if (bind(socket_handle, reinterpret_cast<struct sockaddr*>(&sockaddr),
+           addrlen)) {
+    TRACE_GDB_REMOTE("Failed to bind server.\n");
+    return SocketBinding(InvalidSocket);
+  }
+
+  if (listen(socket_handle, 1)) {
+    TRACE_GDB_REMOTE("Failed to listen.\n");
+    return SocketBinding(InvalidSocket);
+  }
+  return SocketBinding(socket_handle);
+}
+
+std::unique_ptr<SocketTransport> SocketBinding::CreateTransport() {
+  return std::make_unique<SocketTransport>(socket_handle_);
+}
+
+uint16_t SocketBinding::GetBoundPort() {
+  struct sockaddr_in saddr;
+  struct sockaddr* psaddr = reinterpret_cast<struct sockaddr*>(&saddr);
+  // Clearing sockaddr_in first appears to be necessary on Mac OS X.
+  memset(&saddr, 0, sizeof(saddr));
+  socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
+  if (::getsockname(socket_handle_, psaddr, &addrlen)) {
+    TRACE_GDB_REMOTE("Failed to retrieve bound address.\n");
+    return 0;
+  }
+  return ntohs(saddr.sin_port);
+}
+
+// Do not delay sending small packets.  This significantly speeds up
+// remote debugging.  Debug stub uses buffering to send outgoing packets
+// so they are not split into more TCP packets than necessary.
+void DisableNagleAlgorithm(SocketHandle socket) {
+  int nodelay = 1;
+  if (::setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
+                   reinterpret_cast<char*>(&nodelay), sizeof(nodelay))) {
+    TRACE_GDB_REMOTE("Failed to set TCP_NODELAY option.\n");
+  }
+}
+
+Transport::Transport(SocketHandle s)
+    : buf_(new char[kBufSize]),
+      pos_(0),
+      size_(0),
+      handle_bind_(s),
+      handle_accept_(InvalidSocket) {}
+
+Transport::~Transport() {
+  if (handle_accept_ != InvalidSocket) {
+    CloseSocket(handle_accept_);
+  }
+}
+
+void Transport::CopyFromBuffer(char** dst, int32_t* len) {
+  int32_t copy_bytes = std::min(*len, size_ - pos_);
+  memcpy(*dst, buf_.get() + pos_, copy_bytes);
+  pos_ += copy_bytes;
+  *len -= copy_bytes;
+  *dst += copy_bytes;
+}
+
+bool Transport::Read(char* dst, int32_t len) {
+  if (pos_ < size_) {
+    CopyFromBuffer(&dst, &len);
+  }
+  while (len > 0) {
+    pos_ = 0;
+    size_ = 0;
+    if (!ReadSomeData()) {
+      return false;
+    }
+    CopyFromBuffer(&dst, &len);
+  }
+  return true;
+}
+
+bool Transport::Write(const char* src, int32_t len) {
+  while (len > 0) {
+    ssize_t result = ::send(handle_accept_, src, len, 0);
+    if (result > 0) {
+      src += result;
+      len -= result;
+      continue;
+    }
+    if (result == 0) {
+      return false;
+    }
+    if (SocketGetLastError() != kErrInterrupt) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Return true if there is data to read.
+bool Transport::IsDataAvailable() const {
+  if (pos_ < size_) {
+    return true;
+  }
+  fd_set fds;
+
+  FD_ZERO(&fds);
+  FD_SET(handle_accept_, &fds);
+
+  // We want a "non-blocking" check
+  struct timeval timeout;
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 0;
+
+  // Check if this file handle can select on read
+  int cnt = select(static_cast<int>(handle_accept_) + 1, &fds, 0, 0, &timeout);
+
+  // If we are ready, or if there is an error.  We return true
+  // on error, to let the next IO request fail.
+  if (cnt != 0) return true;
+
+  return false;
+}
+
+void Transport::Close() {
+  ::shutdown(handle_bind_, SD_BOTH);
+  CloseSocket(handle_bind_);
+  Disconnect();
+}
+
+void Transport::Disconnect() {
+  if (handle_accept_ != InvalidSocket) {
+    // Shutdown the connection in both directions.  This should
+    // always succeed, and nothing we can do if this fails.
+    ::shutdown(handle_accept_, SD_BOTH);
+    CloseSocket(handle_accept_);
+    handle_accept_ = InvalidSocket;
+  }
+}
+
+#if _WIN32
+
+SocketTransport::SocketTransport(SocketHandle s) : Transport(s) {
+  socket_event_ = WSA_INVALID_EVENT;
+  faulted_thread_event_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+  if (faulted_thread_event_ == NULL) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport::SocketTransport: Failed to create event object for "
+        "faulted thread\n");
+  }
+}
+
+SocketTransport::~SocketTransport() {
+  if (!CloseHandle(faulted_thread_event_)) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport::~SocketTransport: Failed to close "
+        "event\n");
+  }
+
+  if (socket_event_) {
+    if (!::WSACloseEvent(socket_event_)) {
+      TRACE_GDB_REMOTE(
+          "SocketTransport::~SocketTransport: Failed to close "
+          "socket event\n");
+    }
+  }
+}
+
+bool SocketTransport::AcceptConnection() {
+  CHECK(handle_accept_ == InvalidSocket);
+  handle_accept_ = ::accept(handle_bind_, NULL, 0);
+  if (handle_accept_ != InvalidSocket) {
+    DisableNagleAlgorithm(handle_accept_);
+
+    // Create socket event
+    socket_event_ = ::WSACreateEvent();
+    if (socket_event_ == WSA_INVALID_EVENT) {
+      TRACE_GDB_REMOTE(
+          "SocketTransport::AcceptConnection: Failed to create socket event\n");
+    }
+
+    // Listen for close events in order to handle them correctly.
+    // Additionally listen for read readiness as WSAEventSelect sets the socket
+    // to non-blocking mode.
+    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms738547(v=vs.85).aspx
+    if (::WSAEventSelect(handle_accept_, socket_event_, FD_CLOSE | FD_READ) ==
+        SOCKET_ERROR) {
+      TRACE_GDB_REMOTE(
+          "SocketTransport::AcceptConnection: Failed to bind event to "
+          "socket\n");
+    }
+    return true;
+  }
+  return false;
+}
+
+bool SocketTransport::ReadSomeData() {
+  while (true) {
+    ssize_t result =
+        ::recv(handle_accept_, buf_.get() + size_, kBufSize - size_, 0);
+    if (result > 0) {
+      size_ += result;
+      return true;
+    }
+    if (result == 0) {
+      return false;  // The connection was gracefully closed.
+    }
+    // WSAEventSelect sets socket to non-blocking mode. This is essential
+    // for socket event notification to work, there is no workaround.
+    // See remarks section at the page
+    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
+    if (SocketGetLastError() == WSAEWOULDBLOCK) {
+      if (::WaitForSingleObject(socket_event_, INFINITE) == WAIT_FAILED) {
+        TRACE_GDB_REMOTE(
+            "SocketTransport::ReadSomeData: Failed to wait on socket event\n");
+      }
+      if (!::ResetEvent(socket_event_)) {
+        TRACE_GDB_REMOTE(
+            "SocketTransport::ReadSomeData: Failed to reset socket event\n");
+      }
+      continue;
+    }
+
+    if (SocketGetLastError() != kErrInterrupt) {
+      return false;
+    }
+  }
+}
+
+void SocketTransport::WaitForDebugStubEvent() {
+  // Don't wait if we already have data to read.
+  bool wait = !(pos_ < size_);
+
+  HANDLE handles[2];
+  handles[0] = faulted_thread_event_;
+  handles[1] = socket_event_;
+  int count = size_ < kBufSize ? 2 : 1;
+  int result =
+      WaitForMultipleObjects(count, handles, FALSE, wait ? INFINITE : 0);
+  if (result == WAIT_OBJECT_0 + 1) {
+    if (!ResetEvent(socket_event_)) {
+      TRACE_GDB_REMOTE(
+          "SocketTransport::WaitForDebugStubEvent: Failed to reset socket "
+          "event\n");
+    }
+    return;
+  } else if (result == WAIT_OBJECT_0) {
+    if (!ResetEvent(faulted_thread_event_)) {
+      TRACE_GDB_REMOTE(
+          "SocketTransport::WaitForDebugStubEvent: Failed to reset event\n");
+    }
+    return;
+  } else if (result == WAIT_TIMEOUT) {
+    return;
+  }
+  TRACE_GDB_REMOTE(
+      "SocketTransport::WaitForDebugStubEvent: Wait for events failed\n");
+}
+
+bool SocketTransport::SignalThreadEvent() {
+  if (!SetEvent(faulted_thread_event_)) {
+    return false;
+  }
+  return true;
+}
+
+void SocketTransport::Disconnect() {
+  Transport::Disconnect();
+
+  if (socket_event_ != WSA_INVALID_EVENT && !::WSACloseEvent(socket_event_)) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport::~SocketTransport: Failed to close "
+        "socket event\n");
+  }
+  socket_event_ = WSA_INVALID_EVENT;
+  SignalThreadEvent();
+}
+
+#else  // _WIN32
+
+SocketTransport::SocketTransport(SocketHandle s) : Transport(s) {
+  int fds[2];
+#if defined(__linux__)
+  int ret = pipe2(fds, O_CLOEXEC);
+#else
+  int ret = pipe(fds);
+#endif
+  if (ret < 0) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport::SocketTransport: Failed to allocate pipe for faulted "
+        "thread\n");
+  }
+  faulted_thread_fd_read_ = fds[0];
+  faulted_thread_fd_write_ = fds[1];
+}
+
+SocketTransport::~SocketTransport() {
+  if (close(faulted_thread_fd_read_) != 0) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport::~SocketTransport: Failed to close "
+        "event\n");
+  }
+  if (close(faulted_thread_fd_write_) != 0) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport::~SocketTransport: Failed to close "
+        "event\n");
+  }
+}
+
+bool SocketTransport::AcceptConnection() {
+  CHECK(handle_accept_ == InvalidSocket);
+  handle_accept_ = ::accept(handle_bind_, NULL, 0);
+  if (handle_accept_ != InvalidSocket) {
+    DisableNagleAlgorithm(handle_accept_);
+    return true;
+  }
+  return false;
+}
+
+bool SocketTransport::ReadSomeData() {
+  while (true) {
+    ssize_t result =
+        ::recv(handle_accept_, buf_.get() + size_, kBufSize - size_, 0);
+    if (result > 0) {
+      size_ += result;
+      return true;
+    }
+    if (result == 0) {
+      return false;  // The connection was gracefully closed.
+    }
+    if (SocketGetLastError() != kErrInterrupt) {
+      return false;
+    }
+  }
+}
+
+void SocketTransport::WaitForDebugStubEvent() {
+  // Don't wait if we already have data to read.
+  bool wait = !(pos_ < size_);
+
+  fd_set fds;
+  FD_ZERO(&fds);
+  FD_SET(faulted_thread_fd_read_, &fds);
+  int max_fd = faulted_thread_fd_read_;
+  if (size_ < kBufSize) {
+    FD_SET(handle_accept_, &fds);
+    max_fd = std::max(max_fd, handle_accept_);
+  }
+
+  int ret;
+  // We don't need sleep-polling on Linux now, so we set either zero or infinite
+  // timeout.
+  if (wait) {
+    ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
+  } else {
+    struct timeval timeout;
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+    ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
+  }
+  if (ret < 0) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport::WaitForDebugStubEvent: Failed to wait for "
+        "debug stub event\n");
+  }
+
+  if (ret > 0) {
+    if (FD_ISSET(faulted_thread_fd_read_, &fds)) {
+      char buf[16];
+      if (read(faulted_thread_fd_read_, &buf, sizeof(buf)) < 0) {
+        TRACE_GDB_REMOTE(
+            "SocketTransport::WaitForDebugStubEvent: Failed to read from "
+            "debug stub event pipe fd\n");
+      }
+    }
+    if (FD_ISSET(handle_accept_, &fds)) ReadSomeData();
+  }
+}
+
+bool SocketTransport::SignalThreadEvent() {
+  // Notify the debug stub by marking the thread as faulted.
+  char buf = 0;
+  if (write(faulted_thread_fd_write_, &buf, sizeof(buf)) != sizeof(buf)) {
+    TRACE_GDB_REMOTE(
+        "SocketTransport:SignalThreadEvent: Can't send debug stub "
+        "event\n");
+    return false;
+  }
+  return true;
+}
+
+#endif  // _WIN32
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#undef SD_BOTH
diff --git a/src/debug/wasm/gdb-server/transport.h b/src/debug/wasm/gdb-server/transport.h
new file mode 100644
index 0000000..42bf438
--- /dev/null
+++ b/src/debug/wasm/gdb-server/transport.h
@@ -0,0 +1,193 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_TRANSPORT_H_
+#define V8_DEBUG_WASM_GDB_SERVER_TRANSPORT_H_
+
+#include <sstream>
+#include <vector>
+#include "src/base/macros.h"
+#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
+
+#if _WIN32
+#include <windows.h>
+#include <winsock2.h>
+
+typedef SOCKET SocketHandle;
+
+#define CloseSocket closesocket
+#define InvalidSocket INVALID_SOCKET
+#define SocketGetLastError() WSAGetLastError()
+static const int kErrInterrupt = WSAEINTR;
+typedef int ssize_t;
+typedef int socklen_t;
+
+#else  // _WIN32
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <string>
+
+typedef int SocketHandle;
+
+#define CloseSocket close
+#define InvalidSocket (-1)
+#define SocketGetLastError() errno
+static const int kErrInterrupt = EINTR;
+
+#endif  // _WIN32
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+class SocketTransport;
+
+// Acts as a factory for Transport objects bound to a specified TCP port.
+class SocketBinding {
+ public:
+  // Wrap existing socket handle.
+  explicit SocketBinding(SocketHandle socket_handle);
+
+  // Bind to the specified TCP port.
+  static SocketBinding Bind(uint16_t tcp_port);
+
+  bool IsValid() const { return socket_handle_ != InvalidSocket; }
+
+  // Create a transport object from this socket binding
+  std::unique_ptr<SocketTransport> CreateTransport();
+
+  // Get port the socket is bound to.
+  uint16_t GetBoundPort();
+
+ private:
+  SocketHandle socket_handle_;
+};
+
+class V8_EXPORT_PRIVATE TransportBase {
+ public:
+  virtual ~TransportBase() {}
+
+  // Waits for an incoming connection on the bound port.
+  virtual bool AcceptConnection() = 0;
+
+  // Read {len} bytes from this transport, possibly blocking until enough data
+  // is available.
+  // {dst} must point to a buffer large enough to contain {len} bytes.
+  // Returns true on success.
+  // Returns false if the connection is closed; in that case the {dst} may have
+  // been partially overwritten.
+  virtual bool Read(char* dst, int32_t len) = 0;
+
+  // Write {len} bytes to this transport.
+  // Return true on success, false if the connection is closed.
+  virtual bool Write(const char* src, int32_t len) = 0;
+
+  // Return true if there is data to read.
+  virtual bool IsDataAvailable() const = 0;
+
+  // If we are connected to a debugger, gracefully closes the connection.
+  // This should be called when a debugging session gets closed.
+  virtual void Disconnect() = 0;
+
+  // Shuts down this transport, gracefully closing the existing connection and
+  // also closing the listening socket. This should be called when the GDB stub
+  // shuts down, when the program terminates.
+  virtual void Close() = 0;
+
+  // Blocks waiting for one of these two events to occur:
+  // - A network event (a new packet arrives, or the connection is dropped),
+  // - A thread event is signaled (the execution stopped because of a trap or
+  // breakpoint).
+  virtual void WaitForDebugStubEvent() = 0;
+
+  // Signal that this transport should leave an alertable wait state because
+  // the execution of the debuggee was stopped because of a trap or breakpoint.
+  virtual bool SignalThreadEvent() = 0;
+};
+
+class Transport : public TransportBase {
+ public:
+  explicit Transport(SocketHandle s);
+  ~Transport() override;
+
+  // TransportBase
+  bool Read(char* dst, int32_t len) override;
+  bool Write(const char* src, int32_t len) override;
+  bool IsDataAvailable() const override;
+  void Disconnect() override;
+  void Close() override;
+
+  static const int kBufSize = 4096;
+
+ protected:
+  // Copy buffered data to *dst up to len bytes and update dst and len.
+  void CopyFromBuffer(char** dst, int32_t* len);
+
+  // Read available data from the socket. Return false on EOF or error.
+  virtual bool ReadSomeData() = 0;
+
+  std::unique_ptr<char[]> buf_;
+  int32_t pos_;
+  int32_t size_;
+  SocketHandle handle_bind_;
+  SocketHandle handle_accept_;
+};
+
+#if _WIN32
+
+class SocketTransport : public Transport {
+ public:
+  explicit SocketTransport(SocketHandle s);
+  ~SocketTransport() override;
+
+  // TransportBase
+  bool AcceptConnection() override;
+  void Disconnect() override;
+  void WaitForDebugStubEvent() override;
+  bool SignalThreadEvent() override;
+
+ private:
+  bool ReadSomeData() override;
+
+  HANDLE socket_event_;
+  HANDLE faulted_thread_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketTransport);
+};
+
+#else  // _WIN32
+
+class SocketTransport : public Transport {
+ public:
+  explicit SocketTransport(SocketHandle s);
+  ~SocketTransport() override;
+
+  // TransportBase
+  bool AcceptConnection() override;
+  void WaitForDebugStubEvent() override;
+  bool SignalThreadEvent() override;
+
+ private:
+  bool ReadSomeData() override;
+
+  int faulted_thread_fd_read_;
+  int faulted_thread_fd_write_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketTransport);
+};
+
+#endif  // _WIN32
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_TRANSPORT_H_
diff --git a/src/debug/wasm/gdb-server/wasm-module-debug.cc b/src/debug/wasm/gdb-server/wasm-module-debug.cc
new file mode 100644
index 0000000..f0b77bc
--- /dev/null
+++ b/src/debug/wasm/gdb-server/wasm-module-debug.cc
@@ -0,0 +1,387 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/debug/wasm/gdb-server/wasm-module-debug.h"
+
+#include "src/api/api-inl.h"
+#include "src/api/api.h"
+#include "src/execution/frames-inl.h"
+#include "src/execution/frames.h"
+#include "src/objects/script.h"
+#include "src/wasm/wasm-debug.h"
+#include "src/wasm/wasm-value.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+namespace gdb_server {
+
+WasmModuleDebug::WasmModuleDebug(v8::Isolate* isolate,
+                                 Local<debug::WasmScript> wasm_script) {
+  DCHECK_EQ(Script::TYPE_WASM, Utils::OpenHandle(*wasm_script)->type());
+
+  isolate_ = isolate;
+  wasm_script_ = Global<debug::WasmScript>(isolate, wasm_script);
+}
+
+std::string WasmModuleDebug::GetModuleName() const {
+  v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
+  v8::Local<v8::String> name;
+  std::string module_name;
+  if (wasm_script->Name().ToLocal(&name)) {
+    module_name = *(v8::String::Utf8Value(isolate_, name));
+  }
+  return module_name;
+}
+
+Handle<WasmInstanceObject> WasmModuleDebug::GetFirstWasmInstance() {
+  v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
+  Handle<Script> script = Utils::OpenHandle(*wasm_script);
+
+  Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
+                                           GetIsolate());
+  if (weak_instance_list->length() > 0) {
+    MaybeObject maybe_instance = weak_instance_list->Get(0);
+    if (maybe_instance->IsWeak()) {
+      Handle<WasmInstanceObject> instance(
+          WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
+          GetIsolate());
+      return instance;
+    }
+  }
+  return Handle<WasmInstanceObject>::null();
+}
+
+int GetLEB128Size(Vector<const uint8_t> module_bytes, int offset) {
+  int index = offset;
+  while (module_bytes[index] & 0x80) index++;
+  return index + 1 - offset;
+}
+
+int ReturnPc(const NativeModule* native_module, int pc) {
+  Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
+  uint8_t opcode = wire_bytes[pc];
+  switch (opcode) {
+    case kExprCallFunction: {
+      // skip opcode
+      pc++;
+      // skip function index
+      return pc + GetLEB128Size(wire_bytes, pc);
+    }
+    case kExprCallIndirect: {
+      // skip opcode
+      pc++;
+      // skip signature index
+      pc += GetLEB128Size(wire_bytes, pc);
+      // skip table index
+      return pc + GetLEB128Size(wire_bytes, pc);
+    }
+    default:
+      UNREACHABLE();
+  }
+}
+
+// static
+std::vector<wasm_addr_t> WasmModuleDebug::GetCallStack(
+    uint32_t debug_context_id, Isolate* isolate) {
+  std::vector<wasm_addr_t> call_stack;
+  for (StackFrameIterator frame_it(isolate); !frame_it.done();
+       frame_it.Advance()) {
+    StackFrame* const frame = frame_it.frame();
+    switch (frame->type()) {
+      case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION:
+      case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
+      case StackFrame::OPTIMIZED:
+      case StackFrame::INTERPRETED:
+      case StackFrame::BUILTIN:
+      case StackFrame::WASM: {
+        // A standard frame may include many summarized frames, due to inlining.
+        std::vector<FrameSummary> frames;
+        CommonFrame::cast(frame)->Summarize(&frames);
+        for (size_t i = frames.size(); i-- != 0;) {
+          int offset = 0;
+          Handle<Script> script;
+
+          auto& summary = frames[i];
+          if (summary.IsJavaScript()) {
+            FrameSummary::JavaScriptFrameSummary const& java_script =
+                summary.AsJavaScript();
+            offset = java_script.code_offset();
+            script = Handle<Script>::cast(java_script.script());
+          } else if (summary.IsWasm()) {
+            FrameSummary::WasmFrameSummary const& wasm = summary.AsWasm();
+            offset = GetWasmFunctionOffset(wasm.wasm_instance()->module(),
+                                           wasm.function_index()) +
+                     wasm.byte_offset();
+            script = wasm.script();
+
+            bool zeroth_frame = call_stack.empty();
+            if (!zeroth_frame) {
+              const NativeModule* native_module =
+                  wasm.wasm_instance()->module_object().native_module();
+              offset = ReturnPc(native_module, offset);
+            }
+          }
+
+          if (offset > 0) {
+            call_stack.push_back(
+                {debug_context_id << 16 | script->id(), uint32_t(offset)});
+          }
+        }
+        break;
+      }
+
+      case StackFrame::BUILTIN_EXIT:
+      default:
+        // ignore the frame.
+        break;
+    }
+  }
+  if (call_stack.empty()) call_stack.push_back({1, 0});
+  return call_stack;
+}
+
+// static
+std::vector<FrameSummary> WasmModuleDebug::FindWasmFrame(
+    StackTraceFrameIterator* frame_it, uint32_t* frame_index) {
+  while (!frame_it->done()) {
+    StackFrame* const frame = frame_it->frame();
+    switch (frame->type()) {
+      case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION:
+      case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
+      case StackFrame::OPTIMIZED:
+      case StackFrame::INTERPRETED:
+      case StackFrame::BUILTIN:
+      case StackFrame::WASM: {
+        // A standard frame may include many summarized frames, due to inlining.
+        std::vector<FrameSummary> frames;
+        CommonFrame::cast(frame)->Summarize(&frames);
+        const size_t frame_count = frames.size();
+        DCHECK_GT(frame_count, 0);
+
+        if (frame_count > *frame_index) {
+          if (frame_it->is_wasm())
+            return frames;
+          else
+            return {};
+        } else {
+          *frame_index -= frame_count;
+          frame_it->Advance();
+        }
+        break;
+      }
+
+      case StackFrame::BUILTIN_EXIT:
+      default:
+        // ignore the frame.
+        break;
+    }
+  }
+  return {};
+}
+
+// static
+Handle<WasmInstanceObject> WasmModuleDebug::GetWasmInstance(
+    Isolate* isolate, uint32_t frame_index) {
+  StackTraceFrameIterator frame_it(isolate);
+  std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
+  if (frames.empty()) {
+    return Handle<WasmInstanceObject>::null();
+  }
+
+  int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
+  const FrameSummary::WasmFrameSummary& summary =
+      frames[reversed_index].AsWasm();
+  return summary.wasm_instance();
+}
+
+// static
+bool WasmModuleDebug::GetWasmGlobal(Isolate* isolate, uint32_t frame_index,
+                                    uint32_t index, uint8_t* buffer,
+                                    uint32_t buffer_size, uint32_t* size) {
+  HandleScope handles(isolate);
+
+  Handle<WasmInstanceObject> instance = GetWasmInstance(isolate, frame_index);
+  if (!instance.is_null()) {
+    Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
+    const wasm::WasmModule* module = module_object->module();
+    if (index < module->globals.size()) {
+      wasm::WasmValue wasm_value =
+          WasmInstanceObject::GetGlobalValue(instance, module->globals[index]);
+      return GetWasmValue(wasm_value, buffer, buffer_size, size);
+    }
+  }
+  return false;
+}
+
+// static
+bool WasmModuleDebug::GetWasmLocal(Isolate* isolate, uint32_t frame_index,
+                                   uint32_t index, uint8_t* buffer,
+                                   uint32_t buffer_size, uint32_t* size) {
+  HandleScope handles(isolate);
+
+  StackTraceFrameIterator frame_it(isolate);
+  std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
+  if (frames.empty()) {
+    return false;
+  }
+
+  int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
+  const FrameSummary& summary = frames[reversed_index];
+  if (summary.IsWasm()) {
+    Handle<WasmInstanceObject> instance = summary.AsWasm().wasm_instance();
+    if (!instance.is_null()) {
+      Handle<WasmModuleObject> module_object(instance->module_object(),
+                                             isolate);
+      wasm::NativeModule* native_module = module_object->native_module();
+      DebugInfo* debug_info = native_module->GetDebugInfo();
+      if (static_cast<uint32_t>(debug_info->GetNumLocals(
+              isolate, frame_it.frame()->pc())) > index) {
+        wasm::WasmValue wasm_value = debug_info->GetLocalValue(
+            index, isolate, frame_it.frame()->pc(), frame_it.frame()->fp(),
+            frame_it.frame()->callee_fp());
+        return GetWasmValue(wasm_value, buffer, buffer_size, size);
+      }
+    }
+  }
+  return false;
+}
+
+// static
+bool WasmModuleDebug::GetWasmStackValue(Isolate* isolate, uint32_t frame_index,
+                                        uint32_t index, uint8_t* buffer,
+                                        uint32_t buffer_size, uint32_t* size) {
+  HandleScope handles(isolate);
+
+  StackTraceFrameIterator frame_it(isolate);
+  std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
+  if (frames.empty()) {
+    return false;
+  }
+
+  int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
+  const FrameSummary& summary = frames[reversed_index];
+  if (summary.IsWasm()) {
+    Handle<WasmInstanceObject> instance = summary.AsWasm().wasm_instance();
+    if (!instance.is_null()) {
+      Handle<WasmModuleObject> module_object(instance->module_object(),
+                                             isolate);
+      wasm::NativeModule* native_module = module_object->native_module();
+      DebugInfo* debug_info = native_module->GetDebugInfo();
+      if (static_cast<uint32_t>(debug_info->GetStackDepth(
+              isolate, frame_it.frame()->pc())) > index) {
+        WasmValue wasm_value = debug_info->GetStackValue(
+            index, isolate, frame_it.frame()->pc(), frame_it.frame()->fp(),
+            frame_it.frame()->callee_fp());
+        return GetWasmValue(wasm_value, buffer, buffer_size, size);
+      }
+    }
+  }
+  return false;
+}
+
+// static
+uint32_t WasmModuleDebug::GetWasmMemory(Isolate* isolate, uint32_t frame_index,
+                                        uint32_t offset, uint8_t* buffer,
+                                        uint32_t size) {
+  HandleScope handles(isolate);
+
+  uint32_t bytes_read = 0;
+  Handle<WasmInstanceObject> instance = GetWasmInstance(isolate, frame_index);
+  if (!instance.is_null()) {
+    uint8_t* mem_start = instance->memory_start();
+    size_t mem_size = instance->memory_size();
+    if (static_cast<uint64_t>(offset) + size <= mem_size) {
+      memcpy(buffer, mem_start + offset, size);
+      bytes_read = size;
+    } else if (offset < mem_size) {
+      bytes_read = static_cast<uint32_t>(mem_size) - offset;
+      memcpy(buffer, mem_start + offset, bytes_read);
+    }
+  }
+  return bytes_read;
+}
+
+uint32_t WasmModuleDebug::GetWasmModuleBytes(wasm_addr_t wasm_addr,
+                                             uint8_t* buffer, uint32_t size) {
+  uint32_t bytes_read = 0;
+  // Any instance will work.
+  Handle<WasmInstanceObject> instance = GetFirstWasmInstance();
+  if (!instance.is_null()) {
+    Handle<WasmModuleObject> module_object(instance->module_object(),
+                                           GetIsolate());
+    wasm::NativeModule* native_module = module_object->native_module();
+    const wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes());
+    uint32_t offset = wasm_addr.Offset();
+    if (offset < wire_bytes.length()) {
+      uint32_t module_size = static_cast<uint32_t>(wire_bytes.length());
+      bytes_read = module_size - offset >= size ? size : module_size - offset;
+      memcpy(buffer, wire_bytes.start() + offset, bytes_read);
+    }
+  }
+  return bytes_read;
+}
+
+bool WasmModuleDebug::AddBreakpoint(uint32_t offset, int* breakpoint_id) {
+  v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
+  Handle<Script> script = Utils::OpenHandle(*wasm_script);
+  Handle<String> condition = GetIsolate()->factory()->empty_string();
+  int breakpoint_address = static_cast<int>(offset);
+  return GetIsolate()->debug()->SetBreakPointForScript(
+      script, condition, &breakpoint_address, breakpoint_id);
+}
+
+void WasmModuleDebug::RemoveBreakpoint(uint32_t offset, int breakpoint_id) {
+  v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
+  Handle<Script> script = Utils::OpenHandle(*wasm_script);
+  GetIsolate()->debug()->RemoveBreakpointForWasmScript(script, breakpoint_id);
+}
+
+void WasmModuleDebug::PrepareStep() {
+  i::Isolate* isolate = GetIsolate();
+  DebugScope debug_scope(isolate->debug());
+  debug::PrepareStep(reinterpret_cast<v8::Isolate*>(isolate),
+                     debug::StepAction::StepIn);
+}
+
+template <typename T>
+bool StoreValue(const T& value, uint8_t* buffer, uint32_t buffer_size,
+                uint32_t* size) {
+  *size = sizeof(value);
+  if (*size > buffer_size) return false;
+  memcpy(buffer, &value, *size);
+  return true;
+}
+
+// static
+bool WasmModuleDebug::GetWasmValue(const wasm::WasmValue& wasm_value,
+                                   uint8_t* buffer, uint32_t buffer_size,
+                                   uint32_t* size) {
+  switch (wasm_value.type().kind()) {
+    case wasm::kWasmI32.kind():
+      return StoreValue(wasm_value.to_i32(), buffer, buffer_size, size);
+    case wasm::kWasmI64.kind():
+      return StoreValue(wasm_value.to_i64(), buffer, buffer_size, size);
+    case wasm::kWasmF32.kind():
+      return StoreValue(wasm_value.to_f32(), buffer, buffer_size, size);
+    case wasm::kWasmF64.kind():
+      return StoreValue(wasm_value.to_f64(), buffer, buffer_size, size);
+    case wasm::kWasmS128.kind():
+      return StoreValue(wasm_value.to_s128(), buffer, buffer_size, size);
+
+    case wasm::kWasmStmt.kind():
+    case wasm::kWasmExternRef.kind():
+    case wasm::kWasmFuncRef.kind():
+    case wasm::kWasmExnRef.kind():
+    case wasm::kWasmBottom.kind():
+    default:
+      // Not supported
+      return false;
+  }
+}
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
diff --git a/src/debug/wasm/gdb-server/wasm-module-debug.h b/src/debug/wasm/gdb-server/wasm-module-debug.h
new file mode 100644
index 0000000..10e6a5d
--- /dev/null
+++ b/src/debug/wasm/gdb-server/wasm-module-debug.h
@@ -0,0 +1,105 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_WASM_GDB_SERVER_WASM_MODULE_DEBUG_H_
+#define V8_DEBUG_WASM_GDB_SERVER_WASM_MODULE_DEBUG_H_
+
+#include "src/debug/debug.h"
+#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
+#include "src/execution/frames.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+class WasmValue;
+
+namespace gdb_server {
+
+// Represents the interface to access the Wasm engine state for a given module.
+// For the moment it only works with interpreted functions, in the future it
+// could be extended to also support Liftoff.
+class WasmModuleDebug {
+ public:
+  WasmModuleDebug(v8::Isolate* isolate, Local<debug::WasmScript> script);
+
+  std::string GetModuleName() const;
+  i::Isolate* GetIsolate() const {
+    return reinterpret_cast<i::Isolate*>(isolate_);
+  }
+
+  // Gets the value of the {index}th global value.
+  static bool GetWasmGlobal(Isolate* isolate, uint32_t frame_index,
+                            uint32_t index, uint8_t* buffer,
+                            uint32_t buffer_size, uint32_t* size);
+
+  // Gets the value of the {index}th local value in the {frame_index}th stack
+  // frame.
+  static bool GetWasmLocal(Isolate* isolate, uint32_t frame_index,
+                           uint32_t index, uint8_t* buffer,
+                           uint32_t buffer_size, uint32_t* size);
+
+  // Gets the value of the {index}th value in the operand stack.
+  static bool GetWasmStackValue(Isolate* isolate, uint32_t frame_index,
+                                uint32_t index, uint8_t* buffer,
+                                uint32_t buffer_size, uint32_t* size);
+
+  // Reads {size} bytes, starting from {offset}, from the Memory instance
+  // associated to this module.
+  // Returns the number of byte copied to {buffer}, or 0 is case of error.
+  // Note: only one Memory for Module is currently supported.
+  static uint32_t GetWasmMemory(Isolate* isolate, uint32_t frame_index,
+                                uint32_t offset, uint8_t* buffer,
+                                uint32_t size);
+
+  // Gets {size} bytes, starting from {offset}, from the Code space of this
+  // module.
+  // Returns the number of byte copied to {buffer}, or 0 is case of error.
+  uint32_t GetWasmModuleBytes(wasm_addr_t wasm_addr, uint8_t* buffer,
+                              uint32_t size);
+
+  // Inserts a breakpoint at the offset {offset} of this module.
+  // Returns {true} if the breakpoint was successfully added.
+  bool AddBreakpoint(uint32_t offset, int* breakpoint_id);
+
+  // Removes a breakpoint at the offset {offset} of the this module.
+  void RemoveBreakpoint(uint32_t offset, int breakpoint_id);
+
+  // Handle stepping in wasm functions via the wasm interpreter.
+  void PrepareStep();
+
+  // Returns the current stack trace as a vector of instruction pointers.
+  static std::vector<wasm_addr_t> GetCallStack(uint32_t debug_context_id,
+                                               Isolate* isolate);
+
+ private:
+  // Returns the module WasmInstance associated to the {frame_index}th frame
+  // in the call stack.
+  static Handle<WasmInstanceObject> GetWasmInstance(Isolate* isolate,
+                                                    uint32_t frame_index);
+
+  // Returns its first WasmInstance for this Wasm module.
+  Handle<WasmInstanceObject> GetFirstWasmInstance();
+
+  // Iterates on current stack frames and return frame information for the
+  // {frame_index} specified.
+  // Returns an empty array if the frame specified does not correspond to a Wasm
+  // stack frame.
+  static std::vector<FrameSummary> FindWasmFrame(
+      StackTraceFrameIterator* frame_it, uint32_t* frame_index);
+
+  // Converts a WasmValue into an array of bytes.
+  static bool GetWasmValue(const wasm::WasmValue& wasm_value, uint8_t* buffer,
+                           uint32_t buffer_size, uint32_t* size);
+
+  v8::Isolate* isolate_;
+  Global<debug::WasmScript> wasm_script_;
+};
+
+}  // namespace gdb_server
+}  // namespace wasm
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_DEBUG_WASM_GDB_SERVER_WASM_MODULE_DEBUG_H_
diff --git a/src/debug/x64/debug-x64.cc b/src/debug/x64/debug-x64.cc
index 63689de..3d25c5f 100644
--- a/src/debug/x64/debug-x64.cc
+++ b/src/debug/x64/debug-x64.cc
@@ -6,95 +6,17 @@
 
 #include "src/debug/debug.h"
 
-#include "src/assembler.h"
-#include "src/codegen.h"
+#include "src/codegen/assembler.h"
+#include "src/codegen/macro-assembler.h"
 #include "src/debug/liveedit.h"
-#include "src/objects-inl.h"
+#include "src/execution/frames-inl.h"
+#include "src/objects/objects-inl.h"
 
 namespace v8 {
 namespace internal {
 
 #define __ ACCESS_MASM(masm)
 
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_codesize;
-  __ bind(&check_codesize);
-  __ Nop(Assembler::kDebugBreakSlotLength);
-  DCHECK_EQ(Assembler::kDebugBreakSlotLength,
-            masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction.
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  static const int kSize = Assembler::kDebugBreakSlotLength;
-  CodePatcher patcher(isolate, pc, kSize);
-  Label check_codesize;
-  patcher.masm()->bind(&check_codesize);
-  patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(code->entry()),
-                       Assembler::RelocInfoNone());
-  patcher.masm()->call(kScratchRegister);
-  // Check that the size of the code generated is as expected.
-  DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  return !Assembler::IsNop(pc);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-
-  // Enter an internal frame.
-  {
-    FrameScope scope(masm, StackFrame::INTERNAL);
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ Push(rax);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-
-    __ CallRuntime(Runtime::kDebugBreak, 1, kDontSaveFPRegs);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; ++i) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber rax if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function.
-        if (!(reg.is(rax) && (mode == SAVE_RESULT_REGISTER))) {
-          __ Set(reg, kDebugZapValue);
-        }
-      }
-    }
-    // Get rid of the internal frame.
-  }
-
-  __ MaybeDropFrames();
-
-  // Return to caller.
-  __ ret(0);
-}
-
 void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) {
   {
     FrameScope scope(masm, StackFrame::INTERNAL);
@@ -112,17 +34,17 @@
   // - Look up current function on the frame.
   // - Leave the frame.
   // - Restart the frame by calling the function.
-  __ movp(rbp, rbx);
-  __ movp(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+
+  __ movq(rbp, rbx);
+  __ movq(rdi, Operand(rbp, StandardFrameConstants::kFunctionOffset));
   __ leave();
 
-  __ movp(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
-  __ LoadSharedFunctionInfoSpecialField(
-      rbx, rbx, SharedFunctionInfo::kFormalParameterCountOffset);
+  __ LoadTaggedPointerField(
+      rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+  __ movzxwq(
+      rbx, FieldOperand(rbx, SharedFunctionInfo::kFormalParameterCountOffset));
 
-  ParameterCount dummy(rbx);
-  __ InvokeFunction(rdi, no_reg, dummy, dummy, JUMP_FUNCTION,
-                    CheckDebugStepCallWrapper());
+  __ InvokeFunction(rdi, no_reg, rbx, rbx, JUMP_FUNCTION);
 }
 
 const bool LiveEdit::kFrameDropperSupported = true;
diff --git a/src/debug/x87/OWNERS b/src/debug/x87/OWNERS
deleted file mode 100644
index 61245ae..0000000
--- a/src/debug/x87/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-weiliang.lin@intel.com
-chunyang.dai@intel.com
diff --git a/src/debug/x87/debug-x87.cc b/src/debug/x87/debug-x87.cc
deleted file mode 100644
index 8810f01..0000000
--- a/src/debug/x87/debug-x87.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2012 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#if V8_TARGET_ARCH_X87
-
-#include "src/debug/debug.h"
-
-#include "src/codegen.h"
-#include "src/debug/liveedit.h"
-#include "src/x87/frames-x87.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
-  Label check_codesize;
-  __ bind(&check_codesize);
-  __ Nop(Assembler::kDebugBreakSlotLength);
-  DCHECK_EQ(Assembler::kDebugBreakSlotLength,
-            masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
-  // Generate enough nop's to make space for a call instruction.
-  masm->RecordDebugBreakSlot(mode);
-  EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
-  CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength);
-  EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
-                                       Handle<Code> code) {
-  DCHECK(code->is_debug_stub());
-  static const int kSize = Assembler::kDebugBreakSlotLength;
-  CodePatcher patcher(isolate, pc, kSize);
-
-  // Add a label for checking the size of the code used for returning.
-  Label check_codesize;
-  patcher.masm()->bind(&check_codesize);
-  patcher.masm()->call(code->entry(), RelocInfo::NONE32);
-  // Check that the size of the code generated is as expected.
-  DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
-  return !Assembler::IsNop(pc);
-}
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
-                                          DebugBreakCallHelperMode mode) {
-  __ RecordComment("Debug break");
-
-  // Enter an internal frame.
-  {
-    FrameScope scope(masm, StackFrame::INTERNAL);
-
-    // Load padding words on stack.
-    for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
-      __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue)));
-    }
-    __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
-
-    // Push arguments for DebugBreak call.
-    if (mode == SAVE_RESULT_REGISTER) {
-      // Break on return.
-      __ push(eax);
-    } else {
-      // Non-return breaks.
-      __ Push(masm->isolate()->factory()->the_hole_value());
-    }
-    __ Move(eax, Immediate(1));
-    __ mov(ebx,
-           Immediate(ExternalReference(
-               Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
-    CEntryStub ceb(masm->isolate(), 1);
-    __ CallStub(&ceb);
-
-    if (FLAG_debug_code) {
-      for (int i = 0; i < kNumJSCallerSaved; ++i) {
-        Register reg = {JSCallerSavedCode(i)};
-        // Do not clobber eax if mode is SAVE_RESULT_REGISTER. It will
-        // contain return value of the function.
-        if (!(reg.is(eax) && (mode == SAVE_RESULT_REGISTER))) {
-          __ Move(reg, Immediate(kDebugZapValue));
-        }
-      }
-    }
-
-    __ pop(ebx);
-    // We divide stored value by 2 (untagging) and multiply it by word's size.
-    STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
-    __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0));
-
-    // Get rid of the internal frame.
-  }
-
-  // This call did not replace a call , so there will be an unwanted
-  // return address left on the stack. Here we get rid of that.
-  __ add(esp, Immediate(kPointerSize));
-
-  // Now that the break point has been handled, resume normal execution by
-  // jumping to the target address intended by the caller and that was
-  // overwritten by the address of DebugBreakXXX.
-  ExternalReference after_break_target =
-      ExternalReference::debug_after_break_target_address(masm->isolate());
-  __ jmp(Operand::StaticVariable(after_break_target));
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
-  // We do not know our frame height, but set esp based on ebp.
-  __ lea(esp, Operand(ebp, FrameDropperFrameConstants::kFunctionOffset));
-  __ pop(edi);  // Function.
-  __ add(esp, Immediate(-FrameDropperFrameConstants::kCodeOffset));  // INTERNAL
-                                                                     // frame
-                                                                     // marker
-                                                                     // and code
-  __ pop(ebp);
-
-  ParameterCount dummy(0);
-  __ CheckDebugHook(edi, no_reg, dummy, dummy);
-
-  // Load context from the function.
-  __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
-
-  // Clear new.target register as a safety measure.
-  __ mov(edx, masm->isolate()->factory()->undefined_value());
-
-  // Get function code.
-  __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
-  __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kCodeOffset));
-  __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
-
-  // Re-run JSFunction, edi is function, esi is context.
-  __ jmp(ebx);
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-
-}  // namespace internal
-}  // namespace v8
-
-#endif  // V8_TARGET_ARCH_X87