Merge V8 5.8.283.32

Test: Build V8 for arm, arm64, x86, x86_64, mips, mips64 and
set a PAC script from the UI on bullhead

Change-Id: I7cc773b5daca34d869e768a1deebae3876f2dfac
diff --git a/src/inspector/BUILD.gn b/src/inspector/BUILD.gn
index 6ebb91c..e6742c0 100644
--- a/src/inspector/BUILD.gn
+++ b/src/inspector/BUILD.gn
@@ -140,7 +140,6 @@
     "inspected-context.h",
     "java-script-call-frame.cc",
     "java-script-call-frame.h",
-    "protocol-platform.h",
     "remote-object-id.cc",
     "remote-object-id.h",
     "script-breakpoint.h",
@@ -150,6 +149,8 @@
     "string-16.h",
     "string-util.cc",
     "string-util.h",
+    "test-interface.cc",
+    "test-interface.h",
     "v8-console-agent-impl.cc",
     "v8-console-agent-impl.h",
     "v8-console-message.cc",
@@ -186,5 +187,7 @@
     "v8-stack-trace-impl.h",
     "v8-value-copier.cc",
     "v8-value-copier.h",
+    "wasm-translation.cc",
+    "wasm-translation.h",
   ]
 }
diff --git a/src/inspector/DEPS b/src/inspector/DEPS
index d49c6a6..2d77fb7 100644
--- a/src/inspector/DEPS
+++ b/src/inspector/DEPS
@@ -1,11 +1,13 @@
 include_rules = [
   "-src",
+  "-include/v8-debug.h",
   "+src/base/atomicops.h",
   "+src/base/macros.h",
   "+src/base/logging.h",
   "+src/base/platform/platform.h",
+  "+src/conversions.h",
   "+src/inspector",
   "+src/tracing",
-  "-include/v8-debug.h",
   "+src/debug/debug-interface.h",
+  "+src/debug/interface-types.h",
 ]
diff --git a/src/inspector/debugger-script.js b/src/inspector/debugger-script.js
index 1614566..d9cb12a 100644
--- a/src/inspector/debugger-script.js
+++ b/src/inspector/debugger-script.js
@@ -33,17 +33,6 @@
 
 var DebuggerScript = {};
 
-/**
- * @param {?CompileEvent} eventData
- */
-DebuggerScript.getAfterCompileScript = function(eventData)
-{
-    var script = eventData.script().value();
-    if (!script.is_debugger_script)
-        return script;
-    return null;
-}
-
 /** @type {!Map<!ScopeType, string>} */
 DebuggerScript._scopeTypeNames = new Map();
 DebuggerScript._scopeTypeNames.set(ScopeType.Global, "global");
@@ -53,6 +42,8 @@
 DebuggerScript._scopeTypeNames.set(ScopeType.Catch, "catch");
 DebuggerScript._scopeTypeNames.set(ScopeType.Block, "block");
 DebuggerScript._scopeTypeNames.set(ScopeType.Script, "script");
+DebuggerScript._scopeTypeNames.set(ScopeType.Eval, "eval");
+DebuggerScript._scopeTypeNames.set(ScopeType.Module, "module");
 
 /**
  * @param {function()} fun
@@ -83,60 +74,31 @@
 }
 
 /**
- * @param {Object} object
- * @return {?RawLocation}
+ * @param {Object} gen
+ * @return {?Array<!Scope>}
  */
-DebuggerScript.getGeneratorObjectLocation = function(object)
+DebuggerScript.getGeneratorScopes = function(gen)
 {
-    var mirror = MakeMirror(object, true /* transient */);
+    var mirror = MakeMirror(gen);
     if (!mirror.isGenerator())
         return null;
     var generatorMirror = /** @type {!GeneratorMirror} */(mirror);
-    var funcMirror = generatorMirror.func();
-    if (!funcMirror.resolved())
+    var count = generatorMirror.scopeCount();
+    if (count == 0)
         return null;
-    var location = generatorMirror.sourceLocation() || funcMirror.sourceLocation();
-    var script = funcMirror.script();
-    if (script && location) {
-        return {
-            scriptId: "" + script.id(),
-            lineNumber: location.line,
-            columnNumber: location.column
-        };
+    var result = [];
+    for (var i = 0; i < count; i++) {
+        var scopeDetails = generatorMirror.scope(i).details();
+        var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object());
+        if (!scopeObject)
+            continue;
+        result.push({
+            type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeDetails.type())),
+            object: scopeObject,
+            name: scopeDetails.name() || ""
+        });
     }
-    return null;
-}
-
-/**
- * @param {Object} object
- * @return {!Array<!{value: *}>|undefined}
- */
-DebuggerScript.getCollectionEntries = function(object)
-{
-    var mirror = MakeMirror(object, true /* transient */);
-    if (mirror.isMap())
-        return /** @type {!MapMirror} */(mirror).entries();
-    if (mirror.isSet() || mirror.isIterator()) {
-        var result = [];
-        var values = mirror.isSet() ? /** @type {!SetMirror} */(mirror).values() : /** @type {!IteratorMirror} */(mirror).preview();
-        for (var i = 0; i < values.length; ++i)
-            result.push({ value: values[i] });
-        return result;
-    }
-}
-
-/**
- * @param {string|undefined} contextData
- * @return {number}
- */
-DebuggerScript._executionContextId = function(contextData)
-{
-    if (!contextData)
-        return 0;
-    var match = contextData.match(/^[^,]*,([^,]*),.*$/);
-    if (!match)
-        return 0;
-    return parseInt(match[1], 10) || 0;
+    return result;
 }
 
 /**
@@ -146,7 +108,7 @@
  */
 DebuggerScript.setBreakpoint = function(execState, info)
 {
-    var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, Debug.BreakPositionAlignment.Statement);
+    var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, Debug.BreakPositionAlignment.BreakPosition);
     var locations = Debug.findBreakPointActualLocations(breakId);
     if (!locations.length)
         return undefined;
@@ -225,20 +187,10 @@
 }
 
 /**
- * @param {!ExecutionState} execState
- * @param {!{enabled: boolean}} info
+ * @param {!Array<!BreakPoint>|undefined} breakpoints
  */
-DebuggerScript.setBreakpointsActivated = function(execState, info)
+DebuggerScript.getBreakpointNumbers = function(breakpoints)
 {
-    Debug.debuggerFlags().breakPointsActive.setValue(info.enabled);
-}
-
-/**
- * @param {!BreakEvent} eventData
- */
-DebuggerScript.getBreakpointNumbers = function(eventData)
-{
-    var breakpoints = eventData.breakPointsHit();
     var numbers = [];
     if (!breakpoints)
         return numbers;
@@ -386,8 +338,8 @@
             details = {
                 "functionName": ensureFuncMirror().debugName(),
                 "location": {
-                    "lineNumber": line(),
-                    "columnNumber": column(),
+                    "lineNumber": ensureLocation().line,
+                    "columnNumber": ensureLocation().column,
                     "scriptId": String(script.id())
                 },
                 "this": thisObject,
@@ -448,50 +400,23 @@
     /**
      * @return {number}
      */
-    function line()
-    {
-        return ensureLocation().line;
-    }
-
-    /**
-     * @return {number}
-     */
-    function column()
-    {
-        return ensureLocation().column;
-    }
-
-    /**
-     * @return {number}
-     */
     function contextId()
     {
         var mirror = ensureFuncMirror();
-        // Old V8 do not have context() function on these objects
-        if (!mirror.context)
-            return DebuggerScript._executionContextId(mirror.script().value().context_data);
         var context = mirror.context();
-        if (context)
-            return DebuggerScript._executionContextId(context.data());
+        if (context && context.data())
+            return Number(context.data());
         return 0;
     }
 
     /**
-     * @return {number}
-     */
-    function sourceID()
-    {
-        var script = ensureScriptMirror();
-        return script.id();
-    }
-
-    /**
      * @param {string} expression
+     * @param {boolean} throwOnSideEffect
      * @return {*}
      */
-    function evaluate(expression)
+    function evaluate(expression, throwOnSideEffect)
     {
-        return frameMirror.evaluate(expression, false).value();
+        return frameMirror.evaluate(expression, throwOnSideEffect).value();
     }
 
     /** @return {undefined} */
@@ -514,9 +439,6 @@
     }
 
     return {
-        "sourceID": sourceID,
-        "line": line,
-        "column": column,
         "contextId": contextId,
         "thisObject": thisObject,
         "evaluate": evaluate,
@@ -541,15 +463,21 @@
     case ScopeType.Catch:
     case ScopeType.Block:
     case ScopeType.Script:
+    case ScopeType.Eval:
+    case ScopeType.Module:
         // For transient objects we create a "persistent" copy that contains
         // the same properties.
         // Reset scope object prototype to null so that the proto properties
         // don't appear in the local scope section.
-        var properties = /** @type {!ObjectMirror} */(MakeMirror(scopeObject, true /* transient */)).properties();
+        var properties = /** @type {!ObjectMirror} */(MakeMirror(scopeObject)).properties();
         // Almost always Script scope will be empty, so just filter out that noise.
-        // Also drop empty Block scopes, should we get any.
-        if (!properties.length && (scopeType === ScopeType.Script || scopeType === ScopeType.Block))
+        // Also drop empty Block, Eval and Script scopes, should we get any.
+        if (!properties.length && (scopeType === ScopeType.Script ||
+                                   scopeType === ScopeType.Block ||
+                                   scopeType === ScopeType.Eval ||
+                                   scopeType === ScopeType.Module)) {
             break;
+        }
         result = { __proto__: null };
         for (var j = 0; j < properties.length; j++) {
             var name = properties[j].name();
@@ -566,8 +494,5 @@
     return result;
 }
 
-// We never resolve Mirror by its handle so to avoid memory leaks caused by Mirrors in the cache we disable it.
-ToggleMirrorCache(false);
-
 return DebuggerScript;
 })();
diff --git a/src/inspector/debugger_script_externs.js b/src/inspector/debugger_script_externs.js
index cc152d5..6f36fb9 100644
--- a/src/inspector/debugger_script_externs.js
+++ b/src/inspector/debugger_script_externs.js
@@ -19,21 +19,6 @@
 var RawLocation;
 
 /** @typedef {{
-        id: number,
-        name: string,
-        sourceURL: (string|undefined),
-        sourceMappingURL: (string|undefined),
-        source: string,
-        startLine: number,
-        endLine: number,
-        startColumn: number,
-        endColumn: number,
-        executionContextId: number,
-        executionContextAuxData: string
-    }} */
-var FormattedScript;
-
-/** @typedef {{
         functionName: string,
         location: !RawLocation,
         this: !Object,
@@ -44,11 +29,9 @@
 var JavaScriptCallFrameDetails;
 
 /** @typedef {{
-        sourceID: function():(number),
-        line: function():number,
-        column: function():number,
+        contextId: function():number,
         thisObject: !Object,
-        evaluate: function(string):*,
+        evaluate: function(string, boolean):*,
         restart: function():undefined,
         setVariableValue: function(number, string, *):undefined,
         isAtReturn: boolean,
@@ -89,10 +72,6 @@
  */
 Debug.findBreakPoint = function(breakId, remove) {}
 
-/** @return {!DebuggerFlags} */
-Debug.debuggerFlags = function() {}
-
-
 /** @enum */
 const BreakPositionAlignment = {
     Statement: 0,
@@ -100,32 +79,6 @@
 };
 Debug.BreakPositionAlignment = BreakPositionAlignment;
 
-/** @enum */
-Debug.StepAction = { StepOut: 0,
-                     StepNext: 1,
-                     StepIn: 2,
-                     StepFrame: 3 };
-
-/** @enum */
-const ScriptCompilationType = { Host: 0,
-                              Eval: 1,
-                              JSON: 2 };
-Debug.ScriptCompilationType = ScriptCompilationType;
-
-
-/** @interface */
-function DebuggerFlag() {}
-
-/** @param {boolean} value */
-DebuggerFlag.prototype.setValue = function(value) {}
-
-
-/** @typedef {{
- *    breakPointsActive: !DebuggerFlag
- *  }}
- */
-var DebuggerFlags;
-
 /** @const */
 var LiveEdit = {}
 
@@ -174,28 +127,12 @@
 
 
 /** @interface */
-function CompileEvent() {}
-
-/** @return {!ScriptMirror} */
-CompileEvent.prototype.script = function() {}
-
-
-/** @interface */
-function BreakEvent() {}
-
-/** @return {!Array<!BreakPoint>|undefined} */
-BreakEvent.prototype.breakPointsHit = function() {}
-
-
-/** @interface */
 function ExecutionState() {}
 
 /**
  * @param {string} source
- * @param {boolean} disableBreak
- * @param {*=} additionalContext
  */
-ExecutionState.prototype.evaluateGlobal = function(source, disableBreak, additionalContext) {}
+ExecutionState.prototype.evaluateGlobal = function(source) {}
 
 /** @return {number} */
 ExecutionState.prototype.frameCount = function() {}
@@ -220,7 +157,9 @@
                   Closure: 3,
                   Catch: 4,
                   Block: 5,
-                  Script: 6 };
+                  Script: 6,
+                  Eval: 7,
+                  Module: 8};
 
 
 /** @typedef {{
@@ -237,14 +176,6 @@
 /** @typedef{{
  *    id: number,
  *    context_data: (string|undefined),
- *    source_url: (string|undefined),
- *    source_mapping_url: (string|undefined),
- *    is_debugger_script: boolean,
- *    source: string,
- *    line_offset: number,
- *    column_offset: number,
- *    nameOrSourceURL: function():string,
- *    compilationType: function():!ScriptCompilationType,
  *    }}
  */
 var Script;
@@ -286,16 +217,11 @@
 /** @return {number} */
 FrameDetails.prototype.scopeCount = function() {}
 
-
-/** @param {boolean} value */
-function ToggleMirrorCache(value) {}
-
 /**
  * @param {*} value
- * @param {boolean=} transient
  * @return {!Mirror}
  */
-function MakeMirror(value, transient) {}
+function MakeMirror(value) {}
 
 
 /** @interface */
@@ -307,16 +233,6 @@
 /** @return {boolean} */
 Mirror.prototype.isGenerator = function() {}
 
-/** @return {boolean} */
-Mirror.prototype.isMap = function() {}
-
-/** @return {boolean} */
-Mirror.prototype.isSet = function() {}
-
-/** @return {boolean} */
-Mirror.prototype.isIterator = function() {}
-
-
 /**
  * @interface
  * @extends {Mirror}
@@ -366,60 +282,20 @@
  */
 function UnresolvedFunctionMirror(value) {}
 
-
-/**
- * @interface
- * @extends {ObjectMirror}
- */
-function MapMirror () {}
-
-/**
- * @param {number=} limit
- * @return {!Array<!{key: *, value: *}>}
- */
-MapMirror.prototype.entries = function(limit) {}
-
-
-/**
- * @interface
- * @extends {ObjectMirror}
- */
-function SetMirror () {}
-
-/**
- * @param {number=} limit
- * @return {!Array<*>}
- */
-SetMirror.prototype.values = function(limit) {}
-
-
-/**
- * @interface
- * @extends {ObjectMirror}
- */
-function IteratorMirror () {}
-
-/**
- * @param {number=} limit
- * @return {!Array<*>}
- */
-IteratorMirror.prototype.preview = function(limit) {}
-
-
 /**
  * @interface
  * @extends {ObjectMirror}
  */
 function GeneratorMirror () {}
 
-/** @return {string} */
-GeneratorMirror.prototype.status = function() {}
+/** @return {number} */
+GeneratorMirror.prototype.scopeCount = function() {}
 
-/** @return {!SourceLocation|undefined} */
-GeneratorMirror.prototype.sourceLocation = function() {}
-
-/** @return {!FunctionMirror} */
-GeneratorMirror.prototype.func = function() {}
+/**
+ * @param {number} index
+ * @return {!ScopeMirror|undefined}
+ */
+GeneratorMirror.prototype.scope = function(index) {}
 
 
 /**
@@ -457,9 +333,9 @@
 
 /**
  * @param {string} source
- * @param {boolean} disableBreak
+ * @param {boolean} throwOnSideEffect
  */
-FrameMirror.prototype.evaluate = function(source, disableBreak) {}
+FrameMirror.prototype.evaluate = function(source, throwOnSideEffect) {}
 
 FrameMirror.prototype.restart = function() {}
 
diff --git a/src/inspector/injected-script-native.cc b/src/inspector/injected-script-native.cc
index fcf2ead..5d0136b 100644
--- a/src/inspector/injected-script-native.cc
+++ b/src/inspector/injected-script-native.cc
@@ -44,8 +44,8 @@
                                const String16& groupName) {
   if (m_lastBoundObjectId <= 0) m_lastBoundObjectId = 1;
   int id = m_lastBoundObjectId++;
-  m_idToWrappedObject[id] =
-      wrapUnique(new v8::Global<v8::Value>(m_isolate, value));
+  m_idToWrappedObject.insert(
+      std::make_pair(id, v8::Global<v8::Value>(m_isolate, value)));
   addObjectToGroup(id, groupName);
   return id;
 }
@@ -57,7 +57,7 @@
 
 v8::Local<v8::Value> InjectedScriptNative::objectForId(int id) {
   auto iter = m_idToWrappedObject.find(id);
-  return iter != m_idToWrappedObject.end() ? iter->second->Get(m_isolate)
+  return iter != m_idToWrappedObject.end() ? iter->second.Get(m_isolate)
                                            : v8::Local<v8::Value>();
 }
 
diff --git a/src/inspector/injected-script-native.h b/src/inspector/injected-script-native.h
index 3bdf247..c0b9301 100644
--- a/src/inspector/injected-script-native.h
+++ b/src/inspector/injected-script-native.h
@@ -34,8 +34,7 @@
 
   int m_lastBoundObjectId;
   v8::Isolate* m_isolate;
-  protocol::HashMap<int, std::unique_ptr<v8::Global<v8::Value>>>
-      m_idToWrappedObject;
+  protocol::HashMap<int, v8::Global<v8::Value>> m_idToWrappedObject;
   typedef protocol::HashMap<int, String16> IdToObjectGroupName;
   IdToObjectGroupName m_idToObjectGroupName;
   typedef protocol::HashMap<String16, std::vector<int>> NameToObjectGroup;
diff --git a/src/inspector/injected-script-source.js b/src/inspector/injected-script-source.js
index f3c8d6b..a828b76 100644
--- a/src/inspector/injected-script-source.js
+++ b/src/inspector/injected-script-source.js
@@ -157,11 +157,11 @@
  * @type {!Object<string, !Object<string, boolean>>}
  * @const
  */
-var domAttributesWithObservableSideEffectOnGet = nullifyObjectProto({});
-domAttributesWithObservableSideEffectOnGet["Request"] = nullifyObjectProto({});
-domAttributesWithObservableSideEffectOnGet["Request"]["body"] = true;
-domAttributesWithObservableSideEffectOnGet["Response"] = nullifyObjectProto({});
-domAttributesWithObservableSideEffectOnGet["Response"]["body"] = true;
+var domAttributesWithObservableSideEffectOnGet = {
+    Request: { body: true, __proto__: null },
+    Response: { body: true, __proto__: null },
+    __proto__: null
+}
 
 /**
  * @param {!Object} object
@@ -186,6 +186,7 @@
 var InjectedScript = function()
 {
 }
+InjectedScriptHost.nullifyPrototype(InjectedScript);
 
 /**
  * @type {!Object.<string, boolean>}
@@ -211,6 +212,8 @@
 InjectedScript.closureTypes["script"] = "Script";
 InjectedScript.closureTypes["with"] = "With Block";
 InjectedScript.closureTypes["global"] = "Global";
+InjectedScript.closureTypes["eval"] = "Eval";
+InjectedScript.closureTypes["module"] = "Module";
 
 InjectedScript.prototype = {
     /**
@@ -617,7 +620,13 @@
         var className = InjectedScriptHost.internalConstructorName(obj);
         if (subtype === "array" || subtype === "typedarray") {
             if (typeof obj.length === "number")
-                className += "[" + obj.length + "]";
+                return className + "(" + obj.length + ")";
+            return className;
+        }
+
+        if (subtype === "map" || subtype === "set") {
+            if (typeof obj.size === "number")
+                return className + "(" + obj.size + ")";
             return className;
         }
 
@@ -929,17 +938,16 @@
             if (!descriptor.isOwn)
                 continue;
 
-            // Ignore computed properties.
-            if (!("value" in descriptor))
+            // Ignore computed properties unless they have getters.
+            if (!("value" in descriptor)) {
+                if (descriptor.get)
+                    this._appendPropertyPreview(preview, { name: name, type: "accessor", __proto__: null }, propertiesThreshold);
                 continue;
+            }
 
             var value = descriptor.value;
             var type = typeof value;
 
-            // Never render functions in object preview.
-            if (type === "function" && (this.subtype !== "array" || !isUInt32(name)))
-                continue;
-
             // Special-case HTMLAll.
             if (type === "undefined" && injectedScript._isHTMLAllCollection(value))
                 type = "object";
diff --git a/src/inspector/injected-script.cc b/src/inspector/injected-script.cc
index d605227..9d9c327 100644
--- a/src/inspector/injected-script.cc
+++ b/src/inspector/injected-script.cc
@@ -105,9 +105,9 @@
   if (inspector->getContext(contextGroupId, contextId) != inspectedContext)
     return nullptr;
   if (!injectedScriptValue->IsObject()) return nullptr;
-  return wrapUnique(new InjectedScript(inspectedContext,
-                                       injectedScriptValue.As<v8::Object>(),
-                                       std::move(injectedScriptNative)));
+  return std::unique_ptr<InjectedScript>(
+      new InjectedScript(inspectedContext, injectedScriptValue.As<v8::Object>(),
+                         std::move(injectedScriptNative)));
 }
 
 InjectedScript::InjectedScript(
@@ -150,7 +150,7 @@
   if (!response.isSuccess()) return response;
   protocol::ErrorSupport errors;
   std::unique_ptr<Array<PropertyDescriptor>> result =
-      Array<PropertyDescriptor>::parse(protocolValue.get(), &errors);
+      Array<PropertyDescriptor>::fromValue(protocolValue.get(), &errors);
   if (errors.hasErrors()) return Response::Error(errors.errors());
   *properties = std::move(result);
   return Response::OK();
@@ -158,7 +158,7 @@
 
 void InjectedScript::releaseObject(const String16& objectId) {
   std::unique_ptr<protocol::Value> parsedObjectId =
-      protocol::parseJSON(objectId);
+      protocol::StringUtil::parseJSON(objectId);
   if (!parsedObjectId) return;
   protocol::DictionaryValue* object =
       protocol::DictionaryValue::cast(parsedObjectId.get());
@@ -184,7 +184,7 @@
   if (!response.isSuccess()) return response;
 
   *result =
-      protocol::Runtime::RemoteObject::parse(protocolValue.get(), &errors);
+      protocol::Runtime::RemoteObject::fromValue(protocolValue.get(), &errors);
   if (!result->get()) return Response::Error(errors.errors());
   return Response::OK();
 }
@@ -260,7 +260,8 @@
   Response response = toProtocolValue(context, r, &protocolValue);
   if (!response.isSuccess()) return nullptr;
   protocol::ErrorSupport errors;
-  return protocol::Runtime::RemoteObject::parse(protocolValue.get(), &errors);
+  return protocol::Runtime::RemoteObject::fromValue(protocolValue.get(),
+                                                    &errors);
 }
 
 Response InjectedScript::findObject(const RemoteObjectId& objectId,
@@ -317,7 +318,7 @@
   if (callArgument->hasValue() || callArgument->hasUnserializableValue()) {
     String16 value =
         callArgument->hasValue()
-            ? callArgument->getValue(nullptr)->toJSONString()
+            ? callArgument->getValue(nullptr)->serialize()
             : "Number(\"" + callArgument->getUnserializableValue("") + "\")";
     if (!m_context->inspector()
              ->compileAndRunInternalScript(
@@ -418,7 +419,7 @@
       m_handleScope(inspector->isolate()),
       m_tryCatch(inspector->isolate()),
       m_ignoreExceptionsAndMuteConsole(false),
-      m_previousPauseOnExceptionsState(v8::DebugInterface::NoBreakOnException),
+      m_previousPauseOnExceptionsState(v8::debug::NoBreakOnException),
       m_userGesture(false) {}
 
 Response InjectedScript::Scope::initialize() {
@@ -448,14 +449,13 @@
   m_inspector->client()->muteMetrics(m_contextGroupId);
   m_inspector->muteExceptions(m_contextGroupId);
   m_previousPauseOnExceptionsState =
-      setPauseOnExceptionsState(v8::DebugInterface::NoBreakOnException);
+      setPauseOnExceptionsState(v8::debug::NoBreakOnException);
 }
 
-v8::DebugInterface::ExceptionBreakState
-InjectedScript::Scope::setPauseOnExceptionsState(
-    v8::DebugInterface::ExceptionBreakState newState) {
+v8::debug::ExceptionBreakState InjectedScript::Scope::setPauseOnExceptionsState(
+    v8::debug::ExceptionBreakState newState) {
   if (!m_inspector->debugger()->enabled()) return newState;
-  v8::DebugInterface::ExceptionBreakState presentState =
+  v8::debug::ExceptionBreakState presentState =
       m_inspector->debugger()->getPauseOnExceptionsState();
   if (presentState != newState)
     m_inspector->debugger()->setPauseOnExceptionsState(newState);
diff --git a/src/inspector/injected-script.h b/src/inspector/injected-script.h
index 6500f4d..9e6680a 100644
--- a/src/inspector/injected-script.h
+++ b/src/inspector/injected-script.h
@@ -120,15 +120,15 @@
 
    private:
     void cleanup();
-    v8::DebugInterface::ExceptionBreakState setPauseOnExceptionsState(
-        v8::DebugInterface::ExceptionBreakState);
+    v8::debug::ExceptionBreakState setPauseOnExceptionsState(
+        v8::debug::ExceptionBreakState);
 
     v8::HandleScope m_handleScope;
     v8::TryCatch m_tryCatch;
     v8::Local<v8::Context> m_context;
     std::unique_ptr<V8Console::CommandLineAPIScope> m_commandLineAPIScope;
     bool m_ignoreExceptionsAndMuteConsole;
-    v8::DebugInterface::ExceptionBreakState m_previousPauseOnExceptionsState;
+    v8::debug::ExceptionBreakState m_previousPauseOnExceptionsState;
     bool m_userGesture;
   };
 
diff --git a/src/inspector/injected_script_externs.js b/src/inspector/injected_script_externs.js
index b6339c6..14b14e6 100644
--- a/src/inspector/injected_script_externs.js
+++ b/src/inspector/injected_script_externs.js
@@ -9,6 +9,11 @@
 
 /**
  * @param {*} obj
+ */
+InjectedScriptHostClass.prototype.nullifyPrototype = function(obj) {}
+
+/**
+ * @param {*} obj
  * @return {string}
  */
 InjectedScriptHostClass.prototype.internalConstructorName = function(obj) {}
diff --git a/src/inspector/inspected-context.cc b/src/inspector/inspected-context.cc
index dab3bba..6d9f51e 100644
--- a/src/inspector/inspected-context.cc
+++ b/src/inspector/inspected-context.cc
@@ -41,10 +41,12 @@
       m_humanReadableName(toString16(info.humanReadableName)),
       m_auxData(toString16(info.auxData)),
       m_reported(false) {
+  v8::Isolate* isolate = m_inspector->isolate();
+  info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex),
+                                v8::Int32::New(isolate, contextId));
   m_context.SetWeak(&m_context, &clearContext,
                     v8::WeakCallbackType::kParameter);
 
-  v8::Isolate* isolate = m_inspector->isolate();
   v8::Local<v8::Object> global = info.context->Global();
   v8::Local<v8::Object> console =
       V8Console::createConsole(this, info.hasMemoryOnConsole);
@@ -65,6 +67,14 @@
   }
 }
 
+// static
+int InspectedContext::contextId(v8::Local<v8::Context> context) {
+  v8::Local<v8::Value> data =
+      context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex));
+  if (data.IsEmpty() || !data->IsInt32()) return 0;
+  return static_cast<int>(data.As<v8::Int32>()->Value());
+}
+
 v8::Local<v8::Context> InspectedContext::context() const {
   return m_context.Get(isolate());
 }
diff --git a/src/inspector/inspected-context.h b/src/inspector/inspected-context.h
index f31eb76..f8d97e9 100644
--- a/src/inspector/inspected-context.h
+++ b/src/inspector/inspected-context.h
@@ -21,6 +21,8 @@
  public:
   ~InspectedContext();
 
+  static int contextId(v8::Local<v8::Context>);
+
   v8::Local<v8::Context> context() const;
   int contextId() const { return m_contextId; }
   int contextGroupId() const { return m_contextGroupId; }
