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(¤tCallFramesV8)) {
+ 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_