diff --git a/src/inspector/inspector.gyp b/src/inspector/inspector.gyp
index c70722f..91507bd 100644
--- a/src/inspector/inspector.gyp
+++ b/src/inspector/inspector.gyp
@@ -13,6 +13,13 @@
   'targets': [
     { 'target_name': 'inspector_injected_script',
       'type': 'none',
+      'conditions': [
+        ['want_separate_host_toolset==1', {
+          'toolsets': ['host', 'target'],
+        }, {
+          'toolsets': ['target'],
+        }]
+      ],
       'actions': [
         {
           'action_name': 'convert_js_to_cpp_char_array',
@@ -37,6 +44,13 @@
     },
     { 'target_name': 'inspector_debugger_script',
       'type': 'none',
+      'conditions': [
+        ['want_separate_host_toolset==1', {
+          'toolsets': ['host', 'target'],
+        }, {
+          'toolsets': ['target'],
+        }]
+      ],
       'actions': [
         {
           'action_name': 'convert_js_to_cpp_char_array',
@@ -61,6 +75,13 @@
     },
     { 'target_name': 'protocol_compatibility',
       'type': 'none',
+      'conditions': [
+        ['want_separate_host_toolset==1', {
+          'toolsets': ['host', 'target'],
+        }, {
+          'toolsets': ['target'],
+        }]
+      ],
       'actions': [
         {
           'action_name': 'protocol_compatibility',
@@ -83,6 +104,13 @@
     { 'target_name': 'protocol_generated_sources',
       'type': 'none',
       'dependencies': [ 'protocol_compatibility' ],
+      'conditions': [
+        ['want_separate_host_toolset==1', {
+          'toolsets': ['host', 'target'],
+        }, {
+          'toolsets': ['target'],
+        }]
+      ],
       'actions': [
         {
           'action_name': 'protocol_generated_sources',
diff --git a/src/inspector/inspector.gypi b/src/inspector/inspector.gypi
index 863c038..8aff49d 100644
--- a/src/inspector/inspector.gypi
+++ b/src/inspector/inspector.gypi
@@ -44,7 +44,6 @@
       'inspector/inspected-context.h',
       'inspector/java-script-call-frame.cc',
       'inspector/java-script-call-frame.h',
-      'inspector/protocol-platform.h',
       'inspector/remote-object-id.cc',
       'inspector/remote-object-id.h',
       'inspector/script-breakpoint.h',
@@ -54,6 +53,8 @@
       'inspector/string-16.h',
       'inspector/string-util.cc',
       'inspector/string-util.h',
+      'inspector/test-interface.cc',
+      'inspector/test-interface.h',
       'inspector/v8-console.cc',
       'inspector/v8-console.h',
       'inspector/v8-console-agent-impl.cc',
@@ -90,6 +91,8 @@
       'inspector/v8-stack-trace-impl.h',
       'inspector/v8-value-copier.cc',
       'inspector/v8-value-copier.h',
+      'inspector/wasm-translation.cc',
+      'inspector/wasm-translation.h',
     ]
   }
 }
diff --git a/src/inspector/inspector_protocol_config.json b/src/inspector/inspector_protocol_config.json
index cb9e669..22e2cf5 100644
--- a/src/inspector/inspector_protocol_config.json
+++ b/src/inspector/inspector_protocol_config.json
@@ -3,7 +3,31 @@
         "path": "js_protocol.json",
         "package": "src/inspector/protocol",
         "output": "protocol",
-        "namespace": ["v8_inspector", "protocol"]
+        "namespace": ["v8_inspector", "protocol"],
+        "options": [
+            {
+                "domain": "Schema",
+                "exported": ["Domain"]
+            },
+            {
+                "domain": "Runtime",
+                "async": ["evaluate", "awaitPromise", "callFunctionOn", "runScript"],
+                "exported": ["StackTrace", "RemoteObject"]
+            },
+            {
+                "domain": "Debugger",
+                "exported": ["SearchMatch", "paused.reason"]
+            },
+            {
+                "domain": "Console"
+            },
+            {
+                "domain": "Profiler"
+            },
+            {
+                "domain": "HeapProfiler"
+            }
+        ]
     },
 
     "exported": {
@@ -19,7 +43,6 @@
     "lib": {
         "package": "src/inspector/protocol",
         "output": "protocol",
-        "string_header": "src/inspector/string-util.h",
-        "platform_header": "src/inspector/protocol-platform.h"
+        "string_header": "src/inspector/string-util.h"
     }
 }
diff --git a/src/inspector/java-script-call-frame.cc b/src/inspector/java-script-call-frame.cc
index 2da4f04..9847944 100644
--- a/src/inspector/java-script-call-frame.cc
+++ b/src/inspector/java-script-call-frame.cc
@@ -61,18 +61,6 @@
   return result.As<v8::Int32>()->Value();
 }
 
-int JavaScriptCallFrame::sourceID() const {
-  return callV8FunctionReturnInt("sourceID");
-}
-
-int JavaScriptCallFrame::line() const {
-  return callV8FunctionReturnInt("line");
-}
-
-int JavaScriptCallFrame::column() const {
-  return callV8FunctionReturnInt("column");
-}
-
 int JavaScriptCallFrame::contextId() const {
   return callV8FunctionReturnInt("contextId");
 }
@@ -91,7 +79,7 @@
   return result.As<v8::Boolean>()->BooleanValue(context).FromMaybe(false);
 }
 
-v8::Local<v8::Object> JavaScriptCallFrame::details() const {
+v8::MaybeLocal<v8::Object> JavaScriptCallFrame::details() const {
   v8::MicrotasksScope microtasks(m_isolate,
                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
   v8::Local<v8::Context> context =
@@ -101,12 +89,16 @@
   v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(
       callFrame->Get(context, toV8StringInternalized(m_isolate, "details"))
           .ToLocalChecked());
-  return v8::Local<v8::Object>::Cast(
-      func->Call(context, callFrame, 0, nullptr).ToLocalChecked());
+  v8::TryCatch try_catch(m_isolate);
+  v8::Local<v8::Value> details;
+  if (func->Call(context, callFrame, 0, nullptr).ToLocal(&details)) {
+    return v8::Local<v8::Object>::Cast(details);
+  }
+  return v8::MaybeLocal<v8::Object>();
 }
 
 v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate(
-    v8::Local<v8::Value> expression) {
+    v8::Local<v8::Value> expression, bool throwOnSideEffect) {
   v8::MicrotasksScope microtasks(m_isolate,
                                  v8::MicrotasksScope::kRunMicrotasks);
   v8::Local<v8::Context> context =
@@ -116,7 +108,9 @@
   v8::Local<v8::Function> evalFunction = v8::Local<v8::Function>::Cast(
       callFrame->Get(context, toV8StringInternalized(m_isolate, "evaluate"))
           .ToLocalChecked());
-  return evalFunction->Call(context, callFrame, 1, &expression);
+  v8::Local<v8::Value> argv[] = {
+      expression, v8::Boolean::New(m_isolate, throwOnSideEffect)};
+  return evalFunction->Call(context, callFrame, arraysize(argv), argv);
 }
 
 v8::MaybeLocal<v8::Value> JavaScriptCallFrame::restart() {
@@ -129,10 +123,11 @@
   v8::Local<v8::Function> restartFunction = v8::Local<v8::Function>::Cast(
       callFrame->Get(context, toV8StringInternalized(m_isolate, "restart"))
           .ToLocalChecked());
-  v8::DebugInterface::SetLiveEditEnabled(m_isolate, true);
+  v8::TryCatch try_catch(m_isolate);
+  v8::debug::SetLiveEditEnabled(m_isolate, true);
   v8::MaybeLocal<v8::Value> result = restartFunction->Call(
       m_debuggerContext.Get(m_isolate), callFrame, 0, nullptr);
-  v8::DebugInterface::SetLiveEditEnabled(m_isolate, false);
+  v8::debug::SetLiveEditEnabled(m_isolate, false);
   return result;
 }
 
@@ -154,6 +149,7 @@
   v8::Local<v8::Value> argv[] = {
       v8::Local<v8::Value>(v8::Integer::New(m_isolate, scopeNumber)),
       variableName, newValue};
+  v8::TryCatch try_catch(m_isolate);
   return setVariableValueFunction->Call(context, callFrame, arraysize(argv),
                                         argv);
 }
diff --git a/src/inspector/java-script-call-frame.h b/src/inspector/java-script-call-frame.h
index 5a4ce19..b3930c0 100644
--- a/src/inspector/java-script-call-frame.h
+++ b/src/inspector/java-script-call-frame.h
@@ -31,10 +31,10 @@
 #ifndef V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_
 #define V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_
 
+#include <memory>
 #include <vector>
 
 #include "src/base/macros.h"
-#include "src/inspector/protocol-platform.h"
 
 #include "include/v8.h"
 
@@ -44,19 +44,18 @@
  public:
   static std::unique_ptr<JavaScriptCallFrame> create(
       v8::Local<v8::Context> debuggerContext, v8::Local<v8::Object> callFrame) {
-    return wrapUnique(new JavaScriptCallFrame(debuggerContext, callFrame));
+    return std::unique_ptr<JavaScriptCallFrame>(
+        new JavaScriptCallFrame(debuggerContext, callFrame));
   }
   ~JavaScriptCallFrame();
 
-  int sourceID() const;
-  int line() const;
-  int column() const;
   int contextId() const;
 
   bool isAtReturn() const;
-  v8::Local<v8::Object> details() const;
+  v8::MaybeLocal<v8::Object> details() const;
 
-  v8::MaybeLocal<v8::Value> evaluate(v8::Local<v8::Value> expression);
+  v8::MaybeLocal<v8::Value> evaluate(v8::Local<v8::Value> expression,
+                                     bool throwOnSideEffect);
   v8::MaybeLocal<v8::Value> restart();
   v8::MaybeLocal<v8::Value> setVariableValue(int scopeNumber,
                                              v8::Local<v8::Value> variableName,
diff --git a/src/inspector/js_protocol.json b/src/inspector/js_protocol.json
index c1ac585..ef046a5 100644
--- a/src/inspector/js_protocol.json
+++ b/src/inspector/js_protocol.json
@@ -9,7 +9,6 @@
                 "id": "Domain",
                 "type": "object",
                 "description": "Description of the protocol domain.",
-                "exported": true,
                 "properties": [
                     { "name": "name", "type": "string", "description": "Domain name." },
                     { "name": "version", "type": "string", "description": "Domain version." }
@@ -51,7 +50,6 @@
                 "id": "RemoteObject",
                 "type": "object",
                 "description": "Mirror object referencing original JavaScript object.",
-                "exported": true,
                 "properties": [
                     { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." },
                     { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error", "proxy", "promise", "typedarray"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
@@ -200,18 +198,17 @@
                 "id": "StackTrace",
                 "type": "object",
                 "description": "Call frames for assertions or error messages.",
-                "exported": true,
                 "properties": [
                     { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." },
                     { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." },
-                    { "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." }
+                    { "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." },
+                    { "name": "promiseCreationFrame", "$ref": "CallFrame", "optional": true, "experimental": true, "description": "Creation frame of the Promise which produced the next synchronous trace when resolved, if available." }
                 ]
             }
         ],
         "commands": [
             {
                 "name": "evaluate",
-                "async": true,
                 "parameters": [
                     { "name": "expression", "type": "string", "description": "Expression to evaluate." },
                     { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
@@ -231,7 +228,6 @@
             },
             {
                 "name": "awaitPromise",
-                "async": true,
                 "parameters": [
                     { "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." },
                     { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
@@ -245,7 +241,6 @@
             },
             {
                 "name": "callFunctionOn",
-                "async": true,
                 "parameters": [
                     { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." },
                     { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." },
@@ -333,7 +328,6 @@
             },
             {
                 "name": "runScript",
-                "async": true,
                 "parameters": [
                     { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." },
                     { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." },
@@ -390,7 +384,7 @@
                 "name": "consoleAPICalled",
                 "description": "Issued when console API was called.",
                 "parameters": [
-                    { "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Type of the call." },
+                    { "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd", "count", "timeEnd"], "description": "Type of the call." },
                     { "name": "args", "type": "array", "items": { "$ref": "RemoteObject" }, "description": "Call arguments." },
                     { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." },
                     { "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." },
@@ -460,7 +454,7 @@
                 "id": "Scope",
                 "type": "object",
                 "properties": [
-                    { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." },
+                    { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script", "eval", "module"], "description": "Scope type." },
                     { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." },
                     { "name": "name", "type": "string", "optional": true },
                     { "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" },
@@ -472,7 +466,6 @@
                 "id": "SearchMatch",
                 "type": "object",
                 "description": "Search match for resource.",
-                "exported": true,
                 "properties": [
                     { "name": "lineNumber", "type": "number", "description": "Line number in resource content." },
                     { "name": "lineContent", "type": "string", "description": "Line with match content." }
@@ -642,7 +635,8 @@
                     { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." },
                     { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." },
                     { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." },
-                    { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }
+                    { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." },
+                    { "name": "throwOnSideEffect", "type": "boolean", "optional": true, "experimental": true, "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation." }
                 ],
                 "returns": [
                     { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
@@ -700,7 +694,8 @@
                     { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." },
                     { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "experimental": true },
                     { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." },
-                    { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true }
+                    { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true },
+                    { "name": "isModule", "type": "boolean", "optional": true, "description": "True, if this script is ES6 module.", "experimental": true }
                 ],
                 "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
             },
@@ -717,7 +712,8 @@
                     { "name": "hash", "type": "string", "description": "Content hash of the script."},
                     { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." },
                     { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." },
-                    { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true }
+                    { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true },
+                    { "name": "isModule", "type": "boolean", "optional": true, "description": "True, if this script is ES6 module.", "experimental": true }
                 ],
                 "description": "Fired when virtual machine fails to parse the script."
             },
@@ -733,7 +729,7 @@
                 "name": "paused",
                 "parameters": [
                     { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
-                    { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason.", "exported": true },
+                    { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "OOM", "other", "ambiguous" ], "description": "Pause reason." },
                     { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." },
                     { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" },
                     { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }
@@ -828,6 +824,38 @@
                     { "name": "line", "type": "integer", "description": "Source line number (1-based)." },
                     { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." }
                 ]
+            },
+            {   "id": "CoverageRange",
+                "type": "object",
+                "description": "Coverage data for a source range.",
+                "properties": [
+                    { "name": "startLineNumber", "type": "integer", "description": "JavaScript script line number (0-based) for the range start." },
+                    { "name": "startColumnNumber", "type": "integer", "description": "JavaScript script column number (0-based) for the range start." },
+                    { "name": "endLineNumber", "type": "integer", "description": "JavaScript script line number (0-based) for the range end." },
+                    { "name": "endColumnNumber", "type": "integer", "description": "JavaScript script column number (0-based) for the range end." },
+                    { "name": "count", "type": "integer", "description": "Collected execution count of the source range." }
+                ],
+                "experimental": true
+            },
+            {   "id": "FunctionCoverage",
+                "type": "object",
+                "description": "Coverage data for a JavaScript function.",
+                "properties": [
+                    { "name": "functionName", "type": "string", "description": "JavaScript function name." },
+                    { "name": "ranges", "type": "array", "items": { "$ref": "CoverageRange" }, "description": "Source ranges inside the function with coverage data." }
+                ],
+                "experimental": true
+            },
+            {
+                "id": "ScriptCoverage",
+                "type": "object",
+                "description": "Coverage data for a JavaScript script.",
+                "properties": [
+                    { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "JavaScript script id." },
+                    { "name": "url", "type": "string", "description": "JavaScript script name or url." },
+                    { "name": "functions", "type": "array", "items": { "$ref": "FunctionCoverage" }, "description": "Functions contained in the script that has coverage data." }
+                ],
+                "experimental": true
             }
         ],
         "commands": [
@@ -852,6 +880,32 @@
                 "returns": [
                     { "name": "profile", "$ref": "Profile", "description": "Recorded profile." }
                 ]
+            },
+            {
+                "name": "startPreciseCoverage",
+                "description": "Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code coverage may be incomplete. Enabling prevents running optimized code and resets execution counters.",
+                "experimental": true
+            },
+            {
+                "name": "stopPreciseCoverage",
+                "description": "Disable precise code coverage. Disabling releases unnecessary execution count records and allows executing optimized code.",
+                "experimental": true
+            },
+            {
+                "name": "takePreciseCoverage",
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "ScriptCoverage" }, "description": "Coverage data for the current isolate." }
+                ],
+                "description": "Collect coverage data for the current isolate, and resets execution counters. Precise code coverage needs to have started.",
+                "experimental": true
+            },
+            {
+                "name": "getBestEffortCoverage",
+                "returns": [
+                    { "name": "result", "type": "array", "items": { "$ref": "ScriptCoverage" }, "description": "Coverage data for the current isolate." }
+                ],
+                "description": "Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection.",
+                "experimental": true
             }
         ],
         "events": [
diff --git a/src/inspector/protocol-platform.h b/src/inspector/protocol-platform.h
deleted file mode 100644
index c772393..0000000
--- a/src/inspector/protocol-platform.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 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_INSPECTOR_PROTOCOLPLATFORM_H_
-#define V8_INSPECTOR_PROTOCOLPLATFORM_H_
-
-#include <memory>
-
-#include "src/base/logging.h"
-
-namespace v8_inspector {
-
-template <typename T>
-std::unique_ptr<T> wrapUnique(T* ptr) {
-  return std::unique_ptr<T>(ptr);
-}
-
-}  // namespace v8_inspector
-
-#endif  // V8_INSPECTOR_PROTOCOLPLATFORM_H_
diff --git a/src/inspector/remote-object-id.cc b/src/inspector/remote-object-id.cc
index aac6724..2f5f051 100644
--- a/src/inspector/remote-object-id.cc
+++ b/src/inspector/remote-object-id.cc
@@ -13,7 +13,8 @@
 
 std::unique_ptr<protocol::DictionaryValue>
 RemoteObjectIdBase::parseInjectedScriptId(const String16& objectId) {
-  std::unique_ptr<protocol::Value> parsedValue = protocol::parseJSON(objectId);
+  std::unique_ptr<protocol::Value> parsedValue =
+      protocol::StringUtil::parseJSON(objectId);
   if (!parsedValue || parsedValue->type() != protocol::Value::TypeObject)
     return nullptr;
 
diff --git a/src/inspector/script-breakpoint.h b/src/inspector/script-breakpoint.h
index 025233d..a981b16 100644
--- a/src/inspector/script-breakpoint.h
+++ b/src/inspector/script-breakpoint.h
@@ -35,15 +35,18 @@
 namespace v8_inspector {
 
 struct ScriptBreakpoint {
-  ScriptBreakpoint() : ScriptBreakpoint(0, 0, String16()) {}
+  ScriptBreakpoint() {}
 
-  ScriptBreakpoint(int lineNumber, int columnNumber, const String16& condition)
-      : lineNumber(lineNumber),
-        columnNumber(columnNumber),
-        condition(condition) {}
+  ScriptBreakpoint(String16 script_id, int line_number, int column_number,
+                   String16 condition)
+      : script_id(std::move(script_id)),
+        line_number(line_number),
+        column_number(column_number),
+        condition(std::move(condition)) {}
 
-  int lineNumber;
-  int columnNumber;
+  String16 script_id;
+  int line_number = 0;
+  int column_number = 0;
   String16 condition;
 };
 
diff --git a/src/inspector/search-util.cc b/src/inspector/search-util.cc
index a6fba06..b05d7a0 100644
--- a/src/inspector/search-util.cc
+++ b/src/inspector/search-util.cc
@@ -132,7 +132,8 @@
                                            const String16& query,
                                            bool caseSensitive, bool isRegex) {
   String16 regexSource = isRegex ? query : createSearchRegexSource(query);
-  return wrapUnique(new V8Regex(inspector, regexSource, caseSensitive));
+  return std::unique_ptr<V8Regex>(
+      new V8Regex(inspector, regexSource, caseSensitive));
 }
 
 }  // namespace
diff --git a/src/inspector/string-16.cc b/src/inspector/string-16.cc
index 09909a9..6544646 100644
--- a/src/inspector/string-16.cc
+++ b/src/inspector/string-16.cc
@@ -8,14 +8,11 @@
 #include <cctype>
 #include <cstdlib>
 #include <cstring>
-#include <iomanip>
 #include <limits>
-#include <locale>
-#include <sstream>
 #include <string>
 
 #include "src/base/platform/platform.h"
-#include "src/inspector/protocol-platform.h"
+#include "src/conversions.h"
 
 namespace v8_inspector {
 
@@ -367,10 +364,9 @@
 
 // static
 String16 String16::fromInteger(int number) {
-  const size_t kBufferSize = 50;
-  char buffer[kBufferSize];
-  v8::base::OS::SNPrintF(buffer, kBufferSize, "%d", number);
-  return String16(buffer);
+  char arr[50];
+  v8::internal::Vector<char> buffer(arr, arraysize(arr));
+  return String16(IntToCString(number, buffer));
 }
 
 // static
@@ -387,19 +383,16 @@
 
 // static
 String16 String16::fromDouble(double number) {
-  std::ostringstream s;
-  s.imbue(std::locale("C"));
-  s << std::fixed << std::setprecision(std::numeric_limits<double>::digits10)
-    << number;
-  return String16(s.str().c_str());
+  char arr[50];
+  v8::internal::Vector<char> buffer(arr, arraysize(arr));
+  return String16(DoubleToCString(number, buffer));
 }
 
 // static
 String16 String16::fromDouble(double number, int precision) {
-  std::ostringstream s;
-  s.imbue(std::locale("C"));
-  s << std::fixed << std::setprecision(precision) << number;
-  return String16(s.str().c_str());
+  std::unique_ptr<char[]> str(
+      v8::internal::DoubleToPrecisionCString(number, precision));
+  return String16(str.get());
 }
 
 int String16::toInteger(bool* ok) const {
diff --git a/src/inspector/string-16.h b/src/inspector/string-16.h
index 360ec93..0270f51 100644
--- a/src/inspector/string-16.h
+++ b/src/inspector/string-16.h
@@ -23,7 +23,7 @@
   String16() {}
   String16(const String16& other)
       : m_impl(other.m_impl), hash_code(other.hash_code) {}
-  String16(const String16&& other)
+  String16(String16&& other)
       : m_impl(std::move(other.m_impl)), hash_code(other.hash_code) {}
   String16(const UChar* characters, size_t size) : m_impl(characters, size) {}
   String16(const UChar* characters)  // NOLINT(runtime/explicit)
diff --git a/src/inspector/string-util.cc b/src/inspector/string-util.cc
index e6ad5d0..31b2db5 100644
--- a/src/inspector/string-util.cc
+++ b/src/inspector/string-util.cc
@@ -50,8 +50,7 @@
 }
 
 String16 toProtocolString(v8::Local<v8::String> value) {
-  if (value.IsEmpty() || value->IsNull() || value->IsUndefined())
-    return String16();
+  if (value.IsEmpty() || value->IsNullOrUndefined()) return String16();
   std::unique_ptr<UChar[]> buffer(new UChar[value->Length()]);
   value->Write(reinterpret_cast<uint16_t*>(buffer.get()), 0, value->Length());
   return String16(buffer.get(), value->Length());
@@ -93,19 +92,20 @@
 
 namespace protocol {
 
-std::unique_ptr<protocol::Value> parseJSON(const StringView& string) {
+std::unique_ptr<protocol::Value> StringUtil::parseJSON(
+    const StringView& string) {
   if (!string.length()) return nullptr;
   if (string.is8Bit()) {
-    return protocol::parseJSON(string.characters8(),
+    return parseJSONCharacters(string.characters8(),
                                static_cast<int>(string.length()));
   }
-  return protocol::parseJSON(string.characters16(),
+  return parseJSONCharacters(string.characters16(),
                              static_cast<int>(string.length()));
 }
 
-std::unique_ptr<protocol::Value> parseJSON(const String16& string) {
+std::unique_ptr<protocol::Value> StringUtil::parseJSON(const String16& string) {
   if (!string.length()) return nullptr;
-  return protocol::parseJSON(string.characters16(),
+  return parseJSONCharacters(string.characters16(),
                              static_cast<int>(string.length()));
 }
 
@@ -119,7 +119,7 @@
 
 // static
 std::unique_ptr<StringBufferImpl> StringBufferImpl::adopt(String16& string) {
-  return wrapUnique(new StringBufferImpl(string));
+  return std::unique_ptr<StringBufferImpl>(new StringBufferImpl(string));
 }
 
 StringBufferImpl::StringBufferImpl(String16& string) {
diff --git a/src/inspector/string-util.h b/src/inspector/string-util.h
index e1a69e8..6f0e3d5 100644
--- a/src/inspector/string-util.h
+++ b/src/inspector/string-util.h
@@ -5,6 +5,9 @@
 #ifndef V8_INSPECTOR_STRINGUTIL_H_
 #define V8_INSPECTOR_STRINGUTIL_H_
 
+#include <memory>
+
+#include "src/base/logging.h"
 #include "src/base/macros.h"
 #include "src/inspector/string-16.h"
 
@@ -29,15 +32,32 @@
     return String::fromInteger(number);
   }
   static String fromDouble(double number) { return String::fromDouble(number); }
+  static size_t find(const String& s, const char* needle) {
+    return s.find(needle);
+  }
+  static size_t find(const String& s, const String& needle) {
+    return s.find(needle);
+  }
   static const size_t kNotFound = String::kNotFound;
+  static void builderAppend(StringBuilder& builder, const String& s) {
+    builder.append(s);
+  }
+  static void builderAppend(StringBuilder& builder, UChar c) {
+    builder.append(c);
+  }
+  static void builderAppend(StringBuilder& builder, const char* s, size_t len) {
+    builder.append(s, len);
+  }
   static void builderReserve(StringBuilder& builder, size_t capacity) {
     builder.reserveCapacity(capacity);
   }
+  static String builderToString(StringBuilder& builder) {
+    return builder.toString();
+  }
+  static std::unique_ptr<protocol::Value> parseJSON(const String16& json);
+  static std::unique_ptr<protocol::Value> parseJSON(const StringView& json);
 };
 
-std::unique_ptr<protocol::Value> parseJSON(const StringView& json);
-std::unique_ptr<protocol::Value> parseJSON(const String16& json);
-
 }  // namespace protocol
 
 v8::Local<v8::String> toV8String(v8::Isolate*, const String16&);
diff --git a/src/inspector/test-interface.cc b/src/inspector/test-interface.cc
new file mode 100644
index 0000000..ead1dc3
--- /dev/null
+++ b/src/inspector/test-interface.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 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/inspector/test-interface.h"
+
+#include "src/inspector/v8-debugger.h"
+#include "src/inspector/v8-inspector-impl.h"
+
+namespace v8_inspector {
+
+void SetMaxAsyncTaskStacksForTest(V8Inspector* inspector, int limit) {
+  static_cast<V8InspectorImpl*>(inspector)
+      ->debugger()
+      ->setMaxAsyncTaskStacksForTest(limit);
+}
+
+}  //  v8_inspector
diff --git a/src/inspector/test-interface.h b/src/inspector/test-interface.h
new file mode 100644
index 0000000..98bedc2
--- /dev/null
+++ b/src/inspector/test-interface.h
@@ -0,0 +1,18 @@
+// Copyright 2016 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_INSPECTOR_TEST_INTERFACE_H_
+#define V8_INSPECTOR_TEST_INTERFACE_H_
+
+#include "include/v8.h"
+
+namespace v8_inspector {
+
+class V8Inspector;
+
+V8_EXPORT void SetMaxAsyncTaskStacksForTest(V8Inspector* inspector, int limit);
+
+}  //  v8_inspector
+
+#endif  //  V8_INSPECTOR_TEST_INTERFACE_H_
diff --git a/src/inspector/v8-console-message.cc b/src/inspector/v8-console-message.cc
index 63f1d49..73f74e4 100644
--- a/src/inspector/v8-console-message.cc
+++ b/src/inspector/v8-console-message.cc
@@ -4,6 +4,7 @@
 
 #include "src/inspector/v8-console-message.h"
 
+#include "src/debug/debug-interface.h"
 #include "src/inspector/inspected-context.h"
 #include "src/inspector/protocol/Protocol.h"
 #include "src/inspector/string-util.h"
@@ -50,14 +51,15 @@
     case ConsoleAPIType::kAssert:
       return protocol::Runtime::ConsoleAPICalled::TypeEnum::Assert;
     case ConsoleAPIType::kTimeEnd:
-      return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug;
+      return protocol::Runtime::ConsoleAPICalled::TypeEnum::TimeEnd;
     case ConsoleAPIType::kCount:
-      return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug;
+      return protocol::Runtime::ConsoleAPICalled::TypeEnum::Count;
   }
   return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log;
 }
 
 const unsigned maxConsoleMessageCount = 1000;
+const int maxConsoleMessageV8Size = 10 * 1024 * 1024;
 const unsigned maxArrayItemsLimit = 10000;
 const unsigned maxStackDepthLimit = 32;
 
@@ -361,7 +363,7 @@
   V8InspectorImpl* inspector = inspectedContext->inspector();
   v8::Local<v8::Context> context = inspectedContext->context();
 
-  std::unique_ptr<V8ConsoleMessage> message = wrapUnique(
+  std::unique_ptr<V8ConsoleMessage> message(
       new V8ConsoleMessage(V8MessageOrigin::kConsole, timestamp, String16()));
   if (stackTrace && !stackTrace->isEmpty()) {
     message->m_url = toString16(stackTrace->topSourceURL());
@@ -371,28 +373,34 @@
   message->m_stackTrace = std::move(stackTrace);
   message->m_type = type;
   message->m_contextId = contextId;
-  for (size_t i = 0; i < arguments.size(); ++i)
-    message->m_arguments.push_back(
-        wrapUnique(new v8::Global<v8::Value>(isolate, arguments.at(i))));
+  for (size_t i = 0; i < arguments.size(); ++i) {
+    message->m_arguments.push_back(std::unique_ptr<v8::Global<v8::Value>>(
+        new v8::Global<v8::Value>(isolate, arguments.at(i))));
+    message->m_v8Size +=
+        v8::debug::EstimatedValueSize(isolate, arguments.at(i));
+  }
   if (arguments.size())
     message->m_message = V8ValueStringBuilder::toString(arguments[0], context);
 
-  V8ConsoleAPIType clientType = V8ConsoleAPIType::kLog;
+  v8::Isolate::MessageErrorLevel clientLevel = v8::Isolate::kMessageInfo;
   if (type == ConsoleAPIType::kDebug || type == ConsoleAPIType::kCount ||
-      type == ConsoleAPIType::kTimeEnd)
-    clientType = V8ConsoleAPIType::kDebug;
-  else if (type == ConsoleAPIType::kError || type == ConsoleAPIType::kAssert)
-    clientType = V8ConsoleAPIType::kError;
-  else if (type == ConsoleAPIType::kWarning)
-    clientType = V8ConsoleAPIType::kWarning;
-  else if (type == ConsoleAPIType::kInfo)
-    clientType = V8ConsoleAPIType::kInfo;
-  else if (type == ConsoleAPIType::kClear)
-    clientType = V8ConsoleAPIType::kClear;
-  inspector->client()->consoleAPIMessage(
-      contextGroupId, clientType, toStringView(message->m_message),
-      toStringView(message->m_url), message->m_lineNumber,
-      message->m_columnNumber, message->m_stackTrace.get());
+      type == ConsoleAPIType::kTimeEnd) {
+    clientLevel = v8::Isolate::kMessageDebug;
+  } else if (type == ConsoleAPIType::kError ||
+             type == ConsoleAPIType::kAssert) {
+    clientLevel = v8::Isolate::kMessageError;
+  } else if (type == ConsoleAPIType::kWarning) {
+    clientLevel = v8::Isolate::kMessageWarning;
+  } else if (type == ConsoleAPIType::kInfo || type == ConsoleAPIType::kLog) {
+    clientLevel = v8::Isolate::kMessageInfo;
+  }
+
+  if (type != ConsoleAPIType::kClear) {
+    inspector->client()->consoleAPIMessage(
+        contextGroupId, clientLevel, toStringView(message->m_message),
+        toStringView(message->m_url), message->m_lineNumber,
+        message->m_columnNumber, message->m_stackTrace.get());
+  }
 
   return message;
 }
@@ -404,7 +412,7 @@
     std::unique_ptr<V8StackTraceImpl> stackTrace, int scriptId,
     v8::Isolate* isolate, const String16& message, int contextId,
     v8::Local<v8::Value> exception, unsigned exceptionId) {
-  std::unique_ptr<V8ConsoleMessage> consoleMessage = wrapUnique(
+  std::unique_ptr<V8ConsoleMessage> consoleMessage(
       new V8ConsoleMessage(V8MessageOrigin::kException, timestamp, message));
   consoleMessage->setLocation(url, lineNumber, columnNumber,
                               std::move(stackTrace), scriptId);
@@ -413,7 +421,10 @@
   if (contextId && !exception.IsEmpty()) {
     consoleMessage->m_contextId = contextId;
     consoleMessage->m_arguments.push_back(
-        wrapUnique(new v8::Global<v8::Value>(isolate, exception)));
+        std::unique_ptr<v8::Global<v8::Value>>(
+            new v8::Global<v8::Value>(isolate, exception)));
+    consoleMessage->m_v8Size +=
+        v8::debug::EstimatedValueSize(isolate, exception);
   }
   return consoleMessage;
 }
@@ -422,7 +433,7 @@
 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException(
     double timestamp, const String16& messageText,
     unsigned revokedExceptionId) {
-  std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(
+  std::unique_ptr<V8ConsoleMessage> message(new V8ConsoleMessage(
       V8MessageOrigin::kRevokedException, timestamp, messageText));
   message->m_revokedExceptionId = revokedExceptionId;
   return message;
@@ -434,15 +445,14 @@
   if (m_message.isEmpty()) m_message = "<message collected>";
   Arguments empty;
   m_arguments.swap(empty);
+  m_v8Size = 0;
 }
 
 // ------------------------ V8ConsoleMessageStorage ----------------------------
 
 V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector,
                                                  int contextGroupId)
-    : m_inspector(inspector),
-      m_contextGroupId(contextGroupId),
-      m_expiredCount(0) {}
+    : m_inspector(inspector), m_contextGroupId(contextGroupId) {}
 
 V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); }
 
@@ -463,23 +473,33 @@
 
   DCHECK(m_messages.size() <= maxConsoleMessageCount);
   if (m_messages.size() == maxConsoleMessageCount) {
-    ++m_expiredCount;
+    m_estimatedSize -= m_messages.front()->estimatedSize();
     m_messages.pop_front();
   }
+  while (m_estimatedSize + message->estimatedSize() > maxConsoleMessageV8Size &&
+         !m_messages.empty()) {
+    m_estimatedSize -= m_messages.front()->estimatedSize();
+    m_messages.pop_front();
+  }
+
   m_messages.push_back(std::move(message));
+  m_estimatedSize += m_messages.back()->estimatedSize();
 }
 
 void V8ConsoleMessageStorage::clear() {
   m_messages.clear();
-  m_expiredCount = 0;
+  m_estimatedSize = 0;
   if (V8InspectorSessionImpl* session =
           m_inspector->sessionForContextGroup(m_contextGroupId))
     session->releaseObjectGroup("console");
 }
 
 void V8ConsoleMessageStorage::contextDestroyed(int contextId) {
-  for (size_t i = 0; i < m_messages.size(); ++i)
+  m_estimatedSize = 0;
+  for (size_t i = 0; i < m_messages.size(); ++i) {
     m_messages[i]->contextDestroyed(contextId);
+    m_estimatedSize += m_messages[i]->estimatedSize();
+  }
 }
 
 }  // namespace v8_inspector
diff --git a/src/inspector/v8-console-message.h b/src/inspector/v8-console-message.h
index a6e9eaf..8ab81f4 100644
--- a/src/inspector/v8-console-message.h
+++ b/src/inspector/v8-console-message.h
@@ -65,6 +65,10 @@
   ConsoleAPIType type() const;
   void contextDestroyed(int contextId);
 
+  int estimatedSize() const {
+    return m_v8Size + static_cast<int>(m_message.length() * sizeof(UChar));
+  }
+
  private:
   V8ConsoleMessage(V8MessageOrigin, double timestamp, const String16& message);
 
@@ -89,6 +93,7 @@
   ConsoleAPIType m_type;
   unsigned m_exceptionId;
   unsigned m_revokedExceptionId;
+  int m_v8Size = 0;
   Arguments m_arguments;
   String16 m_detailedMessage;
 };
@@ -99,7 +104,6 @@
   ~V8ConsoleMessageStorage();
 
   int contextGroupId() { return m_contextGroupId; }
-  int expiredCount() { return m_expiredCount; }
   const std::deque<std::unique_ptr<V8ConsoleMessage>>& messages() const {
     return m_messages;
   }
@@ -111,7 +115,7 @@
  private:
   V8InspectorImpl* m_inspector;
   int m_contextGroupId;
-  int m_expiredCount;
+  int m_estimatedSize = 0;
   std::deque<std::unique_ptr<V8ConsoleMessage>> m_messages;
 };
 
diff --git a/src/inspector/v8-console.cc b/src/inspector/v8-console.cc
index fee6117..cfe7fc1 100644
--- a/src/inspector/v8-console.cc
+++ b/src/inspector/v8-console.cc
@@ -336,8 +336,14 @@
 }
 
 void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
-  ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kClear,
-                                                    String16("console.clear"));
+  ConsoleHelper helper(info);
+  InspectedContext* context = helper.ensureInspectedContext();
+  if (!context) return;
+  int contextGroupId = context->contextGroupId();
+  if (V8InspectorClient* client = helper.ensureDebuggerClient())
+    client->consoleClear(contextGroupId);
+  helper.reportCallWithDefaultArgument(ConsoleAPIType::kClear,
+                                       String16("console.clear"));
 }
 
 void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -360,8 +366,10 @@
   if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) return;
   int32_t count = helper.getIntFromMap(countMap, identifier, 0) + 1;
   helper.setIntOnMap(countMap, identifier, count);
-  helper.reportCallWithArgument(ConsoleAPIType::kCount,
-                                title + ": " + String16::fromInteger(count));
+  String16 countString = String16::fromInteger(count);
+  helper.reportCallWithArgument(
+      ConsoleAPIType::kCount,
+      title.isEmpty() ? countString : (title + ": " + countString));
 }
 
 void V8Console::assertCallback(
@@ -431,7 +439,7 @@
     double elapsed = client->currentTimeMS() -
                      helper.getDoubleFromMap(timeMap, protocolTitle, 0.0);
     String16 message =
-        protocolTitle + ": " + String16::fromDouble(elapsed, 3) + "ms";
+        protocolTitle + ": " + String16::fromDouble(elapsed) + "ms";
     helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message);
   }
 }
@@ -714,6 +722,29 @@
   createBoundFunctionProperty(context, console, "timeStamp",
                               V8Console::timeStampCallback);
 
+  const char* jsConsoleAssert =
+      "(function(){\n"
+      "  var originAssert = this.assert;\n"
+      "  originAssert.apply = Function.prototype.apply;\n"
+      "  this.assert = assertWrapper;\n"
+      "  assertWrapper.toString = () => originAssert.toString();\n"
+      "  function assertWrapper(){\n"
+      "    if (!!arguments[0]) return;\n"
+      "    originAssert.apply(null, arguments);\n"
+      "  }\n"
+      "})";
+
+  v8::Local<v8::String> assertSource = toV8String(isolate, jsConsoleAssert);
+  V8InspectorImpl* inspector = inspectedContext->inspector();
+  v8::Local<v8::Value> setupFunction;
+  if (inspector->compileAndRunInternalScript(context, assertSource)
+          .ToLocal(&setupFunction) &&
+      setupFunction->IsFunction()) {
+    inspector->callInternalFunction(
+        v8::Local<v8::Function>::Cast(setupFunction), context, console, 0,
+        nullptr);
+  }
+
   if (hasMemoryAttribute)
     console->SetAccessorProperty(
         toV8StringInternalized(isolate, "memory"),
diff --git a/src/inspector/v8-debugger-agent-impl.cc b/src/inspector/v8-debugger-agent-impl.cc
index 224ae28..7de46a1 100644
--- a/src/inspector/v8-debugger-agent-impl.cc
+++ b/src/inspector/v8-debugger-agent-impl.cc
@@ -54,14 +54,33 @@
 
 }  // namespace DebuggerAgentState
 
-static const int kMaxSkipStepFrameCount = 128;
 static const char kBacktraceObjectGroup[] = "backtrace";
 static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
 static const char kDebuggerNotPaused[] =
     "Can only perform operation while paused.";
 
-static String16 breakpointIdSuffix(
-    V8DebuggerAgentImpl::BreakpointSource source) {
+namespace {
+
+void TranslateWasmStackTraceLocations(Array<CallFrame>* stackTrace,
+                                      WasmTranslation* wasmTranslation) {
+  for (size_t i = 0, e = stackTrace->length(); i != e; ++i) {
+    protocol::Debugger::Location* location = stackTrace->get(i)->getLocation();
+    String16 scriptId = location->getScriptId();
+    int lineNumber = location->getLineNumber();
+    int columnNumber = location->getColumnNumber(-1);
+
+    if (!wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
+            &scriptId, &lineNumber, &columnNumber)) {
+      continue;
+    }
+
+    location->setScriptId(std::move(scriptId));
+    location->setLineNumber(lineNumber);
+    location->setColumnNumber(columnNumber);
+  }
+}
+
+String16 breakpointIdSuffix(V8DebuggerAgentImpl::BreakpointSource source) {
   switch (source) {
     case V8DebuggerAgentImpl::UserBreakpointSource:
       break;
@@ -73,26 +92,25 @@
   return String16();
 }
 
-static String16 generateBreakpointId(
-    const String16& scriptId, int lineNumber, int columnNumber,
-    V8DebuggerAgentImpl::BreakpointSource source) {
+String16 generateBreakpointId(const ScriptBreakpoint& breakpoint,
+                              V8DebuggerAgentImpl::BreakpointSource source) {
   String16Builder builder;
-  builder.append(scriptId);
+  builder.append(breakpoint.script_id);
   builder.append(':');
-  builder.appendNumber(lineNumber);
+  builder.appendNumber(breakpoint.line_number);
   builder.append(':');
-  builder.appendNumber(columnNumber);
+  builder.appendNumber(breakpoint.column_number);
   builder.append(breakpointIdSuffix(source));
   return builder.toString();
 }
 
-static bool positionComparator(const std::pair<int, int>& a,
-                               const std::pair<int, int>& b) {
+bool positionComparator(const std::pair<int, int>& a,
+                        const std::pair<int, int>& b) {
   if (a.first != b.first) return a.first < b.first;
   return a.second < b.second;
 }
 
-static std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
+std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
     const String16& scriptId, int lineNumber, int columnNumber) {
   return protocol::Debugger::Location::create()
       .setScriptId(scriptId)
@@ -101,6 +119,8 @@
       .build();
 }
 
+}  // namespace
+
 V8DebuggerAgentImpl::V8DebuggerAgentImpl(
     V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
     protocol::DictionaryValue* state)
@@ -111,24 +131,14 @@
       m_state(state),
       m_frontend(frontendChannel),
       m_isolate(m_inspector->isolate()),
-      m_breakReason(protocol::Debugger::Paused::ReasonEnum::Other),
       m_scheduledDebuggerStep(NoStep),
-      m_skipNextDebuggerStepOut(false),
       m_javaScriptPauseScheduled(false),
-      m_steppingFromFramework(false),
-      m_pausingOnNativeEvent(false),
-      m_skippedStepFrameCount(0),
-      m_recursionLevelForStepOut(0),
-      m_recursionLevelForStepFrame(0),
-      m_skipAllPauses(false) {
-  clearBreakDetails();
+      m_recursionLevelForStepOut(0) {
 }
 
 V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
 
 void V8DebuggerAgentImpl::enableImpl() {
-  // m_inspector->addListener may result in reporting all parsed scripts to
-  // the agent so it should already be in enabled state by then.
   m_enabled = true;
   m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
   m_debugger->enable();
@@ -161,29 +171,25 @@
   m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
                      protocol::DictionaryValue::create());
   m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
-                      v8::DebugInterface::NoBreakOnException);
+                      v8::debug::NoBreakOnException);
   m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
 
-  if (!m_pausedContext.IsEmpty()) m_debugger->continueProgram();
+  if (isPaused()) m_debugger->continueProgram();
   m_debugger->disable();
-  m_pausedContext.Reset();
   JavaScriptCallFrames emptyCallFrames;
   m_pausedCallFrames.swap(emptyCallFrames);
-  m_scripts.clear();
   m_blackboxedPositions.clear();
+  m_blackboxPattern.reset();
+  resetBlackboxedStateCache();
+  m_scripts.clear();
   m_breakpointIdToDebuggerBreakpointIds.clear();
   m_debugger->setAsyncCallStackDepth(this, 0);
   m_continueToLocationBreakpointId = String16();
   clearBreakDetails();
   m_scheduledDebuggerStep = NoStep;
-  m_skipNextDebuggerStepOut = false;
   m_javaScriptPauseScheduled = false;
-  m_steppingFromFramework = false;
-  m_pausingOnNativeEvent = false;
-  m_skippedStepFrameCount = 0;
-  m_recursionLevelForStepFrame = 0;
   m_skipAllPauses = false;
-  m_blackboxPattern = nullptr;
+  m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
   m_state->remove(DebuggerAgentState::blackboxPattern);
   m_enabled = false;
   m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
@@ -199,7 +205,7 @@
 
   enableImpl();
 
-  int pauseState = v8::DebugInterface::NoBreakOnException;
+  int pauseState = v8::debug::NoBreakOnException;
   m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
   setPauseOnExceptionsImpl(pauseState);
 
@@ -225,8 +231,8 @@
 }
 
 Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
+  m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
   m_skipAllPauses = skip;
-  m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses);
   return Response::OK();
 }
 
@@ -291,12 +297,13 @@
       breakpointId, buildObjectForBreakpointCookie(
                         url, lineNumber, columnNumber, condition, isRegex));
 
-  ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
+  ScriptBreakpoint breakpoint(String16(), lineNumber, columnNumber, condition);
   for (const auto& script : m_scripts) {
     if (!matches(m_inspector, script.second->sourceURL(), url, isRegex))
       continue;
-    std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(
-        breakpointId, script.first, breakpoint, UserBreakpointSource);
+    breakpoint.script_id = script.first;
+    std::unique_ptr<protocol::Debugger::Location> location =
+        resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource);
     if (location) (*locations)->addItem(std::move(location));
   }
 
@@ -308,21 +315,18 @@
     std::unique_ptr<protocol::Debugger::Location> location,
     Maybe<String16> optionalCondition, String16* outBreakpointId,
     std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
-  String16 scriptId = location->getScriptId();
-  int lineNumber = location->getLineNumber();
-  int columnNumber = location->getColumnNumber(0);
+  ScriptBreakpoint breakpoint(
+      location->getScriptId(), location->getLineNumber(),
+      location->getColumnNumber(0), optionalCondition.fromMaybe(String16()));
 
-  String16 condition = optionalCondition.fromMaybe("");
-
-  String16 breakpointId = generateBreakpointId(
-      scriptId, lineNumber, columnNumber, UserBreakpointSource);
+  String16 breakpointId =
+      generateBreakpointId(breakpoint, UserBreakpointSource);
   if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
       m_breakpointIdToDebuggerBreakpointIds.end()) {
     return Response::Error("Breakpoint at specified location already exists.");
   }
-  ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
-  *actualLocation = resolveBreakpoint(breakpointId, scriptId, breakpoint,
-                                      UserBreakpointSource);
+  *actualLocation =
+      resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource);
   if (!*actualLocation) return Response::Error("Could not resolve breakpoint");
   *outBreakpointId = breakpointId;
   return Response::OK();
@@ -365,9 +369,9 @@
     return Response::Error(
         "start.lineNumber and start.columnNumber should be >= 0");
 
-  v8::DebugInterface::Location v8Start(start->getLineNumber(),
-                                       start->getColumnNumber(0));
-  v8::DebugInterface::Location v8End;
+  v8::debug::Location v8Start(start->getLineNumber(),
+                              start->getColumnNumber(0));
+  v8::debug::Location v8End;
   if (end.isJust()) {
     if (end.fromJust()->getScriptId() != scriptId)
       return Response::Error("Locations should contain the same scriptId");
@@ -376,12 +380,12 @@
     if (line < 0 || column < 0)
       return Response::Error(
           "end.lineNumber and end.columnNumber should be >= 0");
-    v8End = v8::DebugInterface::Location(line, column);
+    v8End = v8::debug::Location(line, column);
   }
   auto it = m_scripts.find(scriptId);
   if (it == m_scripts.end()) return Response::Error("Script not found");
 
-  std::vector<v8::DebugInterface::Location> v8Locations;
+  std::vector<v8::debug::Location> v8Locations;
   if (!it->second->getPossibleBreakpoints(v8Start, v8End, &v8Locations))
     return Response::InternalError();
 
@@ -405,38 +409,20 @@
     m_continueToLocationBreakpointId = "";
   }
 
-  String16 scriptId = location->getScriptId();
-  int lineNumber = location->getLineNumber();
-  int columnNumber = location->getColumnNumber(0);
+  ScriptBreakpoint breakpoint(location->getScriptId(),
+                              location->getLineNumber(),
+                              location->getColumnNumber(0), String16());
 
-  ScriptBreakpoint breakpoint(lineNumber, columnNumber, "");
   m_continueToLocationBreakpointId = m_debugger->setBreakpoint(
-      scriptId, breakpoint, &lineNumber, &columnNumber);
+      breakpoint, &breakpoint.line_number, &breakpoint.column_number);
+  // TODO(kozyatinskiy): Return actual line and column number.
   return resume();
 }
 
-bool V8DebuggerAgentImpl::isCurrentCallStackEmptyOrBlackboxed() {
-  DCHECK(enabled());
-  JavaScriptCallFrames callFrames = m_debugger->currentCallFrames();
-  for (size_t index = 0; index < callFrames.size(); ++index) {
-    if (!isCallFrameWithUnknownScriptOrBlackboxed(callFrames[index].get()))
-      return false;
-  }
-  return true;
-}
-
-bool V8DebuggerAgentImpl::isTopPausedCallFrameBlackboxed() {
-  DCHECK(enabled());
-  JavaScriptCallFrame* frame =
-      m_pausedCallFrames.size() ? m_pausedCallFrames[0].get() : nullptr;
-  return isCallFrameWithUnknownScriptOrBlackboxed(frame);
-}
-
-bool V8DebuggerAgentImpl::isCallFrameWithUnknownScriptOrBlackboxed(
-    JavaScriptCallFrame* frame) {
-  if (!frame) return true;
-  ScriptsMap::iterator it =
-      m_scripts.find(String16::fromInteger(frame->sourceID()));
+bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
+                                               const v8::debug::Location& start,
+                                               const v8::debug::Location& end) {
+  ScriptsMap::iterator it = m_scripts.find(scriptId);
   if (it == m_scripts.end()) {
     // Unknown scripts are blackboxed.
     return true;
@@ -447,78 +433,65 @@
         m_blackboxPattern->match(scriptSourceURL) != -1)
       return true;
   }
-  auto itBlackboxedPositions =
-      m_blackboxedPositions.find(String16::fromInteger(frame->sourceID()));
+  auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
   if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
 
   const std::vector<std::pair<int, int>>& ranges =
       itBlackboxedPositions->second;
-  auto itRange = std::lower_bound(
+  auto itStartRange = std::lower_bound(
       ranges.begin(), ranges.end(),
-      std::make_pair(frame->line(), frame->column()), positionComparator);
+      std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
+      positionComparator);
+  auto itEndRange = std::lower_bound(
+      itStartRange, ranges.end(),
+      std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
+      positionComparator);
   // Ranges array contains positions in script where blackbox state is changed.
   // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
   // blackboxed...
-  return std::distance(ranges.begin(), itRange) % 2;
-}
-
-V8DebuggerAgentImpl::SkipPauseRequest
-V8DebuggerAgentImpl::shouldSkipExceptionPause(
-    JavaScriptCallFrame* topCallFrame) {
-  if (m_steppingFromFramework) return RequestNoSkip;
-  if (isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame))
-    return RequestContinue;
-  return RequestNoSkip;
-}
-
-V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::shouldSkipStepPause(
-    JavaScriptCallFrame* topCallFrame) {
-  if (m_steppingFromFramework) return RequestNoSkip;
-
-  if (m_skipNextDebuggerStepOut) {
-    m_skipNextDebuggerStepOut = false;
-    if (m_scheduledDebuggerStep == StepOut) return RequestStepOut;
-  }
-
-  if (!isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame))
-    return RequestNoSkip;
-
-  if (m_skippedStepFrameCount >= kMaxSkipStepFrameCount) return RequestStepOut;
-
-  if (!m_skippedStepFrameCount) m_recursionLevelForStepFrame = 1;
-
-  ++m_skippedStepFrameCount;
-  return RequestStepFrame;
+  return itStartRange == itEndRange &&
+         std::distance(ranges.begin(), itStartRange) % 2;
 }
 
 std::unique_ptr<protocol::Debugger::Location>
 V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
-                                       const String16& scriptId,
                                        const ScriptBreakpoint& breakpoint,
                                        BreakpointSource source) {
+  v8::HandleScope handles(m_isolate);
   DCHECK(enabled());
   // FIXME: remove these checks once crbug.com/520702 is resolved.
   CHECK(!breakpointId.isEmpty());
-  CHECK(!scriptId.isEmpty());
-  ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
+  CHECK(!breakpoint.script_id.isEmpty());
+  ScriptsMap::iterator scriptIterator = m_scripts.find(breakpoint.script_id);
   if (scriptIterator == m_scripts.end()) return nullptr;
-  if (breakpoint.lineNumber < scriptIterator->second->startLine() ||
-      scriptIterator->second->endLine() < breakpoint.lineNumber)
+  if (breakpoint.line_number < scriptIterator->second->startLine() ||
+      scriptIterator->second->endLine() < breakpoint.line_number)
     return nullptr;
 
+  // Translate from protocol location to v8 location for the debugger.
+  ScriptBreakpoint translatedBreakpoint = breakpoint;
+  m_debugger->wasmTranslation()->TranslateProtocolLocationToWasmScriptLocation(
+      &translatedBreakpoint.script_id, &translatedBreakpoint.line_number,
+      &translatedBreakpoint.column_number);
+
   int actualLineNumber;
   int actualColumnNumber;
   String16 debuggerBreakpointId = m_debugger->setBreakpoint(
-      scriptId, breakpoint, &actualLineNumber, &actualColumnNumber);
+      translatedBreakpoint, &actualLineNumber, &actualColumnNumber);
   if (debuggerBreakpointId.isEmpty()) return nullptr;
 
+  // Translate back from v8 location to protocol location for the return value.
+  m_debugger->wasmTranslation()->TranslateWasmScriptLocationToProtocolLocation(
+      &translatedBreakpoint.script_id, &actualLineNumber, &actualColumnNumber);
+
   m_serverBreakpoints[debuggerBreakpointId] =
       std::make_pair(breakpointId, source);
   CHECK(!breakpointId.isEmpty());
 
   m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
       debuggerBreakpointId);
-  return buildProtocolLocation(scriptId, actualLineNumber, actualColumnNumber);
+  return buildProtocolLocation(translatedBreakpoint.script_id, actualLineNumber,
+                               actualColumnNumber);
 }
 
 Response V8DebuggerAgentImpl::searchInContent(
@@ -531,9 +504,8 @@
     return Response::Error("No script for id: " + scriptId);
 
   std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
-      searchInTextByLinesImpl(m_session,
-                              toProtocolString(it->second->source(m_isolate)),
-                              query, optionalCaseSensitive.fromMaybe(false),
+      searchInTextByLinesImpl(m_session, it->second->source(m_isolate), query,
+                              optionalCaseSensitive.fromMaybe(false),
                               optionalIsRegex.fromMaybe(false));
   *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
   for (size_t i = 0; i < matches.size(); ++i)
@@ -548,6 +520,15 @@
     Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
 
+  ScriptsMap::iterator it = m_scripts.find(scriptId);
+  if (it == m_scripts.end()) {
+    return Response::Error("No script with given id found");
+  }
+  if (it->second->isModule()) {
+    // TODO(kozyatinskiy): LiveEdit should support ES6 module
+    return Response::Error("Editing module's script is not supported.");
+  }
+
   v8::HandleScope handles(m_isolate);
   v8::Local<v8::String> newSource = toV8String(m_isolate, newContent);
   bool compileError = false;
@@ -556,9 +537,7 @@
       &m_pausedCallFrames, stackChanged, &compileError);
   if (!response.isSuccess() || compileError) return response;
 
-  ScriptsMap::iterator it = m_scripts.find(scriptId);
-  if (it != m_scripts.end()) it->second->setSource(newSource);
-
+  it->second->setSource(newSource);
   std::unique_ptr<Array<CallFrame>> callFrames;
   response = currentCallFrames(&callFrames);
   if (!response.isSuccess()) return response;
@@ -571,7 +550,7 @@
     const String16& callFrameId,
     std::unique_ptr<Array<CallFrame>>* newCallFrames,
     Maybe<StackTrace>* asyncStackTrace) {
-  if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
+  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
   InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
                                        callFrameId);
   Response response = scope.initialize();
@@ -604,93 +583,93 @@
   if (it == m_scripts.end())
     return Response::Error("No script for id: " + scriptId);
   v8::HandleScope handles(m_isolate);
-  *scriptSource = toProtocolString(it->second->source(m_isolate));
+  *scriptSource = it->second->source(m_isolate);
   return Response::OK();
 }
 
+void V8DebuggerAgentImpl::pushBreakDetails(
+    const String16& breakReason,
+    std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
+  m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
+}
+
+void V8DebuggerAgentImpl::popBreakDetails() {
+  if (m_breakReason.empty()) return;
+  m_breakReason.pop_back();
+}
+
+void V8DebuggerAgentImpl::clearBreakDetails() {
+  std::vector<BreakReason> emptyBreakReason;
+  m_breakReason.swap(emptyBreakReason);
+}
+
 void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
     const String16& breakReason,
     std::unique_ptr<protocol::DictionaryValue> data) {
   if (!enabled() || m_scheduledDebuggerStep == StepInto ||
-      m_javaScriptPauseScheduled || m_debugger->isPaused() ||
+      m_javaScriptPauseScheduled || isPaused() ||
       !m_debugger->breakpointsActivated())
     return;
-  m_breakReason = breakReason;
-  m_breakAuxData = std::move(data);
-  m_pausingOnNativeEvent = true;
-  m_skipNextDebuggerStepOut = false;
-  m_debugger->setPauseOnNextStatement(true);
+  if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(true);
+  pushBreakDetails(breakReason, std::move(data));
 }
 
 void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() {
   DCHECK(enabled());
   if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled ||
-      m_debugger->isPaused())
+      isPaused())
     return;
-  clearBreakDetails();
-  m_pausingOnNativeEvent = false;
-  m_skippedStepFrameCount = 0;
-  m_recursionLevelForStepFrame = 0;
   m_debugger->setPauseOnNextStatement(true);
 }
 
 void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
-  if (m_javaScriptPauseScheduled || m_debugger->isPaused()) return;
-  clearBreakDetails();
-  m_pausingOnNativeEvent = false;
-  m_debugger->setPauseOnNextStatement(false);
+  if (m_javaScriptPauseScheduled || isPaused()) return;
+  popBreakDetails();
+  if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(false);
 }
 
 Response V8DebuggerAgentImpl::pause() {
   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  if (m_javaScriptPauseScheduled || m_debugger->isPaused())
-    return Response::OK();
+  if (m_javaScriptPauseScheduled || isPaused()) return Response::OK();
   clearBreakDetails();
   m_javaScriptPauseScheduled = true;
   m_scheduledDebuggerStep = NoStep;
-  m_skippedStepFrameCount = 0;
-  m_steppingFromFramework = false;
   m_debugger->setPauseOnNextStatement(true);
   return Response::OK();
 }
 
 Response V8DebuggerAgentImpl::resume() {
-  if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
+  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
   m_scheduledDebuggerStep = NoStep;
-  m_steppingFromFramework = false;
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
   m_debugger->continueProgram();
   return Response::OK();
 }
 
 Response V8DebuggerAgentImpl::stepOver() {
-  if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
+  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
   // StepOver at function return point should fallback to StepInto.
   JavaScriptCallFrame* frame =
       !m_pausedCallFrames.empty() ? m_pausedCallFrames[0].get() : nullptr;
   if (frame && frame->isAtReturn()) return stepInto();
   m_scheduledDebuggerStep = StepOver;
-  m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
   m_debugger->stepOverStatement();
   return Response::OK();
 }
 
 Response V8DebuggerAgentImpl::stepInto() {
-  if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
+  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
   m_scheduledDebuggerStep = StepInto;
-  m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
   m_debugger->stepIntoStatement();
   return Response::OK();
 }
 
 Response V8DebuggerAgentImpl::stepOut() {
-  if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
+  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
   m_scheduledDebuggerStep = StepOut;
-  m_skipNextDebuggerStepOut = false;
   m_recursionLevelForStepOut = 1;
-  m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
   m_debugger->stepOutOfFunction();
   return Response::OK();
@@ -699,13 +678,13 @@
 Response V8DebuggerAgentImpl::setPauseOnExceptions(
     const String16& stringPauseState) {
   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  v8::DebugInterface::ExceptionBreakState pauseState;
+  v8::debug::ExceptionBreakState pauseState;
   if (stringPauseState == "none") {
-    pauseState = v8::DebugInterface::NoBreakOnException;
+    pauseState = v8::debug::NoBreakOnException;
   } else if (stringPauseState == "all") {
-    pauseState = v8::DebugInterface::BreakOnAnyException;
+    pauseState = v8::debug::BreakOnAnyException;
   } else if (stringPauseState == "uncaught") {
-    pauseState = v8::DebugInterface::BreakOnUncaughtException;
+    pauseState = v8::debug::BreakOnUncaughtException;
   } else {
     return Response::Error("Unknown pause on exceptions mode: " +
                            stringPauseState);
@@ -716,7 +695,7 @@
 
 void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
   m_debugger->setPauseOnExceptionsState(
-      static_cast<v8::DebugInterface::ExceptionBreakState>(pauseState));
+      static_cast<v8::debug::ExceptionBreakState>(pauseState));
   m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
 }
 
@@ -724,9 +703,9 @@
     const String16& callFrameId, const String16& expression,
     Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
     Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
-    std::unique_ptr<RemoteObject>* result,
+    Maybe<bool> throwOnSideEffect, std::unique_ptr<RemoteObject>* result,
     Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
-  if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
+  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
   InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
                                        callFrameId);
   Response response = scope.initialize();
@@ -739,7 +718,8 @@
 
   v8::MaybeLocal<v8::Value> maybeResultValue =
       m_pausedCallFrames[scope.frameOrdinal()]->evaluate(
-          toV8String(m_isolate, expression));
+          toV8String(m_isolate, expression),
+          throwOnSideEffect.fromMaybe(false));
 
   // Re-initialize after running client's code, as it could have destroyed
   // context or session.
@@ -756,7 +736,7 @@
     std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
     const String16& callFrameId) {
   if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
+  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
   InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
                                        callFrameId);
   Response response = scope.initialize();
@@ -787,6 +767,7 @@
     std::unique_ptr<protocol::Array<String16>> patterns) {
   if (!patterns->length()) {
     m_blackboxPattern = nullptr;
+    resetBlackboxedStateCache();
     m_state->remove(DebuggerAgentState::blackboxPattern);
     return Response::OK();
   }
@@ -802,6 +783,7 @@
   String16 pattern = patternBuilder.toString();
   Response response = setBlackboxPattern(pattern);
   if (!response.isSuccess()) return response;
+  resetBlackboxedStateCache();
   m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
   return Response::OK();
 }
@@ -815,15 +797,23 @@
   return Response::OK();
 }
 
+void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
+  for (const auto& it : m_scripts) {
+    it.second->resetBlackboxedStateCache();
+  }
+}
+
 Response V8DebuggerAgentImpl::setBlackboxedRanges(
     const String16& scriptId,
     std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
         inPositions) {
-  if (m_scripts.find(scriptId) == m_scripts.end())
+  auto it = m_scripts.find(scriptId);
+  if (it == m_scripts.end())
     return Response::Error("No script with passed id.");
 
   if (!inPositions->length()) {
     m_blackboxedPositions.erase(scriptId);
+    it->second->resetBlackboxedStateCache();
     return Response::OK();
   }
 
@@ -849,12 +839,12 @@
   }
 
   m_blackboxedPositions[scriptId] = positions;
+  it->second->resetBlackboxedStateCache();
   return Response::OK();
 }
 
 void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
   changeJavaScriptRecursionLevel(+1);
-  // Fast return.
   if (m_scheduledDebuggerStep != StepInto) return;
   schedulePauseOnNextStatementIfSteppingInto();
 }
@@ -864,8 +854,7 @@
 }
 
 void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) {
-  if (m_javaScriptPauseScheduled && !m_skipAllPauses &&
-      !m_debugger->isPaused()) {
+  if (m_javaScriptPauseScheduled && !m_skipAllPauses && !isPaused()) {
     // Do not ever loose user's pause request until we have actually paused.
     m_debugger->setPauseOnNextStatement(true);
   }
@@ -877,40 +866,19 @@
       // switch stepping to step into a next JS task, as if we exited to a
       // blackboxed framework.
       m_scheduledDebuggerStep = StepInto;
-      m_skipNextDebuggerStepOut = false;
-    }
-  }
-  if (m_recursionLevelForStepFrame) {
-    m_recursionLevelForStepFrame += step;
-    if (!m_recursionLevelForStepFrame) {
-      // We have walked through a blackboxed framework and got back to where we
-      // started.
-      // If there was no stepping scheduled, we should cancel the stepping
-      // explicitly,
-      // since there may be a scheduled StepFrame left.
-      // Otherwise, if we were stepping in/over, the StepFrame will stop at the
-      // right location,
-      // whereas if we were stepping out, we should continue doing so after
-      // debugger pauses
-      // from the old StepFrame.
-      m_skippedStepFrameCount = 0;
-      if (m_scheduledDebuggerStep == NoStep)
-        m_debugger->clearStepping();
-      else if (m_scheduledDebuggerStep == StepOut)
-        m_skipNextDebuggerStepOut = true;
     }
   }
 }
 
 Response V8DebuggerAgentImpl::currentCallFrames(
     std::unique_ptr<Array<CallFrame>>* result) {
-  if (m_pausedContext.IsEmpty() || !m_pausedCallFrames.size()) {
+  if (!isPaused()) {
     *result = Array<CallFrame>::create();
     return Response::OK();
   }
   v8::HandleScope handles(m_isolate);
   v8::Local<v8::Context> debuggerContext =
-      v8::DebugInterface::GetDebugContext(m_isolate);
+      v8::debug::GetDebugContext(m_isolate);
   v8::Context::Scope contextScope(debuggerContext);
 
   v8::Local<v8::Array> objects = v8::Array::New(m_isolate);
@@ -920,8 +888,9 @@
     const std::unique_ptr<JavaScriptCallFrame>& currentCallFrame =
         m_pausedCallFrames[frameOrdinal];
 
-    v8::Local<v8::Object> details = currentCallFrame->details();
-    if (details.IsEmpty()) return Response::InternalError();
+    v8::Local<v8::Object> details;
+    if (!currentCallFrame->details().ToLocal(&details))
+      return Response::InternalError();
 
     int contextId = currentCallFrame->contextId();
 
@@ -1004,54 +973,77 @@
   Response response = toProtocolValue(debuggerContext, objects, &protocolValue);
   if (!response.isSuccess()) return response;
   protocol::ErrorSupport errorSupport;
-  *result = Array<CallFrame>::parse(protocolValue.get(), &errorSupport);
+  *result = Array<CallFrame>::fromValue(protocolValue.get(), &errorSupport);
   if (!*result) return Response::Error(errorSupport.errors());
+  TranslateWasmStackTraceLocations(result->get(),
+                                   m_debugger->wasmTranslation());
   return Response::OK();
 }
 
 std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
-  if (m_pausedContext.IsEmpty()) return nullptr;
+  if (!isPaused()) return nullptr;
   V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain();
   return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
                     : nullptr;
 }
 
+bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
+
 void V8DebuggerAgentImpl::didParseSource(
     std::unique_ptr<V8DebuggerScript> script, bool success) {
   v8::HandleScope handles(m_isolate);
-  String16 scriptSource = toProtocolString(script->source(m_isolate));
+  String16 scriptSource = script->source(m_isolate);
   if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
   if (!success)
     script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
 
+  int contextId = script->executionContextId();
+  int contextGroupId = m_inspector->contextGroupId(contextId);
+  InspectedContext* inspected =
+      m_inspector->getContext(contextGroupId, contextId);
   std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
-  if (!script->executionContextAuxData().isEmpty())
+  if (inspected) {
+    // Script reused between different groups/sessions can have a stale
+    // execution context id.
     executionContextAuxData = protocol::DictionaryValue::cast(
-        protocol::parseJSON(script->executionContextAuxData()));
+        protocol::StringUtil::parseJSON(inspected->auxData()));
+  }
   bool isLiveEdit = script->isLiveEdit();
   bool hasSourceURL = script->hasSourceURL();
+  bool isModule = script->isModule();
   String16 scriptId = script->scriptId();
   String16 scriptURL = script->sourceURL();
 
-  Maybe<String16> sourceMapURLParam = script->sourceMappingURL();
+  m_scripts[scriptId] = std::move(script);
+
+  ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
+  DCHECK(scriptIterator != m_scripts.end());
+  V8DebuggerScript* scriptRef = scriptIterator->second.get();
+  // V8 could create functions for parsed scripts before reporting and asks
+  // inspector about blackboxed state, we should reset state each time when we
+  // make any change that change isFunctionBlackboxed output - adding parsed
+  // script is changing.
+  scriptRef->resetBlackboxedStateCache();
+
+  Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
   Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
       std::move(executionContextAuxData));
   const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
   const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
+  const bool* isModuleParam = isModule ? &isModule : nullptr;
   if (success)
     m_frontend.scriptParsed(
-        scriptId, scriptURL, script->startLine(), script->startColumn(),
-        script->endLine(), script->endColumn(), script->executionContextId(),
-        script->hash(), std::move(executionContextAuxDataParam),
-        isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam);
+        scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
+        scriptRef->endLine(), scriptRef->endColumn(), contextId,
+        scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam),
+        isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
+        isModuleParam);
   else
     m_frontend.scriptFailedToParse(
-        scriptId, scriptURL, script->startLine(), script->startColumn(),
-        script->endLine(), script->endColumn(), script->executionContextId(),
-        script->hash(), std::move(executionContextAuxDataParam),
-        std::move(sourceMapURLParam), hasSourceURLParam);
-
-  m_scripts[scriptId] = std::move(script);
+        scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
+        scriptRef->endLine(), scriptRef->endColumn(), contextId,
+        scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam),
+        std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam);
 
   if (scriptURL.isEmpty() || !success) return;
 
@@ -1069,76 +1061,60 @@
     breakpointObject->getString(DebuggerAgentState::url, &url);
     if (!matches(m_inspector, scriptURL, url, isRegex)) continue;
     ScriptBreakpoint breakpoint;
+    breakpoint.script_id = scriptId;
     breakpointObject->getInteger(DebuggerAgentState::lineNumber,
-                                 &breakpoint.lineNumber);
+                                 &breakpoint.line_number);
     breakpointObject->getInteger(DebuggerAgentState::columnNumber,
-                                 &breakpoint.columnNumber);
+                                 &breakpoint.column_number);
     breakpointObject->getString(DebuggerAgentState::condition,
                                 &breakpoint.condition);
-    std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(
-        cookie.first, scriptId, breakpoint, UserBreakpointSource);
+    std::unique_ptr<protocol::Debugger::Location> location =
+        resolveBreakpoint(cookie.first, breakpoint, UserBreakpointSource);
     if (location)
       m_frontend.breakpointResolved(cookie.first, std::move(location));
   }
 }
 
-V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(
-    v8::Local<v8::Context> context, v8::Local<v8::Value> exception,
-    const std::vector<String16>& hitBreakpoints, bool isPromiseRejection,
-    bool isUncaught) {
-  JavaScriptCallFrames callFrames = m_debugger->currentCallFrames(1);
-  JavaScriptCallFrame* topCallFrame =
-      !callFrames.empty() ? callFrames.begin()->get() : nullptr;
-
-  V8DebuggerAgentImpl::SkipPauseRequest result;
-  if (m_skipAllPauses)
-    result = RequestContinue;
-  else if (!hitBreakpoints.empty())
-    result = RequestNoSkip;  // Don't skip explicit breakpoints even if set in
-                             // frameworks.
-  else if (!exception.IsEmpty())
-    result = shouldSkipExceptionPause(topCallFrame);
-  else if (m_scheduledDebuggerStep != NoStep || m_javaScriptPauseScheduled ||
-           m_pausingOnNativeEvent)
-    result = shouldSkipStepPause(topCallFrame);
-  else
-    result = RequestNoSkip;
-
-  m_skipNextDebuggerStepOut = false;
-  if (result != RequestNoSkip) return result;
-  // Skip pauses inside V8 internal scripts and on syntax errors.
-  if (!topCallFrame) return RequestContinue;
-
-  DCHECK(m_pausedContext.IsEmpty());
+void V8DebuggerAgentImpl::didPause(int contextId,
+                                   v8::Local<v8::Value> exception,
+                                   const std::vector<String16>& hitBreakpoints,
+                                   bool isPromiseRejection, bool isUncaught,
+                                   bool isOOMBreak) {
   JavaScriptCallFrames frames = m_debugger->currentCallFrames();
   m_pausedCallFrames.swap(frames);
-  m_pausedContext.Reset(m_isolate, context);
   v8::HandleScope handles(m_isolate);
 
-  if (!exception.IsEmpty()) {
+  std::vector<BreakReason> hitReasons;
+
+  if (isOOMBreak) {
+    hitReasons.push_back(
+        std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
+  } else if (!exception.IsEmpty()) {
     InjectedScript* injectedScript = nullptr;
-    m_session->findInjectedScript(V8Debugger::contextId(context),
-                                  injectedScript);
+    m_session->findInjectedScript(contextId, injectedScript);
     if (injectedScript) {
-      m_breakReason =
+      String16 breakReason =
           isPromiseRejection
               ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
               : protocol::Debugger::Paused::ReasonEnum::Exception;
       std::unique_ptr<protocol::Runtime::RemoteObject> obj;
       injectedScript->wrapObject(exception, kBacktraceObjectGroup, false, false,
                                  &obj);
+      std::unique_ptr<protocol::DictionaryValue> breakAuxData;
       if (obj) {
-        m_breakAuxData = obj->serialize();
-        m_breakAuxData->setBoolean("uncaught", isUncaught);
+        breakAuxData = obj->toValue();
+        breakAuxData->setBoolean("uncaught", isUncaught);
       } else {
-        m_breakAuxData = nullptr;
+        breakAuxData = nullptr;
       }
-      // m_breakAuxData might be null after this.
+      hitReasons.push_back(
+          std::make_pair(breakReason, std::move(breakAuxData)));
     }
   }
 
   std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
 
+  bool hasDebugCommandBreakpointReason = false;
   for (const auto& point : hitBreakpoints) {
     DebugServerBreakpointToBreakpointIdAndSourceMap::iterator
         breakpointIterator = m_serverBreakpoints.find(point);
@@ -1147,34 +1123,57 @@
       hitBreakpointIds->addItem(localId);
 
       BreakpointSource source = breakpointIterator->second.second;
-      if (m_breakReason == protocol::Debugger::Paused::ReasonEnum::Other &&
-          source == DebugCommandBreakpointSource)
-        m_breakReason = protocol::Debugger::Paused::ReasonEnum::DebugCommand;
+      if (!hasDebugCommandBreakpointReason &&
+          source == DebugCommandBreakpointSource) {
+        hasDebugCommandBreakpointReason = true;
+        hitReasons.push_back(std::make_pair(
+            protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
+      }
     }
   }
 
+  for (size_t i = 0; i < m_breakReason.size(); ++i) {
+    hitReasons.push_back(std::move(m_breakReason[i]));
+  }
+  clearBreakDetails();
+
+  String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
+  std::unique_ptr<protocol::DictionaryValue> breakAuxData;
+  if (hitReasons.size() == 1) {
+    breakReason = hitReasons[0].first;
+    breakAuxData = std::move(hitReasons[0].second);
+  } else if (hitReasons.size() > 1) {
+    breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
+    std::unique_ptr<protocol::ListValue> reasons =
+        protocol::ListValue::create();
+    for (size_t i = 0; i < hitReasons.size(); ++i) {
+      std::unique_ptr<protocol::DictionaryValue> reason =
+          protocol::DictionaryValue::create();
+      reason->setString("reason", hitReasons[i].first);
+      if (hitReasons[i].second)
+        reason->setObject("auxData", std::move(hitReasons[i].second));
+      reasons->pushValue(std::move(reason));
+    }
+    breakAuxData = protocol::DictionaryValue::create();
+    breakAuxData->setArray("reasons", std::move(reasons));
+  }
+
   std::unique_ptr<Array<CallFrame>> protocolCallFrames;
   Response response = currentCallFrames(&protocolCallFrames);
   if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
-  m_frontend.paused(std::move(protocolCallFrames), m_breakReason,
-                    std::move(m_breakAuxData), std::move(hitBreakpointIds),
+  m_frontend.paused(std::move(protocolCallFrames), breakReason,
+                    std::move(breakAuxData), std::move(hitBreakpointIds),
                     currentAsyncStackTrace());
   m_scheduledDebuggerStep = NoStep;
   m_javaScriptPauseScheduled = false;
-  m_steppingFromFramework = false;
-  m_pausingOnNativeEvent = false;
-  m_skippedStepFrameCount = 0;
-  m_recursionLevelForStepFrame = 0;
 
   if (!m_continueToLocationBreakpointId.isEmpty()) {
     m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
     m_continueToLocationBreakpointId = "";
   }
-  return result;
 }
 
 void V8DebuggerAgentImpl::didContinue() {
-  m_pausedContext.Reset();
   JavaScriptCallFrames emptyCallFrames;
   m_pausedCallFrames.swap(emptyCallFrames);
   clearBreakDetails();
@@ -1184,55 +1183,48 @@
 void V8DebuggerAgentImpl::breakProgram(
     const String16& breakReason,
     std::unique_ptr<protocol::DictionaryValue> data) {
-  if (!enabled() || m_skipAllPauses || !m_pausedContext.IsEmpty() ||
-      isCurrentCallStackEmptyOrBlackboxed() ||
-      !m_debugger->breakpointsActivated())
-    return;
-  m_breakReason = breakReason;
-  m_breakAuxData = std::move(data);
+  if (!enabled() || !m_debugger->canBreakProgram() || m_skipAllPauses) return;
+  std::vector<BreakReason> currentScheduledReason;
+  currentScheduledReason.swap(m_breakReason);
+  pushBreakDetails(breakReason, std::move(data));
   m_scheduledDebuggerStep = NoStep;
-  m_steppingFromFramework = false;
-  m_pausingOnNativeEvent = false;
   m_debugger->breakProgram();
+  popBreakDetails();
+  m_breakReason.swap(currentScheduledReason);
 }
 
 void V8DebuggerAgentImpl::breakProgramOnException(
     const String16& breakReason,
     std::unique_ptr<protocol::DictionaryValue> data) {
   if (!enabled() ||
-      m_debugger->getPauseOnExceptionsState() ==
-          v8::DebugInterface::NoBreakOnException)
+      m_debugger->getPauseOnExceptionsState() == v8::debug::NoBreakOnException)
     return;
   breakProgram(breakReason, std::move(data));
 }
 
-void V8DebuggerAgentImpl::clearBreakDetails() {
-  m_breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
-  m_breakAuxData = nullptr;
-}
-
 void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId,
                                           int lineNumber, int columnNumber,
                                           BreakpointSource source,
                                           const String16& condition) {
-  String16 breakpointId =
-      generateBreakpointId(scriptId, lineNumber, columnNumber, source);
-  ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
-  resolveBreakpoint(breakpointId, scriptId, breakpoint, source);
+  ScriptBreakpoint breakpoint(scriptId, lineNumber, columnNumber, condition);
+  String16 breakpointId = generateBreakpointId(breakpoint, source);
+  resolveBreakpoint(breakpointId, breakpoint, source);
 }
 
 void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId,
                                              int lineNumber, int columnNumber,
                                              BreakpointSource source) {
-  removeBreakpointImpl(
-      generateBreakpointId(scriptId, lineNumber, columnNumber, source));
+  removeBreakpointImpl(generateBreakpointId(
+      ScriptBreakpoint(scriptId, lineNumber, columnNumber, String16()),
+      source));
 }
 
 void V8DebuggerAgentImpl::reset() {
   if (!enabled()) return;
   m_scheduledDebuggerStep = NoStep;
-  m_scripts.clear();
   m_blackboxedPositions.clear();
+  resetBlackboxedStateCache();
+  m_scripts.clear();
   m_breakpointIdToDebuggerBreakpointIds.clear();
 }
 
diff --git a/src/inspector/v8-debugger-agent-impl.h b/src/inspector/v8-debugger-agent-impl.h
index e5285f4..41a18a8 100644
--- a/src/inspector/v8-debugger-agent-impl.h
+++ b/src/inspector/v8-debugger-agent-impl.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "src/base/macros.h"
+#include "src/debug/interface-types.h"
 #include "src/inspector/java-script-call-frame.h"
 #include "src/inspector/protocol/Debugger.h"
 #include "src/inspector/protocol/Forward.h"
@@ -29,14 +30,6 @@
 
 class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
  public:
-  enum SkipPauseRequest {
-    RequestNoSkip,
-    RequestContinue,
-    RequestStepInto,
-    RequestStepOut,
-    RequestStepFrame
-  };
-
   enum BreakpointSource {
     UserBreakpointSource,
     DebugCommandBreakpointSource,
@@ -100,7 +93,7 @@
       const String16& callFrameId, const String16& expression,
       Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
       Maybe<bool> silent, Maybe<bool> returnByValue,
-      Maybe<bool> generatePreview,
+      Maybe<bool> generatePreview, Maybe<bool> throwOnSideEffect,
       std::unique_ptr<protocol::Runtime::RemoteObject>* result,
       Maybe<protocol::Runtime::ExceptionDetails>*) override;
   Response setVariableValue(
@@ -134,23 +127,25 @@
   void reset();
 
   // Interface for V8InspectorImpl
-  SkipPauseRequest didPause(v8::Local<v8::Context>,
-                            v8::Local<v8::Value> exception,
-                            const std::vector<String16>& hitBreakpoints,
-                            bool isPromiseRejection, bool isUncaught);
+  void didPause(int contextId, v8::Local<v8::Value> exception,
+                const std::vector<String16>& hitBreakpoints,
+                bool isPromiseRejection, bool isUncaught, bool isOOMBreak);
   void didContinue();
   void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success);
   void willExecuteScript(int scriptId);
   void didExecuteScript();
 
+  bool isFunctionBlackboxed(const String16& scriptId,
+                            const v8::debug::Location& start,
+                            const v8::debug::Location& end);
+
+  bool skipAllPauses() const { return m_skipAllPauses; }
+
   v8::Isolate* isolate() { return m_isolate; }
 
  private:
   void enableImpl();
 
-  SkipPauseRequest shouldSkipExceptionPause(JavaScriptCallFrame* topCallFrame);
-  SkipPauseRequest shouldSkipStepPause(JavaScriptCallFrame* topCallFrame);
-
   void schedulePauseOnNextStatementIfSteppingInto();
 
   Response currentCallFrames(
@@ -162,19 +157,17 @@
   void setPauseOnExceptionsImpl(int);
 
   std::unique_ptr<protocol::Debugger::Location> resolveBreakpoint(
-      const String16& breakpointId, const String16& scriptId,
-      const ScriptBreakpoint&, BreakpointSource);
+      const String16& breakpointId, const ScriptBreakpoint&, BreakpointSource);
   void removeBreakpointImpl(const String16& breakpointId);
   void clearBreakDetails();
 
-  bool isCurrentCallStackEmptyOrBlackboxed();
-  bool isTopPausedCallFrameBlackboxed();
-  bool isCallFrameWithUnknownScriptOrBlackboxed(JavaScriptCallFrame*);
-
   void internalSetAsyncCallStackDepth(int);
   void increaseCachedSkipStackGeneration();
 
   Response setBlackboxPattern(const String16& pattern);
+  void resetBlackboxedStateCache();
+
+  bool isPaused() const;
 
   using ScriptsMap =
       protocol::HashMap<String16, std::unique_ptr<V8DebuggerScript>>;
@@ -193,24 +186,26 @@
   protocol::DictionaryValue* m_state;
   protocol::Debugger::Frontend m_frontend;
   v8::Isolate* m_isolate;
-  v8::Global<v8::Context> m_pausedContext;
   JavaScriptCallFrames m_pausedCallFrames;
   ScriptsMap m_scripts;
   BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds;
   DebugServerBreakpointToBreakpointIdAndSourceMap m_serverBreakpoints;
   String16 m_continueToLocationBreakpointId;
-  String16 m_breakReason;
-  std::unique_ptr<protocol::DictionaryValue> m_breakAuxData;
-  DebuggerStep m_scheduledDebuggerStep;
-  bool m_skipNextDebuggerStepOut;
-  bool m_javaScriptPauseScheduled;
-  bool m_steppingFromFramework;
-  bool m_pausingOnNativeEvent;
 
-  int m_skippedStepFrameCount;
+  using BreakReason =
+      std::pair<String16, std::unique_ptr<protocol::DictionaryValue>>;
+  std::vector<BreakReason> m_breakReason;
+
+  void pushBreakDetails(
+      const String16& breakReason,
+      std::unique_ptr<protocol::DictionaryValue> breakAuxData);
+  void popBreakDetails();
+
+  DebuggerStep m_scheduledDebuggerStep;
+  bool m_javaScriptPauseScheduled;
+
   int m_recursionLevelForStepOut;
-  int m_recursionLevelForStepFrame;
-  bool m_skipAllPauses;
+  bool m_skipAllPauses = false;
 
   std::unique_ptr<V8Regex> m_blackboxPattern;
   protocol::HashMap<String16, std::vector<std::pair<int, int>>>
diff --git a/src/inspector/v8-debugger-script.cc b/src/inspector/v8-debugger-script.cc
index ed0c0d6..200cdc7 100644
--- a/src/inspector/v8-debugger-script.cc
+++ b/src/inspector/v8-debugger-script.cc
@@ -4,14 +4,17 @@
 
 #include "src/inspector/v8-debugger-script.h"
 
-#include "src/inspector/protocol-platform.h"
+#include "src/inspector/inspected-context.h"
 #include "src/inspector/string-util.h"
+#include "src/inspector/wasm-translation.h"
 
 namespace v8_inspector {
 
-static const char hexDigits[17] = "0123456789ABCDEF";
+namespace {
 
-static void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
+const char hexDigits[17] = "0123456789ABCDEF";
+
+void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
   for (size_t i = 0; i < 8; ++i) {
     UChar c = hexDigits[number & 0xF];
     destination->append(c);
@@ -23,7 +26,7 @@
 // Multiplikation in
 // eingeschränkten Branchingprogrammmodellen" by Woelfe.
 // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
-static String16 calculateHash(const String16& str) {
+String16 calculateHash(const String16& str) {
   static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35,
                              0x81ABE279};
   static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
@@ -67,98 +70,240 @@
   return hash.toString();
 }
 
-V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate,
-                                   v8::Local<v8::DebugInterface::Script> script,
-                                   bool isLiveEdit) {
-  m_isolate = script->GetIsolate();
-  m_id = String16::fromInteger(script->Id());
-  v8::Local<v8::String> tmp;
-  if (script->Name().ToLocal(&tmp)) m_url = toProtocolString(tmp);
-  if (script->SourceURL().ToLocal(&tmp)) {
-    m_sourceURL = toProtocolString(tmp);
-    if (m_url.isEmpty()) m_url = toProtocolString(tmp);
-  }
-  if (script->SourceMappingURL().ToLocal(&tmp))
-    m_sourceMappingURL = toProtocolString(tmp);
-  m_startLine = script->LineOffset();
-  m_startColumn = script->ColumnOffset();
-  std::vector<int> lineEnds = script->LineEnds();
-  CHECK(lineEnds.size());
-  int source_length = lineEnds[lineEnds.size() - 1];
-  if (lineEnds.size()) {
-    m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
-    if (lineEnds.size() > 1) {
-      m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
-    } else {
-      m_endColumn = source_length + m_startColumn;
-    }
-  } else {
-    m_endLine = m_startLine;
-    m_endColumn = m_startColumn;
-  }
-
-  if (script->ContextData().ToLocal(&tmp)) {
-    String16 contextData = toProtocolString(tmp);
-    size_t firstComma = contextData.find(",", 0);
-    size_t secondComma = firstComma != String16::kNotFound
-                             ? contextData.find(",", firstComma + 1)
-                             : String16::kNotFound;
-    if (secondComma != String16::kNotFound) {
-      String16 executionContextId =
-          contextData.substring(firstComma + 1, secondComma - firstComma - 1);
-      bool isOk = false;
-      m_executionContextId = executionContextId.toInteger(&isOk);
-      if (!isOk) m_executionContextId = 0;
-      m_executionContextAuxData = contextData.substring(secondComma + 1);
-    }
-  }
-
-  m_isLiveEdit = isLiveEdit;
-
-  if (script->Source().ToLocal(&tmp)) {
-    m_source.Reset(m_isolate, tmp);
-    String16 source = toProtocolString(tmp);
-    m_hash = calculateHash(source);
-    // V8 will not count last line if script source ends with \n.
-    if (source.length() > 1 && source[source.length() - 1] == '\n') {
-      m_endLine++;
-      m_endColumn = 0;
-    }
-  }
-
-  m_script.Reset(m_isolate, script);
+void TranslateProtocolLocationToV8Location(WasmTranslation* wasmTranslation,
+                                           v8::debug::Location* loc,
+                                           const String16& scriptId,
+                                           const String16& expectedV8ScriptId) {
+  if (loc->IsEmpty()) return;
+  int lineNumber = loc->GetLineNumber();
+  int columnNumber = loc->GetColumnNumber();
+  String16 translatedScriptId = scriptId;
+  wasmTranslation->TranslateProtocolLocationToWasmScriptLocation(
+      &translatedScriptId, &lineNumber, &columnNumber);
+  DCHECK_EQ(expectedV8ScriptId.utf8(), translatedScriptId.utf8());
+  *loc = v8::debug::Location(lineNumber, columnNumber);
 }
 
+void TranslateV8LocationToProtocolLocation(
+    WasmTranslation* wasmTranslation, v8::debug::Location* loc,
+    const String16& scriptId, const String16& expectedProtocolScriptId) {
+  int lineNumber = loc->GetLineNumber();
+  int columnNumber = loc->GetColumnNumber();
+  String16 translatedScriptId = scriptId;
+  wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
+      &translatedScriptId, &lineNumber, &columnNumber);
+  DCHECK_EQ(expectedProtocolScriptId.utf8(), translatedScriptId.utf8());
+  *loc = v8::debug::Location(lineNumber, columnNumber);
+}
+
+class ActualScript : public V8DebuggerScript {
+  friend class V8DebuggerScript;
+
+ public:
+  ActualScript(v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
+               bool isLiveEdit)
+      : V8DebuggerScript(isolate, String16::fromInteger(script->Id()),
+                         GetNameOrSourceUrl(script)),
+        m_isLiveEdit(isLiveEdit) {
+    v8::Local<v8::String> tmp;
+    if (script->SourceURL().ToLocal(&tmp)) m_sourceURL = toProtocolString(tmp);
+    if (script->SourceMappingURL().ToLocal(&tmp))
+      m_sourceMappingURL = toProtocolString(tmp);
+    m_startLine = script->LineOffset();
+    m_startColumn = script->ColumnOffset();
+    std::vector<int> lineEnds = script->LineEnds();
+    CHECK(lineEnds.size());
+    int source_length = lineEnds[lineEnds.size() - 1];
+    if (lineEnds.size()) {
+      m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
+      if (lineEnds.size() > 1) {
+        m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
+      } else {
+        m_endColumn = source_length + m_startColumn;
+      }
+    } else {
+      m_endLine = m_startLine;
+      m_endColumn = m_startColumn;
+    }
+
+    v8::Local<v8::Value> contextData;
+    if (script->ContextData().ToLocal(&contextData) && contextData->IsInt32()) {
+      m_executionContextId =
+          static_cast<int>(contextData.As<v8::Int32>()->Value());
+    }
+
+    if (script->Source().ToLocal(&tmp)) {
+      m_sourceObj.Reset(m_isolate, tmp);
+      String16 source = toProtocolString(tmp);
+      // V8 will not count last line if script source ends with \n.
+      if (source.length() > 1 && source[source.length() - 1] == '\n') {
+        m_endLine++;
+        m_endColumn = 0;
+      }
+    }
+
+    m_isModule = script->IsModule();
+
+    m_script.Reset(m_isolate, script);
+  }
+
+  bool isLiveEdit() const override { return m_isLiveEdit; }
+  bool isModule() const override { return m_isModule; }
+
+  const String16& sourceMappingURL() const override {
+    return m_sourceMappingURL;
+  }
+
+  String16 source(v8::Isolate* isolate) const override {
+    if (!m_sourceObj.IsEmpty())
+      return toProtocolString(m_sourceObj.Get(isolate));
+    return V8DebuggerScript::source(isolate);
+  }
+
+  void setSourceMappingURL(const String16& sourceMappingURL) override {
+    m_sourceMappingURL = sourceMappingURL;
+  }
+
+  void setSource(v8::Local<v8::String> source) override {
+    m_source = String16();
+    m_sourceObj.Reset(m_isolate, source);
+    m_hash = String16();
+  }
+
+  bool getPossibleBreakpoints(
+      const v8::debug::Location& start, const v8::debug::Location& end,
+      std::vector<v8::debug::Location>* locations) override {
+    v8::HandleScope scope(m_isolate);
+    v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
+    return script->GetPossibleBreakpoints(start, end, locations);
+  }
+
+  void resetBlackboxedStateCache() override {
+    v8::HandleScope scope(m_isolate);
+    v8::debug::ResetBlackboxedStateCache(m_isolate, m_script.Get(m_isolate));
+  }
+
+ private:
+  String16 GetNameOrSourceUrl(v8::Local<v8::debug::Script> script) {
+    v8::Local<v8::String> name;
+    if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name))
+      return toProtocolString(name);
+    return String16();
+  }
+
+  String16 m_sourceMappingURL;
+  v8::Global<v8::String> m_sourceObj;
+  bool m_isLiveEdit = false;
+  bool m_isModule = false;
+  v8::Global<v8::debug::Script> m_script;
+};
+
+class WasmVirtualScript : public V8DebuggerScript {
+  friend class V8DebuggerScript;
+
+ public:
+  WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
+                    v8::Local<v8::debug::WasmScript> script, String16 id,
+                    String16 url, String16 source)
+      : V8DebuggerScript(isolate, std::move(id), std::move(url)),
+        m_script(isolate, script),
+        m_wasmTranslation(wasmTranslation) {
+    int num_lines = 0;
+    int last_newline = -1;
+    size_t next_newline = source.find('\n', last_newline + 1);
+    while (next_newline != String16::kNotFound) {
+      last_newline = static_cast<int>(next_newline);
+      next_newline = source.find('\n', last_newline + 1);
+      ++num_lines;
+    }
+    m_endLine = num_lines;
+    m_endColumn = static_cast<int>(source.length()) - last_newline - 1;
+    m_source = std::move(source);
+  }
+
+  const String16& sourceMappingURL() const override { return emptyString(); }
+  bool isLiveEdit() const override { return false; }
+  bool isModule() const override { return false; }
+  void setSourceMappingURL(const String16&) override {}
+
+  bool getPossibleBreakpoints(
+      const v8::debug::Location& start, const v8::debug::Location& end,
+      std::vector<v8::debug::Location>* locations) override {
+    v8::HandleScope scope(m_isolate);
+    v8::Local<v8::debug::Script> script = m_script.Get(m_isolate);
+    String16 v8ScriptId = String16::fromInteger(script->Id());
+
+    v8::debug::Location translatedStart = start;
+    TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedStart,
+                                          scriptId(), v8ScriptId);
+
+    v8::debug::Location translatedEnd = end;
+    if (translatedEnd.IsEmpty()) {
+      // Stop before the start of the next function.
+      translatedEnd =
+          v8::debug::Location(translatedStart.GetLineNumber() + 1, 0);
+    } else {
+      TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedEnd,
+                                            scriptId(), v8ScriptId);
+    }
+
+    bool success = script->GetPossibleBreakpoints(translatedStart,
+                                                  translatedEnd, locations);
+    for (v8::debug::Location& loc : *locations) {
+      TranslateV8LocationToProtocolLocation(m_wasmTranslation, &loc, v8ScriptId,
+                                            scriptId());
+    }
+    return success;
+  }
+
+  void resetBlackboxedStateCache() override {}
+
+ private:
+  static const String16& emptyString() {
+    static const String16 singleEmptyString;
+    return singleEmptyString;
+  }
+
+  v8::Global<v8::debug::WasmScript> m_script;
+  WasmTranslation* m_wasmTranslation;
+};
+
+}  // namespace
+
+std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
+    v8::Isolate* isolate, v8::Local<v8::debug::Script> scriptObj,
+    bool isLiveEdit) {
+  return std::unique_ptr<ActualScript>(
+      new ActualScript(isolate, scriptObj, isLiveEdit));
+}
+
+std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
+    v8::Isolate* isolate, WasmTranslation* wasmTranslation,
+    v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
+    String16 url, String16 source) {
+  return std::unique_ptr<WasmVirtualScript>(
+      new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
+                            std::move(id), std::move(url), std::move(source)));
+}
+
+V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
+                                   String16 url)
+    : m_id(std::move(id)), m_url(std::move(url)), m_isolate(isolate) {}
+
 V8DebuggerScript::~V8DebuggerScript() {}
 
 const String16& V8DebuggerScript::sourceURL() const {
   return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
 }
 
-v8::Local<v8::String> V8DebuggerScript::source(v8::Isolate* isolate) const {
-  return m_source.Get(isolate);
+const String16& V8DebuggerScript::hash(v8::Isolate* isolate) const {
+  if (m_hash.isEmpty()) m_hash = calculateHash(source(isolate));
+  DCHECK(!m_hash.isEmpty());
+  return m_hash;
 }
 
 void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
   m_sourceURL = sourceURL;
 }
 
-void V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) {
-  m_sourceMappingURL = sourceMappingURL;
-}
-
-void V8DebuggerScript::setSource(v8::Local<v8::String> source) {
-  m_source.Reset(m_isolate, source);
-  m_hash = calculateHash(toProtocolString(source));
-}
-
-bool V8DebuggerScript::getPossibleBreakpoints(
-    const v8::DebugInterface::Location& start,
-    const v8::DebugInterface::Location& end,
-    std::vector<v8::DebugInterface::Location>* locations) {
-  v8::HandleScope scope(m_isolate);
-  v8::Local<v8::DebugInterface::Script> script = m_script.Get(m_isolate);
-  return script->GetPossibleBreakpoints(start, end, locations);
-}
-
 }  // namespace v8_inspector
diff --git a/src/inspector/v8-debugger-script.h b/src/inspector/v8-debugger-script.h
index 97b5ba9..9250c9d 100644
--- a/src/inspector/v8-debugger-script.h
+++ b/src/inspector/v8-debugger-script.h
@@ -32,63 +32,71 @@
 
 #include "src/base/macros.h"
 #include "src/inspector/string-16.h"
+#include "src/inspector/string-util.h"
 
 #include "include/v8.h"
 #include "src/debug/debug-interface.h"
 
 namespace v8_inspector {
 
+// Forward declaration.
+class WasmTranslation;
+
 class V8DebuggerScript {
  public:
-  V8DebuggerScript(v8::Isolate* isolate,
-                   v8::Local<v8::DebugInterface::Script> script,
-                   bool isLiveEdit);
-  ~V8DebuggerScript();
+  static std::unique_ptr<V8DebuggerScript> Create(
+      v8::Isolate* isolate, v8::Local<v8::debug::Script> script,
+      bool isLiveEdit);
+  static std::unique_ptr<V8DebuggerScript> CreateWasm(
+      v8::Isolate* isolate, WasmTranslation* wasmTranslation,
+      v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
+      String16 url, String16 source);
+
+  virtual ~V8DebuggerScript();
 
   const String16& scriptId() const { return m_id; }
   const String16& url() const { return m_url; }
   bool hasSourceURL() const { return !m_sourceURL.isEmpty(); }
   const String16& sourceURL() const;
-  const String16& sourceMappingURL() const { return m_sourceMappingURL; }
-  v8::Local<v8::String> source(v8::Isolate*) const;
-  const String16& hash() const { return m_hash; }
+  virtual const String16& sourceMappingURL() const = 0;
+  virtual String16 source(v8::Isolate*) const { return m_source; }
+  const String16& hash(v8::Isolate*) const;
   int startLine() const { return m_startLine; }
   int startColumn() const { return m_startColumn; }
   int endLine() const { return m_endLine; }
   int endColumn() const { return m_endColumn; }
   int executionContextId() const { return m_executionContextId; }
-  const String16& executionContextAuxData() const {
-    return m_executionContextAuxData;
-  }
-  bool isLiveEdit() const { return m_isLiveEdit; }
+  virtual bool isLiveEdit() const = 0;
+  virtual bool isModule() const = 0;
 
   void setSourceURL(const String16&);
-  void setSourceMappingURL(const String16&);
-  void setSource(v8::Local<v8::String>);
+  virtual void setSourceMappingURL(const String16&) = 0;
+  virtual void setSource(v8::Local<v8::String> source) {
+    m_source = toProtocolString(source);
+  }
 
-  bool getPossibleBreakpoints(
-      const v8::DebugInterface::Location& start,
-      const v8::DebugInterface::Location& end,
-      std::vector<v8::DebugInterface::Location>* locations);
+  virtual bool getPossibleBreakpoints(
+      const v8::debug::Location& start, const v8::debug::Location& end,
+      std::vector<v8::debug::Location>* locations) = 0;
+  virtual void resetBlackboxedStateCache() = 0;
 
- private:
+ protected:
+  V8DebuggerScript(v8::Isolate*, String16 id, String16 url);
+
   String16 m_id;
   String16 m_url;
   String16 m_sourceURL;
-  String16 m_sourceMappingURL;
-  v8::Global<v8::String> m_source;
-  String16 m_hash;
-  int m_startLine;
-  int m_startColumn;
-  int m_endLine;
-  int m_endColumn;
-  int m_executionContextId;
-  String16 m_executionContextAuxData;
-  bool m_isLiveEdit;
+  String16 m_source;
+  mutable String16 m_hash;
+  int m_startLine = 0;
+  int m_startColumn = 0;
+  int m_endLine = 0;
+  int m_endColumn = 0;
+  int m_executionContextId = 0;
 
   v8::Isolate* m_isolate;
-  v8::Global<v8::DebugInterface::Script> m_script;
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(V8DebuggerScript);
 };
 
diff --git a/src/inspector/v8-debugger.cc b/src/inspector/v8-debugger.cc
index b3657e5..3a2fc89 100644
--- a/src/inspector/v8-debugger.cc
+++ b/src/inspector/v8-debugger.cc
@@ -5,6 +5,7 @@
 #include "src/inspector/v8-debugger.h"
 
 #include "src/inspector/debugger-script.h"
+#include "src/inspector/inspected-context.h"
 #include "src/inspector/protocol/Protocol.h"
 #include "src/inspector/script-breakpoint.h"
 #include "src/inspector/string-util.h"
@@ -19,22 +20,129 @@
 namespace v8_inspector {
 
 namespace {
-static const char v8AsyncTaskEventEnqueue[] = "enqueue";
-static const char v8AsyncTaskEventEnqueueRecurring[] = "enqueueRecurring";
-static const char v8AsyncTaskEventWillHandle[] = "willHandle";
-static const char v8AsyncTaskEventDidHandle[] = "didHandle";
-static const char v8AsyncTaskEventCancel[] = "cancel";
+
+// Based on DevTools frontend measurement, with asyncCallStackDepth = 4,
+// average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb
+// for async stacks.
+static const int kMaxAsyncTaskStacks = 128 * 1024;
 
 inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
   return value ? v8::True(isolate) : v8::False(isolate);
 }
 
+V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector,
+                                    v8::Local<v8::debug::Script> script) {
+  v8::Local<v8::Value> contextData;
+  if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) {
+    return nullptr;
+  }
+  int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value());
+  int contextGroupId = inspector->contextGroupId(contextId);
+  if (!contextGroupId) return nullptr;
+  return inspector->enabledDebuggerAgentForGroup(contextGroupId);
+}
+
+v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
+                                             v8::Local<v8::Value> value) {
+  v8::Isolate* isolate = context->GetIsolate();
+  v8::Local<v8::Array> entries;
+  bool isKeyValue = false;
+  if (!v8::debug::EntriesPreview(isolate, value, &isKeyValue).ToLocal(&entries))
+    return v8::MaybeLocal<v8::Array>();
+
+  v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
+  CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
+  if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
+           .FromMaybe(false))
+    return v8::MaybeLocal<v8::Array>();
+  for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
+    v8::Local<v8::Value> item;
+    if (!entries->Get(context, i).ToLocal(&item)) continue;
+    v8::Local<v8::Value> value;
+    if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
+    v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
+    if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
+      continue;
+    createDataProperty(
+        context, wrapper,
+        toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
+    if (isKeyValue) {
+      createDataProperty(context, wrapper,
+                         toV8StringInternalized(isolate, "value"), value);
+    }
+    createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
+                       wrapper);
+  }
+  if (!markArrayEntriesAsInternal(context, wrappedEntries,
+                                  V8InternalValueType::kEntry)) {
+    return v8::MaybeLocal<v8::Array>();
+  }
+  return wrappedEntries;
+}
+
+v8::MaybeLocal<v8::Object> buildLocation(v8::Local<v8::Context> context,
+                                         int scriptId, int lineNumber,
+                                         int columnNumber) {
+  if (scriptId == v8::UnboundScript::kNoScriptId)
+    return v8::MaybeLocal<v8::Object>();
+  if (lineNumber == v8::Function::kLineOffsetNotFound ||
+      columnNumber == v8::Function::kLineOffsetNotFound) {
+    return v8::MaybeLocal<v8::Object>();
+  }
+  v8::Isolate* isolate = context->GetIsolate();
+  v8::Local<v8::Object> location = v8::Object::New(isolate);
+  if (!location->SetPrototype(context, v8::Null(isolate)).FromMaybe(false)) {
+    return v8::MaybeLocal<v8::Object>();
+  }
+  if (!createDataProperty(context, location,
+                          toV8StringInternalized(isolate, "scriptId"),
+                          toV8String(isolate, String16::fromInteger(scriptId)))
+           .FromMaybe(false)) {
+    return v8::MaybeLocal<v8::Object>();
+  }
+  if (!createDataProperty(context, location,
+                          toV8StringInternalized(isolate, "lineNumber"),
+                          v8::Integer::New(isolate, lineNumber))
+           .FromMaybe(false)) {
+    return v8::MaybeLocal<v8::Object>();
+  }
+  if (!createDataProperty(context, location,
+                          toV8StringInternalized(isolate, "columnNumber"),
+                          v8::Integer::New(isolate, columnNumber))
+           .FromMaybe(false)) {
+    return v8::MaybeLocal<v8::Object>();
+  }
+  if (!markAsInternal(context, location, V8InternalValueType::kLocation)) {
+    return v8::MaybeLocal<v8::Object>();
+  }
+  return location;
+}
+
+v8::MaybeLocal<v8::Object> generatorObjectLocation(
+    v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
+  if (!value->IsGeneratorObject()) return v8::MaybeLocal<v8::Object>();
+  v8::Local<v8::debug::GeneratorObject> generatorObject =
+      v8::debug::GeneratorObject::Cast(value);
+  if (!generatorObject->IsSuspended()) {
+    v8::Local<v8::Function> func = generatorObject->Function();
+    return buildLocation(context, func->ScriptId(), func->GetScriptLineNumber(),
+                         func->GetScriptColumnNumber());
+  }
+  v8::Local<v8::debug::Script> script;
+  if (!generatorObject->Script().ToLocal(&script))
+    return v8::MaybeLocal<v8::Object>();
+  v8::debug::Location suspendedLocation = generatorObject->SuspendedLocation();
+  return buildLocation(context, script->Id(), suspendedLocation.GetLineNumber(),
+                       suspendedLocation.GetColumnNumber());
+}
+
 }  // namespace
 
 static bool inLiveEditScope = false;
 
 v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod(
-    const char* functionName, int argc, v8::Local<v8::Value> argv[]) {
+    const char* functionName, int argc, v8::Local<v8::Value> argv[],
+    bool catchExceptions) {
   v8::MicrotasksScope microtasks(m_isolate,
                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
   DCHECK(m_isolate->InContext());
@@ -44,19 +152,25 @@
       debuggerScript
           ->Get(context, toV8StringInternalized(m_isolate, functionName))
           .ToLocalChecked());
+  if (catchExceptions) {
+    v8::TryCatch try_catch(m_isolate);
+    return function->Call(context, debuggerScript, argc, argv);
+  }
   return function->Call(context, debuggerScript, argc, argv);
 }
 
 V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
     : m_isolate(isolate),
       m_inspector(inspector),
-      m_lastContextId(0),
       m_enableCount(0),
       m_breakpointsActivated(true),
       m_runningNestedMessageLoop(false),
       m_ignoreScriptParsedEventsCounter(0),
+      m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
+      m_lastTaskId(0),
       m_maxAsyncCallStackDepth(0),
-      m_pauseOnExceptionsState(v8::DebugInterface::NoBreakOnException) {}
+      m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
+      m_wasmTranslation(isolate) {}
 
 V8Debugger::~V8Debugger() {}
 
@@ -64,14 +178,12 @@
   if (m_enableCount++) return;
   DCHECK(!enabled());
   v8::HandleScope scope(m_isolate);
-  v8::DebugInterface::SetDebugEventListener(m_isolate,
-                                            &V8Debugger::v8DebugEventCallback,
-                                            v8::External::New(m_isolate, this));
-  m_debuggerContext.Reset(m_isolate,
-                          v8::DebugInterface::GetDebugContext(m_isolate));
-  v8::DebugInterface::ChangeBreakOnException(
-      m_isolate, v8::DebugInterface::NoBreakOnException);
-  m_pauseOnExceptionsState = v8::DebugInterface::NoBreakOnException;
+  v8::debug::SetDebugDelegate(m_isolate, this);
+  v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback,
+                                    this);
+  m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate));
+  v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
+  m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
   compileDebuggerScript();
 }
 
@@ -82,61 +194,33 @@
   m_debuggerScript.Reset();
   m_debuggerContext.Reset();
   allAsyncTasksCanceled();
-  v8::DebugInterface::SetDebugEventListener(m_isolate, nullptr);
+  m_wasmTranslation.Clear();
+  v8::debug::SetDebugDelegate(m_isolate, nullptr);
+  v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr);
+  m_isolate->RestoreOriginalHeapLimit();
 }
 
 bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); }
 
-// static
-int V8Debugger::contextId(v8::Local<v8::Context> context) {
-  v8::Local<v8::Value> data =
-      context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex));
-  if (data.IsEmpty() || !data->IsString()) return 0;
-  String16 dataString = toProtocolString(data.As<v8::String>());
-  if (dataString.isEmpty()) return 0;
-  size_t commaPos = dataString.find(",");
-  if (commaPos == String16::kNotFound) return 0;
-  size_t commaPos2 = dataString.find(",", commaPos + 1);
-  if (commaPos2 == String16::kNotFound) return 0;
-  return dataString.substring(commaPos + 1, commaPos2 - commaPos - 1)
-      .toInteger();
-}
-
-// static
-int V8Debugger::getGroupId(v8::Local<v8::Context> context) {
-  v8::Local<v8::Value> data =
-      context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex));
-  if (data.IsEmpty() || !data->IsString()) return 0;
-  String16 dataString = toProtocolString(data.As<v8::String>());
-  if (dataString.isEmpty()) return 0;
-  size_t commaPos = dataString.find(",");
-  if (commaPos == String16::kNotFound) return 0;
-  return dataString.substring(0, commaPos).toInteger();
-}
-
 void V8Debugger::getCompiledScripts(
     int contextGroupId,
     std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
   v8::HandleScope scope(m_isolate);
-  v8::PersistentValueVector<v8::DebugInterface::Script> scripts(m_isolate);
-  v8::DebugInterface::GetLoadedScripts(m_isolate, scripts);
-  String16 contextPrefix = String16::fromInteger(contextGroupId) + ",";
+  v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate);
+  v8::debug::GetLoadedScripts(m_isolate, scripts);
   for (size_t i = 0; i < scripts.Size(); ++i) {
-    v8::Local<v8::DebugInterface::Script> script = scripts.Get(i);
+    v8::Local<v8::debug::Script> script = scripts.Get(i);
     if (!script->WasCompiled()) continue;
-    v8::ScriptOriginOptions origin = script->OriginOptions();
-    if (origin.IsEmbedderDebugScript()) continue;
-    v8::Local<v8::String> v8ContextData;
-    if (!script->ContextData().ToLocal(&v8ContextData)) continue;
-    String16 contextData = toProtocolString(v8ContextData);
-    if (contextData.find(contextPrefix) != 0) continue;
-    result.push_back(
-        wrapUnique(new V8DebuggerScript(m_isolate, script, false)));
+    v8::Local<v8::Value> contextData;
+    if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32())
+      continue;
+    int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value());
+    if (m_inspector->contextGroupId(contextId) != contextGroupId) continue;
+    result.push_back(V8DebuggerScript::Create(m_isolate, script, false));
   }
 }
 
-String16 V8Debugger::setBreakpoint(const String16& sourceID,
-                                   const ScriptBreakpoint& scriptBreakpoint,
+String16 V8Debugger::setBreakpoint(const ScriptBreakpoint& breakpoint,
                                    int* actualLineNumber,
                                    int* actualColumnNumber) {
   v8::HandleScope scope(m_isolate);
@@ -146,29 +230,30 @@
   v8::Local<v8::Object> info = v8::Object::New(m_isolate);
   bool success = false;
   success = info->Set(context, toV8StringInternalized(m_isolate, "sourceID"),
-                      toV8String(m_isolate, sourceID))
+                      toV8String(m_isolate, breakpoint.script_id))
                 .FromMaybe(false);
   DCHECK(success);
   success = info->Set(context, toV8StringInternalized(m_isolate, "lineNumber"),
-                      v8::Integer::New(m_isolate, scriptBreakpoint.lineNumber))
+                      v8::Integer::New(m_isolate, breakpoint.line_number))
                 .FromMaybe(false);
   DCHECK(success);
   success =
       info->Set(context, toV8StringInternalized(m_isolate, "columnNumber"),
-                v8::Integer::New(m_isolate, scriptBreakpoint.columnNumber))
+                v8::Integer::New(m_isolate, breakpoint.column_number))
           .FromMaybe(false);
   DCHECK(success);
   success = info->Set(context, toV8StringInternalized(m_isolate, "condition"),
-                      toV8String(m_isolate, scriptBreakpoint.condition))
+                      toV8String(m_isolate, breakpoint.condition))
                 .FromMaybe(false);
   DCHECK(success);
+  USE(success);
 
   v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(
       m_debuggerScript.Get(m_isolate)
           ->Get(context, toV8StringInternalized(m_isolate, "setBreakpoint"))
           .ToLocalChecked());
   v8::Local<v8::Value> breakpointId =
-      v8::DebugInterface::Call(debuggerContext(), setBreakpointFunction, info)
+      v8::debug::Call(debuggerContext(), setBreakpointFunction, info)
           .ToLocalChecked();
   if (!breakpointId->IsString()) return "";
   *actualLineNumber =
@@ -196,6 +281,7 @@
                 toV8String(m_isolate, breakpointId))
           .FromMaybe(false);
   DCHECK(success);
+  USE(success);
 
   v8::Local<v8::Function> removeBreakpointFunction =
       v8::Local<v8::Function>::Cast(
@@ -203,7 +289,7 @@
               ->Get(context,
                     toV8StringInternalized(m_isolate, "removeBreakpoint"))
               .ToLocalChecked());
-  v8::DebugInterface::Call(debuggerContext(), removeBreakpointFunction, info)
+  v8::debug::Call(debuggerContext(), removeBreakpointFunction, info)
       .ToLocalChecked();
 }
 
@@ -216,8 +302,7 @@
       m_debuggerScript.Get(m_isolate)
           ->Get(context, toV8StringInternalized(m_isolate, "clearBreakpoints"))
           .ToLocalChecked());
-  v8::DebugInterface::Call(debuggerContext(), clearBreakpoints)
-      .ToLocalChecked();
+  v8::debug::Call(debuggerContext(), clearBreakpoints).ToLocalChecked();
 }
 
 void V8Debugger::setBreakpointsActivated(bool activated) {
@@ -225,65 +310,39 @@
     UNREACHABLE();
     return;
   }
-  v8::HandleScope scope(m_isolate);
-  v8::Local<v8::Context> context = debuggerContext();
-  v8::Context::Scope contextScope(context);
-
-  v8::Local<v8::Object> info = v8::Object::New(m_isolate);
-  bool success = false;
-  success = info->Set(context, toV8StringInternalized(m_isolate, "enabled"),
-                      v8::Boolean::New(m_isolate, activated))
-                .FromMaybe(false);
-  DCHECK(success);
-  v8::Local<v8::Function> setBreakpointsActivated =
-      v8::Local<v8::Function>::Cast(
-          m_debuggerScript.Get(m_isolate)
-              ->Get(context, toV8StringInternalized(m_isolate,
-                                                    "setBreakpointsActivated"))
-              .ToLocalChecked());
-  v8::DebugInterface::Call(debuggerContext(), setBreakpointsActivated, info)
-      .ToLocalChecked();
-
+  v8::debug::SetBreakPointsActive(m_isolate, activated);
   m_breakpointsActivated = activated;
 }
 
-v8::DebugInterface::ExceptionBreakState
-V8Debugger::getPauseOnExceptionsState() {
+v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
   DCHECK(enabled());
   return m_pauseOnExceptionsState;
 }
 
 void V8Debugger::setPauseOnExceptionsState(
-    v8::DebugInterface::ExceptionBreakState pauseOnExceptionsState) {
+    v8::debug::ExceptionBreakState pauseOnExceptionsState) {
   DCHECK(enabled());
   if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
-  v8::DebugInterface::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
+  v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
   m_pauseOnExceptionsState = pauseOnExceptionsState;
 }
 
 void V8Debugger::setPauseOnNextStatement(bool pause) {
-  if (m_runningNestedMessageLoop) return;
+  if (isPaused()) return;
   if (pause)
-    v8::DebugInterface::DebugBreak(m_isolate);
+    v8::debug::DebugBreak(m_isolate);
   else
-    v8::DebugInterface::CancelDebugBreak(m_isolate);
+    v8::debug::CancelDebugBreak(m_isolate);
 }
 
 bool V8Debugger::canBreakProgram() {
   if (!m_breakpointsActivated) return false;
-  return m_isolate->InContext();
+  return v8::debug::HasNonBlackboxedFrameOnStack(m_isolate);
 }
 
 void V8Debugger::breakProgram() {
-  if (isPaused()) {
-    DCHECK(!m_runningNestedMessageLoop);
-    v8::Local<v8::Value> exception;
-    v8::Local<v8::Array> hitBreakpoints;
-    handleProgramBreak(m_pausedContext, m_executionState, exception,
-                       hitBreakpoints);
-    return;
-  }
-
+  // Don't allow nested breaks.
+  if (isPaused()) return;
   if (!canBreakProgram()) return;
 
   v8::HandleScope scope(m_isolate);
@@ -294,7 +353,7 @@
                          v8::ConstructorBehavior::kThrow)
            .ToLocal(&breakFunction))
     return;
-  v8::DebugInterface::Call(debuggerContext(), breakFunction).ToLocalChecked();
+  v8::debug::Call(debuggerContext(), breakFunction).ToLocalChecked();
 }
 
 void V8Debugger::continueProgram() {
@@ -306,29 +365,24 @@
 void V8Debugger::stepIntoStatement() {
   DCHECK(isPaused());
   DCHECK(!m_executionState.IsEmpty());
-  v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepIn);
+  v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
   continueProgram();
 }
 
 void V8Debugger::stepOverStatement() {
   DCHECK(isPaused());
   DCHECK(!m_executionState.IsEmpty());
-  v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepNext);
+  v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
   continueProgram();
 }
 
 void V8Debugger::stepOutOfFunction() {
   DCHECK(isPaused());
   DCHECK(!m_executionState.IsEmpty());
-  v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepOut);
+  v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
   continueProgram();
 }
 
-void V8Debugger::clearStepping() {
-  DCHECK(enabled());
-  v8::DebugInterface::ClearStepping(m_isolate);
-}
-
 Response V8Debugger::setScriptSource(
     const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
     Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
@@ -337,11 +391,11 @@
   class EnableLiveEditScope {
    public:
     explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) {
-      v8::DebugInterface::SetLiveEditEnabled(m_isolate, true);
+      v8::debug::SetLiveEditEnabled(m_isolate, true);
       inLiveEditScope = true;
     }
     ~EnableLiveEditScope() {
-      v8::DebugInterface::SetLiveEditEnabled(m_isolate, false);
+      v8::debug::SetLiveEditEnabled(m_isolate, false);
       inLiveEditScope = false;
     }
 
@@ -355,7 +409,7 @@
 
   std::unique_ptr<v8::Context::Scope> contextScope;
   if (!isPaused())
-    contextScope = wrapUnique(new v8::Context::Scope(debuggerContext()));
+    contextScope.reset(new v8::Context::Scope(debuggerContext()));
 
   v8::Local<v8::Value> argv[] = {toV8String(m_isolate, sourceID), newSource,
                                  v8Boolean(dryRun, m_isolate)};
@@ -366,7 +420,7 @@
     v8::TryCatch tryCatch(m_isolate);
     tryCatch.SetVerbose(false);
     v8::MaybeLocal<v8::Value> maybeResult =
-        callDebuggerMethod("liveEditScriptSource", 3, argv);
+        callDebuggerMethod("liveEditScriptSource", 3, argv, false);
     if (tryCatch.HasCaught()) {
       v8::Local<v8::Message> message = tryCatch.Message();
       if (!message.IsEmpty())
@@ -427,27 +481,14 @@
 }
 
 JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) {
-  if (!m_isolate->InContext()) return JavaScriptCallFrames();
+  if (!isPaused()) return JavaScriptCallFrames();
   v8::Local<v8::Value> currentCallFramesV8;
-  if (m_executionState.IsEmpty()) {
-    v8::Local<v8::Function> currentCallFramesFunction =
-        v8::Local<v8::Function>::Cast(
-            m_debuggerScript.Get(m_isolate)
-                ->Get(debuggerContext(),
-                      toV8StringInternalized(m_isolate, "currentCallFrames"))
-                .ToLocalChecked());
-    currentCallFramesV8 =
-        v8::DebugInterface::Call(debuggerContext(), currentCallFramesFunction,
-                                 v8::Integer::New(m_isolate, limit))
-            .ToLocalChecked();
-  } else {
-    v8::Local<v8::Value> argv[] = {m_executionState,
-                                   v8::Integer::New(m_isolate, limit)};
-    currentCallFramesV8 =
-        callDebuggerMethod("currentCallFrames", arraysize(argv), argv)
-            .ToLocalChecked();
+  v8::Local<v8::Value> argv[] = {m_executionState,
+                                 v8::Integer::New(m_isolate, limit)};
+  if (!callDebuggerMethod("currentCallFrames", arraysize(argv), argv, true)
+           .ToLocal(&currentCallFramesV8)) {
+    return JavaScriptCallFrames();
   }
-  DCHECK(!currentCallFramesV8.IsEmpty());
   if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames();
   v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
   JavaScriptCallFrames callFrames;
@@ -488,11 +529,11 @@
                                     v8::Local<v8::Array> hitBreakpointNumbers,
                                     bool isPromiseRejection, bool isUncaught) {
   // Don't allow nested breaks.
-  if (m_runningNestedMessageLoop) return;
+  if (isPaused()) return;
 
-  V8DebuggerAgentImpl* agent =
-      m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext));
-  if (!agent) return;
+  V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup(
+      m_inspector->contextGroupId(pausedContext));
+  if (!agent || (agent->skipAllPauses() && !m_scheduledOOMBreak)) return;
 
   std::vector<String16> breakpointIds;
   if (!hitBreakpointNumbers.IsEmpty()) {
@@ -508,147 +549,113 @@
 
   m_pausedContext = pausedContext;
   m_executionState = executionState;
-  V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause(
-      pausedContext, exception, breakpointIds, isPromiseRejection, isUncaught);
-  if (result == V8DebuggerAgentImpl::RequestNoSkip) {
-    m_runningNestedMessageLoop = true;
-    int groupId = getGroupId(pausedContext);
-    DCHECK(groupId);
+  m_runningNestedMessageLoop = true;
+  agent->didPause(InspectedContext::contextId(pausedContext), exception,
+                  breakpointIds, isPromiseRejection, isUncaught,
+                  m_scheduledOOMBreak);
+  int groupId = m_inspector->contextGroupId(pausedContext);
+  DCHECK(groupId);
+  {
+    v8::Context::Scope scope(pausedContext);
+    v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
+    CHECK(!context.IsEmpty() &&
+          context != v8::debug::GetDebugContext(m_isolate));
     m_inspector->client()->runMessageLoopOnPause(groupId);
-    // The agent may have been removed in the nested loop.
-    agent =
-        m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext));
-    if (agent) agent->didContinue();
     m_runningNestedMessageLoop = false;
   }
+  // The agent may have been removed in the nested loop.
+  agent = m_inspector->enabledDebuggerAgentForGroup(groupId);
+  if (agent) agent->didContinue();
+  if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
+  m_scheduledOOMBreak = false;
   m_pausedContext.Clear();
   m_executionState.Clear();
+}
 
-  if (result == V8DebuggerAgentImpl::RequestStepFrame) {
-    v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepFrame);
-  } else if (result == V8DebuggerAgentImpl::RequestStepInto) {
-    v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepIn);
-  } else if (result == V8DebuggerAgentImpl::RequestStepOut) {
-    v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepOut);
+void V8Debugger::v8OOMCallback(void* data) {
+  V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
+  thisPtr->m_isolate->IncreaseHeapLimitForDebugging();
+  thisPtr->m_scheduledOOMBreak = true;
+  thisPtr->setPauseOnNextStatement(true);
+}
+
+void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
+                                bool has_compile_error) {
+  V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script);
+  if (!agent) return;
+  if (script->IsWasm()) {
+    m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
+  } else if (m_ignoreScriptParsedEventsCounter == 0) {
+    agent->didParseSource(
+        V8DebuggerScript::Create(m_isolate, script, inLiveEditScope),
+        !has_compile_error);
   }
 }
 
-void V8Debugger::v8DebugEventCallback(
-    const v8::DebugInterface::EventDetails& eventDetails) {
-  V8Debugger* thisPtr = toV8Debugger(eventDetails.GetCallbackData());
-  thisPtr->handleV8DebugEvent(eventDetails);
-}
-
-v8::Local<v8::Value> V8Debugger::callInternalGetterFunction(
-    v8::Local<v8::Object> object, const char* functionName) {
-  v8::MicrotasksScope microtasks(m_isolate,
-                                 v8::MicrotasksScope::kDoNotRunMicrotasks);
-  v8::Local<v8::Value> getterValue =
-      object
-          ->Get(m_isolate->GetCurrentContext(),
-                toV8StringInternalized(m_isolate, functionName))
-          .ToLocalChecked();
-  DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction());
-  return v8::Local<v8::Function>::Cast(getterValue)
-      ->Call(m_isolate->GetCurrentContext(), object, 0, nullptr)
-      .ToLocalChecked();
-}
-
-void V8Debugger::handleV8DebugEvent(
-    const v8::DebugInterface::EventDetails& eventDetails) {
-  if (!enabled()) return;
-  v8::DebugEvent event = eventDetails.GetEvent();
-  if (event != v8::AsyncTaskEvent && event != v8::Break &&
-      event != v8::Exception && event != v8::AfterCompile &&
-      event != v8::BeforeCompile && event != v8::CompileError)
-    return;
-
-  v8::Local<v8::Context> eventContext = eventDetails.GetEventContext();
-  DCHECK(!eventContext.IsEmpty());
-
-  if (event == v8::AsyncTaskEvent) {
-    v8::HandleScope scope(m_isolate);
-    handleV8AsyncTaskEvent(eventContext, eventDetails.GetExecutionState(),
-                           eventDetails.GetEventData());
+void V8Debugger::BreakProgramRequested(v8::Local<v8::Context> pausedContext,
+                                       v8::Local<v8::Object> execState,
+                                       v8::Local<v8::Value> breakPointsHit) {
+  v8::Local<v8::Value> argv[] = {breakPointsHit};
+  v8::Local<v8::Value> hitBreakpoints;
+  if (!callDebuggerMethod("getBreakpointNumbers", 1, argv, true)
+           .ToLocal(&hitBreakpoints)) {
     return;
   }
-
-  V8DebuggerAgentImpl* agent =
-      m_inspector->enabledDebuggerAgentForGroup(getGroupId(eventContext));
-  if (agent) {
-    v8::HandleScope scope(m_isolate);
-    if (m_ignoreScriptParsedEventsCounter == 0 &&
-        (event == v8::AfterCompile || event == v8::CompileError)) {
-      v8::Local<v8::Context> context = debuggerContext();
-      v8::Context::Scope contextScope(context);
-      v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
-      v8::Local<v8::Value> value =
-          callDebuggerMethod("getAfterCompileScript", 1, argv).ToLocalChecked();
-      if (value->IsNull()) return;
-      DCHECK(value->IsObject());
-      v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(value);
-      v8::Local<v8::DebugInterface::Script> script;
-      if (!v8::DebugInterface::Script::Wrap(m_isolate, scriptObject)
-               .ToLocal(&script))
-        return;
-      agent->didParseSource(
-          wrapUnique(new V8DebuggerScript(m_isolate, script, inLiveEditScope)),
-          event == v8::AfterCompile);
-    } else if (event == v8::Exception) {
-      v8::Local<v8::Context> context = debuggerContext();
-      v8::Local<v8::Object> eventData = eventDetails.GetEventData();
-      v8::Local<v8::Value> exception =
-          callInternalGetterFunction(eventData, "exception");
-      v8::Local<v8::Value> promise =
-          callInternalGetterFunction(eventData, "promise");
-      bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject();
-      v8::Local<v8::Value> uncaught =
-          callInternalGetterFunction(eventData, "uncaught");
-      bool isUncaught = uncaught->BooleanValue(context).FromJust();
-      handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
-                         exception, v8::Local<v8::Array>(), isPromiseRejection,
-                         isUncaught);
-    } else if (event == v8::Break) {
-      v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
-      v8::Local<v8::Value> hitBreakpoints =
-          callDebuggerMethod("getBreakpointNumbers", 1, argv).ToLocalChecked();
-      DCHECK(hitBreakpoints->IsArray());
-      handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
-                         v8::Local<v8::Value>(),
-                         hitBreakpoints.As<v8::Array>());
-    }
-  }
+  DCHECK(hitBreakpoints->IsArray());
+  handleProgramBreak(pausedContext, execState, v8::Local<v8::Value>(),
+                     hitBreakpoints.As<v8::Array>());
 }
 
-void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Context> context,
-                                        v8::Local<v8::Object> executionState,
-                                        v8::Local<v8::Object> eventData) {
+void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
+                                 v8::Local<v8::Object> execState,
+                                 v8::Local<v8::Value> exception,
+                                 v8::Local<v8::Value> promise,
+                                 bool isUncaught) {
+  bool isPromiseRejection = promise->IsPromise();
+  handleProgramBreak(pausedContext, execState, exception,
+                     v8::Local<v8::Array>(), isPromiseRejection, isUncaught);
+}
+
+bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
+                                      const v8::debug::Location& start,
+                                      const v8::debug::Location& end) {
+  V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script);
+  if (!agent) return false;
+  return agent->isFunctionBlackboxed(String16::fromInteger(script->Id()), start,
+                                     end);
+}
+
+void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
+                                      int id, int parentId) {
   if (!m_maxAsyncCallStackDepth) return;
-
-  String16 type = toProtocolStringWithTypeCheck(
-      callInternalGetterFunction(eventData, "type"));
-  String16 name = toProtocolStringWithTypeCheck(
-      callInternalGetterFunction(eventData, "name"));
-  int id = static_cast<int>(callInternalGetterFunction(eventData, "id")
-                                ->ToInteger(context)
-                                .ToLocalChecked()
-                                ->Value());
   // Async task events from Promises are given misaligned pointers to prevent
   // from overlapping with other Blink task identifiers. There is a single
   // namespace of such ids, managed by src/js/promise.js.
   void* ptr = reinterpret_cast<void*>(id * 2 + 1);
-  if (type == v8AsyncTaskEventEnqueue)
-    asyncTaskScheduled(name, ptr, false);
-  else if (type == v8AsyncTaskEventEnqueueRecurring)
-    asyncTaskScheduled(name, ptr, true);
-  else if (type == v8AsyncTaskEventWillHandle)
-    asyncTaskStarted(ptr);
-  else if (type == v8AsyncTaskEventDidHandle)
-    asyncTaskFinished(ptr);
-  else if (type == v8AsyncTaskEventCancel)
-    asyncTaskCanceled(ptr);
-  else
-    UNREACHABLE();
+  switch (type) {
+    case v8::debug::kDebugPromiseCreated:
+      asyncTaskCreated(
+          ptr, parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr);
+      break;
+    case v8::debug::kDebugEnqueueAsyncFunction:
+      asyncTaskScheduled("async function", ptr, true);
+      break;
+    case v8::debug::kDebugEnqueuePromiseResolve:
+      asyncTaskScheduled("Promise.resolve", ptr, true);
+      break;
+    case v8::debug::kDebugEnqueuePromiseReject:
+      asyncTaskScheduled("Promise.reject", ptr, true);
+      break;
+    case v8::debug::kDebugPromiseCollected:
+      asyncTaskCanceled(ptr);
+      break;
+    case v8::debug::kDebugWillHandle:
+      asyncTaskStarted(ptr);
+      break;
+    case v8::debug::kDebugDidHandle:
+      asyncTaskFinished(ptr);
+      break;
+  }
 }
 
 V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
@@ -685,15 +692,27 @@
   return m_debuggerContext.Get(m_isolate);
 }
 
-v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
-    v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
+v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
+    v8::Local<v8::Context> context, v8::Local<v8::Value> value,
+    ScopeTargetKind kind) {
   if (!enabled()) {
     UNREACHABLE();
     return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
   }
-  v8::Local<v8::Value> argv[] = {function};
+  v8::Local<v8::Value> argv[] = {value};
   v8::Local<v8::Value> scopesValue;
-  if (!callDebuggerMethod("getFunctionScopes", 1, argv).ToLocal(&scopesValue))
+
+  const char* debuggerMethod = nullptr;
+  switch (kind) {
+    case FUNCTION:
+      debuggerMethod = "getFunctionScopes";
+      break;
+    case GENERATOR:
+      debuggerMethod = "getGeneratorScopes";
+      break;
+  }
+
+  if (!callDebuggerMethod(debuggerMethod, 1, argv, true).ToLocal(&scopesValue))
     return v8::MaybeLocal<v8::Value>();
   v8::Local<v8::Value> copied;
   if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
@@ -710,16 +729,28 @@
   return copied;
 }
 
+v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
+    v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
+  return getTargetScopes(context, function, FUNCTION);
+}
+
+v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
+    v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
+  return getTargetScopes(context, generator, GENERATOR);
+}
+
 v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
     v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
   v8::Local<v8::Array> properties;
-  if (!v8::DebugInterface::GetInternalProperties(m_isolate, value)
-           .ToLocal(&properties))
+  if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
     return v8::MaybeLocal<v8::Array>();
   if (value->IsFunction()) {
     v8::Local<v8::Function> function = value.As<v8::Function>();
-    v8::Local<v8::Value> location = functionLocation(context, function);
-    if (location->IsObject()) {
+    v8::Local<v8::Object> location;
+    if (buildLocation(context, function->ScriptId(),
+                      function->GetScriptLineNumber(),
+                      function->GetScriptColumnNumber())
+            .ToLocal(&location)) {
       createDataProperty(
           context, properties, properties->Length(),
           toV8StringInternalized(m_isolate, "[[FunctionLocation]]"));
@@ -732,27 +763,29 @@
                          v8::True(m_isolate));
     }
   }
-  if (!enabled()) return properties;
-  if (value->IsMap() || value->IsWeakMap() || value->IsSet() ||
-      value->IsWeakSet() || value->IsSetIterator() || value->IsMapIterator()) {
-    v8::Local<v8::Value> entries =
-        collectionEntries(context, v8::Local<v8::Object>::Cast(value));
-    if (entries->IsArray()) {
-      createDataProperty(context, properties, properties->Length(),
-                         toV8StringInternalized(m_isolate, "[[Entries]]"));
-      createDataProperty(context, properties, properties->Length(), entries);
-    }
+  v8::Local<v8::Array> entries;
+  if (collectionsEntries(context, value).ToLocal(&entries)) {
+    createDataProperty(context, properties, properties->Length(),
+                       toV8StringInternalized(m_isolate, "[[Entries]]"));
+    createDataProperty(context, properties, properties->Length(), entries);
   }
   if (value->IsGeneratorObject()) {
-    v8::Local<v8::Value> location =
-        generatorObjectLocation(context, v8::Local<v8::Object>::Cast(value));
-    if (location->IsObject()) {
+    v8::Local<v8::Object> location;
+    if (generatorObjectLocation(context, value).ToLocal(&location)) {
       createDataProperty(
           context, properties, properties->Length(),
           toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
       createDataProperty(context, properties, properties->Length(), location);
     }
+    if (!enabled()) return properties;
+    v8::Local<v8::Value> scopes;
+    if (generatorScopes(context, value).ToLocal(&scopes)) {
+      createDataProperty(context, properties, properties->Length(),
+                         toV8StringInternalized(m_isolate, "[[Scopes]]"));
+      createDataProperty(context, properties, properties->Length(), scopes);
+    }
   }
+  if (!enabled()) return properties;
   if (value->IsFunction()) {
     v8::Local<v8::Function> function = value.As<v8::Function>();
     v8::Local<v8::Value> boundFunction = function->GetBoundFunction();
@@ -767,117 +800,16 @@
   return properties;
 }
 
-v8::Local<v8::Value> V8Debugger::collectionEntries(
-    v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
-  if (!enabled()) {
-    UNREACHABLE();
-    return v8::Undefined(m_isolate);
-  }
-  v8::Local<v8::Value> argv[] = {object};
-  v8::Local<v8::Value> entriesValue =
-      callDebuggerMethod("getCollectionEntries", 1, argv).ToLocalChecked();
-  if (!entriesValue->IsArray()) return v8::Undefined(m_isolate);
-
-  v8::Local<v8::Array> entries = entriesValue.As<v8::Array>();
-  v8::Local<v8::Array> copiedArray =
-      v8::Array::New(m_isolate, entries->Length());
-  if (!copiedArray->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false))
-    return v8::Undefined(m_isolate);
-  for (uint32_t i = 0; i < entries->Length(); ++i) {
-    v8::Local<v8::Value> item;
-    if (!entries->Get(debuggerContext(), i).ToLocal(&item))
-      return v8::Undefined(m_isolate);
-    v8::Local<v8::Value> copied;
-    if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
-                                      item)
-             .ToLocal(&copied))
-      return v8::Undefined(m_isolate);
-    if (!createDataProperty(context, copiedArray, i, copied).FromMaybe(false))
-      return v8::Undefined(m_isolate);
-  }
-  if (!markArrayEntriesAsInternal(context,
-                                  v8::Local<v8::Array>::Cast(copiedArray),
-                                  V8InternalValueType::kEntry))
-    return v8::Undefined(m_isolate);
-  return copiedArray;
-}
-
-v8::Local<v8::Value> V8Debugger::generatorObjectLocation(
-    v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
-  if (!enabled()) {
-    UNREACHABLE();
-    return v8::Null(m_isolate);
-  }
-  v8::Local<v8::Value> argv[] = {object};
-  v8::Local<v8::Value> location =
-      callDebuggerMethod("getGeneratorObjectLocation", 1, argv)
-          .ToLocalChecked();
-  v8::Local<v8::Value> copied;
-  if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
-                                    location)
-           .ToLocal(&copied) ||
-      !copied->IsObject())
-    return v8::Null(m_isolate);
-  if (!markAsInternal(context, v8::Local<v8::Object>::Cast(copied),
-                      V8InternalValueType::kLocation))
-    return v8::Null(m_isolate);
-  return copied;
-}
-
-v8::Local<v8::Value> V8Debugger::functionLocation(
-    v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
-  int scriptId = function->ScriptId();
-  if (scriptId == v8::UnboundScript::kNoScriptId) return v8::Null(m_isolate);
-  int lineNumber = function->GetScriptLineNumber();
-  int columnNumber = function->GetScriptColumnNumber();
-  if (lineNumber == v8::Function::kLineOffsetNotFound ||
-      columnNumber == v8::Function::kLineOffsetNotFound)
-    return v8::Null(m_isolate);
-  v8::Local<v8::Object> location = v8::Object::New(m_isolate);
-  if (!location->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false))
-    return v8::Null(m_isolate);
-  if (!createDataProperty(
-           context, location, toV8StringInternalized(m_isolate, "scriptId"),
-           toV8String(m_isolate, String16::fromInteger(scriptId)))
-           .FromMaybe(false))
-    return v8::Null(m_isolate);
-  if (!createDataProperty(context, location,
-                          toV8StringInternalized(m_isolate, "lineNumber"),
-                          v8::Integer::New(m_isolate, lineNumber))
-           .FromMaybe(false))
-    return v8::Null(m_isolate);
-  if (!createDataProperty(context, location,
-                          toV8StringInternalized(m_isolate, "columnNumber"),
-                          v8::Integer::New(m_isolate, columnNumber))
-           .FromMaybe(false))
-    return v8::Null(m_isolate);
-  if (!markAsInternal(context, location, V8InternalValueType::kLocation))
-    return v8::Null(m_isolate);
-  return location;
-}
-
-bool V8Debugger::isPaused() { return !m_pausedContext.IsEmpty(); }
-
 std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
     v8::Local<v8::StackTrace> stackTrace) {
   int contextGroupId =
-      m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0;
+      m_isolate->InContext()
+          ? m_inspector->contextGroupId(m_isolate->GetCurrentContext())
+          : 0;
   return V8StackTraceImpl::create(this, contextGroupId, stackTrace,
                                   V8StackTraceImpl::maxCallStackSizeToCapture);
 }
 
-int V8Debugger::markContext(const V8ContextInfo& info) {
-  DCHECK(info.context->GetIsolate() == m_isolate);
-  int contextId = ++m_lastContextId;
-  String16 debugData = String16::fromInteger(info.contextGroupId) + "," +
-                       String16::fromInteger(contextId) + "," +
-                       toString16(info.auxData);
-  v8::Context::Scope contextScope(info.context);
-  info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex),
-                                toV8String(m_isolate, debugData));
-  return contextId;
-}
-
 void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
   if (depth <= 0)
     m_maxAsyncCallStackDepthMap.erase(agent);
@@ -895,6 +827,34 @@
   if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
 }
 
+void V8Debugger::registerAsyncTaskIfNeeded(void* task) {
+  if (m_taskToId.find(task) != m_taskToId.end()) return;
+
+  int id = ++m_lastTaskId;
+  m_taskToId[task] = id;
+  m_idToTask[id] = task;
+  if (static_cast<int>(m_idToTask.size()) > m_maxAsyncCallStacks) {
+    void* taskToRemove = m_idToTask.begin()->second;
+    asyncTaskCanceled(taskToRemove);
+  }
+}
+
+void V8Debugger::asyncTaskCreated(void* task, void* parentTask) {
+  if (!m_maxAsyncCallStackDepth) return;
+  if (parentTask) m_parentTask[task] = parentTask;
+  v8::HandleScope scope(m_isolate);
+  // We don't need to pass context group id here because we gets this callback
+  // from V8 for promise events only.
+  // Passing one as maxStackSize forces no async chain for the new stack and
+  // allows us to not grow exponentially.
+  std::unique_ptr<V8StackTraceImpl> creationStack =
+      V8StackTraceImpl::capture(this, 0, 1, String16());
+  if (creationStack && !creationStack->isEmpty()) {
+    m_asyncTaskCreationStacks[task] = std::move(creationStack);
+    registerAsyncTaskIfNeeded(task);
+  }
+}
+
 void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
                                     bool recurring) {
   if (!m_maxAsyncCallStackDepth) return;
@@ -906,13 +866,16 @@
   if (!m_maxAsyncCallStackDepth) return;
   v8::HandleScope scope(m_isolate);
   int contextGroupId =
-      m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0;
+      m_isolate->InContext()
+          ? m_inspector->contextGroupId(m_isolate->GetCurrentContext())
+          : 0;
   std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
       this, contextGroupId, V8StackTraceImpl::maxCallStackSizeToCapture,
       taskName);
   if (chain) {
     m_asyncTaskStacks[task] = std::move(chain);
     if (recurring) m_recurringTasks.insert(task);
+    registerAsyncTaskIfNeeded(task);
   }
 }
 
@@ -920,12 +883,20 @@
   if (!m_maxAsyncCallStackDepth) return;
   m_asyncTaskStacks.erase(task);
   m_recurringTasks.erase(task);
+  m_parentTask.erase(task);
+  m_asyncTaskCreationStacks.erase(task);
+  auto it = m_taskToId.find(task);
+  if (it == m_taskToId.end()) return;
+  m_idToTask.erase(it->second);
+  m_taskToId.erase(it);
 }
 
 void V8Debugger::asyncTaskStarted(void* task) {
   if (!m_maxAsyncCallStackDepth) return;
   m_currentTasks.push_back(task);
-  AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
+  auto parentIt = m_parentTask.find(task);
+  AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(
+      parentIt == m_parentTask.end() ? task : parentIt->second);
   // Needs to support following order of events:
   // - asyncTaskScheduled
   //   <-- attached here -->
@@ -936,6 +907,10 @@
   std::unique_ptr<V8StackTraceImpl> stack;
   if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
     stack = stackIt->second->cloneImpl();
+  auto itCreation = m_asyncTaskCreationStacks.find(task);
+  if (stack && itCreation != m_asyncTaskCreationStacks.end()) {
+    stack->setCreation(itCreation->second->cloneImpl());
+  }
   m_currentStacks.push_back(std::move(stack));
 }
 
@@ -948,8 +923,9 @@
   m_currentTasks.pop_back();
 
   m_currentStacks.pop_back();
-  if (m_recurringTasks.find(task) == m_recurringTasks.end())
-    m_asyncTaskStacks.erase(task);
+  if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
+    asyncTaskCanceled(task);
+  }
 }
 
 void V8Debugger::allAsyncTasksCanceled() {
@@ -957,6 +933,11 @@
   m_recurringTasks.clear();
   m_currentStacks.clear();
   m_currentTasks.clear();
+  m_parentTask.clear();
+  m_asyncTaskCreationStacks.clear();
+  m_idToTask.clear();
+  m_taskToId.clear();
+  m_lastTaskId = 0;
 }
 
 void V8Debugger::muteScriptParsedEvents() {
@@ -973,7 +954,8 @@
   if (!m_isolate->InContext()) return nullptr;
 
   v8::HandleScope handles(m_isolate);
-  int contextGroupId = getGroupId(m_isolate->GetCurrentContext());
+  int contextGroupId =
+      m_inspector->contextGroupId(m_isolate->GetCurrentContext());
   if (!contextGroupId) return nullptr;
 
   size_t stackSize =
diff --git a/src/inspector/v8-debugger.h b/src/inspector/v8-debugger.h
index 4c74778..c45c76f 100644
--- a/src/inspector/v8-debugger.h
+++ b/src/inspector/v8-debugger.h
@@ -13,6 +13,7 @@
 #include "src/inspector/protocol/Forward.h"
 #include "src/inspector/protocol/Runtime.h"
 #include "src/inspector/v8-debugger-script.h"
+#include "src/inspector/wasm-translation.h"
 
 #include "include/v8-inspector.h"
 
@@ -25,25 +26,21 @@
 
 using protocol::Response;
 
-class V8Debugger {
+class V8Debugger : public v8::debug::DebugDelegate {
  public:
   V8Debugger(v8::Isolate*, V8InspectorImpl*);
   ~V8Debugger();
 
-  static int contextId(v8::Local<v8::Context>);
-  static int getGroupId(v8::Local<v8::Context>);
-  int markContext(const V8ContextInfo&);
-
   bool enabled() const;
 
-  String16 setBreakpoint(const String16& sourceID, const ScriptBreakpoint&,
-                         int* actualLineNumber, int* actualColumnNumber);
+  String16 setBreakpoint(const ScriptBreakpoint&, int* actualLineNumber,
+                         int* actualColumnNumber);
   void removeBreakpoint(const String16& breakpointId);
   void setBreakpointsActivated(bool);
   bool breakpointsActivated() const { return m_breakpointsActivated; }
 
-  v8::DebugInterface::ExceptionBreakState getPauseOnExceptionsState();
-  void setPauseOnExceptionsState(v8::DebugInterface::ExceptionBreakState);
+  v8::debug::ExceptionBreakState getPauseOnExceptionsState();
+  void setPauseOnExceptionsState(v8::debug::ExceptionBreakState);
   void setPauseOnNextStatement(bool);
   bool canBreakProgram();
   void breakProgram();
@@ -51,7 +48,6 @@
   void stepIntoStatement();
   void stepOverStatement();
   void stepOutOfFunction();
-  void clearStepping();
 
   Response setScriptSource(
       const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
@@ -69,7 +65,7 @@
   void enable();
   void disable();
 
-  bool isPaused();
+  bool isPaused() const { return m_runningNestedMessageLoop; }
   v8::Local<v8::Context> pausedContext() { return m_pausedContext; }
 
   int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
@@ -94,14 +90,21 @@
 
   V8InspectorImpl* inspector() { return m_inspector; }
 
+  WasmTranslation* wasmTranslation() { return &m_wasmTranslation; }
+
+  void setMaxAsyncTaskStacksForTest(int limit) { m_maxAsyncCallStacks = limit; }
+
  private:
   void compileDebuggerScript();
   v8::MaybeLocal<v8::Value> callDebuggerMethod(const char* functionName,
                                                int argc,
-                                               v8::Local<v8::Value> argv[]);
+                                               v8::Local<v8::Value> argv[],
+                                               bool catchExceptions);
   v8::Local<v8::Context> debuggerContext() const;
   void clearBreakpoints();
 
+  static void v8OOMCallback(void* data);
+
   static void breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>&);
   void handleProgramBreak(v8::Local<v8::Context> pausedContext,
                           v8::Local<v8::Object> executionState,
@@ -109,26 +112,41 @@
                           v8::Local<v8::Array> hitBreakpoints,
                           bool isPromiseRejection = false,
                           bool isUncaught = false);
-  static void v8DebugEventCallback(const v8::DebugInterface::EventDetails&);
-  v8::Local<v8::Value> callInternalGetterFunction(v8::Local<v8::Object>,
-                                                  const char* functionName);
-  void handleV8DebugEvent(const v8::DebugInterface::EventDetails&);
-  void handleV8AsyncTaskEvent(v8::Local<v8::Context>,
-                              v8::Local<v8::Object> executionState,
-                              v8::Local<v8::Object> eventData);
 
-  v8::Local<v8::Value> collectionEntries(v8::Local<v8::Context>,
-                                         v8::Local<v8::Object>);
-  v8::Local<v8::Value> generatorObjectLocation(v8::Local<v8::Context>,
-                                               v8::Local<v8::Object>);
-  v8::Local<v8::Value> functionLocation(v8::Local<v8::Context>,
-                                        v8::Local<v8::Function>);
+  enum ScopeTargetKind {
+    FUNCTION,
+    GENERATOR,
+  };
+  v8::MaybeLocal<v8::Value> getTargetScopes(v8::Local<v8::Context>,
+                                            v8::Local<v8::Value>,
+                                            ScopeTargetKind);
+
   v8::MaybeLocal<v8::Value> functionScopes(v8::Local<v8::Context>,
                                            v8::Local<v8::Function>);
+  v8::MaybeLocal<v8::Value> generatorScopes(v8::Local<v8::Context>,
+                                            v8::Local<v8::Value>);
+
+  void asyncTaskCreated(void* task, void* parentTask);
+  void registerAsyncTaskIfNeeded(void* task);
+
+  // v8::debug::DebugEventListener implementation.
+  void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
+                            int parentId) 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;
 
   v8::Isolate* m_isolate;
   V8InspectorImpl* m_inspector;
-  int m_lastContextId;
   int m_enableCount;
   bool m_breakpointsActivated;
   v8::Global<v8::Object> m_debuggerScript;
@@ -137,17 +155,26 @@
   v8::Local<v8::Context> m_pausedContext;
   bool m_runningNestedMessageLoop;
   int m_ignoreScriptParsedEventsCounter;
+  bool m_scheduledOOMBreak = false;
 
   using AsyncTaskToStackTrace =
       protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>;
   AsyncTaskToStackTrace m_asyncTaskStacks;
+  AsyncTaskToStackTrace m_asyncTaskCreationStacks;
+  int m_maxAsyncCallStacks;
+  std::map<int, void*> m_idToTask;
+  std::unordered_map<void*, int> m_taskToId;
+  int m_lastTaskId;
   protocol::HashSet<void*> m_recurringTasks;
   int m_maxAsyncCallStackDepth;
   std::vector<void*> m_currentTasks;
   std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks;
   protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
+  protocol::HashMap<void*, void*> m_parentTask;
 
-  v8::DebugInterface::ExceptionBreakState m_pauseOnExceptionsState;
+  v8::debug::ExceptionBreakState m_pauseOnExceptionsState;
+
+  WasmTranslation m_wasmTranslation;
 
   DISALLOW_COPY_AND_ASSIGN(V8Debugger);
 };
diff --git a/src/inspector/v8-function-call.cc b/src/inspector/v8-function-call.cc
index 3880e31..b8c86d3 100644
--- a/src/inspector/v8-function-call.cc
+++ b/src/inspector/v8-function-call.cc
@@ -30,6 +30,7 @@
 
 #include "src/inspector/v8-function-call.h"
 
+#include "src/inspector/inspected-context.h"
 #include "src/inspector/string-util.h"
 #include "src/inspector/v8-debugger.h"
 #include "src/inspector/v8-inspector-impl.h"
@@ -89,7 +90,7 @@
     DCHECK(!info[i].IsEmpty());
   }
 
-  int contextGroupId = V8Debugger::getGroupId(m_context);
+  int contextGroupId = m_inspector->contextGroupId(m_context);
   if (contextGroupId) {
     m_inspector->client()->muteMetrics(contextGroupId);
     m_inspector->muteExceptions(contextGroupId);
diff --git a/src/inspector/v8-heap-profiler-agent-impl.cc b/src/inspector/v8-heap-profiler-agent-impl.cc
index 0ff04e7..b3e3d11 100644
--- a/src/inspector/v8-heap-profiler-agent-impl.cc
+++ b/src/inspector/v8-heap-profiler-agent-impl.cc
@@ -5,6 +5,7 @@
 #include "src/inspector/v8-heap-profiler-agent-impl.h"
 
 #include "src/inspector/injected-script.h"
+#include "src/inspector/inspected-context.h"
 #include "src/inspector/protocol/Protocol.h"
 #include "src/inspector/string-util.h"
 #include "src/inspector/v8-debugger.h"
@@ -55,7 +56,7 @@
   const char* GetName(v8::Local<v8::Object> object) override {
     InspectedContext* context = m_session->inspector()->getContext(
         m_session->contextGroupId(),
-        V8Debugger::contextId(object->CreationContext()));
+        InspectedContext::contextId(object->CreationContext()));
     if (!context) return "";
     String16 name = context->origin();
     size_t length = name.length();
@@ -216,7 +217,7 @@
   if (!profiler) return Response::Error("Cannot access v8 heap profiler");
   std::unique_ptr<HeapSnapshotProgress> progress;
   if (reportProgress.fromMaybe(false))
-    progress = wrapUnique(new HeapSnapshotProgress(&m_frontend));
+    progress.reset(new HeapSnapshotProgress(&m_frontend));
 
   GlobalObjectNameResolver resolver(m_session);
   const v8::HeapSnapshot* snapshot =
@@ -244,7 +245,7 @@
 
   *result = m_session->wrapObject(heapObject->CreationContext(), heapObject,
                                   objectGroup.fromMaybe(""), false);
-  if (!result) return Response::Error("Object is not available");
+  if (!*result) return Response::Error("Object is not available");
   return Response::OK();
 }
 
@@ -260,7 +261,8 @@
 
   if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
     return Response::Error("Object is not available");
-  m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id)));
+  m_session->addInspectedObject(
+      std::unique_ptr<InspectableHeapObject>(new InspectableHeapObject(id)));
   return Response::OK();
 }
 
diff --git a/src/inspector/v8-injected-script-host.cc b/src/inspector/v8-injected-script-host.cc
index 3748ec9..b3bd5ef 100644
--- a/src/inspector/v8-injected-script-host.cc
+++ b/src/inspector/v8-injected-script-host.cc
@@ -55,6 +55,9 @@
   USE(success);
   v8::Local<v8::External> debuggerExternal =
       v8::External::New(isolate, inspector);
+  setFunctionProperty(context, injectedScriptHost, "nullifyPrototype",
+                      V8InjectedScriptHost::nullifyPrototypeCallback,
+                      debuggerExternal);
   setFunctionProperty(context, injectedScriptHost, "internalConstructorName",
                       V8InjectedScriptHost::internalConstructorNameCallback,
                       debuggerExternal);
@@ -77,6 +80,16 @@
   return injectedScriptHost;
 }
 
+void V8InjectedScriptHost::nullifyPrototypeCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  CHECK(info.Length() == 1 && info[0]->IsObject());
+  v8::Isolate* isolate = info.GetIsolate();
+  info[0]
+      .As<v8::Object>()
+      ->SetPrototype(isolate->GetCurrentContext(), v8::Null(isolate))
+      .ToChecked();
+}
+
 void V8InjectedScriptHost::internalConstructorNameCallback(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
   if (info.Length() < 1 || !info[0]->IsObject()) return;
diff --git a/src/inspector/v8-injected-script-host.h b/src/inspector/v8-injected-script-host.h
index 7d293af..a64c2f8 100644
--- a/src/inspector/v8-injected-script-host.h
+++ b/src/inspector/v8-injected-script-host.h
@@ -27,6 +27,8 @@
   static v8::Local<v8::Object> create(v8::Local<v8::Context>, V8InspectorImpl*);
 
  private:
+  static void nullifyPrototypeCallback(
+      const v8::FunctionCallbackInfo<v8::Value>&);
   static void internalConstructorNameCallback(
       const v8::FunctionCallbackInfo<v8::Value>&);
   static void formatAccessorsAsProperties(
diff --git a/src/inspector/v8-inspector-impl.cc b/src/inspector/v8-inspector-impl.cc
index bd68548..34e4120 100644
--- a/src/inspector/v8-inspector-impl.cc
+++ b/src/inspector/v8-inspector-impl.cc
@@ -45,7 +45,7 @@
 
 std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate,
                                                  V8InspectorClient* client) {
-  return wrapUnique(new V8InspectorImpl(isolate, client));
+  return std::unique_ptr<V8Inspector>(new V8InspectorImpl(isolate, client));
 }
 
 V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate,
@@ -54,10 +54,21 @@
       m_client(client),
       m_debugger(new V8Debugger(isolate, this)),
       m_capturingStackTracesCount(0),
-      m_lastExceptionId(0) {}
+      m_lastExceptionId(0),
+      m_lastContextId(0) {}
 
 V8InspectorImpl::~V8InspectorImpl() {}
 
+int V8InspectorImpl::contextGroupId(v8::Local<v8::Context> context) {
+  return contextGroupId(InspectedContext::contextId(context));
+}
+
+int V8InspectorImpl::contextGroupId(int contextId) {
+  protocol::HashMap<int, int>::iterator it =
+      m_contextIdToGroupIdMap.find(contextId);
+  return it != m_contextIdToGroupIdMap.end() ? it->second : 0;
+}
+
 V8DebuggerAgentImpl* V8InspectorImpl::enabledDebuggerAgentForGroup(
     int contextGroupId) {
   V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId);
@@ -83,7 +94,7 @@
     v8::Local<v8::Context> context, v8::Local<v8::Script> script) {
   v8::MicrotasksScope microtasksScope(m_isolate,
                                       v8::MicrotasksScope::kRunMicrotasks);
-  int groupId = V8Debugger::getGroupId(context);
+  int groupId = contextGroupId(context);
   if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
     agent->willExecuteScript(script->GetUnboundScript()->GetId());
   v8::MaybeLocal<v8::Value> result = script->Run(context);
@@ -97,9 +108,23 @@
 v8::MaybeLocal<v8::Value> V8InspectorImpl::callFunction(
     v8::Local<v8::Function> function, v8::Local<v8::Context> context,
     v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]) {
-  v8::MicrotasksScope microtasksScope(m_isolate,
-                                      v8::MicrotasksScope::kRunMicrotasks);
-  int groupId = V8Debugger::getGroupId(context);
+  return callFunction(function, context, receiver, argc, info,
+                      v8::MicrotasksScope::kRunMicrotasks);
+}
+
+v8::MaybeLocal<v8::Value> V8InspectorImpl::callInternalFunction(
+    v8::Local<v8::Function> function, v8::Local<v8::Context> context,
+    v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]) {
+  return callFunction(function, context, receiver, argc, info,
+                      v8::MicrotasksScope::kDoNotRunMicrotasks);
+}
+
+v8::MaybeLocal<v8::Value> V8InspectorImpl::callFunction(
+    v8::Local<v8::Function> function, v8::Local<v8::Context> context,
+    v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[],
+    v8::MicrotasksScope::Type runMicrotasks) {
+  v8::MicrotasksScope microtasksScope(m_isolate, runMicrotasks);
+  int groupId = contextGroupId(context);
   if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
     agent->willExecuteScript(function->ScriptId());
   v8::MaybeLocal<v8::Value> result =
@@ -113,32 +138,28 @@
 
 v8::MaybeLocal<v8::Value> V8InspectorImpl::compileAndRunInternalScript(
     v8::Local<v8::Context> context, v8::Local<v8::String> source) {
-  v8::Local<v8::Script> script =
-      compileScript(context, source, String16(), true);
-  if (script.IsEmpty()) return v8::MaybeLocal<v8::Value>();
+  v8::Local<v8::UnboundScript> unboundScript;
+  if (!v8::debug::CompileInspectorScript(m_isolate, source)
+           .ToLocal(&unboundScript))
+    return v8::MaybeLocal<v8::Value>();
   v8::MicrotasksScope microtasksScope(m_isolate,
                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
-  return script->Run(context);
+  v8::Context::Scope contextScope(context);
+  return unboundScript->BindToCurrentContext()->Run(context);
 }
 
-v8::Local<v8::Script> V8InspectorImpl::compileScript(
-    v8::Local<v8::Context> context, v8::Local<v8::String> code,
-    const String16& fileName, bool markAsInternal) {
+v8::MaybeLocal<v8::Script> V8InspectorImpl::compileScript(
+    v8::Local<v8::Context> context, const String16& code,
+    const String16& fileName) {
   v8::ScriptOrigin origin(
       toV8String(m_isolate, fileName), v8::Integer::New(m_isolate, 0),
       v8::Integer::New(m_isolate, 0),
-      v8::False(m_isolate),  // sharable
-      v8::Local<v8::Integer>(),
-      v8::Boolean::New(m_isolate, markAsInternal),  // internal
-      toV8String(m_isolate, String16()),            // sourceMap
-      v8::True(m_isolate));                         // opaqueresource
-  v8::ScriptCompiler::Source source(code, origin);
-  v8::Local<v8::Script> script;
-  if (!v8::ScriptCompiler::Compile(context, &source,
-                                   v8::ScriptCompiler::kNoCompileOptions)
-           .ToLocal(&script))
-    return v8::Local<v8::Script>();
-  return script;
+      v8::False(m_isolate),                                         // sharable
+      v8::Local<v8::Integer>(), toV8String(m_isolate, String16()),  // sourceMap
+      v8::True(m_isolate));  // opaqueresource
+  v8::ScriptCompiler::Source source(toV8String(m_isolate, code), origin);
+  return v8::ScriptCompiler::Compile(context, &source,
+                                     v8::ScriptCompiler::kNoCompileOptions);
 }
 
 void V8InspectorImpl::enableStackCapturingIfNeeded() {
@@ -167,12 +188,12 @@
   ConsoleStorageMap::iterator storageIt =
       m_consoleStorageMap.find(contextGroupId);
   if (storageIt == m_consoleStorageMap.end())
-    storageIt =
-        m_consoleStorageMap
-            .insert(std::make_pair(
-                contextGroupId,
-                wrapUnique(new V8ConsoleMessageStorage(this, contextGroupId))))
-            .first;
+    storageIt = m_consoleStorageMap
+                    .insert(std::make_pair(
+                        contextGroupId,
+                        std::unique_ptr<V8ConsoleMessageStorage>(
+                            new V8ConsoleMessageStorage(this, contextGroupId))))
+                    .first;
   return storageIt->second.get();
 }
 
@@ -216,42 +237,43 @@
 }
 
 void V8InspectorImpl::contextCreated(const V8ContextInfo& info) {
-  int contextId = m_debugger->markContext(info);
+  int contextId = ++m_lastContextId;
+  InspectedContext* context = new InspectedContext(this, info, contextId);
+  m_contextIdToGroupIdMap[contextId] = info.contextGroupId;
 
   ContextsByGroupMap::iterator contextIt = m_contexts.find(info.contextGroupId);
   if (contextIt == m_contexts.end())
     contextIt = m_contexts
-                    .insert(std::make_pair(info.contextGroupId,
-                                           wrapUnique(new ContextByIdMap())))
+                    .insert(std::make_pair(
+                        info.contextGroupId,
+                        std::unique_ptr<ContextByIdMap>(new ContextByIdMap())))
                     .first;
-
   const auto& contextById = contextIt->second;
 
   DCHECK(contextById->find(contextId) == contextById->cend());
-  InspectedContext* context = new InspectedContext(this, info, contextId);
-  (*contextById)[contextId] = wrapUnique(context);
+  (*contextById)[contextId].reset(context);
   SessionMap::iterator sessionIt = m_sessions.find(info.contextGroupId);
   if (sessionIt != m_sessions.end())
     sessionIt->second->runtimeAgent()->reportExecutionContextCreated(context);
 }
 
 void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) {
-  int contextId = V8Debugger::contextId(context);
-  int contextGroupId = V8Debugger::getGroupId(context);
+  int contextId = InspectedContext::contextId(context);
+  int groupId = contextGroupId(context);
+  m_contextIdToGroupIdMap.erase(contextId);
 
-  ConsoleStorageMap::iterator storageIt =
-      m_consoleStorageMap.find(contextGroupId);
+  ConsoleStorageMap::iterator storageIt = m_consoleStorageMap.find(groupId);
   if (storageIt != m_consoleStorageMap.end())
     storageIt->second->contextDestroyed(contextId);
 
-  InspectedContext* inspectedContext = getContext(contextGroupId, contextId);
+  InspectedContext* inspectedContext = getContext(groupId, contextId);
   if (!inspectedContext) return;
 
-  SessionMap::iterator iter = m_sessions.find(contextGroupId);
+  SessionMap::iterator iter = m_sessions.find(groupId);
   if (iter != m_sessions.end())
     iter->second->runtimeAgent()->reportExecutionContextDestroyed(
         inspectedContext);
-  discardInspectedContext(contextGroupId, contextId);
+  discardInspectedContext(groupId, contextId);
 }
 
 void V8InspectorImpl::resetContextGroup(int contextGroupId) {
@@ -260,19 +282,22 @@
   SessionMap::iterator session = m_sessions.find(contextGroupId);
   if (session != m_sessions.end()) session->second->reset();
   m_contexts.erase(contextGroupId);
+  m_debugger->wasmTranslation()->Clear();
 }
 
 void V8InspectorImpl::willExecuteScript(v8::Local<v8::Context> context,
                                         int scriptId) {
   if (V8DebuggerAgentImpl* agent =
-          enabledDebuggerAgentForGroup(V8Debugger::getGroupId(context)))
+          enabledDebuggerAgentForGroup(contextGroupId(context))) {
     agent->willExecuteScript(scriptId);
+  }
 }
 
 void V8InspectorImpl::didExecuteScript(v8::Local<v8::Context> context) {
   if (V8DebuggerAgentImpl* agent =
-          enabledDebuggerAgentForGroup(V8Debugger::getGroupId(context)))
+          enabledDebuggerAgentForGroup(contextGroupId(context))) {
     agent->didExecuteScript();
+  }
 }
 
 void V8InspectorImpl::idleStarted() {
@@ -292,33 +317,31 @@
     v8::Local<v8::Value> exception, const StringView& detailedMessage,
     const StringView& url, unsigned lineNumber, unsigned columnNumber,
     std::unique_ptr<V8StackTrace> stackTrace, int scriptId) {
-  int contextGroupId = V8Debugger::getGroupId(context);
-  if (!contextGroupId || m_muteExceptionsMap[contextGroupId]) return 0;
-  std::unique_ptr<V8StackTraceImpl> stackTraceImpl =
-      wrapUnique(static_cast<V8StackTraceImpl*>(stackTrace.release()));
+  int groupId = contextGroupId(context);
+  if (!groupId || m_muteExceptionsMap[groupId]) return 0;
+  std::unique_ptr<V8StackTraceImpl> stackTraceImpl(
+      static_cast<V8StackTraceImpl*>(stackTrace.release()));
   unsigned exceptionId = nextExceptionId();
   std::unique_ptr<V8ConsoleMessage> consoleMessage =
       V8ConsoleMessage::createForException(
           m_client->currentTimeMS(), toString16(detailedMessage),
           toString16(url), lineNumber, columnNumber, std::move(stackTraceImpl),
           scriptId, m_isolate, toString16(message),
-          V8Debugger::contextId(context), exception, exceptionId);
-  ensureConsoleMessageStorage(contextGroupId)
-      ->addMessage(std::move(consoleMessage));
+          InspectedContext::contextId(context), exception, exceptionId);
+  ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
   return exceptionId;
 }
 
 void V8InspectorImpl::exceptionRevoked(v8::Local<v8::Context> context,
                                        unsigned exceptionId,
                                        const StringView& message) {
-  int contextGroupId = V8Debugger::getGroupId(context);
-  if (!contextGroupId) return;
+  int groupId = contextGroupId(context);
+  if (!groupId) return;
 
   std::unique_ptr<V8ConsoleMessage> consoleMessage =
       V8ConsoleMessage::createForRevokedException(
           m_client->currentTimeMS(), toString16(message), exceptionId);
-  ensureConsoleMessageStorage(contextGroupId)
-      ->addMessage(std::move(consoleMessage));
+  ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
 }
 
 std::unique_ptr<V8StackTrace> V8InspectorImpl::captureStackTrace(
diff --git a/src/inspector/v8-inspector-impl.h b/src/inspector/v8-inspector-impl.h
index 0ca1a6a..9d6e62c 100644
--- a/src/inspector/v8-inspector-impl.h
+++ b/src/inspector/v8-inspector-impl.h
@@ -36,7 +36,6 @@
 #include "src/base/macros.h"
 #include "src/inspector/protocol/Protocol.h"
 
-#include "include/v8-debug.h"
 #include "include/v8-inspector.h"
 
 namespace v8_inspector {
@@ -58,6 +57,8 @@
   v8::Isolate* isolate() const { return m_isolate; }
   V8InspectorClient* client() { return m_client; }
   V8Debugger* debugger() { return m_debugger.get(); }
+  int contextGroupId(v8::Local<v8::Context>);
+  int contextGroupId(int contextId);
 
   v8::MaybeLocal<v8::Value> runCompiledScript(v8::Local<v8::Context>,
                                               v8::Local<v8::Script>);
@@ -67,10 +68,14 @@
                                          int argc, v8::Local<v8::Value> info[]);
   v8::MaybeLocal<v8::Value> compileAndRunInternalScript(v8::Local<v8::Context>,
                                                         v8::Local<v8::String>);
-  v8::Local<v8::Script> compileScript(v8::Local<v8::Context>,
-                                      v8::Local<v8::String>,
-                                      const String16& fileName,
-                                      bool markAsInternal);
+  v8::MaybeLocal<v8::Value> callInternalFunction(v8::Local<v8::Function>,
+                                                 v8::Local<v8::Context>,
+                                                 v8::Local<v8::Value> receiver,
+                                                 int argc,
+                                                 v8::Local<v8::Value> info[]);
+  v8::MaybeLocal<v8::Script> compileScript(v8::Local<v8::Context>,
+                                           const String16& code,
+                                           const String16& fileName);
   v8::Local<v8::Context> regexContext();
 
   // V8Inspector implementation.
@@ -121,12 +126,18 @@
   V8ProfilerAgentImpl* enabledProfilerAgentForGroup(int contextGroupId);
 
  private:
+  v8::MaybeLocal<v8::Value> callFunction(
+      v8::Local<v8::Function>, v8::Local<v8::Context>,
+      v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[],
+      v8::MicrotasksScope::Type runMicrotasks);
+
   v8::Isolate* m_isolate;
   V8InspectorClient* m_client;
   std::unique_ptr<V8Debugger> m_debugger;
   v8::Global<v8::Context> m_regexContext;
   int m_capturingStackTracesCount;
   unsigned m_lastExceptionId;
+  int m_lastContextId;
 
   using MuteExceptionsMap = protocol::HashMap<int, int>;
   MuteExceptionsMap m_muteExceptionsMap;
@@ -142,6 +153,8 @@
       protocol::HashMap<int, std::unique_ptr<V8ConsoleMessageStorage>>;
   ConsoleStorageMap m_consoleStorageMap;
 
+  protocol::HashMap<int, int> m_contextIdToGroupIdMap;
+
   DISALLOW_COPY_AND_ASSIGN(V8InspectorImpl);
 };
 
diff --git a/src/inspector/v8-inspector-session-impl.cc b/src/inspector/v8-inspector-session-impl.cc
index e415575..2674fc2 100644
--- a/src/inspector/v8-inspector-session-impl.cc
+++ b/src/inspector/v8-inspector-session-impl.cc
@@ -37,10 +37,15 @@
                               protocol::Schema::Metainfo::commandPrefix);
 }
 
+// static
+int V8ContextInfo::executionContextId(v8::Local<v8::Context> context) {
+  return InspectedContext::contextId(context);
+}
+
 std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(
     V8InspectorImpl* inspector, int contextGroupId,
     V8Inspector::Channel* channel, const StringView& state) {
-  return wrapUnique(
+  return std::unique_ptr<V8InspectorSessionImpl>(
       new V8InspectorSessionImpl(inspector, contextGroupId, channel, state));
 }
 
@@ -62,35 +67,35 @@
       m_schemaAgent(nullptr) {
   if (savedState.length()) {
     std::unique_ptr<protocol::Value> state =
-        protocol::parseJSON(toString16(savedState));
+        protocol::StringUtil::parseJSON(toString16(savedState));
     if (state) m_state = protocol::DictionaryValue::cast(std::move(state));
     if (!m_state) m_state = protocol::DictionaryValue::create();
   } else {
     m_state = protocol::DictionaryValue::create();
   }
 
-  m_runtimeAgent = wrapUnique(new V8RuntimeAgentImpl(
+  m_runtimeAgent.reset(new V8RuntimeAgentImpl(
       this, this, agentState(protocol::Runtime::Metainfo::domainName)));
   protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get());
 
-  m_debuggerAgent = wrapUnique(new V8DebuggerAgentImpl(
+  m_debuggerAgent.reset(new V8DebuggerAgentImpl(
       this, this, agentState(protocol::Debugger::Metainfo::domainName)));
   protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get());
 
-  m_profilerAgent = wrapUnique(new V8ProfilerAgentImpl(
+  m_profilerAgent.reset(new V8ProfilerAgentImpl(
       this, this, agentState(protocol::Profiler::Metainfo::domainName)));
   protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get());
 
-  m_heapProfilerAgent = wrapUnique(new V8HeapProfilerAgentImpl(
+  m_heapProfilerAgent.reset(new V8HeapProfilerAgentImpl(
       this, this, agentState(protocol::HeapProfiler::Metainfo::domainName)));
   protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher,
                                            m_heapProfilerAgent.get());
 
-  m_consoleAgent = wrapUnique(new V8ConsoleAgentImpl(
+  m_consoleAgent.reset(new V8ConsoleAgentImpl(
       this, this, agentState(protocol::Console::Metainfo::domainName)));
   protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get());
 
-  m_schemaAgent = wrapUnique(new V8SchemaAgentImpl(
+  m_schemaAgent.reset(new V8SchemaAgentImpl(
       this, this, agentState(protocol::Schema::Metainfo::domainName)));
   protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get());
 
@@ -126,13 +131,42 @@
   return state;
 }
 
-void V8InspectorSessionImpl::sendProtocolResponse(int callId,
-                                                  const String16& message) {
-  m_channel->sendProtocolResponse(callId, toStringView(message));
+namespace {
+
+class MessageBuffer : public StringBuffer {
+ public:
+  static std::unique_ptr<MessageBuffer> create(
+      std::unique_ptr<protocol::Serializable> message) {
+    return std::unique_ptr<MessageBuffer>(
+        new MessageBuffer(std::move(message)));
+  }
+
+  const StringView& string() override {
+    if (!m_serialized) {
+      m_serialized = StringBuffer::create(toStringView(m_message->serialize()));
+      m_message.reset(nullptr);
+    }
+    return m_serialized->string();
+  }
+
+ private:
+  explicit MessageBuffer(std::unique_ptr<protocol::Serializable> message)
+      : m_message(std::move(message)) {}
+
+  std::unique_ptr<protocol::Serializable> m_message;
+  std::unique_ptr<StringBuffer> m_serialized;
+};
+
+}  // namespace
+
+void V8InspectorSessionImpl::sendProtocolResponse(
+    int callId, std::unique_ptr<protocol::Serializable> message) {
+  m_channel->sendResponse(callId, MessageBuffer::create(std::move(message)));
 }
 
-void V8InspectorSessionImpl::sendProtocolNotification(const String16& message) {
-  m_channel->sendProtocolNotification(toStringView(message));
+void V8InspectorSessionImpl::sendProtocolNotification(
+    std::unique_ptr<protocol::Serializable> message) {
+  m_channel->sendNotification(MessageBuffer::create(std::move(message)));
 }
 
 void V8InspectorSessionImpl::flushProtocolNotifications() {
@@ -266,7 +300,7 @@
                                    const String16& groupName,
                                    bool generatePreview) {
   InjectedScript* injectedScript = nullptr;
-  findInjectedScript(V8Debugger::contextId(context), injectedScript);
+  findInjectedScript(InspectedContext::contextId(context), injectedScript);
   if (!injectedScript) return nullptr;
   std::unique_ptr<protocol::Runtime::RemoteObject> result;
   injectedScript->wrapObject(value, groupName, false, generatePreview, &result);
@@ -278,7 +312,7 @@
                                   v8::Local<v8::Value> table,
                                   v8::Local<v8::Value> columns) {
   InjectedScript* injectedScript = nullptr;
-  findInjectedScript(V8Debugger::contextId(context), injectedScript);
+  findInjectedScript(InspectedContext::contextId(context), injectedScript);
   if (!injectedScript) return nullptr;
   return injectedScript->wrapTable(table, columns);
 }
@@ -305,11 +339,11 @@
 
 void V8InspectorSessionImpl::dispatchProtocolMessage(
     const StringView& message) {
-  m_dispatcher.dispatch(protocol::parseJSON(message));
+  m_dispatcher.dispatch(protocol::StringUtil::parseJSON(message));
 }
 
 std::unique_ptr<StringBuffer> V8InspectorSessionImpl::stateJSON() {
-  String16 json = m_state->toJSONString();
+  String16 json = m_state->serialize();
   return StringBufferImpl::adopt(json);
 }
 
@@ -366,7 +400,8 @@
     const StringView& breakReason, const StringView& breakDetails) {
   m_debuggerAgent->schedulePauseOnNextStatement(
       toString16(breakReason),
-      protocol::DictionaryValue::cast(protocol::parseJSON(breakDetails)));
+      protocol::DictionaryValue::cast(
+          protocol::StringUtil::parseJSON(breakDetails)));
 }
 
 void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
@@ -377,7 +412,8 @@
                                           const StringView& breakDetails) {
   m_debuggerAgent->breakProgram(
       toString16(breakReason),
-      protocol::DictionaryValue::cast(protocol::parseJSON(breakDetails)));
+      protocol::DictionaryValue::cast(
+          protocol::StringUtil::parseJSON(breakDetails)));
 }
 
 void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
diff --git a/src/inspector/v8-inspector-session-impl.h b/src/inspector/v8-inspector-session-impl.h
index af65aa3..7a59e1c 100644
--- a/src/inspector/v8-inspector-session-impl.h
+++ b/src/inspector/v8-inspector-session-impl.h
@@ -96,8 +96,10 @@
   protocol::DictionaryValue* agentState(const String16& name);
 
   // protocol::FrontendChannel implementation.
-  void sendProtocolResponse(int callId, const String16& message) override;
-  void sendProtocolNotification(const String16& message) override;
+  void sendProtocolResponse(
+      int callId, std::unique_ptr<protocol::Serializable> message) override;
+  void sendProtocolNotification(
+      std::unique_ptr<protocol::Serializable> message) override;
   void flushProtocolNotifications() override;
 
   int m_contextGroupId;
diff --git a/src/inspector/v8-internal-value-type.cc b/src/inspector/v8-internal-value-type.cc
index cde8bc9..46f5dac 100644
--- a/src/inspector/v8-internal-value-type.cc
+++ b/src/inspector/v8-internal-value-type.cc
@@ -4,7 +4,6 @@
 
 #include "src/inspector/v8-internal-value-type.h"
 
-#include "src/inspector/protocol-platform.h"
 #include "src/inspector/string-util.h"
 
 namespace v8_inspector {
diff --git a/src/inspector/v8-profiler-agent-impl.cc b/src/inspector/v8-profiler-agent-impl.cc
index 8b888a0..c7d1cc2 100644
--- a/src/inspector/v8-profiler-agent-impl.cc
+++ b/src/inspector/v8-profiler-agent-impl.cc
@@ -22,6 +22,7 @@
 static const char samplingInterval[] = "samplingInterval";
 static const char userInitiatedProfiling[] = "userInitiatedProfiling";
 static const char profilerEnabled[] = "profilerEnabled";
+static const char preciseCoverageStarted[] = "preciseCoverageStarted";
 }
 
 namespace {
@@ -152,11 +153,8 @@
     protocol::DictionaryValue* state)
     : m_session(session),
       m_isolate(m_session->inspector()->isolate()),
-      m_profiler(nullptr),
       m_state(state),
-      m_frontend(frontendChannel),
-      m_enabled(false),
-      m_recordingCPUProfile(false) {}
+      m_frontend(frontendChannel) {}
 
 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
   if (m_profiler) m_profiler->Dispose();
@@ -204,8 +202,6 @@
 Response V8ProfilerAgentImpl::enable() {
   if (m_enabled) return Response::OK();
   m_enabled = true;
-  DCHECK(!m_profiler);
-  m_profiler = v8::CpuProfiler::New(m_isolate);
   m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
   return Response::OK();
 }
@@ -216,18 +212,18 @@
     stopProfiling(m_startedProfiles[i - 1].m_id, false);
   m_startedProfiles.clear();
   stop(nullptr);
-  m_profiler->Dispose();
-  m_profiler = nullptr;
+  stopPreciseCoverage();
+  DCHECK(!m_profiler);
   m_enabled = false;
   m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
   return Response::OK();
 }
 
 Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
-  if (m_recordingCPUProfile)
+  if (m_profiler) {
     return Response::Error("Cannot change sampling interval when profiling.");
+  }
   m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
-  m_profiler->SetSamplingInterval(interval);
   return Response::OK();
 }
 
@@ -237,14 +233,14 @@
     return;
   m_enabled = true;
   DCHECK(!m_profiler);
-  m_profiler = v8::CpuProfiler::New(m_isolate);
-  int interval = 0;
-  m_state->getInteger(ProfilerAgentState::samplingInterval, &interval);
-  if (interval) m_profiler->SetSamplingInterval(interval);
   if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
                                false)) {
     start();
   }
+  if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
+                               false)) {
+    startPreciseCoverage();
+  }
 }
 
 Response V8ProfilerAgentImpl::start() {
@@ -259,8 +255,9 @@
 
 Response V8ProfilerAgentImpl::stop(
     std::unique_ptr<protocol::Profiler::Profile>* profile) {
-  if (!m_recordingCPUProfile)
+  if (!m_recordingCPUProfile) {
     return Response::Error("No recording profiles found");
+  }
   m_recordingCPUProfile = false;
   std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
       stopProfiling(m_frontendInitiatedProfileId, !!profile);
@@ -273,6 +270,90 @@
   return Response::OK();
 }
 
+Response V8ProfilerAgentImpl::startPreciseCoverage() {
+  if (!m_enabled) return Response::Error("Profiler is not enabled");
+  m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, true);
+  v8::debug::Coverage::TogglePrecise(m_isolate, true);
+  return Response::OK();
+}
+
+Response V8ProfilerAgentImpl::stopPreciseCoverage() {
+  if (!m_enabled) return Response::Error("Profiler is not enabled");
+  m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, false);
+  v8::debug::Coverage::TogglePrecise(m_isolate, false);
+  return Response::OK();
+}
+
+namespace {
+Response takeCoverage(
+    v8::Isolate* isolate, bool reset_count,
+    std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
+        out_result) {
+  std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> result =
+      protocol::Array<protocol::Profiler::ScriptCoverage>::create();
+  v8::HandleScope handle_scope(isolate);
+  v8::debug::Coverage coverage =
+      v8::debug::Coverage::Collect(isolate, reset_count);
+  for (size_t i = 0; i < coverage.ScriptCount(); i++) {
+    v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
+    v8::Local<v8::debug::Script> script = script_data.GetScript();
+    std::unique_ptr<protocol::Array<protocol::Profiler::FunctionCoverage>>
+        functions =
+            protocol::Array<protocol::Profiler::FunctionCoverage>::create();
+    for (size_t j = 0; j < script_data.FunctionCount(); j++) {
+      v8::debug::Coverage::FunctionData function_data =
+          script_data.GetFunctionData(j);
+      std::unique_ptr<protocol::Array<protocol::Profiler::CoverageRange>>
+          ranges = protocol::Array<protocol::Profiler::CoverageRange>::create();
+      // At this point we only have per-function coverage data, so there is
+      // only one range per function.
+      ranges->addItem(
+          protocol::Profiler::CoverageRange::create()
+              .setStartLineNumber(function_data.Start().GetLineNumber())
+              .setStartColumnNumber(function_data.Start().GetColumnNumber())
+              .setEndLineNumber(function_data.End().GetLineNumber())
+              .setEndColumnNumber(function_data.End().GetColumnNumber())
+              .setCount(function_data.Count())
+              .build());
+      functions->addItem(
+          protocol::Profiler::FunctionCoverage::create()
+              .setFunctionName(toProtocolString(
+                  function_data.Name().FromMaybe(v8::Local<v8::String>())))
+              .setRanges(std::move(ranges))
+              .build());
+    }
+    String16 url;
+    v8::Local<v8::String> name;
+    if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name)) {
+      url = toProtocolString(name);
+    }
+    result->addItem(protocol::Profiler::ScriptCoverage::create()
+                        .setScriptId(String16::fromInteger(script->Id()))
+                        .setUrl(url)
+                        .setFunctions(std::move(functions))
+                        .build());
+  }
+  *out_result = std::move(result);
+  return Response::OK();
+}
+}  // anonymous namespace
+
+Response V8ProfilerAgentImpl::takePreciseCoverage(
+    std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
+        out_result) {
+  if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
+                                false)) {
+    return Response::Error("Precise coverage has not been started.");
+  }
+  return takeCoverage(m_isolate, true, out_result);
+}
+
+Response V8ProfilerAgentImpl::getBestEffortCoverage(
+    std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
+        out_result) {
+  return takeCoverage(m_isolate, false, out_result);
+}
+
 String16 V8ProfilerAgentImpl::nextProfileId() {
   return String16::fromInteger(
       v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1));
@@ -280,6 +361,15 @@
 
 void V8ProfilerAgentImpl::startProfiling(const String16& title) {
   v8::HandleScope handleScope(m_isolate);
+  if (!m_startedProfilesCount) {
+    DCHECK(!m_profiler);
+    m_profiler = v8::CpuProfiler::New(m_isolate);
+    m_profiler->SetIdle(m_idle);
+    int interval =
+        m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
+    if (interval) m_profiler->SetSamplingInterval(interval);
+  }
+  ++m_startedProfilesCount;
   m_profiler->StartProfiling(toV8String(m_isolate, title), true);
 }
 
@@ -288,29 +378,29 @@
   v8::HandleScope handleScope(m_isolate);
   v8::CpuProfile* profile =
       m_profiler->StopProfiling(toV8String(m_isolate, title));
-  if (!profile) return nullptr;
   std::unique_ptr<protocol::Profiler::Profile> result;
-  if (serialize) result = createCPUProfile(m_isolate, profile);
-  profile->Delete();
+  if (profile) {
+    if (serialize) result = createCPUProfile(m_isolate, profile);
+    profile->Delete();
+  }
+  --m_startedProfilesCount;
+  if (!m_startedProfilesCount) {
+    m_profiler->Dispose();
+    m_profiler = nullptr;
+  }
   return result;
 }
 
-bool V8ProfilerAgentImpl::isRecording() const {
-  return m_recordingCPUProfile || !m_startedProfiles.empty();
-}
-
 bool V8ProfilerAgentImpl::idleStarted() {
-  if (m_profiler) m_profiler->SetIdle(true);
+  m_idle = true;
+  if (m_profiler) m_profiler->SetIdle(m_idle);
   return m_profiler;
 }
 
 bool V8ProfilerAgentImpl::idleFinished() {
-  if (m_profiler) m_profiler->SetIdle(false);
+  m_idle = false;
+  if (m_profiler) m_profiler->SetIdle(m_idle);
   return m_profiler;
 }
 
-void V8ProfilerAgentImpl::collectSample() {
-  if (m_profiler) m_profiler->CollectSample();
-}
-
 }  // namespace v8_inspector
diff --git a/src/inspector/v8-profiler-agent-impl.h b/src/inspector/v8-profiler-agent-impl.h
index a634ff3..c60ff86 100644
--- a/src/inspector/v8-profiler-agent-impl.h
+++ b/src/inspector/v8-profiler-agent-impl.h
@@ -37,14 +37,21 @@
   Response start() override;
   Response stop(std::unique_ptr<protocol::Profiler::Profile>*) override;
 
+  Response startPreciseCoverage() override;
+  Response stopPreciseCoverage() override;
+  Response takePreciseCoverage(
+      std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
+          out_result) override;
+  Response getBestEffortCoverage(
+      std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
+          out_result) override;
+
   void consoleProfile(const String16& title);
   void consoleProfileEnd(const String16& title);
 
   bool idleStarted();
   bool idleFinished();
 
-  void collectSample();
-
  private:
   String16 nextProfileId();
 
@@ -52,18 +59,18 @@
   std::unique_ptr<protocol::Profiler::Profile> stopProfiling(
       const String16& title, bool serialize);
 
-  bool isRecording() const;
-
   V8InspectorSessionImpl* m_session;
   v8::Isolate* m_isolate;
-  v8::CpuProfiler* m_profiler;
+  v8::CpuProfiler* m_profiler = nullptr;
   protocol::DictionaryValue* m_state;
   protocol::Profiler::Frontend m_frontend;
-  bool m_enabled;
-  bool m_recordingCPUProfile;
+  bool m_enabled = false;
+  bool m_recordingCPUProfile = false;
   class ProfileDescriptor;
   std::vector<ProfileDescriptor> m_startedProfiles;
   String16 m_frontendInitiatedProfileId;
+  bool m_idle = false;
+  int m_startedProfilesCount = 0;
 
   DISALLOW_COPY_AND_ASSIGN(V8ProfilerAgentImpl);
 };
diff --git a/src/inspector/v8-runtime-agent-impl.cc b/src/inspector/v8-runtime-agent-impl.cc
index 4dbe60f..17c8a7b 100644
--- a/src/inspector/v8-runtime-agent-impl.cc
+++ b/src/inspector/v8-runtime-agent-impl.cc
@@ -30,6 +30,7 @@
 
 #include "src/inspector/v8-runtime-agent-impl.h"
 
+#include "src/debug/debug-interface.h"
 #include "src/inspector/injected-script.h"
 #include "src/inspector/inspected-context.h"
 #include "src/inspector/protocol/Protocol.h"
@@ -241,7 +242,7 @@
         inspector->client()->ensureDefaultContextInGroup(contextGroupId);
     if (defaultContext.IsEmpty())
       return Response::Error("Cannot find default execution context");
-    *contextId = V8Debugger::contextId(defaultContext);
+    *contextId = InspectedContext::contextId(defaultContext);
   }
   return Response::OK();
 }
@@ -293,11 +294,11 @@
   if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(true);
 
   v8::MaybeLocal<v8::Value> maybeResultValue;
-  v8::Local<v8::Script> script = m_inspector->compileScript(
-      scope.context(), toV8String(m_inspector->isolate(), expression),
-      String16(), false);
-  if (!script.IsEmpty())
+  v8::Local<v8::Script> script;
+  if (m_inspector->compileScript(scope.context(), expression, String16())
+          .ToLocal(&script)) {
     maybeResultValue = m_inspector->runCompiledScript(scope.context(), script);
+  }
 
   if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(false);
 
@@ -379,10 +380,14 @@
   if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
   if (userGesture.fromMaybe(false)) scope.pretendUserGesture();
 
-  v8::MaybeLocal<v8::Value> maybeFunctionValue =
-      m_inspector->compileAndRunInternalScript(
-          scope.context(),
-          toV8String(m_inspector->isolate(), "(" + expression + ")"));
+  v8::MaybeLocal<v8::Value> maybeFunctionValue;
+  v8::Local<v8::Script> functionScript;
+  if (m_inspector
+          ->compileScript(scope.context(), "(" + expression + ")", String16())
+          .ToLocal(&functionScript)) {
+    maybeFunctionValue =
+        m_inspector->runCompiledScript(scope.context(), functionScript);
+  }
   // Re-initialize after running client's code, as it could have destroyed
   // context or session.
   response = scope.initialize();
@@ -543,11 +548,11 @@
   if (!response.isSuccess()) return response;
 
   if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents();
-  v8::Local<v8::Script> script = m_inspector->compileScript(
-      scope.context(), toV8String(m_inspector->isolate(), expression),
-      sourceURL, false);
+  v8::Local<v8::Script> script;
+  bool isOk = m_inspector->compileScript(scope.context(), expression, sourceURL)
+                  .ToLocal(&script);
   if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents();
-  if (script.IsEmpty()) {
+  if (!isOk) {
     if (scope.tryCatch().HasCaught()) {
       response = scope.injectedScript()->createExceptionDetails(
           scope.tryCatch(), String16(), false, exceptionDetails);
@@ -702,7 +707,7 @@
           .build();
   if (!context->auxData().isEmpty())
     description->setAuxData(protocol::DictionaryValue::cast(
-        protocol::parseJSON(context->auxData())));
+        protocol::StringUtil::parseJSON(context->auxData())));
   m_frontend.executionContextCreated(std::move(description));
 }
 
diff --git a/src/inspector/v8-stack-trace-impl.cc b/src/inspector/v8-stack-trace-impl.cc
index 1a38c6d..7d0edef 100644
--- a/src/inspector/v8-stack-trace-impl.cc
+++ b/src/inspector/v8-stack-trace-impl.cc
@@ -5,12 +5,10 @@
 #include "src/inspector/v8-stack-trace-impl.h"
 
 #include "src/inspector/string-util.h"
+#include "src/inspector/v8-debugger-agent-impl.h"
 #include "src/inspector/v8-debugger.h"
 #include "src/inspector/v8-inspector-impl.h"
-#include "src/inspector/v8-profiler-agent-impl.h"
 
-#include "include/v8-debug.h"
-#include "include/v8-profiler.h"
 #include "include/v8-version.h"
 
 namespace v8_inspector {
@@ -23,7 +21,9 @@
         v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
         v8::StackTrace::kFunctionName);
 
-V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) {
+V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame,
+                                WasmTranslation* wasmTranslation,
+                                int contextGroupId) {
   String16 scriptId = String16::fromInteger(frame->GetScriptId());
   String16 sourceName;
   v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
@@ -35,22 +35,30 @@
   if (!functionNameValue.IsEmpty())
     functionName = toProtocolString(functionNameValue);
 
-  int sourceLineNumber = frame->GetLineNumber();
-  int sourceColumn = frame->GetColumn();
+  int sourceLineNumber = frame->GetLineNumber() - 1;
+  int sourceColumn = frame->GetColumn() - 1;
+  // TODO(clemensh): Figure out a way to do this translation only right before
+  // sending the stack trace over wire.
+  if (wasmTranslation)
+    wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
+        &scriptId, &sourceLineNumber, &sourceColumn);
   return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
-                                 sourceLineNumber, sourceColumn);
+                                 sourceLineNumber + 1, sourceColumn + 1);
 }
 
 void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
                     std::vector<V8StackTraceImpl::Frame>& frames,
-                    size_t maxStackSize, v8::Isolate* isolate) {
+                    size_t maxStackSize, v8::Isolate* isolate,
+                    V8Debugger* debugger, int contextGroupId) {
   DCHECK(isolate->InContext());
   int frameCount = stackTrace->GetFrameCount();
   if (frameCount > static_cast<int>(maxStackSize))
     frameCount = static_cast<int>(maxStackSize);
+  WasmTranslation* wasmTranslation =
+      debugger ? debugger->wasmTranslation() : nullptr;
   for (int i = 0; i < frameCount; i++) {
     v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
-    frames.push_back(toFrame(stackFrame));
+    frames.push_back(toFrame(stackFrame, wasmTranslation, contextGroupId));
   }
 }
 
@@ -113,7 +121,8 @@
   v8::HandleScope scope(isolate);
   std::vector<V8StackTraceImpl::Frame> frames;
   if (!stackTrace.IsEmpty())
-    toFramesVector(stackTrace, frames, maxStackSize, isolate);
+    toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger,
+                   contextGroupId);
 
   int maxAsyncCallChainDepth = 1;
   V8StackTraceImpl* asyncCallChain = nullptr;
@@ -131,10 +140,13 @@
     maxAsyncCallChainDepth = 1;
   }
 
-  // Only the top stack in the chain may be empty, so ensure that second stack
-  // is non-empty (it's the top of appended chain).
-  if (asyncCallChain && asyncCallChain->isEmpty())
+  // Only the top stack in the chain may be empty and doesn't contain creation
+  // stack , so ensure that second stack is non-empty (it's the top of appended
+  // chain).
+  if (asyncCallChain && asyncCallChain->isEmpty() &&
+      !asyncCallChain->m_creation) {
     asyncCallChain = asyncCallChain->m_parent.get();
+  }
 
   if (stackTrace.IsEmpty() && !asyncCallChain) return nullptr;
 
@@ -161,12 +173,6 @@
   v8::HandleScope handleScope(isolate);
   v8::Local<v8::StackTrace> stackTrace;
   if (isolate->InContext()) {
-    if (debugger) {
-      V8InspectorImpl* inspector = debugger->inspector();
-      V8ProfilerAgentImpl* profilerAgent =
-          inspector->enabledProfilerAgentForGroup(contextGroupId);
-      if (profilerAgent) profilerAgent->collectSample();
-    }
     stackTrace = v8::StackTrace::CurrentStackTrace(
         isolate, static_cast<int>(maxStackSize), stackTraceOptions);
   }
@@ -176,16 +182,18 @@
 
 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() {
   std::vector<Frame> framesCopy(m_frames);
-  return wrapUnique(
+  std::unique_ptr<V8StackTraceImpl> copy(
       new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy,
                            m_parent ? m_parent->cloneImpl() : nullptr));
+  if (m_creation) copy->setCreation(m_creation->cloneImpl());
+  return copy;
 }
 
 std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
   std::vector<Frame> frames;
   for (size_t i = 0; i < m_frames.size(); i++)
     frames.push_back(m_frames.at(i).clone());
-  return wrapUnique(
+  return std::unique_ptr<V8StackTraceImpl>(
       new V8StackTraceImpl(m_contextGroupId, m_description, frames, nullptr));
 }
 
@@ -201,6 +209,19 @@
 
 V8StackTraceImpl::~V8StackTraceImpl() {}
 
+void V8StackTraceImpl::setCreation(std::unique_ptr<V8StackTraceImpl> creation) {
+  m_creation = std::move(creation);
+  // When async call chain is empty but doesn't contain useful schedule stack
+  // and parent async call chain contains creationg stack but doesn't
+  // synchronous we can merge them together.
+  // e.g. Promise ThenableJob.
+  if (m_parent && isEmpty() && m_description == m_parent->m_description &&
+      !m_parent->m_creation) {
+    m_frames.swap(m_parent->m_frames);
+    m_parent = std::move(m_parent->m_parent);
+  }
+}
+
 StringView V8StackTraceImpl::topSourceURL() const {
   DCHECK(m_frames.size());
   return toStringView(m_frames[0].m_scriptName);
@@ -239,6 +260,10 @@
           .build();
   if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
   if (m_parent) stackTrace->setParent(m_parent->buildInspectorObjectImpl());
+  if (m_creation && m_creation->m_frames.size()) {
+    stackTrace->setPromiseCreationFrame(
+        m_creation->m_frames[0].buildInspectorObject());
+  }
   return stackTrace;
 }
 
diff --git a/src/inspector/v8-stack-trace-impl.h b/src/inspector/v8-stack-trace-impl.h
index f0a452e..f8b53d0 100644
--- a/src/inspector/v8-stack-trace-impl.h
+++ b/src/inspector/v8-stack-trace-impl.h
@@ -81,6 +81,8 @@
       const override;
   std::unique_ptr<StringBuffer> toString() const override;
 
+  void setCreation(std::unique_ptr<V8StackTraceImpl> creation);
+
  private:
   V8StackTraceImpl(int contextGroupId, const String16& description,
                    std::vector<Frame>& frames,
@@ -90,6 +92,7 @@
   String16 m_description;
   std::vector<Frame> m_frames;
   std::unique_ptr<V8StackTraceImpl> m_parent;
+  std::unique_ptr<V8StackTraceImpl> m_creation;
 
   DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl);
 };
diff --git a/src/inspector/wasm-translation.cc b/src/inspector/wasm-translation.cc
new file mode 100644
index 0000000..00f1aab
--- /dev/null
+++ b/src/inspector/wasm-translation.cc
@@ -0,0 +1,327 @@
+// Copyright 2016 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/inspector/wasm-translation.h"
+
+#include <algorithm>
+
+#include "src/debug/debug-interface.h"
+#include "src/inspector/protocol/Debugger.h"
+#include "src/inspector/script-breakpoint.h"
+#include "src/inspector/string-util.h"
+#include "src/inspector/v8-debugger-agent-impl.h"
+#include "src/inspector/v8-debugger-script.h"
+#include "src/inspector/v8-debugger.h"
+#include "src/inspector/v8-inspector-impl.h"
+
+using namespace v8_inspector;
+using namespace v8;
+
+class WasmTranslation::TranslatorImpl {
+ public:
+  struct TransLocation {
+    WasmTranslation* translation;
+    String16 script_id;
+    int line;
+    int column;
+    TransLocation(WasmTranslation* translation, String16 script_id, int line,
+                  int column)
+        : translation(translation),
+          script_id(script_id),
+          line(line),
+          column(column) {}
+  };
+
+  virtual void Init(Isolate*, WasmTranslation*, V8DebuggerAgentImpl*) = 0;
+  virtual void Translate(TransLocation*) = 0;
+  virtual void TranslateBack(TransLocation*) = 0;
+  virtual ~TranslatorImpl() {}
+
+  class RawTranslator;
+  class DisassemblingTranslator;
+};
+
+class WasmTranslation::TranslatorImpl::RawTranslator
+    : public WasmTranslation::TranslatorImpl {
+ public:
+  void Init(Isolate*, WasmTranslation*, V8DebuggerAgentImpl*) {}
+  void Translate(TransLocation*) {}
+  void TranslateBack(TransLocation*) {}
+};
+
+class WasmTranslation::TranslatorImpl::DisassemblingTranslator
+    : public WasmTranslation::TranslatorImpl {
+  using OffsetTable = debug::WasmDisassembly::OffsetTable;
+
+ public:
+  DisassemblingTranslator(Isolate* isolate, Local<debug::WasmScript> script)
+      : script_(isolate, script) {}
+
+  void Init(Isolate* isolate, WasmTranslation* translation,
+            V8DebuggerAgentImpl* agent) override {
+    // Register fake scripts for each function in this wasm module/script.
+    Local<debug::WasmScript> script = script_.Get(isolate);
+    int num_functions = script->NumFunctions();
+    int num_imported_functions = script->NumImportedFunctions();
+    DCHECK_LE(0, num_imported_functions);
+    DCHECK_LE(0, num_functions);
+    DCHECK_GE(num_functions, num_imported_functions);
+    String16 script_id = String16::fromInteger(script->Id());
+    for (int func_idx = num_imported_functions; func_idx < num_functions;
+         ++func_idx) {
+      AddFakeScript(isolate, script_id, func_idx, translation, agent);
+    }
+  }
+
+  void Translate(TransLocation* loc) override {
+    const OffsetTable& offset_table = GetOffsetTable(loc);
+    DCHECK(!offset_table.empty());
+    uint32_t byte_offset = static_cast<uint32_t>(loc->column);
+
+    // Binary search for the given offset.
+    unsigned left = 0;                                            // inclusive
+    unsigned right = static_cast<unsigned>(offset_table.size());  // exclusive
+    while (right - left > 1) {
+      unsigned mid = (left + right) / 2;
+      if (offset_table[mid].byte_offset <= byte_offset) {
+        left = mid;
+      } else {
+        right = mid;
+      }
+    }
+
+    loc->script_id = GetFakeScriptId(loc);
+    if (offset_table[left].byte_offset == byte_offset) {
+      loc->line = offset_table[left].line;
+      loc->column = offset_table[left].column;
+    } else {
+      loc->line = 0;
+      loc->column = 0;
+    }
+  }
+
+  void TranslateBack(TransLocation* loc) override {
+    int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id);
+    const OffsetTable* reverse_table = GetReverseTable(func_index);
+    if (!reverse_table) return;
+    DCHECK(!reverse_table->empty());
+    v8::Isolate* isolate = loc->translation->isolate_;
+
+    // Binary search for the given line and column.
+    unsigned left = 0;                                              // inclusive
+    unsigned right = static_cast<unsigned>(reverse_table->size());  // exclusive
+    while (right - left > 1) {
+      unsigned mid = (left + right) / 2;
+      auto& entry = (*reverse_table)[mid];
+      if (entry.line < loc->line ||
+          (entry.line == loc->line && entry.column <= loc->column)) {
+        left = mid;
+      } else {
+        right = mid;
+      }
+    }
+
+    int found_byte_offset = 0;
+    // If we found an exact match, use it. Otherwise check whether the next
+    // bigger entry is still in the same line. Report that one then.
+    // Otherwise we might have hit the special case of pointing after the last
+    // line, which is translated to the end of the function (one byte after the
+    // last function byte).
+    if ((*reverse_table)[left].line == loc->line &&
+        (*reverse_table)[left].column == loc->column) {
+      found_byte_offset = (*reverse_table)[left].byte_offset;
+    } else if (left + 1 < reverse_table->size() &&
+               (*reverse_table)[left + 1].line == loc->line) {
+      found_byte_offset = (*reverse_table)[left + 1].byte_offset;
+    } else if (left == reverse_table->size() - 1 &&
+               (*reverse_table)[left].line == loc->line - 1 &&
+               loc->column == 0) {
+      std::pair<int, int> func_range =
+          script_.Get(isolate)->GetFunctionRange(func_index);
+      DCHECK_LE(func_range.first, func_range.second);
+      found_byte_offset = func_range.second - func_range.first;
+    }
+
+    loc->script_id = String16::fromInteger(script_.Get(isolate)->Id());
+    loc->line = func_index;
+    loc->column = found_byte_offset;
+  }
+
+ private:
+  String16 GetFakeScriptUrl(v8::Isolate* isolate, int func_index) {
+    Local<debug::WasmScript> script = script_.Get(isolate);
+    String16 script_name = toProtocolString(script->Name().ToLocalChecked());
+    int numFunctions = script->NumFunctions();
+    int numImported = script->NumImportedFunctions();
+    String16Builder builder;
+    builder.appendAll("wasm://wasm/", script_name, '/');
+    if (numFunctions - numImported > 300) {
+      size_t digits = String16::fromInteger(numFunctions - 1).length();
+      String16 thisCategory = String16::fromInteger((func_index / 100) * 100);
+      DCHECK_LE(thisCategory.length(), digits);
+      for (size_t i = thisCategory.length(); i < digits; ++i)
+        builder.append('0');
+      builder.appendAll(thisCategory, '/');
+    }
+    builder.appendAll(script_name, '-');
+    builder.appendNumber(func_index);
+    return builder.toString();
+  }
+
+  String16 GetFakeScriptId(const String16 script_id, int func_index) {
+    return String16::concat(script_id, '-', String16::fromInteger(func_index));
+  }
+  String16 GetFakeScriptId(const TransLocation* loc) {
+    return GetFakeScriptId(loc->script_id, loc->line);
+  }
+
+  void AddFakeScript(v8::Isolate* isolate, const String16& underlyingScriptId,
+                     int func_idx, WasmTranslation* translation,
+                     V8DebuggerAgentImpl* agent) {
+    String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx);
+    String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx);
+
+    v8::Local<debug::WasmScript> script = script_.Get(isolate);
+    // TODO(clemensh): Generate disassembly lazily when queried by the frontend.
+    debug::WasmDisassembly disassembly = script->DisassembleFunction(func_idx);
+
+    DCHECK_EQ(0, offset_tables_.count(func_idx));
+    offset_tables_.insert(
+        std::make_pair(func_idx, std::move(disassembly.offset_table)));
+    String16 source(disassembly.disassembly.data(),
+                    disassembly.disassembly.length());
+    std::unique_ptr<V8DebuggerScript> fake_script =
+        V8DebuggerScript::CreateWasm(isolate, translation, script,
+                                     fake_script_id, std::move(fake_script_url),
+                                     source);
+
+    translation->AddFakeScript(fake_script->scriptId(), this);
+    agent->didParseSource(std::move(fake_script), true);
+  }
+
+  int GetFunctionIndexFromFakeScriptId(const String16& fake_script_id) {
+    size_t last_dash_pos = fake_script_id.reverseFind('-');
+    DCHECK_GT(fake_script_id.length(), last_dash_pos);
+    bool ok = true;
+    int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok);
+    DCHECK(ok);
+    return func_index;
+  }
+
+  const OffsetTable& GetOffsetTable(const TransLocation* loc) {
+    int func_index = loc->line;
+    auto it = offset_tables_.find(func_index);
+    // TODO(clemensh): Once we load disassembly lazily, the offset table
+    // might not be there yet. Load it lazily then.
+    DCHECK(it != offset_tables_.end());
+    return it->second;
+  }
+
+  const OffsetTable* GetReverseTable(int func_index) {
+    auto it = reverse_tables_.find(func_index);
+    if (it != reverse_tables_.end()) return &it->second;
+
+    // Find offset table, copy and sort it to get reverse table.
+    it = offset_tables_.find(func_index);
+    if (it == offset_tables_.end()) return nullptr;
+
+    OffsetTable reverse_table = it->second;
+    // Order by line, column, then byte offset.
+    auto cmp = [](OffsetTable::value_type el1, OffsetTable::value_type el2) {
+      if (el1.line != el2.line) return el1.line < el2.line;
+      if (el1.column != el2.column) return el1.column < el2.column;
+      return el1.byte_offset < el2.byte_offset;
+    };
+    std::sort(reverse_table.begin(), reverse_table.end(), cmp);
+
+    auto inserted = reverse_tables_.insert(
+        std::make_pair(func_index, std::move(reverse_table)));
+    DCHECK(inserted.second);
+    return &inserted.first->second;
+  }
+
+  Global<debug::WasmScript> script_;
+
+  // We assume to only disassemble a subset of the functions, so store them in a
+  // map instead of an array.
+  std::unordered_map<int, const OffsetTable> offset_tables_;
+  std::unordered_map<int, const OffsetTable> reverse_tables_;
+};
+
+WasmTranslation::WasmTranslation(v8::Isolate* isolate)
+    : isolate_(isolate), mode_(Disassemble) {}
+
+WasmTranslation::~WasmTranslation() { Clear(); }
+
+void WasmTranslation::AddScript(Local<debug::WasmScript> script,
+                                V8DebuggerAgentImpl* agent) {
+  std::unique_ptr<TranslatorImpl> impl;
+  switch (mode_) {
+    case Raw:
+      impl.reset(new TranslatorImpl::RawTranslator());
+      break;
+    case Disassemble:
+      impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, script));
+      break;
+  }
+  DCHECK(impl);
+  auto inserted =
+      wasm_translators_.insert(std::make_pair(script->Id(), std::move(impl)));
+  // Check that no mapping for this script id existed before.
+  DCHECK(inserted.second);
+  // impl has been moved, use the returned iterator to call Init.
+  inserted.first->second->Init(isolate_, this, agent);
+}
+
+void WasmTranslation::Clear() {
+  wasm_translators_.clear();
+  fake_scripts_.clear();
+}
+
+// Translation "forward" (to artificial scripts).
+bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
+    String16* script_id, int* line_number, int* column_number) {
+  DCHECK(script_id && line_number && column_number);
+  bool ok = true;
+  int script_id_int = script_id->toInteger(&ok);
+  if (!ok) return false;
+
+  auto it = wasm_translators_.find(script_id_int);
+  if (it == wasm_translators_.end()) return false;
+  TranslatorImpl* translator = it->second.get();
+
+  TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
+                                          *line_number, *column_number);
+  translator->Translate(&trans_loc);
+
+  *script_id = std::move(trans_loc.script_id);
+  *line_number = trans_loc.line;
+  *column_number = trans_loc.column;
+
+  return true;
+}
+
+// Translation "backward" (from artificial to real scripts).
+bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
+    String16* script_id, int* line_number, int* column_number) {
+  auto it = fake_scripts_.find(*script_id);
+  if (it == fake_scripts_.end()) return false;
+  TranslatorImpl* translator = it->second;
+
+  TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
+                                          *line_number, *column_number);
+  translator->TranslateBack(&trans_loc);
+
+  *script_id = std::move(trans_loc.script_id);
+  *line_number = trans_loc.line;
+  *column_number = trans_loc.column;
+
+  return true;
+}
+
+void WasmTranslation::AddFakeScript(const String16& scriptId,
+                                    TranslatorImpl* translator) {
+  DCHECK_EQ(0, fake_scripts_.count(scriptId));
+  fake_scripts_.insert(std::make_pair(scriptId, translator));
+}
diff --git a/src/inspector/wasm-translation.h b/src/inspector/wasm-translation.h
new file mode 100644
index 0000000..2162ede
--- /dev/null
+++ b/src/inspector/wasm-translation.h
@@ -0,0 +1,75 @@
+// Copyright 2016 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_INSPECTOR_WASMTRANSLATION_H_
+#define V8_INSPECTOR_WASMTRANSLATION_H_
+
+#include <unordered_map>
+
+#include "include/v8.h"
+#include "src/base/macros.h"
+#include "src/debug/debug-interface.h"
+#include "src/inspector/string-16.h"
+
+namespace v8_inspector {
+
+// Forward declarations.
+class V8DebuggerAgentImpl;
+
+class WasmTranslation {
+ public:
+  enum Mode { Raw, Disassemble };
+
+  explicit WasmTranslation(v8::Isolate* isolate);
+  ~WasmTranslation();
+
+  // Set translation mode.
+  void SetMode(Mode mode) { mode_ = mode; }
+
+  // Make a wasm script known to the translation. This will trigger a number of
+  // didParseScript calls to the given debugger agent.
+  // Only locations referencing a registered script will be translated by the
+  // Translate functions below.
+  void AddScript(v8::Local<v8::debug::WasmScript> script,
+                 V8DebuggerAgentImpl* agent);
+
+  // Clear all registered scripts.
+  void Clear();
+
+  // Translate a location as generated by V8 to a location that should be sent
+  // over protocol.
+  // Does nothing for locations referencing a script which was not registered
+  // before via AddScript.
+  // Line and column are 0-based.
+  // Returns true if the location was translated, false otherwise.
+  bool TranslateWasmScriptLocationToProtocolLocation(String16* script_id,
+                                                     int* line_number,
+                                                     int* column_number);
+
+  // Translate back from protocol locations (potentially referencing artificial
+  // scripts for individual wasm functions) to locations that make sense to V8.
+  // Does nothing if the location was not generated by the translate method
+  // above.
+  // Returns true if the location was translated, false otherwise.
+  bool TranslateProtocolLocationToWasmScriptLocation(String16* script_id,
+                                                     int* line_number,
+                                                     int* column_number);
+
+ private:
+  class TranslatorImpl;
+  friend class TranslatorImpl;
+
+  void AddFakeScript(const String16& scriptId, TranslatorImpl* translator);
+
+  v8::Isolate* isolate_;
+  std::unordered_map<int, std::unique_ptr<TranslatorImpl>> wasm_translators_;
+  std::unordered_map<String16, TranslatorImpl*> fake_scripts_;
+  Mode mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(WasmTranslation);
+};
+
+}  // namespace v8_inspector
+
+#endif  // V8_INSPECTOR_WASMTRANSLATION_H_