[inspector] Initial import of v8_inspector.

Attempt #2, formatted and disabled lint for src/inspector.

BUG=chromium:635948
NOPRESUBMIT=true (for grammar check in license code)

Review-Url: https://codereview.chromium.org/2292573002
Cr-Commit-Position: refs/heads/master@{#39107}
This commit is contained in:
dgozman 2016-09-01 13:27:46 -07:00 committed by Commit bot
parent 3b8ad45e0f
commit 130d0486c0
75 changed files with 15499 additions and 161 deletions

View File

@ -63,6 +63,7 @@ Felix Geisendörfer <haimuiba@gmail.com>
Filipe David Manana <fdmanana@gmail.com>
Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Geoffrey Garside <ggarside@gmail.com>
Gwang Yoon Hwang <ryumiel@company100.net>
Han Choongwoo <cwhan.tunz@gmail.com>
Hirofumi Mako <mkhrfm@gmail.com>
Honggyu Kim <honggyu.kp@gmail.com>
@ -95,9 +96,11 @@ Mike Pennisi <mike@mikepennisi.com>
Milton Chiang <milton.chiang@mediatek.com>
Myeong-bo Shim <m0609.shim@samsung.com>
Nicolas Antonius Ernst Leopold Maria Kaiser <nikai@nikai.net>
Noj Vek <nojvek@gmail.com>
Oleksandr Chekhovskyi <oleksandr.chekhovskyi@gmail.com>
Paolo Giarrusso <p.giarrusso@gmail.com>
Patrick Gansterer <paroga@paroga.com>
Peter Rybin <peter.rybin@gmail.com>
Peter Varga <pvarga@inf.u-szeged.hu>
Paul Lind <plind44@gmail.com>
Rafal Krypa <rafal@krypa.net>

38
src/inspector/Allocator.h Normal file
View File

@ -0,0 +1,38 @@
// 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_ALLOCATOR_H_
#define V8_INSPECTOR_ALLOCATOR_H_
#include <cstddef>
#include <cstdint>
enum NotNullTagEnum { NotNullLiteral };
#define V8_INSPECTOR_DISALLOW_NEW() \
private: \
void* operator new(size_t) = delete; \
void* operator new(size_t, NotNullTagEnum, void*) = delete; \
void* operator new(size_t, void*) = delete; \
\
public:
#define V8_INSPECTOR_DISALLOW_COPY(ClassName) \
private: \
ClassName(const ClassName&) = delete; \
ClassName& operator=(const ClassName&) = delete
// Macro that returns a compile time constant with the length of an array, but
// gives an error if passed a non-array.
template <typename T, std::size_t Size>
char (&ArrayLengthHelperFunction(T (&)[Size]))[Size];
// GCC needs some help to deduce a 0 length array.
#if defined(__GNUC__)
template <typename T>
char (&ArrayLengthHelperFunction(T (&)[0]))[0];
#endif
#define V8_INSPECTOR_ARRAY_LENGTH(array) \
sizeof(::ArrayLengthHelperFunction(array))
#endif // V8_INSPECTOR_ALLOCATOR_H_

35
src/inspector/Atomics.h Normal file
View File

@ -0,0 +1,35 @@
// 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_ATOMICS_H_
#define V8_INSPECTOR_ATOMICS_H_
#include <stdint.h>
#if defined(_MSC_VER)
#include <windows.h>
#endif
namespace v8_inspector {
#if defined(_MSC_VER)
inline int atomicIncrement(int volatile* addend) {
return InterlockedIncrement(reinterpret_cast<long volatile*>(addend));
}
#else
inline int atomicAdd(int volatile* addend, int increment) {
return __sync_add_and_fetch(addend, increment);
}
inline int atomicIncrement(int volatile* addend) {
return atomicAdd(addend, 1);
}
#endif
} // namespace v8_inspector
#endif // V8_INSPECTOR_ATOMICS_H_

View File

@ -0,0 +1,711 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";
(function () {
var DebuggerScript = {};
/** @enum */
DebuggerScript.PauseOnExceptionsState = {
DontPauseOnExceptions: 0,
PauseOnAllExceptions: 1,
PauseOnUncaughtExceptions: 2
};
DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.DontPauseOnExceptions;
Debug.clearBreakOnException();
Debug.clearBreakOnUncaughtException();
/**
* @param {?CompileEvent} eventData
*/
DebuggerScript.getAfterCompileScript = function(eventData)
{
var script = eventData.script().value();
if (!script.is_debugger_script)
return DebuggerScript._formatScript(eventData.script().value());
return null;
}
/** @type {!Map<!ScopeType, string>} */
DebuggerScript._scopeTypeNames = new Map();
DebuggerScript._scopeTypeNames.set(ScopeType.Global, "global");
DebuggerScript._scopeTypeNames.set(ScopeType.Local, "local");
DebuggerScript._scopeTypeNames.set(ScopeType.With, "with");
DebuggerScript._scopeTypeNames.set(ScopeType.Closure, "closure");
DebuggerScript._scopeTypeNames.set(ScopeType.Catch, "catch");
DebuggerScript._scopeTypeNames.set(ScopeType.Block, "block");
DebuggerScript._scopeTypeNames.set(ScopeType.Script, "script");
/**
* @param {function()} fun
* @return {?Array<!Scope>}
*/
DebuggerScript.getFunctionScopes = function(fun)
{
var mirror = MakeMirror(fun);
if (!mirror.isFunction())
return null;
var functionMirror = /** @type {!FunctionMirror} */(mirror);
var count = functionMirror.scopeCount();
if (count == 0)
return null;
var result = [];
for (var i = 0; i < count; i++) {
var scopeDetails = functionMirror.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 result;
}
/**
* @param {Object} object
* @return {?RawLocation}
*/
DebuggerScript.getGeneratorObjectLocation = function(object)
{
var mirror = MakeMirror(object, true /* transient */);
if (!mirror.isGenerator())
return null;
var generatorMirror = /** @type {!GeneratorMirror} */(mirror);
var funcMirror = generatorMirror.func();
if (!funcMirror.resolved())
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
};
}
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;
}
/**
* @param {string|undefined} contextData
* @return {string}
*/
DebuggerScript._executionContextAuxData = function(contextData)
{
if (!contextData)
return "";
var match = contextData.match(/^[^,]*,[^,]*,(.*)$/);
return match ? match[1] : "";
}
/**
* @param {string} contextGroupId
* @return {!Array<!FormattedScript>}
*/
DebuggerScript.getScripts = function(contextGroupId)
{
var result = [];
var scripts = Debug.scripts();
var contextDataPrefix = null;
if (contextGroupId)
contextDataPrefix = contextGroupId + ",";
for (var i = 0; i < scripts.length; ++i) {
var script = scripts[i];
if (contextDataPrefix) {
if (!script.context_data)
continue;
// Context data is a string in the following format:
// <contextGroupId>,<contextId>,<auxData>
if (script.context_data.indexOf(contextDataPrefix) !== 0)
continue;
}
if (script.is_debugger_script)
continue;
result.push(DebuggerScript._formatScript(script));
}
return result;
}
/**
* @param {!Script} script
* @return {!FormattedScript}
*/
DebuggerScript._formatScript = function(script)
{
var lineEnds = script.line_ends;
var lineCount = lineEnds.length;
var endLine = script.line_offset + lineCount - 1;
var endColumn;
// V8 will not count last line if script source ends with \n.
if (script.source[script.source.length - 1] === '\n') {
endLine += 1;
endColumn = 0;
} else {
if (lineCount === 1)
endColumn = script.source.length + script.column_offset;
else
endColumn = script.source.length - (lineEnds[lineCount - 2] + 1);
}
return {
id: script.id,
name: script.nameOrSourceURL(),
sourceURL: script.source_url,
sourceMappingURL: script.source_mapping_url,
source: script.source,
startLine: script.line_offset,
startColumn: script.column_offset,
endLine: endLine,
endColumn: endColumn,
executionContextId: DebuggerScript._executionContextId(script.context_data),
// Note that we cannot derive aux data from context id because of compilation cache.
executionContextAuxData: DebuggerScript._executionContextAuxData(script.context_data)
};
}
/**
* @param {!ExecutionState} execState
* @param {!BreakpointInfo} info
* @return {string|undefined}
*/
DebuggerScript.setBreakpoint = function(execState, info)
{
var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, Debug.BreakPositionAlignment.Statement);
var locations = Debug.findBreakPointActualLocations(breakId);
if (!locations.length)
return undefined;
info.lineNumber = locations[0].line;
info.columnNumber = locations[0].column;
return breakId.toString();
}
/**
* @param {!ExecutionState} execState
* @param {!{breakpointId: number}} info
*/
DebuggerScript.removeBreakpoint = function(execState, info)
{
Debug.findBreakPoint(info.breakpointId, true);
}
/**
* @return {number}
*/
DebuggerScript.pauseOnExceptionsState = function()
{
return DebuggerScript._pauseOnExceptionsState;
}
/**
* @param {number} newState
*/
DebuggerScript.setPauseOnExceptionsState = function(newState)
{
DebuggerScript._pauseOnExceptionsState = newState;
if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState)
Debug.setBreakOnException();
else
Debug.clearBreakOnException();
if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newState)
Debug.setBreakOnUncaughtException();
else
Debug.clearBreakOnUncaughtException();
}
/**
* @param {!ExecutionState} execState
* @param {number} limit
* @return {!Array<!JavaScriptCallFrame>}
*/
DebuggerScript.currentCallFrames = function(execState, limit)
{
var frames = [];
for (var i = 0; i < execState.frameCount() && (!limit || i < limit); ++i)
frames.push(DebuggerScript._frameMirrorToJSCallFrame(execState.frame(i)));
return frames;
}
/**
* @param {!ExecutionState} execState
*/
DebuggerScript.stepIntoStatement = function(execState)
{
execState.prepareStep(Debug.StepAction.StepIn);
}
/**
* @param {!ExecutionState} execState
*/
DebuggerScript.stepFrameStatement = function(execState)
{
execState.prepareStep(Debug.StepAction.StepFrame);
}
/**
* @param {!ExecutionState} execState
*/
DebuggerScript.stepOverStatement = function(execState)
{
execState.prepareStep(Debug.StepAction.StepNext);
}
/**
* @param {!ExecutionState} execState
*/
DebuggerScript.stepOutOfFunction = function(execState)
{
execState.prepareStep(Debug.StepAction.StepOut);
}
DebuggerScript.clearStepping = function()
{
Debug.clearStepping();
}
// Returns array in form:
// [ 0, <v8_result_report> ] in case of success
// or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based.
// or throws exception with message.
/**
* @param {number} scriptId
* @param {string} newSource
* @param {boolean} preview
* @return {!Array<*>}
*/
DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview)
{
var scripts = Debug.scripts();
var scriptToEdit = null;
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].id == scriptId) {
scriptToEdit = scripts[i];
break;
}
}
if (!scriptToEdit)
throw("Script not found");
var changeLog = [];
try {
var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog);
return [0, result.stack_modified];
} catch (e) {
if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
var details = /** @type {!LiveEditErrorDetails} */(e.details);
if (details.type === "liveedit_compile_error") {
var startPosition = details.position.start;
return [1, String(e), String(details.syntaxErrorMessage), Number(startPosition.line), Number(startPosition.column)];
}
}
throw e;
}
}
/**
* @param {!ExecutionState} execState
*/
DebuggerScript.clearBreakpoints = function(execState)
{
Debug.clearAllBreakPoints();
}
/**
* @param {!ExecutionState} execState
* @param {!{enabled: boolean}} info
*/
DebuggerScript.setBreakpointsActivated = function(execState, info)
{
Debug.debuggerFlags().breakPointsActive.setValue(info.enabled);
}
/**
* @param {!BreakEvent} eventData
*/
DebuggerScript.getBreakpointNumbers = function(eventData)
{
var breakpoints = eventData.breakPointsHit();
var numbers = [];
if (!breakpoints)
return numbers;
for (var i = 0; i < breakpoints.length; i++) {
var breakpoint = breakpoints[i];
var scriptBreakPoint = breakpoint.script_break_point();
numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.number());
}
return numbers;
}
// NOTE: This function is performance critical, as it can be run on every
// statement that generates an async event (like addEventListener) to support
// asynchronous call stacks. Thus, when possible, initialize the data lazily.
/**
* @param {!FrameMirror} frameMirror
* @return {!JavaScriptCallFrame}
*/
DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror)
{
// Stuff that can not be initialized lazily (i.e. valid while paused with a valid break_id).
// The frameMirror and scopeMirror can be accessed only while paused on the debugger.
var frameDetails = frameMirror.details();
var funcObject = frameDetails.func();
var sourcePosition = frameDetails.sourcePosition();
var thisObject = frameDetails.receiver();
var isAtReturn = !!frameDetails.isAtReturn();
var returnValue = isAtReturn ? frameDetails.returnValue() : undefined;
var scopeMirrors = frameMirror.allScopes(false);
/** @type {!Array<ScopeType>} */
var scopeTypes = new Array(scopeMirrors.length);
/** @type {?Array<!Object>} */
var scopeObjects = new Array(scopeMirrors.length);
/** @type {!Array<string|undefined>} */
var scopeNames = new Array(scopeMirrors.length);
/** @type {?Array<number>} */
var scopeStartPositions = new Array(scopeMirrors.length);
/** @type {?Array<number>} */
var scopeEndPositions = new Array(scopeMirrors.length);
/** @type {?Array<function()|null>} */
var scopeFunctions = new Array(scopeMirrors.length);
for (var i = 0; i < scopeMirrors.length; ++i) {
var scopeDetails = scopeMirrors[i].details();
scopeTypes[i] = scopeDetails.type();
scopeObjects[i] = scopeDetails.object();
scopeNames[i] = scopeDetails.name();
scopeStartPositions[i] = scopeDetails.startPosition ? scopeDetails.startPosition() : 0;
scopeEndPositions[i] = scopeDetails.endPosition ? scopeDetails.endPosition() : 0;
scopeFunctions[i] = scopeDetails.func ? scopeDetails.func() : null;
}
// Calculated lazily.
var scopeChain;
var funcMirror;
var location;
/** @type {!Array<?RawLocation>} */
var scopeStartLocations;
/** @type {!Array<?RawLocation>} */
var scopeEndLocations;
var details;
/**
* @param {!ScriptMirror|undefined} script
* @param {number} pos
* @return {?RawLocation}
*/
function createLocation(script, pos)
{
if (!script)
return null;
var location = script.locationFromPosition(pos, true);
return {
"lineNumber": location.line,
"columnNumber": location.column,
"scriptId": String(script.id())
}
}
/**
* @return {!Array<!Object>}
*/
function ensureScopeChain()
{
if (!scopeChain) {
scopeChain = [];
scopeStartLocations = [];
scopeEndLocations = [];
for (var i = 0, j = 0; i < scopeObjects.length; ++i) {
var scopeObject = DebuggerScript._buildScopeObject(scopeTypes[i], scopeObjects[i]);
if (scopeObject) {
scopeTypes[j] = scopeTypes[i];
scopeNames[j] = scopeNames[i];
scopeChain[j] = scopeObject;
var funcMirror = scopeFunctions ? MakeMirror(scopeFunctions[i]) : null;
if (!funcMirror || !funcMirror.isFunction())
funcMirror = new UnresolvedFunctionMirror(funcObject);
var script = /** @type {!FunctionMirror} */(funcMirror).script();
scopeStartLocations[j] = createLocation(script, scopeStartPositions[i]);
scopeEndLocations[j] = createLocation(script, scopeEndPositions[i]);
++j;
}
}
scopeTypes.length = scopeChain.length;
scopeNames.length = scopeChain.length;
scopeObjects = null; // Free for GC.
scopeFunctions = null;
scopeStartPositions = null;
scopeEndPositions = null;
}
return scopeChain;
}
/**
* @return {!JavaScriptCallFrameDetails}
*/
function lazyDetails()
{
if (!details) {
var scopeObjects = ensureScopeChain();
var script = ensureFuncMirror().script();
/** @type {!Array<Scope>} */
var scopes = [];
for (var i = 0; i < scopeObjects.length; ++i) {
var scope = {
"type": /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeTypes[i])),
"object": scopeObjects[i],
};
if (scopeNames[i])
scope.name = scopeNames[i];
if (scopeStartLocations[i])
scope.startLocation = /** @type {!RawLocation} */(scopeStartLocations[i]);
if (scopeEndLocations[i])
scope.endLocation = /** @type {!RawLocation} */(scopeEndLocations[i]);
scopes.push(scope);
}
details = {
"functionName": ensureFuncMirror().debugName(),
"location": {
"lineNumber": line(),
"columnNumber": column(),
"scriptId": String(script.id())
},
"this": thisObject,
"scopeChain": scopes
};
var functionLocation = ensureFuncMirror().sourceLocation();
if (functionLocation) {
details.functionLocation = {
"lineNumber": functionLocation.line,
"columnNumber": functionLocation.column,
"scriptId": String(script.id())
};
}
if (isAtReturn)
details.returnValue = returnValue;
}
return details;
}
/**
* @return {!FunctionMirror}
*/
function ensureFuncMirror()
{
if (!funcMirror) {
funcMirror = MakeMirror(funcObject);
if (!funcMirror.isFunction())
funcMirror = new UnresolvedFunctionMirror(funcObject);
}
return /** @type {!FunctionMirror} */(funcMirror);
}
/**
* @return {!{line: number, column: number}}
*/
function ensureLocation()
{
if (!location) {
var script = ensureFuncMirror().script();
if (script)
location = script.locationFromPosition(sourcePosition, true);
if (!location)
location = { line: 0, column: 0 };
}
return location;
}
/**
* @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());
return 0;
}
/**
* @return {number|undefined}
*/
function sourceID()
{
var script = ensureFuncMirror().script();
return script && script.id();
}
/**
* @param {string} expression
* @return {*}
*/
function evaluate(expression)
{
return frameMirror.evaluate(expression, false).value();
}
/** @return {undefined} */
function restart()
{
return frameMirror.restart();
}
/**
* @param {number} scopeNumber
* @param {string} variableName
* @param {*} newValue
*/
function setVariableValue(scopeNumber, variableName, newValue)
{
var scopeMirror = frameMirror.scope(scopeNumber);
if (!scopeMirror)
throw new Error("Incorrect scope index");
scopeMirror.setVariableValue(variableName, newValue);
}
return {
"sourceID": sourceID,
"line": line,
"column": column,
"contextId": contextId,
"thisObject": thisObject,
"evaluate": evaluate,
"restart": restart,
"setVariableValue": setVariableValue,
"isAtReturn": isAtReturn,
"details": lazyDetails
};
}
/**
* @param {number} scopeType
* @param {!Object} scopeObject
* @return {!Object|undefined}
*/
DebuggerScript._buildScopeObject = function(scopeType, scopeObject)
{
var result;
switch (scopeType) {
case ScopeType.Local:
case ScopeType.Closure:
case ScopeType.Catch:
case ScopeType.Block:
case ScopeType.Script:
// 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();
// 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))
break;
result = { __proto__: null };
for (var j = 0; j < properties.length; j++) {
var name = properties[j].name();
if (name.length === 0 || name.charAt(0) === ".")
continue; // Skip internal variables like ".arguments" and variables with empty name
result[name] = properties[j].value_;
}
break;
case ScopeType.Global:
case ScopeType.With:
result = scopeObject;
break;
}
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;
})();

View File

@ -0,0 +1,568 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "src/inspector/InjectedScript.h"
#include "src/inspector/InjectedScriptNative.h"
#include "src/inspector/InjectedScriptSource.h"
#include "src/inspector/InspectedContext.h"
#include "src/inspector/RemoteObjectId.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8Console.h"
#include "src/inspector/V8FunctionCall.h"
#include "src/inspector/V8InjectedScriptHost.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/V8ValueCopier.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
using protocol::Array;
using protocol::Runtime::PropertyDescriptor;
using protocol::Runtime::InternalPropertyDescriptor;
using protocol::Runtime::RemoteObject;
using protocol::Maybe;
static bool hasInternalError(ErrorString* errorString, bool hasError) {
if (hasError) *errorString = "Internal error";
return hasError;
}
std::unique_ptr<InjectedScript> InjectedScript::create(
InspectedContext* inspectedContext) {
v8::Isolate* isolate = inspectedContext->isolate();
v8::HandleScope handles(isolate);
v8::Local<v8::Context> context = inspectedContext->context();
v8::Context::Scope scope(context);
std::unique_ptr<InjectedScriptNative> injectedScriptNative(
new InjectedScriptNative(isolate));
v8::Local<v8::Object> scriptHostWrapper =
V8InjectedScriptHost::create(context, inspectedContext->inspector());
injectedScriptNative->setOnInjectedScriptHost(scriptHostWrapper);
// Inject javascript into the context. The compiled script is supposed to
// evaluate into
// a single anonymous function(it's anonymous to avoid cluttering the global
// object with
// inspector's stuff) the function is called a few lines below with
// InjectedScriptHost wrapper,
// injected script id and explicit reference to the inspected global object.
// The function is expected
// to create and configure InjectedScript instance that is going to be used by
// the inspector.
String16 injectedScriptSource(
reinterpret_cast<const char*>(InjectedScriptSource_js),
sizeof(InjectedScriptSource_js));
v8::Local<v8::Value> value;
if (!inspectedContext->inspector()
->compileAndRunInternalScript(
context, toV8String(isolate, injectedScriptSource))
.ToLocal(&value))
return nullptr;
DCHECK(value->IsFunction());
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value);
v8::Local<v8::Object> windowGlobal = context->Global();
v8::Local<v8::Value> info[] = {
scriptHostWrapper, windowGlobal,
v8::Number::New(isolate, inspectedContext->contextId())};
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Value> injectedScriptValue;
if (!function
->Call(context, windowGlobal, V8_INSPECTOR_ARRAY_LENGTH(info), info)
.ToLocal(&injectedScriptValue))
return nullptr;
if (!injectedScriptValue->IsObject()) return nullptr;
return wrapUnique(new InjectedScript(inspectedContext,
injectedScriptValue.As<v8::Object>(),
std::move(injectedScriptNative)));
}
InjectedScript::InjectedScript(
InspectedContext* context, v8::Local<v8::Object> object,
std::unique_ptr<InjectedScriptNative> injectedScriptNative)
: m_context(context),
m_value(context->isolate(), object),
m_native(std::move(injectedScriptNative)) {}
InjectedScript::~InjectedScript() {}
void InjectedScript::getProperties(
ErrorString* errorString, v8::Local<v8::Object> object,
const String16& groupName, bool ownProperties, bool accessorPropertiesOnly,
bool generatePreview,
std::unique_ptr<Array<PropertyDescriptor>>* properties,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
v8::HandleScope handles(m_context->isolate());
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "getProperties");
function.appendArgument(object);
function.appendArgument(groupName);
function.appendArgument(ownProperties);
function.appendArgument(accessorPropertiesOnly);
function.appendArgument(generatePreview);
v8::TryCatch tryCatch(m_context->isolate());
v8::Local<v8::Value> resultValue = function.callWithoutExceptionHandling();
if (tryCatch.HasCaught()) {
*exceptionDetails = createExceptionDetails(errorString, tryCatch, groupName,
generatePreview);
// FIXME: make properties optional
*properties = Array<PropertyDescriptor>::create();
return;
}
std::unique_ptr<protocol::Value> protocolValue =
toProtocolValue(m_context->context(), resultValue);
if (hasInternalError(errorString, !protocolValue)) return;
protocol::ErrorSupport errors(errorString);
std::unique_ptr<Array<PropertyDescriptor>> result =
Array<PropertyDescriptor>::parse(protocolValue.get(), &errors);
if (!hasInternalError(errorString, errors.hasErrors()))
*properties = std::move(result);
}
void InjectedScript::releaseObject(const String16& objectId) {
std::unique_ptr<protocol::Value> parsedObjectId =
protocol::parseJSON(objectId);
if (!parsedObjectId) return;
protocol::DictionaryValue* object =
protocol::DictionaryValue::cast(parsedObjectId.get());
if (!object) return;
int boundId = 0;
if (!object->getInteger("id", &boundId)) return;
m_native->unbind(boundId);
}
std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapObject(
ErrorString* errorString, v8::Local<v8::Value> value,
const String16& groupName, bool forceValueType,
bool generatePreview) const {
v8::HandleScope handles(m_context->isolate());
v8::Local<v8::Value> wrappedObject;
if (!wrapValue(errorString, value, groupName, forceValueType, generatePreview)
.ToLocal(&wrappedObject))
return nullptr;
protocol::ErrorSupport errors;
std::unique_ptr<protocol::Runtime::RemoteObject> remoteObject =
protocol::Runtime::RemoteObject::parse(
toProtocolValue(m_context->context(), wrappedObject).get(), &errors);
if (!remoteObject) *errorString = "Object has too long reference chain";
return remoteObject;
}
bool InjectedScript::wrapObjectProperty(ErrorString* errorString,
v8::Local<v8::Object> object,
v8::Local<v8::Name> key,
const String16& groupName,
bool forceValueType,
bool generatePreview) const {
v8::Local<v8::Value> property;
if (hasInternalError(
errorString,
!object->Get(m_context->context(), key).ToLocal(&property)))
return false;
v8::Local<v8::Value> wrappedProperty;
if (!wrapValue(errorString, property, groupName, forceValueType,
generatePreview)
.ToLocal(&wrappedProperty))
return false;
v8::Maybe<bool> success =
createDataProperty(m_context->context(), object, key, wrappedProperty);
if (hasInternalError(errorString, success.IsNothing() || !success.FromJust()))
return false;
return true;
}
bool InjectedScript::wrapPropertyInArray(ErrorString* errorString,
v8::Local<v8::Array> array,
v8::Local<v8::String> property,
const String16& groupName,
bool forceValueType,
bool generatePreview) const {
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "wrapPropertyInArray");
function.appendArgument(array);
function.appendArgument(property);
function.appendArgument(groupName);
function.appendArgument(forceValueType);
function.appendArgument(generatePreview);
bool hadException = false;
function.call(hadException);
return !hasInternalError(errorString, hadException);
}
bool InjectedScript::wrapObjectsInArray(ErrorString* errorString,
v8::Local<v8::Array> array,
const String16& groupName,
bool forceValueType,
bool generatePreview) const {
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "wrapObjectsInArray");
function.appendArgument(array);
function.appendArgument(groupName);
function.appendArgument(forceValueType);
function.appendArgument(generatePreview);
bool hadException = false;
function.call(hadException);
return !hasInternalError(errorString, hadException);
}
v8::MaybeLocal<v8::Value> InjectedScript::wrapValue(
ErrorString* errorString, v8::Local<v8::Value> value,
const String16& groupName, bool forceValueType,
bool generatePreview) const {
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "wrapObject");
function.appendArgument(value);
function.appendArgument(groupName);
function.appendArgument(forceValueType);
function.appendArgument(generatePreview);
bool hadException = false;
v8::Local<v8::Value> r = function.call(hadException);
if (hasInternalError(errorString, hadException || r.IsEmpty()))
return v8::MaybeLocal<v8::Value>();
return r;
}
std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(
v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const {
v8::HandleScope handles(m_context->isolate());
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "wrapTable");
function.appendArgument(table);
if (columns.IsEmpty())
function.appendArgument(false);
else
function.appendArgument(columns);
bool hadException = false;
v8::Local<v8::Value> r = function.call(hadException);
if (hadException) return nullptr;
protocol::ErrorSupport errors;
return protocol::Runtime::RemoteObject::parse(
toProtocolValue(m_context->context(), r).get(), &errors);
}
bool InjectedScript::findObject(ErrorString* errorString,
const RemoteObjectId& objectId,
v8::Local<v8::Value>* outObject) const {
*outObject = m_native->objectForId(objectId.id());
if (outObject->IsEmpty())
*errorString = "Could not find object with given id";
return !outObject->IsEmpty();
}
String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const {
return m_native->groupName(objectId.id());
}
void InjectedScript::releaseObjectGroup(const String16& objectGroup) {
m_native->releaseObjectGroup(objectGroup);
if (objectGroup == "console") m_lastEvaluationResult.Reset();
}
void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) {
v8::HandleScope handles(m_context->isolate());
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "setCustomObjectFormatterEnabled");
function.appendArgument(enabled);
bool hadException = false;
function.call(hadException);
DCHECK(!hadException);
}
v8::Local<v8::Value> InjectedScript::v8Value() const {
return m_value.Get(m_context->isolate());
}
v8::Local<v8::Value> InjectedScript::lastEvaluationResult() const {
if (m_lastEvaluationResult.IsEmpty())
return v8::Undefined(m_context->isolate());
return m_lastEvaluationResult.Get(m_context->isolate());
}
v8::MaybeLocal<v8::Value> InjectedScript::resolveCallArgument(
ErrorString* errorString, protocol::Runtime::CallArgument* callArgument) {
if (callArgument->hasObjectId()) {
std::unique_ptr<RemoteObjectId> remoteObjectId =
RemoteObjectId::parse(errorString, callArgument->getObjectId(""));
if (!remoteObjectId) return v8::MaybeLocal<v8::Value>();
if (remoteObjectId->contextId() != m_context->contextId()) {
*errorString =
"Argument should belong to the same JavaScript world as target "
"object";
return v8::MaybeLocal<v8::Value>();
}
v8::Local<v8::Value> object;
if (!findObject(errorString, *remoteObjectId, &object))
return v8::MaybeLocal<v8::Value>();
return object;
}
if (callArgument->hasValue() || callArgument->hasUnserializableValue()) {
String16 value =
callArgument->hasValue()
? callArgument->getValue(nullptr)->toJSONString()
: "Number(\"" + callArgument->getUnserializableValue("") + "\")";
v8::Local<v8::Value> object;
if (!m_context->inspector()
->compileAndRunInternalScript(
m_context->context(), toV8String(m_context->isolate(), value))
.ToLocal(&object)) {
*errorString = "Couldn't parse value object in call argument";
return v8::MaybeLocal<v8::Value>();
}
return object;
}
return v8::Undefined(m_context->isolate());
}
std::unique_ptr<protocol::Runtime::ExceptionDetails>
InjectedScript::createExceptionDetails(ErrorString* errorString,
const v8::TryCatch& tryCatch,
const String16& objectGroup,
bool generatePreview) {
if (!tryCatch.HasCaught()) return nullptr;
v8::Local<v8::Message> message = tryCatch.Message();
v8::Local<v8::Value> exception = tryCatch.Exception();
String16 messageText =
message.IsEmpty() ? String16() : toProtocolString(message->Get());
std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
protocol::Runtime::ExceptionDetails::create()
.setExceptionId(m_context->inspector()->nextExceptionId())
.setText(exception.IsEmpty() ? messageText : String16("Uncaught"))
.setLineNumber(
message.IsEmpty()
? 0
: message->GetLineNumber(m_context->context()).FromMaybe(1) -
1)
.setColumnNumber(
message.IsEmpty()
? 0
: message->GetStartColumn(m_context->context()).FromMaybe(0))
.build();
if (!message.IsEmpty()) {
exceptionDetails->setScriptId(
String16::fromInteger(message->GetScriptOrigin().ScriptID()->Value()));
v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace();
if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
exceptionDetails->setStackTrace(m_context->inspector()
->debugger()
->createStackTrace(stackTrace)
->buildInspectorObjectImpl());
}
if (!exception.IsEmpty()) {
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = wrapObject(
errorString, exception, objectGroup, false /* forceValueType */,
generatePreview && !exception->IsNativeError());
if (!wrapped) return nullptr;
exceptionDetails->setException(std::move(wrapped));
}
return exceptionDetails;
}
void InjectedScript::wrapEvaluateResult(
ErrorString* errorString, v8::MaybeLocal<v8::Value> maybeResultValue,
const v8::TryCatch& tryCatch, const String16& objectGroup,
bool returnByValue, bool generatePreview,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
v8::Local<v8::Value> resultValue;
if (!tryCatch.HasCaught()) {
if (hasInternalError(errorString, !maybeResultValue.ToLocal(&resultValue)))
return;
std::unique_ptr<RemoteObject> remoteObject = wrapObject(
errorString, resultValue, objectGroup, returnByValue, generatePreview);
if (!remoteObject) return;
if (objectGroup == "console")
m_lastEvaluationResult.Reset(m_context->isolate(), resultValue);
*result = std::move(remoteObject);
} else {
v8::Local<v8::Value> exception = tryCatch.Exception();
std::unique_ptr<RemoteObject> remoteObject =
wrapObject(errorString, exception, objectGroup, false,
generatePreview && !exception->IsNativeError());
if (!remoteObject) return;
// We send exception in result for compatibility reasons, even though it's
// accessible through exceptionDetails.exception.
*result = std::move(remoteObject);
*exceptionDetails = createExceptionDetails(errorString, tryCatch,
objectGroup, generatePreview);
}
}
v8::Local<v8::Object> InjectedScript::commandLineAPI() {
if (m_commandLineAPI.IsEmpty())
m_commandLineAPI.Reset(m_context->isolate(),
V8Console::createCommandLineAPI(m_context));
return m_commandLineAPI.Get(m_context->isolate());
}
InjectedScript::Scope::Scope(ErrorString* errorString,
V8InspectorImpl* inspector, int contextGroupId)
: m_errorString(errorString),
m_inspector(inspector),
m_contextGroupId(contextGroupId),
m_injectedScript(nullptr),
m_handleScope(inspector->isolate()),
m_tryCatch(inspector->isolate()),
m_ignoreExceptionsAndMuteConsole(false),
m_previousPauseOnExceptionsState(V8Debugger::DontPauseOnExceptions),
m_userGesture(false) {}
bool InjectedScript::Scope::initialize() {
cleanup();
// TODO(dgozman): what if we reattach to the same context group during
// evaluate? Introduce a session id?
V8InspectorSessionImpl* session =
m_inspector->sessionForContextGroup(m_contextGroupId);
if (!session) {
*m_errorString = "Internal error";
return false;
}
findInjectedScript(session);
if (!m_injectedScript) return false;
m_context = m_injectedScript->context()->context();
m_context->Enter();
return true;
}
bool InjectedScript::Scope::installCommandLineAPI() {
DCHECK(m_injectedScript && !m_context.IsEmpty() &&
!m_commandLineAPIScope.get());
m_commandLineAPIScope.reset(new V8Console::CommandLineAPIScope(
m_context, m_injectedScript->commandLineAPI(), m_context->Global()));
return true;
}
void InjectedScript::Scope::ignoreExceptionsAndMuteConsole() {
DCHECK(!m_ignoreExceptionsAndMuteConsole);
m_ignoreExceptionsAndMuteConsole = true;
m_inspector->client()->muteMetrics(m_contextGroupId);
m_inspector->muteExceptions(m_contextGroupId);
m_previousPauseOnExceptionsState =
setPauseOnExceptionsState(V8Debugger::DontPauseOnExceptions);
}
V8Debugger::PauseOnExceptionsState
InjectedScript::Scope::setPauseOnExceptionsState(
V8Debugger::PauseOnExceptionsState newState) {
if (!m_inspector->debugger()->enabled()) return newState;
V8Debugger::PauseOnExceptionsState presentState =
m_inspector->debugger()->getPauseOnExceptionsState();
if (presentState != newState)
m_inspector->debugger()->setPauseOnExceptionsState(newState);
return presentState;
}
void InjectedScript::Scope::pretendUserGesture() {
DCHECK(!m_userGesture);
m_userGesture = true;
m_inspector->client()->beginUserGesture();
}
void InjectedScript::Scope::cleanup() {
m_commandLineAPIScope.reset();
if (!m_context.IsEmpty()) {
m_context->Exit();
m_context.Clear();
}
}
InjectedScript::Scope::~Scope() {
if (m_ignoreExceptionsAndMuteConsole) {
setPauseOnExceptionsState(m_previousPauseOnExceptionsState);
m_inspector->client()->unmuteMetrics(m_contextGroupId);
m_inspector->unmuteExceptions(m_contextGroupId);
}
if (m_userGesture) m_inspector->client()->endUserGesture();
cleanup();
}
InjectedScript::ContextScope::ContextScope(ErrorString* errorString,
V8InspectorImpl* inspector,
int contextGroupId,
int executionContextId)
: InjectedScript::Scope(errorString, inspector, contextGroupId),
m_executionContextId(executionContextId) {}
InjectedScript::ContextScope::~ContextScope() {}
void InjectedScript::ContextScope::findInjectedScript(
V8InspectorSessionImpl* session) {
m_injectedScript =
session->findInjectedScript(m_errorString, m_executionContextId);
}
InjectedScript::ObjectScope::ObjectScope(ErrorString* errorString,
V8InspectorImpl* inspector,
int contextGroupId,
const String16& remoteObjectId)
: InjectedScript::Scope(errorString, inspector, contextGroupId),
m_remoteObjectId(remoteObjectId) {}
InjectedScript::ObjectScope::~ObjectScope() {}
void InjectedScript::ObjectScope::findInjectedScript(
V8InspectorSessionImpl* session) {
std::unique_ptr<RemoteObjectId> remoteId =
RemoteObjectId::parse(m_errorString, m_remoteObjectId);
if (!remoteId) return;
InjectedScript* injectedScript =
session->findInjectedScript(m_errorString, remoteId.get());
if (!injectedScript) return;
m_objectGroupName = injectedScript->objectGroupName(*remoteId);
if (!injectedScript->findObject(m_errorString, *remoteId, &m_object)) return;
m_injectedScript = injectedScript;
}
InjectedScript::CallFrameScope::CallFrameScope(ErrorString* errorString,
V8InspectorImpl* inspector,
int contextGroupId,
const String16& remoteObjectId)
: InjectedScript::Scope(errorString, inspector, contextGroupId),
m_remoteCallFrameId(remoteObjectId) {}
InjectedScript::CallFrameScope::~CallFrameScope() {}
void InjectedScript::CallFrameScope::findInjectedScript(
V8InspectorSessionImpl* session) {
std::unique_ptr<RemoteCallFrameId> remoteId =
RemoteCallFrameId::parse(m_errorString, m_remoteCallFrameId);
if (!remoteId) return;
m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal());
m_injectedScript = session->findInjectedScript(m_errorString, remoteId.get());
}
} // namespace v8_inspector

View File

@ -0,0 +1,207 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef V8_INSPECTOR_INJECTEDSCRIPT_H_
#define V8_INSPECTOR_INJECTEDSCRIPT_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/InjectedScriptNative.h"
#include "src/inspector/InspectedContext.h"
#include "src/inspector/V8Console.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
#include <v8.h>
namespace v8_inspector {
class RemoteObjectId;
class V8FunctionCall;
class V8InspectorImpl;
class V8InspectorSessionImpl;
using protocol::ErrorString;
using protocol::Maybe;
class InjectedScript final {
V8_INSPECTOR_DISALLOW_COPY(InjectedScript);
public:
static std::unique_ptr<InjectedScript> create(InspectedContext*);
~InjectedScript();
InspectedContext* context() const { return m_context; }
void getProperties(
ErrorString*, v8::Local<v8::Object>, const String16& groupName,
bool ownProperties, bool accessorPropertiesOnly, bool generatePreview,
std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>*
result,
Maybe<protocol::Runtime::ExceptionDetails>*);
void releaseObject(const String16& objectId);
std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject(
ErrorString*, v8::Local<v8::Value>, const String16& groupName,
bool forceValueType = false, bool generatePreview = false) const;
bool wrapObjectProperty(ErrorString*, v8::Local<v8::Object>,
v8::Local<v8::Name> key, const String16& groupName,
bool forceValueType = false,
bool generatePreview = false) const;
bool wrapPropertyInArray(ErrorString*, v8::Local<v8::Array>,
v8::Local<v8::String> property,
const String16& groupName,
bool forceValueType = false,
bool generatePreview = false) const;
bool wrapObjectsInArray(ErrorString*, v8::Local<v8::Array>,
const String16& groupName,
bool forceValueType = false,
bool generatePreview = false) const;
std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(
v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const;
bool findObject(ErrorString*, const RemoteObjectId&,
v8::Local<v8::Value>*) const;
String16 objectGroupName(const RemoteObjectId&) const;
void releaseObjectGroup(const String16&);
void setCustomObjectFormatterEnabled(bool);
v8::MaybeLocal<v8::Value> resolveCallArgument(
ErrorString*, protocol::Runtime::CallArgument*);
std::unique_ptr<protocol::Runtime::ExceptionDetails> createExceptionDetails(
ErrorString*, const v8::TryCatch&, const String16& groupName,
bool generatePreview);
void wrapEvaluateResult(
ErrorString*, v8::MaybeLocal<v8::Value> maybeResultValue,
const v8::TryCatch&, const String16& objectGroup, bool returnByValue,
bool generatePreview,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>*);
v8::Local<v8::Value> lastEvaluationResult() const;
class Scope {
public:
bool initialize();
bool installCommandLineAPI();
void ignoreExceptionsAndMuteConsole();
void pretendUserGesture();
v8::Local<v8::Context> context() const { return m_context; }
InjectedScript* injectedScript() const { return m_injectedScript; }
const v8::TryCatch& tryCatch() const { return m_tryCatch; }
protected:
Scope(ErrorString*, V8InspectorImpl*, int contextGroupId);
~Scope();
virtual void findInjectedScript(V8InspectorSessionImpl*) = 0;
ErrorString* m_errorString;
V8InspectorImpl* m_inspector;
int m_contextGroupId;
InjectedScript* m_injectedScript;
private:
void cleanup();
V8Debugger::PauseOnExceptionsState setPauseOnExceptionsState(
V8Debugger::PauseOnExceptionsState);
v8::HandleScope m_handleScope;
v8::TryCatch m_tryCatch;
v8::Local<v8::Context> m_context;
std::unique_ptr<V8Console::CommandLineAPIScope> m_commandLineAPIScope;
bool m_ignoreExceptionsAndMuteConsole;
V8Debugger::PauseOnExceptionsState m_previousPauseOnExceptionsState;
bool m_userGesture;
};
class ContextScope : public Scope {
V8_INSPECTOR_DISALLOW_COPY(ContextScope);
public:
ContextScope(ErrorString*, V8InspectorImpl*, int contextGroupId,
int executionContextId);
~ContextScope();
private:
void findInjectedScript(V8InspectorSessionImpl*) override;
int m_executionContextId;
};
class ObjectScope : public Scope {
V8_INSPECTOR_DISALLOW_COPY(ObjectScope);
public:
ObjectScope(ErrorString*, V8InspectorImpl*, int contextGroupId,
const String16& remoteObjectId);
~ObjectScope();
const String16& objectGroupName() const { return m_objectGroupName; }
v8::Local<v8::Value> object() const { return m_object; }
private:
void findInjectedScript(V8InspectorSessionImpl*) override;
String16 m_remoteObjectId;
String16 m_objectGroupName;
v8::Local<v8::Value> m_object;
};
class CallFrameScope : public Scope {
V8_INSPECTOR_DISALLOW_COPY(CallFrameScope);
public:
CallFrameScope(ErrorString*, V8InspectorImpl*, int contextGroupId,
const String16& remoteCallFrameId);
~CallFrameScope();
size_t frameOrdinal() const { return m_frameOrdinal; }
private:
void findInjectedScript(V8InspectorSessionImpl*) override;
String16 m_remoteCallFrameId;
size_t m_frameOrdinal;
};
private:
InjectedScript(InspectedContext*, v8::Local<v8::Object>,
std::unique_ptr<InjectedScriptNative>);
v8::Local<v8::Value> v8Value() const;
v8::MaybeLocal<v8::Value> wrapValue(ErrorString*, v8::Local<v8::Value>,
const String16& groupName,
bool forceValueType,
bool generatePreview) const;
v8::Local<v8::Object> commandLineAPI();
InspectedContext* m_context;
v8::Global<v8::Value> m_value;
v8::Global<v8::Value> m_lastEvaluationResult;
std::unique_ptr<InjectedScriptNative> m_native;
v8::Global<v8::Object> m_commandLineAPI;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_INJECTEDSCRIPT_H_

View File

@ -0,0 +1,90 @@
// Copyright 2015 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/InjectedScriptNative.h"
namespace v8_inspector {
InjectedScriptNative::InjectedScriptNative(v8::Isolate* isolate)
: m_lastBoundObjectId(1), m_isolate(isolate) {}
static const char privateKeyName[] = "v8-inspector#injectedScript";
InjectedScriptNative::~InjectedScriptNative() {}
void InjectedScriptNative::setOnInjectedScriptHost(
v8::Local<v8::Object> injectedScriptHost) {
v8::HandleScope handleScope(m_isolate);
v8::Local<v8::External> external = v8::External::New(m_isolate, this);
v8::Local<v8::Private> privateKey = v8::Private::ForApi(
m_isolate, v8::String::NewFromUtf8(m_isolate, privateKeyName,
v8::NewStringType::kInternalized)
.ToLocalChecked());
injectedScriptHost->SetPrivate(m_isolate->GetCurrentContext(), privateKey,
external);
}
InjectedScriptNative* InjectedScriptNative::fromInjectedScriptHost(
v8::Local<v8::Object> injectedScriptObject) {
v8::Isolate* isolate = injectedScriptObject->GetIsolate();
v8::HandleScope handleScope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Private> privateKey = v8::Private::ForApi(
isolate, v8::String::NewFromUtf8(isolate, privateKeyName,
v8::NewStringType::kInternalized)
.ToLocalChecked());
v8::Local<v8::Value> value =
injectedScriptObject->GetPrivate(context, privateKey).ToLocalChecked();
DCHECK(value->IsExternal());
v8::Local<v8::External> external = value.As<v8::External>();
return static_cast<InjectedScriptNative*>(external->Value());
}
int InjectedScriptNative::bind(v8::Local<v8::Value> value,
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));
addObjectToGroup(id, groupName);
return id;
}
void InjectedScriptNative::unbind(int id) {
m_idToWrappedObject.erase(id);
m_idToObjectGroupName.erase(id);
}
v8::Local<v8::Value> InjectedScriptNative::objectForId(int id) {
auto iter = m_idToWrappedObject.find(id);
return iter != m_idToWrappedObject.end() ? iter->second->Get(m_isolate)
: v8::Local<v8::Value>();
}
void InjectedScriptNative::addObjectToGroup(int objectId,
const String16& groupName) {
if (groupName.isEmpty()) return;
if (objectId <= 0) return;
m_idToObjectGroupName[objectId] = groupName;
m_nameToObjectGroup[groupName].push_back(
objectId); // Creates an empty vector if key is not there
}
void InjectedScriptNative::releaseObjectGroup(const String16& groupName) {
if (groupName.isEmpty()) return;
NameToObjectGroup::iterator groupIt = m_nameToObjectGroup.find(groupName);
if (groupIt == m_nameToObjectGroup.end()) return;
for (int id : groupIt->second) unbind(id);
m_nameToObjectGroup.erase(groupIt);
}
String16 InjectedScriptNative::groupName(int objectId) const {
if (objectId <= 0) return String16();
IdToObjectGroupName::const_iterator iterator =
m_idToObjectGroupName.find(objectId);
return iterator != m_idToObjectGroupName.end() ? iterator->second
: String16();
}
} // namespace v8_inspector

View File

@ -0,0 +1,45 @@
// Copyright 2015 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_INJECTEDSCRIPTNATIVE_H_
#define V8_INSPECTOR_INJECTEDSCRIPTNATIVE_H_
#include "src/inspector/protocol/Protocol.h"
#include <v8.h>
#include <vector>
namespace v8_inspector {
class InjectedScriptNative final {
public:
explicit InjectedScriptNative(v8::Isolate*);
~InjectedScriptNative();
void setOnInjectedScriptHost(v8::Local<v8::Object>);
static InjectedScriptNative* fromInjectedScriptHost(v8::Local<v8::Object>);
int bind(v8::Local<v8::Value>, const String16& groupName);
void unbind(int id);
v8::Local<v8::Value> objectForId(int id);
void releaseObjectGroup(const String16& groupName);
String16 groupName(int objectId) const;
private:
void addObjectToGroup(int objectId, const String16& groupName);
int m_lastBoundObjectId;
v8::Isolate* m_isolate;
protocol::HashMap<int, std::unique_ptr<v8::Global<v8::Value>>>
m_idToWrappedObject;
typedef protocol::HashMap<int, String16> IdToObjectGroupName;
IdToObjectGroupName m_idToObjectGroupName;
typedef protocol::HashMap<String16, std::vector<int>> NameToObjectGroup;
NameToObjectGroup m_nameToObjectGroup;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_INJECTEDSCRIPTNATIVE_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
// 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/InspectedContext.h"
#include "src/inspector/InjectedScript.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Console.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8ValueCopier.h"
#include "src/inspector/public/V8ContextInfo.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
void InspectedContext::weakCallback(
const v8::WeakCallbackInfo<InspectedContext>& data) {
InspectedContext* context = data.GetParameter();
if (!context->m_context.IsEmpty()) {
context->m_context.Reset();
data.SetSecondPassCallback(&InspectedContext::weakCallback);
} else {
context->m_inspector->discardInspectedContext(context->m_contextGroupId,
context->m_contextId);
}
}
void InspectedContext::consoleWeakCallback(
const v8::WeakCallbackInfo<InspectedContext>& data) {
data.GetParameter()->m_console.Reset();
}
InspectedContext::InspectedContext(V8InspectorImpl* inspector,
const V8ContextInfo& info, int contextId)
: m_inspector(inspector),
m_context(info.context->GetIsolate(), info.context),
m_contextId(contextId),
m_contextGroupId(info.contextGroupId),
m_origin(toString16(info.origin)),
m_humanReadableName(toString16(info.humanReadableName)),
m_auxData(toString16(info.auxData)),
m_reported(false) {
m_context.SetWeak(this, &InspectedContext::weakCallback,
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);
if (!global
->Set(info.context, toV8StringInternalized(isolate, "console"),
console)
.FromMaybe(false))
return;
m_console.Reset(isolate, console);
m_console.SetWeak(this, &InspectedContext::consoleWeakCallback,
v8::WeakCallbackType::kParameter);
}
InspectedContext::~InspectedContext() {
if (!m_context.IsEmpty() && !m_console.IsEmpty()) {
v8::HandleScope scope(isolate());
V8Console::clearInspectedContextIfNeeded(context(),
m_console.Get(isolate()));
}
}
v8::Local<v8::Context> InspectedContext::context() const {
return m_context.Get(isolate());
}
v8::Isolate* InspectedContext::isolate() const {
return m_inspector->isolate();
}
void InspectedContext::createInjectedScript() {
DCHECK(!m_injectedScript);
m_injectedScript = InjectedScript::create(this);
}
void InspectedContext::discardInjectedScript() { m_injectedScript.reset(); }
} // namespace v8_inspector

View File

@ -0,0 +1,64 @@
// 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_INSPECTEDCONTEXT_H_
#define V8_INSPECTOR_INSPECTEDCONTEXT_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/String16.h"
#include <v8.h>
namespace v8_inspector {
class InjectedScript;
class InjectedScriptHost;
class V8ContextInfo;
class V8InspectorImpl;
class InspectedContext {
V8_INSPECTOR_DISALLOW_COPY(InspectedContext);
public:
~InspectedContext();
v8::Local<v8::Context> context() const;
int contextId() const { return m_contextId; }
int contextGroupId() const { return m_contextGroupId; }
String16 origin() const { return m_origin; }
String16 humanReadableName() const { return m_humanReadableName; }
String16 auxData() const { return m_auxData; }
bool isReported() const { return m_reported; }
void setReported(bool reported) { m_reported = reported; }
v8::Isolate* isolate() const;
V8InspectorImpl* inspector() const { return m_inspector; }
InjectedScript* getInjectedScript() { return m_injectedScript.get(); }
void createInjectedScript();
void discardInjectedScript();
private:
friend class V8InspectorImpl;
InspectedContext(V8InspectorImpl*, const V8ContextInfo&, int contextId);
static void weakCallback(const v8::WeakCallbackInfo<InspectedContext>&);
static void consoleWeakCallback(
const v8::WeakCallbackInfo<InspectedContext>&);
V8InspectorImpl* m_inspector;
v8::Global<v8::Context> m_context;
int m_contextId;
int m_contextGroupId;
const String16 m_origin;
const String16 m_humanReadableName;
const String16 m_auxData;
bool m_reported;
std::unique_ptr<InjectedScript> m_injectedScript;
v8::Global<v8::Object> m_console;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_INSPECTEDCONTEXT_H_

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "src/inspector/JavaScriptCallFrame.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include <v8-debug.h>
namespace v8_inspector {
JavaScriptCallFrame::JavaScriptCallFrame(v8::Local<v8::Context> debuggerContext,
v8::Local<v8::Object> callFrame)
: m_isolate(debuggerContext->GetIsolate()),
m_debuggerContext(m_isolate, debuggerContext),
m_callFrame(m_isolate, callFrame) {}
JavaScriptCallFrame::~JavaScriptCallFrame() {}
int JavaScriptCallFrame::callV8FunctionReturnInt(const char* name) const {
v8::HandleScope handleScope(m_isolate);
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(
callFrame->Get(toV8StringInternalized(m_isolate, name)));
v8::Local<v8::Value> result;
if (!func->Call(context, callFrame, 0, nullptr).ToLocal(&result) ||
!result->IsInt32())
return 0;
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");
}
bool JavaScriptCallFrame::isAtReturn() const {
v8::HandleScope handleScope(m_isolate);
v8::Local<v8::Value> result =
v8::Local<v8::Object>::New(m_isolate, m_callFrame)
->Get(toV8StringInternalized(m_isolate, "isAtReturn"));
if (result.IsEmpty() || !result->IsBoolean()) return false;
return result->BooleanValue();
}
v8::Local<v8::Object> JavaScriptCallFrame::details() const {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(
callFrame->Get(toV8StringInternalized(m_isolate, "details")));
return v8::Local<v8::Object>::Cast(
func->Call(m_debuggerContext.Get(m_isolate), callFrame, 0, nullptr)
.ToLocalChecked());
}
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate(
v8::Local<v8::Value> expression) {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kRunMicrotasks);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> evalFunction = v8::Local<v8::Function>::Cast(
callFrame->Get(toV8StringInternalized(m_isolate, "evaluate")));
return evalFunction->Call(m_debuggerContext.Get(m_isolate), callFrame, 1,
&expression);
}
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::restart() {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> restartFunction = v8::Local<v8::Function>::Cast(
callFrame->Get(toV8StringInternalized(m_isolate, "restart")));
v8::Debug::SetLiveEditEnabled(m_isolate, true);
v8::MaybeLocal<v8::Value> result = restartFunction->Call(
m_debuggerContext.Get(m_isolate), callFrame, 0, nullptr);
v8::Debug::SetLiveEditEnabled(m_isolate, false);
return result;
}
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::setVariableValue(
int scopeNumber, v8::Local<v8::Value> variableName,
v8::Local<v8::Value> newValue) {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> setVariableValueFunction =
v8::Local<v8::Function>::Cast(callFrame->Get(
toV8StringInternalized(m_isolate, "setVariableValue")));
v8::Local<v8::Value> argv[] = {
v8::Local<v8::Value>(v8::Integer::New(m_isolate, scopeNumber)),
variableName, newValue};
return setVariableValueFunction->Call(m_debuggerContext.Get(m_isolate),
callFrame,
V8_INSPECTOR_ARRAY_LENGTH(argv), argv);
}
} // namespace v8_inspector

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_
#define V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/ProtocolPlatform.h"
#include <v8.h>
#include <vector>
namespace v8_inspector {
class JavaScriptCallFrame {
V8_INSPECTOR_DISALLOW_COPY(JavaScriptCallFrame);
public:
static std::unique_ptr<JavaScriptCallFrame> create(
v8::Local<v8::Context> debuggerContext, v8::Local<v8::Object> callFrame) {
return wrapUnique(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::Value> evaluate(v8::Local<v8::Value> expression);
v8::MaybeLocal<v8::Value> restart();
v8::MaybeLocal<v8::Value> setVariableValue(int scopeNumber,
v8::Local<v8::Value> variableName,
v8::Local<v8::Value> newValue);
private:
JavaScriptCallFrame(v8::Local<v8::Context> debuggerContext,
v8::Local<v8::Object> callFrame);
int callV8FunctionReturnInt(const char* name) const;
v8::Isolate* m_isolate;
v8::Global<v8::Context> m_debuggerContext;
v8::Global<v8::Object> m_callFrame;
};
using JavaScriptCallFrames = std::vector<std::unique_ptr<JavaScriptCallFrame>>;
} // namespace v8_inspector
#endif // V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_

View File

@ -0,0 +1,15 @@
// 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_
// TODO(dgozman): this file should be removed from v8_inspector.
//#include "wtf/Assertions.h"
//#include "wtf/PtrUtil.h"
#include <memory>
#endif // V8_INSPECTOR_PROTOCOLPLATFORM_H_

View File

@ -0,0 +1,76 @@
// Copyright 2015 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/RemoteObjectId.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/protocol/Protocol.h"
namespace v8_inspector {
RemoteObjectIdBase::RemoteObjectIdBase() : m_injectedScriptId(0) {}
std::unique_ptr<protocol::DictionaryValue>
RemoteObjectIdBase::parseInjectedScriptId(const String16& objectId) {
std::unique_ptr<protocol::Value> parsedValue = protocol::parseJSON(objectId);
if (!parsedValue || parsedValue->type() != protocol::Value::TypeObject)
return nullptr;
std::unique_ptr<protocol::DictionaryValue> parsedObjectId(
protocol::DictionaryValue::cast(parsedValue.release()));
bool success =
parsedObjectId->getInteger("injectedScriptId", &m_injectedScriptId);
if (success) return parsedObjectId;
return nullptr;
}
RemoteObjectId::RemoteObjectId() : RemoteObjectIdBase(), m_id(0) {}
std::unique_ptr<RemoteObjectId> RemoteObjectId::parse(
ErrorString* errorString, const String16& objectId) {
std::unique_ptr<RemoteObjectId> result(new RemoteObjectId());
std::unique_ptr<protocol::DictionaryValue> parsedObjectId =
result->parseInjectedScriptId(objectId);
if (!parsedObjectId) {
*errorString = "Invalid remote object id";
return nullptr;
}
bool success = parsedObjectId->getInteger("id", &result->m_id);
if (!success) {
*errorString = "Invalid remote object id";
return nullptr;
}
return result;
}
RemoteCallFrameId::RemoteCallFrameId()
: RemoteObjectIdBase(), m_frameOrdinal(0) {}
std::unique_ptr<RemoteCallFrameId> RemoteCallFrameId::parse(
ErrorString* errorString, const String16& objectId) {
std::unique_ptr<RemoteCallFrameId> result(new RemoteCallFrameId());
std::unique_ptr<protocol::DictionaryValue> parsedObjectId =
result->parseInjectedScriptId(objectId);
if (!parsedObjectId) {
*errorString = "Invalid call frame id";
return nullptr;
}
bool success = parsedObjectId->getInteger("ordinal", &result->m_frameOrdinal);
if (!success) {
*errorString = "Invalid call frame id";
return nullptr;
}
return result;
}
String16 RemoteCallFrameId::serialize(int injectedScriptId, int frameOrdinal) {
return "{\"ordinal\":" + String16::fromInteger(frameOrdinal) +
",\"injectedScriptId\":" + String16::fromInteger(injectedScriptId) +
"}";
}
} // namespace v8_inspector

View File

@ -0,0 +1,58 @@
// Copyright 2015 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_REMOTEOBJECTID_H_
#define V8_INSPECTOR_REMOTEOBJECTID_H_
#include "src/inspector/protocol/Forward.h"
namespace v8_inspector {
using protocol::ErrorString;
class RemoteObjectIdBase {
public:
int contextId() const { return m_injectedScriptId; }
protected:
RemoteObjectIdBase();
~RemoteObjectIdBase() {}
std::unique_ptr<protocol::DictionaryValue> parseInjectedScriptId(
const String16&);
int m_injectedScriptId;
};
class RemoteObjectId final : public RemoteObjectIdBase {
public:
static std::unique_ptr<RemoteObjectId> parse(ErrorString*, const String16&);
~RemoteObjectId() {}
int id() const { return m_id; }
private:
RemoteObjectId();
int m_id;
};
class RemoteCallFrameId final : public RemoteObjectIdBase {
public:
static std::unique_ptr<RemoteCallFrameId> parse(ErrorString*,
const String16&);
~RemoteCallFrameId() {}
int frameOrdinal() const { return m_frameOrdinal; }
static String16 serialize(int injectedScriptId, int frameOrdinal);
private:
RemoteCallFrameId();
int m_frameOrdinal;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_REMOTEOBJECTID_H_

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2009 Apple Inc. All rights reserved.
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef V8_INSPECTOR_SCRIPTBREAKPOINT_H_
#define V8_INSPECTOR_SCRIPTBREAKPOINT_H_
#include "src/inspector/String16.h"
namespace v8_inspector {
struct ScriptBreakpoint {
ScriptBreakpoint() : ScriptBreakpoint(0, 0, String16()) {}
ScriptBreakpoint(int lineNumber, int columnNumber, const String16& condition)
: lineNumber(lineNumber),
columnNumber(columnNumber),
condition(condition) {}
int lineNumber;
int columnNumber;
String16 condition;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_SCRIPTBREAKPOINT_H_

View File

@ -0,0 +1,164 @@
// 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/SearchUtil.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8Regex.h"
#include "src/inspector/protocol/Protocol.h"
namespace v8_inspector {
namespace {
String16 findMagicComment(const String16& content, const String16& name,
bool multiline) {
DCHECK(name.find("=") == String16::kNotFound);
unsigned length = content.length();
unsigned nameLength = name.length();
size_t pos = length;
size_t equalSignPos = 0;
size_t closingCommentPos = 0;
while (true) {
pos = content.reverseFind(name, pos);
if (pos == String16::kNotFound) return String16();
// Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name.
if (pos < 4) return String16();
pos -= 4;
if (content[pos] != '/') continue;
if ((content[pos + 1] != '/' || multiline) &&
(content[pos + 1] != '*' || !multiline))
continue;
if (content[pos + 2] != '#' && content[pos + 2] != '@') continue;
if (content[pos + 3] != ' ' && content[pos + 3] != '\t') continue;
equalSignPos = pos + 4 + nameLength;
if (equalSignPos < length && content[equalSignPos] != '=') continue;
if (multiline) {
closingCommentPos = content.find("*/", equalSignPos + 1);
if (closingCommentPos == String16::kNotFound) return String16();
}
break;
}
DCHECK(equalSignPos);
DCHECK(!multiline || closingCommentPos);
size_t urlPos = equalSignPos + 1;
String16 match = multiline
? content.substring(urlPos, closingCommentPos - urlPos)
: content.substring(urlPos);
size_t newLine = match.find("\n");
if (newLine != String16::kNotFound) match = match.substring(0, newLine);
match = match.stripWhiteSpace();
for (unsigned i = 0; i < match.length(); ++i) {
UChar c = match[i];
if (c == '"' || c == '\'' || c == ' ' || c == '\t') return "";
}
return match;
}
String16 createSearchRegexSource(const String16& text) {
String16Builder result;
for (unsigned i = 0; i < text.length(); i++) {
UChar c = text[i];
if (c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' ||
c == '+' || c == '-' || c == '*' || c == '.' || c == ',' || c == '?' ||
c == '\\' || c == '^' || c == '$' || c == '|') {
result.append('\\');
}
result.append(c);
}
return result.toString();
}
std::unique_ptr<std::vector<unsigned>> lineEndings(const String16& text) {
std::unique_ptr<std::vector<unsigned>> result(new std::vector<unsigned>());
const String16 lineEndString = "\n";
unsigned start = 0;
while (start < text.length()) {
size_t lineEnd = text.find(lineEndString, start);
if (lineEnd == String16::kNotFound) break;
result->push_back(static_cast<unsigned>(lineEnd));
start = lineEnd + 1;
}
result->push_back(static_cast<unsigned>(text.length()));
return result;
}
std::vector<std::pair<int, String16>> scriptRegexpMatchesByLines(
const V8Regex& regex, const String16& text) {
std::vector<std::pair<int, String16>> result;
if (text.isEmpty()) return result;
std::unique_ptr<std::vector<unsigned>> endings(lineEndings(text));
unsigned size = endings->size();
unsigned start = 0;
for (unsigned lineNumber = 0; lineNumber < size; ++lineNumber) {
unsigned lineEnd = endings->at(lineNumber);
String16 line = text.substring(start, lineEnd - start);
if (line.length() && line[line.length() - 1] == '\r')
line = line.substring(0, line.length() - 1);
int matchLength;
if (regex.match(line, 0, &matchLength) != -1)
result.push_back(std::pair<int, String16>(lineNumber, line));
start = lineEnd + 1;
}
return result;
}
std::unique_ptr<protocol::Debugger::SearchMatch> buildObjectForSearchMatch(
int lineNumber, const String16& lineContent) {
return protocol::Debugger::SearchMatch::create()
.setLineNumber(lineNumber)
.setLineContent(lineContent)
.build();
}
std::unique_ptr<V8Regex> createSearchRegex(V8InspectorImpl* inspector,
const String16& query,
bool caseSensitive, bool isRegex) {
String16 regexSource = isRegex ? query : createSearchRegexSource(query);
return wrapUnique(new V8Regex(inspector, regexSource, caseSensitive));
}
} // namespace
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>>
searchInTextByLinesImpl(V8InspectorSession* session, const String16& text,
const String16& query, const bool caseSensitive,
const bool isRegex) {
std::unique_ptr<V8Regex> regex = createSearchRegex(
static_cast<V8InspectorSessionImpl*>(session)->inspector(), query,
caseSensitive, isRegex);
std::vector<std::pair<int, String16>> matches =
scriptRegexpMatchesByLines(*regex.get(), text);
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> result;
for (const auto& match : matches)
result.push_back(buildObjectForSearchMatch(match.first, match.second));
return result;
}
String16 findSourceURL(const String16& content, bool multiline) {
return findMagicComment(content, "sourceURL", multiline);
}
String16 findSourceMapURL(const String16& content, bool multiline) {
return findMagicComment(content, "sourceMappingURL", multiline);
}
} // namespace v8_inspector

View File

@ -0,0 +1,24 @@
// 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_SEARCHUTIL_H_
#define V8_INSPECTOR_SEARCHUTIL_H_
#include "src/inspector/StringUtil.h"
#include "src/inspector/protocol/Debugger.h"
namespace v8_inspector {
class V8InspectorSession;
String16 findSourceURL(const String16& content, bool multiline);
String16 findSourceMapURL(const String16& content, bool multiline);
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>>
searchInTextByLinesImpl(V8InspectorSession*, const String16& text,
const String16& query, bool caseSensitive,
bool isRegex);
} // namespace v8_inspector
#endif // V8_INSPECTOR_SEARCHUTIL_H_

510
src/inspector/String16.cpp Normal file
View File

@ -0,0 +1,510 @@
// 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/String16.h"
#include "src/inspector/ProtocolPlatform.h"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <locale>
#include <string>
namespace v8_inspector {
namespace {
bool isASCII(UChar c) { return !(c & ~0x7F); }
bool isSpaceOrNewLine(UChar c) {
return isASCII(c) && c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9));
}
int charactersToInteger(const UChar* characters, size_t length,
bool* ok = nullptr) {
std::vector<char> buffer;
buffer.reserve(length + 1);
for (size_t i = 0; i < length; ++i) {
if (!isASCII(characters[i])) {
if (ok) *ok = false;
return 0;
}
buffer.push_back(static_cast<char>(characters[i]));
}
buffer.push_back('\0');
char* endptr;
int result = std::strtol(buffer.data(), &endptr, 10);
if (ok) *ok = !(*endptr);
return result;
}
const UChar replacementCharacter = 0xFFFD;
using UChar32 = uint32_t;
inline int inlineUTF8SequenceLengthNonASCII(char b0) {
if ((b0 & 0xC0) != 0xC0) return 0;
if ((b0 & 0xE0) == 0xC0) return 2;
if ((b0 & 0xF0) == 0xE0) return 3;
if ((b0 & 0xF8) == 0xF0) return 4;
return 0;
}
inline int inlineUTF8SequenceLength(char b0) {
return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0);
}
// Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
// into the first byte, depending on how many bytes follow. There are
// as many entries in this table as there are UTF-8 sequence types.
// (I.e., one byte sequence, two byte... etc.). Remember that sequences
// for *legal* UTF-8 will be 4 or fewer bytes total.
static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0,
0xF0, 0xF8, 0xFC};
typedef enum {
conversionOK, // conversion successful
sourceExhausted, // partial character in source, but hit end
targetExhausted, // insuff. room in target for conversion
sourceIllegal // source sequence is illegal/malformed
} ConversionResult;
ConversionResult convertUTF16ToUTF8(const UChar** sourceStart,
const UChar* sourceEnd, char** targetStart,
char* targetEnd, bool strict) {
ConversionResult result = conversionOK;
const UChar* source = *sourceStart;
char* target = *targetStart;
while (source < sourceEnd) {
UChar32 ch;
unsigned short bytesToWrite = 0;
const UChar32 byteMask = 0xBF;
const UChar32 byteMark = 0x80;
const UChar* oldSource =
source; // In case we have to back up because of target overflow.
ch = static_cast<unsigned short>(*source++);
// If we have a surrogate pair, convert to UChar32 first.
if (ch >= 0xD800 && ch <= 0xDBFF) {
// If the 16 bits following the high surrogate are in the source buffer...
if (source < sourceEnd) {
UChar32 ch2 = static_cast<unsigned short>(*source);
// If it's a low surrogate, convert to UChar32.
if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) {
ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000;
++source;
} else if (strict) { // it's an unpaired high surrogate
--source; // return to the illegal value itself
result = sourceIllegal;
break;
}
} else { // We don't have the 16 bits following the high surrogate.
--source; // return to the high surrogate
result = sourceExhausted;
break;
}
} else if (strict) {
// UTF-16 surrogate values are illegal in UTF-32
if (ch >= 0xDC00 && ch <= 0xDFFF) {
--source; // return to the illegal value itself
result = sourceIllegal;
break;
}
}
// Figure out how many bytes the result will require
if (ch < (UChar32)0x80) {
bytesToWrite = 1;
} else if (ch < (UChar32)0x800) {
bytesToWrite = 2;
} else if (ch < (UChar32)0x10000) {
bytesToWrite = 3;
} else if (ch < (UChar32)0x110000) {
bytesToWrite = 4;
} else {
bytesToWrite = 3;
ch = replacementCharacter;
}
target += bytesToWrite;
if (target > targetEnd) {
source = oldSource; // Back up source pointer!
target -= bytesToWrite;
result = targetExhausted;
break;
}
switch (bytesToWrite) { // note: everything falls through.
case 4:
*--target = (char)((ch | byteMark) & byteMask);
ch >>= 6;
case 3:
*--target = (char)((ch | byteMark) & byteMask);
ch >>= 6;
case 2:
*--target = (char)((ch | byteMark) & byteMask);
ch >>= 6;
case 1:
*--target = (char)(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/**
* Is this code point a BMP code point (U+0000..U+ffff)?
* @param c 32-bit code point
* @return TRUE or FALSE
* @stable ICU 2.8
*/
#define U_IS_BMP(c) ((uint32_t)(c) <= 0xffff)
/**
* Is this code point a supplementary code point (U+10000..U+10ffff)?
* @param c 32-bit code point
* @return TRUE or FALSE
* @stable ICU 2.8
*/
#define U_IS_SUPPLEMENTARY(c) ((uint32_t)((c)-0x10000) <= 0xfffff)
/**
* Is this code point a surrogate (U+d800..U+dfff)?
* @param c 32-bit code point
* @return TRUE or FALSE
* @stable ICU 2.4
*/
#define U_IS_SURROGATE(c) (((c)&0xfffff800) == 0xd800)
/**
* Get the lead surrogate (0xd800..0xdbff) for a
* supplementary code point (0x10000..0x10ffff).
* @param supplementary 32-bit code point (U+10000..U+10ffff)
* @return lead surrogate (U+d800..U+dbff) for supplementary
* @stable ICU 2.4
*/
#define U16_LEAD(supplementary) (UChar)(((supplementary) >> 10) + 0xd7c0)
/**
* Get the trail surrogate (0xdc00..0xdfff) for a
* supplementary code point (0x10000..0x10ffff).
* @param supplementary 32-bit code point (U+10000..U+10ffff)
* @return trail surrogate (U+dc00..U+dfff) for supplementary
* @stable ICU 2.4
*/
#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff) | 0xdc00)
// This must be called with the length pre-determined by the first byte.
// If presented with a length > 4, this returns false. The Unicode
// definition of UTF-8 goes up to 4-byte sequences.
static bool isLegalUTF8(const unsigned char* source, int length) {
unsigned char a;
const unsigned char* srcptr = source + length;
switch (length) {
default:
return false;
// Everything else falls through when "true"...
case 4:
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3:
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2:
if ((a = (*--srcptr)) > 0xBF) return false;
// no fall-through in this inner switch
switch (*source) {
case 0xE0:
if (a < 0xA0) return false;
break;
case 0xED:
if (a > 0x9F) return false;
break;
case 0xF0:
if (a < 0x90) return false;
break;
case 0xF4:
if (a > 0x8F) return false;
break;
default:
if (a < 0x80) return false;
}
case 1:
if (*source >= 0x80 && *source < 0xC2) return false;
}
if (*source > 0xF4) return false;
return true;
}
// Magic values subtracted from a buffer value during UTF8 conversion.
// This table contains as many values as there might be trailing bytes
// in a UTF-8 sequence.
static const UChar32 offsetsFromUTF8[6] = {0x00000000UL,
0x00003080UL,
0x000E2080UL,
0x03C82080UL,
static_cast<UChar32>(0xFA082080UL),
static_cast<UChar32>(0x82082080UL)};
static inline UChar32 readUTF8Sequence(const char*& sequence, unsigned length) {
UChar32 character = 0;
// The cases all fall through.
switch (length) {
case 6:
character += static_cast<unsigned char>(*sequence++);
character <<= 6;
case 5:
character += static_cast<unsigned char>(*sequence++);
character <<= 6;
case 4:
character += static_cast<unsigned char>(*sequence++);
character <<= 6;
case 3:
character += static_cast<unsigned char>(*sequence++);
character <<= 6;
case 2:
character += static_cast<unsigned char>(*sequence++);
character <<= 6;
case 1:
character += static_cast<unsigned char>(*sequence++);
}
return character - offsetsFromUTF8[length - 1];
}
ConversionResult convertUTF8ToUTF16(const char** sourceStart,
const char* sourceEnd, UChar** targetStart,
UChar* targetEnd, bool* sourceAllASCII,
bool strict) {
ConversionResult result = conversionOK;
const char* source = *sourceStart;
UChar* target = *targetStart;
UChar orAllData = 0;
while (source < sourceEnd) {
int utf8SequenceLength = inlineUTF8SequenceLength(*source);
if (sourceEnd - source < utf8SequenceLength) {
result = sourceExhausted;
break;
}
// Do this check whether lenient or strict
if (!isLegalUTF8(reinterpret_cast<const unsigned char*>(source),
utf8SequenceLength)) {
result = sourceIllegal;
break;
}
UChar32 character = readUTF8Sequence(source, utf8SequenceLength);
if (target >= targetEnd) {
source -= utf8SequenceLength; // Back up source pointer!
result = targetExhausted;
break;
}
if (U_IS_BMP(character)) {
// UTF-16 surrogate values are illegal in UTF-32
if (U_IS_SURROGATE(character)) {
if (strict) {
source -= utf8SequenceLength; // return to the illegal value itself
result = sourceIllegal;
break;
}
*target++ = replacementCharacter;
orAllData |= replacementCharacter;
} else {
*target++ = static_cast<UChar>(character); // normal case
orAllData |= character;
}
} else if (U_IS_SUPPLEMENTARY(character)) {
// target is a character in range 0xFFFF - 0x10FFFF
if (target + 1 >= targetEnd) {
source -= utf8SequenceLength; // Back up source pointer!
result = targetExhausted;
break;
}
*target++ = U16_LEAD(character);
*target++ = U16_TRAIL(character);
orAllData = 0xffff;
} else {
if (strict) {
source -= utf8SequenceLength; // return to the start
result = sourceIllegal;
break; // Bail out; shouldn't continue
} else {
*target++ = replacementCharacter;
orAllData |= replacementCharacter;
}
}
}
*sourceStart = source;
*targetStart = target;
if (sourceAllASCII) *sourceAllASCII = !(orAllData & ~0x7f);
return result;
}
// Helper to write a three-byte UTF-8 code point to the buffer, caller must
// check room is available.
static inline void putUTF8Triple(char*& buffer, UChar ch) {
*buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0);
*buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80);
*buffer++ = static_cast<char>((ch & 0x3F) | 0x80);
}
} // namespace
// static
String16 String16::fromInteger(int number) {
const size_t kBufferSize = 50;
char buffer[kBufferSize];
std::snprintf(buffer, kBufferSize, "%d", number);
return String16(buffer);
}
// static
String16 String16::fromDouble(double number) {
const size_t kBufferSize = 100;
char buffer[kBufferSize];
std::snprintf(buffer, kBufferSize, "%f", number);
return String16(buffer);
}
// static
String16 String16::fromDoublePrecision3(double number) {
const size_t kBufferSize = 100;
char buffer[kBufferSize];
std::snprintf(buffer, kBufferSize, "%.3g", number);
return String16(buffer);
}
// static
String16 String16::fromDoublePrecision6(double number) {
const size_t kBufferSize = 100;
char buffer[kBufferSize];
std::snprintf(buffer, kBufferSize, "%.6g", number);
return String16(buffer);
}
int String16::toInteger(bool* ok) const {
return charactersToInteger(characters16(), length(), ok);
}
String16 String16::stripWhiteSpace() const {
if (!length()) return String16();
unsigned start = 0;
unsigned end = length() - 1;
// skip white space from start
while (start <= end && isSpaceOrNewLine(characters16()[start])) ++start;
// only white space
if (start > end) return String16();
// skip white space from end
while (end && isSpaceOrNewLine(characters16()[end])) --end;
if (!start && end == length() - 1) return *this;
return String16(characters16() + start, end + 1 - start);
}
String16Builder::String16Builder() {}
void String16Builder::append(const String16& s) {
m_buffer.insert(m_buffer.end(), s.characters16(),
s.characters16() + s.length());
}
void String16Builder::append(UChar c) { m_buffer.push_back(c); }
void String16Builder::append(char c) {
UChar u = c;
m_buffer.push_back(u);
}
void String16Builder::append(const UChar* characters, size_t length) {
m_buffer.insert(m_buffer.end(), characters, characters + length);
}
void String16Builder::append(const char* characters, size_t length) {
m_buffer.insert(m_buffer.end(), characters, characters + length);
}
String16 String16Builder::toString() {
return String16(m_buffer.data(), m_buffer.size());
}
void String16Builder::reserveCapacity(size_t capacity) {
m_buffer.reserve(capacity);
}
String16 String16::fromUTF8(const char* stringStart, size_t length) {
if (!stringStart || !length) return String16();
std::vector<UChar> buffer(length);
UChar* bufferStart = buffer.data();
UChar* bufferCurrent = bufferStart;
const char* stringCurrent = stringStart;
if (convertUTF8ToUTF16(&stringCurrent, stringStart + length, &bufferCurrent,
bufferCurrent + buffer.size(), 0,
true) != conversionOK)
return String16();
unsigned utf16Length = bufferCurrent - bufferStart;
return String16(bufferStart, utf16Length);
}
std::string String16::utf8() const {
unsigned length = this->length();
if (!length) return std::string("");
// Allocate a buffer big enough to hold all the characters
// (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes).
// Optimization ideas, if we find this function is hot:
// * We could speculatively create a CStringBuffer to contain 'length'
// characters, and resize if necessary (i.e. if the buffer contains
// non-ascii characters). (Alternatively, scan the buffer first for
// ascii characters, so we know this will be sufficient).
// * We could allocate a CStringBuffer with an appropriate size to
// have a good chance of being able to write the string into the
// buffer without reallocing (say, 1.5 x length).
if (length > std::numeric_limits<unsigned>::max() / 3) return std::string();
std::vector<char> bufferVector(length * 3);
char* buffer = bufferVector.data();
const UChar* characters = m_impl.data();
ConversionResult result =
convertUTF16ToUTF8(&characters, characters + length, &buffer,
buffer + bufferVector.size(), false);
DCHECK(
result !=
targetExhausted); // (length * 3) should be sufficient for any conversion
// Only produced from strict conversion.
DCHECK(result != sourceIllegal);
// Check for an unconverted high surrogate.
if (result == sourceExhausted) {
// This should be one unpaired high surrogate. Treat it the same
// was as an unpaired high surrogate would have been handled in
// the middle of a string with non-strict conversion - which is
// to say, simply encode it to UTF-8.
DCHECK((characters + 1) == (m_impl.data() + length));
DCHECK((*characters >= 0xD800) && (*characters <= 0xDBFF));
// There should be room left, since one UChar hasn't been
// converted.
DCHECK((buffer + 3) <= (buffer + bufferVector.size()));
putUTF8Triple(buffer, *characters);
}
return std::string(bufferVector.data(), buffer - bufferVector.data());
}
} // namespace v8_inspector

132
src/inspector/String16.h Normal file
View File

@ -0,0 +1,132 @@
// 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_STRING16_H_
#define V8_INSPECTOR_STRING16_H_
#include <stdint.h>
#include <cctype>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
namespace v8_inspector {
using UChar = uint16_t;
class String16 {
public:
static const size_t kNotFound = static_cast<size_t>(-1);
String16() {}
String16(const String16& other) : m_impl(other.m_impl) {}
String16(const UChar* characters, size_t size) : m_impl(characters, size) {}
String16(const UChar* characters) : m_impl(characters) {}
String16(const char* characters)
: String16(characters, std::strlen(characters)) {}
String16(const char* characters, size_t size) {
m_impl.resize(size);
for (size_t i = 0; i < size; ++i) m_impl[i] = characters[i];
}
static String16 fromInteger(int);
static String16 fromDouble(double);
static String16 fromDoublePrecision3(double);
static String16 fromDoublePrecision6(double);
int toInteger(bool* ok = nullptr) const;
String16 stripWhiteSpace() const;
const UChar* characters16() const { return m_impl.c_str(); }
size_t length() const { return m_impl.length(); }
bool isEmpty() const { return !m_impl.length(); }
UChar operator[](unsigned index) const { return m_impl[index]; }
String16 substring(unsigned pos, unsigned len = UINT_MAX) const {
return String16(m_impl.substr(pos, len));
}
size_t find(const String16& str, unsigned start = 0) const {
return m_impl.find(str.m_impl, start);
}
size_t reverseFind(const String16& str, unsigned start = UINT_MAX) const {
return m_impl.rfind(str.m_impl, start);
}
void swap(String16& other) { m_impl.swap(other.m_impl); }
// Convenience methods.
std::string utf8() const;
static String16 fromUTF8(const char* stringStart, size_t length);
const std::basic_string<UChar>& impl() const { return m_impl; }
explicit String16(const std::basic_string<UChar>& impl) : m_impl(impl) {}
std::size_t hash() const {
if (!has_hash) {
size_t hash = 0;
for (size_t i = 0; i < length(); ++i) hash = 31 * hash + m_impl[i];
hash_code = hash;
has_hash = true;
}
return hash_code;
}
private:
std::basic_string<UChar> m_impl;
mutable bool has_hash = false;
mutable std::size_t hash_code = 0;
};
inline bool operator==(const String16& a, const String16& b) {
return a.impl() == b.impl();
}
inline bool operator<(const String16& a, const String16& b) {
return a.impl() < b.impl();
}
inline bool operator!=(const String16& a, const String16& b) {
return a.impl() != b.impl();
}
inline bool operator==(const String16& a, const char* b) {
return a.impl() == String16(b).impl();
}
inline String16 operator+(const String16& a, const char* b) {
return String16(a.impl() + String16(b).impl());
}
inline String16 operator+(const char* a, const String16& b) {
return String16(String16(a).impl() + b.impl());
}
inline String16 operator+(const String16& a, const String16& b) {
return String16(a.impl() + b.impl());
}
class String16Builder {
public:
String16Builder();
void append(const String16&);
void append(UChar);
void append(char);
void append(const UChar*, size_t);
void append(const char*, size_t);
String16 toString();
void reserveCapacity(size_t);
private:
std::vector<UChar> m_buffer;
};
} // namespace v8_inspector
#if !defined(__APPLE__) || defined(_LIBCPP_VERSION)
namespace std {
template <>
struct hash<v8_inspector::String16> {
std::size_t operator()(const v8_inspector::String16& string) const {
return string.hash();
}
};
} // namespace std
#endif // !defined(__APPLE__) || defined(_LIBCPP_VERSION)
#endif // V8_INSPECTOR_STRING16_H_

View File

@ -0,0 +1,195 @@
// 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/StringUtil.h"
#include "src/inspector/protocol/Protocol.h"
namespace v8_inspector {
v8::Local<v8::String> toV8String(v8::Isolate* isolate, const String16& string) {
if (string.isEmpty()) return v8::String::Empty(isolate);
return v8::String::NewFromTwoByte(
isolate, reinterpret_cast<const uint16_t*>(string.characters16()),
v8::NewStringType::kNormal, string.length())
.ToLocalChecked();
}
v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate,
const String16& string) {
if (string.isEmpty()) return v8::String::Empty(isolate);
return v8::String::NewFromTwoByte(
isolate, reinterpret_cast<const uint16_t*>(string.characters16()),
v8::NewStringType::kInternalized, string.length())
.ToLocalChecked();
}
v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate,
const char* str) {
return v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kInternalized)
.ToLocalChecked();
}
v8::Local<v8::String> toV8String(v8::Isolate* isolate,
const StringView& string) {
if (!string.length()) return v8::String::Empty(isolate);
if (string.is8Bit())
return v8::String::NewFromOneByte(
isolate, reinterpret_cast<const uint8_t*>(string.characters8()),
v8::NewStringType::kNormal, string.length())
.ToLocalChecked();
return v8::String::NewFromTwoByte(
isolate, reinterpret_cast<const uint16_t*>(string.characters16()),
v8::NewStringType::kNormal, string.length())
.ToLocalChecked();
}
String16 toProtocolString(v8::Local<v8::String> value) {
if (value.IsEmpty() || value->IsNull() || value->IsUndefined())
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());
}
String16 toProtocolStringWithTypeCheck(v8::Local<v8::Value> value) {
if (value.IsEmpty() || !value->IsString()) return String16();
return toProtocolString(value.As<v8::String>());
}
String16 toString16(const StringView& string) {
if (!string.length()) return String16();
if (string.is8Bit())
return String16(reinterpret_cast<const char*>(string.characters8()),
string.length());
return String16(reinterpret_cast<const UChar*>(string.characters16()),
string.length());
}
StringView toStringView(const String16& string) {
if (string.isEmpty()) return StringView();
return StringView(reinterpret_cast<const uint16_t*>(string.characters16()),
string.length());
}
bool stringViewStartsWith(const StringView& string, const char* prefix) {
if (!string.length()) return !(*prefix);
if (string.is8Bit()) {
for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) {
if (string.characters8()[i] != prefix[j]) return false;
}
} else {
for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) {
if (string.characters16()[i] != prefix[j]) return false;
}
}
return true;
}
namespace protocol {
std::unique_ptr<protocol::Value> parseJSON(const StringView& string) {
if (!string.length()) return nullptr;
if (string.is8Bit())
return protocol::parseJSON(string.characters8(), string.length());
return protocol::parseJSON(string.characters16(), string.length());
}
std::unique_ptr<protocol::Value> parseJSON(const String16& string) {
if (!string.length()) return nullptr;
return protocol::parseJSON(string.characters16(), string.length());
}
} // namespace protocol
std::unique_ptr<protocol::Value> toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value,
int maxDepth) {
if (value.IsEmpty()) {
NOTREACHED();
return nullptr;
}
if (!maxDepth) return nullptr;
maxDepth--;
if (value->IsNull() || value->IsUndefined()) return protocol::Value::null();
if (value->IsBoolean())
return protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value());
if (value->IsNumber()) {
double doubleValue = value.As<v8::Number>()->Value();
int intValue = static_cast<int>(doubleValue);
if (intValue == doubleValue)
return protocol::FundamentalValue::create(intValue);
return protocol::FundamentalValue::create(doubleValue);
}
if (value->IsString())
return protocol::StringValue::create(
toProtocolString(value.As<v8::String>()));
if (value->IsArray()) {
v8::Local<v8::Array> array = value.As<v8::Array>();
std::unique_ptr<protocol::ListValue> inspectorArray =
protocol::ListValue::create();
uint32_t length = array->Length();
for (uint32_t i = 0; i < length; i++) {
v8::Local<v8::Value> value;
if (!array->Get(context, i).ToLocal(&value)) return nullptr;
std::unique_ptr<protocol::Value> element =
toProtocolValue(context, value, maxDepth);
if (!element) return nullptr;
inspectorArray->pushValue(std::move(element));
}
return std::move(inspectorArray);
}
if (value->IsObject()) {
std::unique_ptr<protocol::DictionaryValue> jsonObject =
protocol::DictionaryValue::create();
v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
v8::Local<v8::Array> propertyNames;
if (!object->GetPropertyNames(context).ToLocal(&propertyNames))
return nullptr;
uint32_t length = propertyNames->Length();
for (uint32_t i = 0; i < length; i++) {
v8::Local<v8::Value> name;
if (!propertyNames->Get(context, i).ToLocal(&name)) return nullptr;
// FIXME(yurys): v8::Object should support GetOwnPropertyNames
if (name->IsString()) {
v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedProperty(
context, v8::Local<v8::String>::Cast(name));
if (!hasRealNamedProperty.IsJust() || !hasRealNamedProperty.FromJust())
continue;
}
v8::Local<v8::String> propertyName;
if (!name->ToString(context).ToLocal(&propertyName)) continue;
v8::Local<v8::Value> property;
if (!object->Get(context, name).ToLocal(&property)) return nullptr;
std::unique_ptr<protocol::Value> propertyValue =
toProtocolValue(context, property, maxDepth);
if (!propertyValue) return nullptr;
jsonObject->setValue(toProtocolString(propertyName),
std::move(propertyValue));
}
return std::move(jsonObject);
}
NOTREACHED();
return nullptr;
}
// static
std::unique_ptr<StringBuffer> StringBuffer::create(const StringView& string) {
String16 owner = toString16(string);
return StringBufferImpl::adopt(owner);
}
// static
std::unique_ptr<StringBufferImpl> StringBufferImpl::adopt(String16& string) {
return wrapUnique(new StringBufferImpl(string));
}
StringBufferImpl::StringBufferImpl(String16& string) {
m_owner.swap(string);
m_string = toStringView(m_owner);
}
} // namespace v8_inspector

View File

@ -0,0 +1,73 @@
// 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_STRINGUTIL_H_
#define V8_INSPECTOR_STRINGUTIL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/String16.h"
#include "src/inspector/public/StringBuffer.h"
#include "src/inspector/public/StringView.h"
#include <v8.h>
namespace v8_inspector {
namespace protocol {
class Value;
using String = v8_inspector::String16;
using StringBuilder = v8_inspector::String16Builder;
class StringUtil {
public:
static String substring(const String& s, unsigned pos, unsigned len) {
return s.substring(pos, len);
}
static String fromInteger(int number) { return String::fromInteger(number); }
static String fromDouble(double number) { return String::fromDouble(number); }
static const size_t kNotFound = String::kNotFound;
static void builderReserve(StringBuilder& builder, unsigned capacity) {
builder.reserveCapacity(capacity);
}
};
std::unique_ptr<protocol::Value> parseJSON(const StringView& json);
std::unique_ptr<protocol::Value> parseJSON(const String16& json);
} // namespace protocol
std::unique_ptr<protocol::Value> toProtocolValue(v8::Local<v8::Context>,
v8::Local<v8::Value>,
int maxDepth = 1000);
v8::Local<v8::String> toV8String(v8::Isolate*, const String16&);
v8::Local<v8::String> toV8StringInternalized(v8::Isolate*, const String16&);
v8::Local<v8::String> toV8StringInternalized(v8::Isolate*, const char*);
v8::Local<v8::String> toV8String(v8::Isolate*, const StringView&);
// TODO(dgozman): rename to toString16.
String16 toProtocolString(v8::Local<v8::String>);
String16 toProtocolStringWithTypeCheck(v8::Local<v8::Value>);
String16 toString16(const StringView&);
StringView toStringView(const String16&);
bool stringViewStartsWith(const StringView&, const char*);
class StringBufferImpl : public StringBuffer {
V8_INSPECTOR_DISALLOW_COPY(StringBufferImpl);
public:
// Destroys string's content.
static std::unique_ptr<StringBufferImpl> adopt(String16&);
const StringView& string() override { return m_string; }
private:
explicit StringBufferImpl(String16&);
String16 m_owner;
StringView m_string;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_STRINGUTIL_H_

33
src/inspector/V8Compat.h Normal file
View File

@ -0,0 +1,33 @@
// 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_V8COMPAT_H_
#define V8_INSPECTOR_V8COMPAT_H_
#include <v8.h>
#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 1)
namespace v8 {
// In standalone V8 inspector this is expected to be noop anyways...
class V8_EXPORT MicrotasksScope {
public:
enum Type { kRunMicrotasks, kDoNotRunMicrotasks };
MicrotasksScope(Isolate* isolate, Type type) {
// No-op
}
};
} // namespace v8
#define V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, callback, data, length) \
v8::Function::New((context), (callback), (data), (length))
#else
#define V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, callback, data, length) \
v8::Function::New((context), (callback), (data), (length), \
v8::ConstructorBehavior::kThrow)
#endif // V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION <
// 1)
#endif // V8_INSPECTOR_V8COMPAT_H_

908
src/inspector/V8Console.cpp Normal file
View File

@ -0,0 +1,908 @@
// 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/V8Console.h"
#include "src/inspector/InjectedScript.h"
#include "src/inspector/InspectedContext.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8ConsoleMessage.h"
#include "src/inspector/V8DebuggerAgentImpl.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8ProfilerAgentImpl.h"
#include "src/inspector/V8RuntimeAgentImpl.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/V8ValueCopier.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
namespace {
v8::Local<v8::Private> inspectedContextPrivateKey(v8::Isolate* isolate) {
return v8::Private::ForApi(
isolate, toV8StringInternalized(isolate, "V8Console#InspectedContext"));
}
class ConsoleHelper {
V8_INSPECTOR_DISALLOW_COPY(ConsoleHelper);
public:
ConsoleHelper(const v8::FunctionCallbackInfo<v8::Value>& info)
: m_info(info),
m_isolate(info.GetIsolate()),
m_context(info.GetIsolate()->GetCurrentContext()),
m_inspectedContext(nullptr),
m_inspectorClient(nullptr) {}
v8::Local<v8::Object> ensureConsole() {
if (m_console.IsEmpty()) {
DCHECK(!m_info.Data().IsEmpty());
DCHECK(!m_info.Data()->IsUndefined());
m_console = m_info.Data().As<v8::Object>();
}
return m_console;
}
InspectedContext* ensureInspectedContext() {
if (m_inspectedContext) return m_inspectedContext;
v8::Local<v8::Object> console = ensureConsole();
v8::Local<v8::Private> key = inspectedContextPrivateKey(m_isolate);
v8::Local<v8::Value> inspectedContextValue;
if (!console->GetPrivate(m_context, key).ToLocal(&inspectedContextValue))
return nullptr;
DCHECK(inspectedContextValue->IsExternal());
m_inspectedContext = static_cast<InspectedContext*>(
inspectedContextValue.As<v8::External>()->Value());
return m_inspectedContext;
}
V8InspectorClient* ensureDebuggerClient() {
if (m_inspectorClient) return m_inspectorClient;
InspectedContext* inspectedContext = ensureInspectedContext();
if (!inspectedContext) return nullptr;
m_inspectorClient = inspectedContext->inspector()->client();
return m_inspectorClient;
}
void reportCall(ConsoleAPIType type) {
if (!m_info.Length()) return;
std::vector<v8::Local<v8::Value>> arguments;
for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
reportCall(type, arguments);
}
void reportCallWithDefaultArgument(ConsoleAPIType type,
const String16& message) {
std::vector<v8::Local<v8::Value>> arguments;
for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
if (!m_info.Length()) arguments.push_back(toV8String(m_isolate, message));
reportCall(type, arguments);
}
void reportCallWithArgument(ConsoleAPIType type, const String16& message) {
std::vector<v8::Local<v8::Value>> arguments(1,
toV8String(m_isolate, message));
reportCall(type, arguments);
}
void reportCall(ConsoleAPIType type,
const std::vector<v8::Local<v8::Value>>& arguments) {
InspectedContext* inspectedContext = ensureInspectedContext();
if (!inspectedContext) return;
V8InspectorImpl* inspector = inspectedContext->inspector();
std::unique_ptr<V8ConsoleMessage> message =
V8ConsoleMessage::createForConsoleAPI(
inspector->client()->currentTimeMS(), type, arguments,
inspector->debugger()->captureStackTrace(false), inspectedContext);
inspector->ensureConsoleMessageStorage(inspectedContext->contextGroupId())
->addMessage(std::move(message));
}
void reportDeprecatedCall(const char* id, const String16& message) {
if (checkAndSetPrivateFlagOnConsole(id, false)) return;
std::vector<v8::Local<v8::Value>> arguments(1,
toV8String(m_isolate, message));
reportCall(ConsoleAPIType::kWarning, arguments);
}
bool firstArgToBoolean(bool defaultValue) {
if (m_info.Length() < 1) return defaultValue;
if (m_info[0]->IsBoolean()) return m_info[0].As<v8::Boolean>()->Value();
return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue);
}
String16 firstArgToString(const String16& defaultValue) {
if (m_info.Length() < 1) return defaultValue;
v8::Local<v8::String> titleValue;
if (m_info[0]->IsObject()) {
if (!m_info[0].As<v8::Object>()->ObjectProtoToString(m_context).ToLocal(
&titleValue))
return defaultValue;
} else {
if (!m_info[0]->ToString(m_context).ToLocal(&titleValue))
return defaultValue;
}
return toProtocolString(titleValue);
}
v8::MaybeLocal<v8::Object> firstArgAsObject() {
if (m_info.Length() < 1 || !m_info[0]->IsObject())
return v8::MaybeLocal<v8::Object>();
return m_info[0].As<v8::Object>();
}
v8::MaybeLocal<v8::Function> firstArgAsFunction() {
if (m_info.Length() < 1 || !m_info[0]->IsFunction())
return v8::MaybeLocal<v8::Function>();
return m_info[0].As<v8::Function>();
}
v8::MaybeLocal<v8::Map> privateMap(const char* name) {
v8::Local<v8::Object> console = ensureConsole();
v8::Local<v8::Private> privateKey =
v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name));
v8::Local<v8::Value> mapValue;
if (!console->GetPrivate(m_context, privateKey).ToLocal(&mapValue))
return v8::MaybeLocal<v8::Map>();
if (mapValue->IsUndefined()) {
v8::Local<v8::Map> map = v8::Map::New(m_isolate);
if (!console->SetPrivate(m_context, privateKey, map).FromMaybe(false))
return v8::MaybeLocal<v8::Map>();
return map;
}
return mapValue->IsMap() ? mapValue.As<v8::Map>()
: v8::MaybeLocal<v8::Map>();
}
int64_t getIntFromMap(v8::Local<v8::Map> map, const String16& key,
int64_t defaultValue) {
v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
if (!map->Has(m_context, v8Key).FromMaybe(false)) return defaultValue;
v8::Local<v8::Value> intValue;
if (!map->Get(m_context, v8Key).ToLocal(&intValue)) return defaultValue;
return intValue.As<v8::Integer>()->Value();
}
void setIntOnMap(v8::Local<v8::Map> map, const String16& key, int64_t value) {
v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
if (!map->Set(m_context, v8Key, v8::Integer::New(m_isolate, value))
.ToLocal(&map))
return;
}
double getDoubleFromMap(v8::Local<v8::Map> map, const String16& key,
double defaultValue) {
v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
if (!map->Has(m_context, v8Key).FromMaybe(false)) return defaultValue;
v8::Local<v8::Value> intValue;
if (!map->Get(m_context, v8Key).ToLocal(&intValue)) return defaultValue;
return intValue.As<v8::Number>()->Value();
}
void setDoubleOnMap(v8::Local<v8::Map> map, const String16& key,
double value) {
v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
if (!map->Set(m_context, v8Key, v8::Number::New(m_isolate, value))
.ToLocal(&map))
return;
}
V8ProfilerAgentImpl* profilerAgent() {
if (V8InspectorSessionImpl* session = currentSession()) {
if (session && session->profilerAgent()->enabled())
return session->profilerAgent();
}
return nullptr;
}
V8DebuggerAgentImpl* debuggerAgent() {
if (V8InspectorSessionImpl* session = currentSession()) {
if (session && session->debuggerAgent()->enabled())
return session->debuggerAgent();
}
return nullptr;
}
V8InspectorSessionImpl* currentSession() {
InspectedContext* inspectedContext = ensureInspectedContext();
if (!inspectedContext) return nullptr;
return inspectedContext->inspector()->sessionForContextGroup(
inspectedContext->contextGroupId());
}
private:
const v8::FunctionCallbackInfo<v8::Value>& m_info;
v8::Isolate* m_isolate;
v8::Local<v8::Context> m_context;
v8::Local<v8::Object> m_console;
InspectedContext* m_inspectedContext;
V8InspectorClient* m_inspectorClient;
bool checkAndSetPrivateFlagOnConsole(const char* name, bool defaultValue) {
v8::Local<v8::Object> console = ensureConsole();
v8::Local<v8::Private> key =
v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name));
v8::Local<v8::Value> flagValue;
if (!console->GetPrivate(m_context, key).ToLocal(&flagValue))
return defaultValue;
DCHECK(flagValue->IsUndefined() || flagValue->IsBoolean());
if (flagValue->IsBoolean()) {
DCHECK(flagValue.As<v8::Boolean>()->Value());
return true;
}
if (!console->SetPrivate(m_context, key, v8::True(m_isolate))
.FromMaybe(false))
return defaultValue;
return false;
}
};
void returnDataCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(info.Data());
}
void createBoundFunctionProperty(v8::Local<v8::Context> context,
v8::Local<v8::Object> console,
const char* name,
v8::FunctionCallback callback,
const char* description = nullptr) {
v8::Local<v8::String> funcName =
toV8StringInternalized(context->GetIsolate(), name);
v8::Local<v8::Function> func;
if (!V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, callback, console, 0)
.ToLocal(&func))
return;
func->SetName(funcName);
if (description) {
v8::Local<v8::String> returnValue =
toV8String(context->GetIsolate(), description);
v8::Local<v8::Function> toStringFunction;
if (V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, returnDataCallback,
returnValue, 0)
.ToLocal(&toStringFunction))
createDataProperty(context, func, toV8StringInternalized(
context->GetIsolate(), "toString"),
toStringFunction);
}
createDataProperty(context, console, funcName, func);
}
} // namespace
void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kDebug);
}
void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kError);
}
void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kInfo);
}
void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kLog);
}
void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kWarning);
}
void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kDir);
}
void V8Console::dirxmlCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kDirXML);
}
void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCall(ConsoleAPIType::kTable);
}
void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kTrace,
String16("console.trace"));
}
void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kStartGroup,
String16("console.group"));
}
void V8Console::groupCollapsedCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCallWithDefaultArgument(
ConsoleAPIType::kStartGroupCollapsed, String16("console.groupCollapsed"));
}
void V8Console::groupEndCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCallWithDefaultArgument(
ConsoleAPIType::kEndGroup, String16("console.groupEnd"));
}
void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kClear,
String16("console.clear"));
}
void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
String16 title = helper.firstArgToString(String16());
String16 identifier;
if (title.isEmpty()) {
std::unique_ptr<V8StackTraceImpl> stackTrace =
V8StackTraceImpl::capture(nullptr, 0, 1);
if (stackTrace)
identifier = toString16(stackTrace->topSourceURL()) + ":" +
String16::fromInteger(stackTrace->topLineNumber());
} else {
identifier = title + "@";
}
v8::Local<v8::Map> countMap;
if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) return;
int64_t count = helper.getIntFromMap(countMap, identifier, 0) + 1;
helper.setIntOnMap(countMap, identifier, count);
helper.reportCallWithArgument(ConsoleAPIType::kCount,
title + ": " + String16::fromInteger(count));
}
void V8Console::assertCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
if (helper.firstArgToBoolean(false)) return;
std::vector<v8::Local<v8::Value>> arguments;
for (int i = 1; i < info.Length(); ++i) arguments.push_back(info[i]);
if (info.Length() < 2)
arguments.push_back(
toV8String(info.GetIsolate(), String16("console.assert")));
helper.reportCall(ConsoleAPIType::kAssert, arguments);
if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent())
debuggerAgent->breakProgramOnException(
protocol::Debugger::Paused::ReasonEnum::Assert, nullptr);
}
void V8Console::markTimelineCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportDeprecatedCall("V8Console#markTimelineDeprecated",
"'console.markTimeline' is "
"deprecated. Please use "
"'console.timeStamp' instead.");
timeStampCallback(info);
}
void V8Console::profileCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
profilerAgent->consoleProfile(helper.firstArgToString(String16()));
}
void V8Console::profileEndCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
profilerAgent->consoleProfileEnd(helper.firstArgToString(String16()));
}
static void timeFunction(const v8::FunctionCallbackInfo<v8::Value>& info,
bool timelinePrefix) {
ConsoleHelper helper(info);
if (V8InspectorClient* client = helper.ensureDebuggerClient()) {
String16 protocolTitle = helper.firstArgToString("default");
if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'";
client->consoleTime(toStringView(protocolTitle));
v8::Local<v8::Map> timeMap;
if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) return;
helper.setDoubleOnMap(timeMap, protocolTitle, client->currentTimeMS());
}
}
static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info,
bool timelinePrefix) {
ConsoleHelper helper(info);
if (V8InspectorClient* client = helper.ensureDebuggerClient()) {
String16 protocolTitle = helper.firstArgToString("default");
if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'";
client->consoleTimeEnd(toStringView(protocolTitle));
v8::Local<v8::Map> timeMap;
if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) return;
double elapsed = client->currentTimeMS() -
helper.getDoubleFromMap(timeMap, protocolTitle, 0.0);
String16 message =
protocolTitle + ": " + String16::fromDoublePrecision3(elapsed) + "ms";
helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message);
}
}
void V8Console::timelineCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportDeprecatedCall(
"V8Console#timeline",
"'console.timeline' is deprecated. Please use 'console.time' instead.");
timeFunction(info, true);
}
void V8Console::timelineEndCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper(info).reportDeprecatedCall("V8Console#timelineEnd",
"'console.timelineEnd' is "
"deprecated. Please use "
"'console.timeEnd' instead.");
timeEndFunction(info, true);
}
void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
timeFunction(info, false);
}
void V8Console::timeEndCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
timeEndFunction(info, false);
}
void V8Console::timeStampCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
if (V8InspectorClient* client = helper.ensureDebuggerClient()) {
String16 title = helper.firstArgToString(String16());
client->consoleTimeStamp(toStringView(title));
}
}
void V8Console::memoryGetterCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (V8InspectorClient* client = ConsoleHelper(info).ensureDebuggerClient()) {
v8::Local<v8::Value> memoryValue;
if (!client
->memoryInfo(info.GetIsolate(),
info.GetIsolate()->GetCurrentContext())
.ToLocal(&memoryValue))
return;
info.GetReturnValue().Set(memoryValue);
}
}
void V8Console::memorySetterCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
// We can't make the attribute readonly as it breaks existing code that relies
// on being able to assign to console.memory in strict mode. Instead, the
// setter just ignores the passed value. http://crbug.com/468611
}
void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
info.GetReturnValue().Set(v8::Array::New(isolate));
ConsoleHelper helper(info);
v8::Local<v8::Object> obj;
if (!helper.firstArgAsObject().ToLocal(&obj)) return;
v8::Local<v8::Array> names;
if (!obj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocal(&names))
return;
info.GetReturnValue().Set(names);
}
void V8Console::valuesCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
info.GetReturnValue().Set(v8::Array::New(isolate));
ConsoleHelper helper(info);
v8::Local<v8::Object> obj;
if (!helper.firstArgAsObject().ToLocal(&obj)) return;
v8::Local<v8::Array> names;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (!obj->GetOwnPropertyNames(context).ToLocal(&names)) return;
v8::Local<v8::Array> values = v8::Array::New(isolate, names->Length());
for (size_t i = 0; i < names->Length(); ++i) {
v8::Local<v8::Value> key;
if (!names->Get(context, i).ToLocal(&key)) continue;
v8::Local<v8::Value> value;
if (!obj->Get(context, key).ToLocal(&value)) continue;
createDataProperty(context, values, i, value);
}
info.GetReturnValue().Set(values);
}
static void setFunctionBreakpoint(ConsoleHelper& helper,
v8::Local<v8::Function> function,
V8DebuggerAgentImpl::BreakpointSource source,
const String16& condition, bool enable) {
V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent();
if (!debuggerAgent) return;
String16 scriptId = String16::fromInteger(function->ScriptId());
int lineNumber = function->GetScriptLineNumber();
int columnNumber = function->GetScriptColumnNumber();
if (lineNumber == v8::Function::kLineOffsetNotFound ||
columnNumber == v8::Function::kLineOffsetNotFound)
return;
if (enable)
debuggerAgent->setBreakpointAt(scriptId, lineNumber, columnNumber, source,
condition);
else
debuggerAgent->removeBreakpointAt(scriptId, lineNumber, columnNumber,
source);
}
void V8Console::debugFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
v8::Local<v8::Function> function;
if (!helper.firstArgAsFunction().ToLocal(&function)) return;
setFunctionBreakpoint(helper, function,
V8DebuggerAgentImpl::DebugCommandBreakpointSource,
String16(), true);
}
void V8Console::undebugFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
v8::Local<v8::Function> function;
if (!helper.firstArgAsFunction().ToLocal(&function)) return;
setFunctionBreakpoint(helper, function,
V8DebuggerAgentImpl::DebugCommandBreakpointSource,
String16(), false);
}
void V8Console::monitorFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
v8::Local<v8::Function> function;
if (!helper.firstArgAsFunction().ToLocal(&function)) return;
v8::Local<v8::Value> name = function->GetName();
if (!name->IsString() || !v8::Local<v8::String>::Cast(name)->Length())
name = function->GetInferredName();
String16 functionName = toProtocolStringWithTypeCheck(name);
String16Builder builder;
builder.append("console.log(\"function ");
if (functionName.isEmpty())
builder.append("(anonymous function)");
else
builder.append(functionName);
builder.append(
" called\" + (arguments.length > 0 ? \" with arguments: \" + "
"Array.prototype.join.call(arguments, \", \") : \"\")) && false");
setFunctionBreakpoint(helper, function,
V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
builder.toString(), true);
}
void V8Console::unmonitorFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
v8::Local<v8::Function> function;
if (!helper.firstArgAsFunction().ToLocal(&function)) return;
setFunctionBreakpoint(helper, function,
V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
String16(), false);
}
void V8Console::lastEvaluationResultCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
ConsoleHelper helper(info);
InspectedContext* context = helper.ensureInspectedContext();
if (!context) return;
if (InjectedScript* injectedScript = context->getInjectedScript())
info.GetReturnValue().Set(injectedScript->lastEvaluationResult());
}
static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
bool copyToClipboard) {
if (info.Length() < 1) return;
if (!copyToClipboard) info.GetReturnValue().Set(info[0]);
ConsoleHelper helper(info);
InspectedContext* context = helper.ensureInspectedContext();
if (!context) return;
InjectedScript* injectedScript = context->getInjectedScript();
if (!injectedScript) return;
ErrorString errorString;
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject =
injectedScript->wrapObject(&errorString, info[0], "",
false /** forceValueType */,
false /** generatePreview */);
if (!wrappedObject || !errorString.isEmpty()) return;
std::unique_ptr<protocol::DictionaryValue> hints =
protocol::DictionaryValue::create();
if (copyToClipboard) hints->setBoolean("copyToClipboard", true);
if (V8InspectorSessionImpl* session = helper.currentSession())
session->runtimeAgent()->inspect(std::move(wrappedObject),
std::move(hints));
}
void V8Console::inspectCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectImpl(info, false);
}
void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectImpl(info, true);
}
void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info,
unsigned num) {
DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize);
ConsoleHelper helper(info);
if (V8InspectorSessionImpl* session = helper.currentSession()) {
V8InspectorSession::Inspectable* object = session->inspectedObject(num);
v8::Isolate* isolate = info.GetIsolate();
if (object)
info.GetReturnValue().Set(object->get(isolate->GetCurrentContext()));
else
info.GetReturnValue().Set(v8::Undefined(isolate));
}
}
v8::Local<v8::Object> V8Console::createConsole(
InspectedContext* inspectedContext, bool hasMemoryAttribute) {
v8::Local<v8::Context> context = inspectedContext->context();
v8::Context::Scope contextScope(context);
v8::Isolate* isolate = context->GetIsolate();
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> console = v8::Object::New(isolate);
bool success =
console->SetPrototype(context, v8::Object::New(isolate)).FromMaybe(false);
DCHECK(success);
createBoundFunctionProperty(context, console, "debug",
V8Console::debugCallback);
createBoundFunctionProperty(context, console, "error",
V8Console::errorCallback);
createBoundFunctionProperty(context, console, "info",
V8Console::infoCallback);
createBoundFunctionProperty(context, console, "log", V8Console::logCallback);
createBoundFunctionProperty(context, console, "warn",
V8Console::warnCallback);
createBoundFunctionProperty(context, console, "dir", V8Console::dirCallback);
createBoundFunctionProperty(context, console, "dirxml",
V8Console::dirxmlCallback);
createBoundFunctionProperty(context, console, "table",
V8Console::tableCallback);
createBoundFunctionProperty(context, console, "trace",
V8Console::traceCallback);
createBoundFunctionProperty(context, console, "group",
V8Console::groupCallback);
createBoundFunctionProperty(context, console, "groupCollapsed",
V8Console::groupCollapsedCallback);
createBoundFunctionProperty(context, console, "groupEnd",
V8Console::groupEndCallback);
createBoundFunctionProperty(context, console, "clear",
V8Console::clearCallback);
createBoundFunctionProperty(context, console, "count",
V8Console::countCallback);
createBoundFunctionProperty(context, console, "assert",
V8Console::assertCallback);
createBoundFunctionProperty(context, console, "markTimeline",
V8Console::markTimelineCallback);
createBoundFunctionProperty(context, console, "profile",
V8Console::profileCallback);
createBoundFunctionProperty(context, console, "profileEnd",
V8Console::profileEndCallback);
createBoundFunctionProperty(context, console, "timeline",
V8Console::timelineCallback);
createBoundFunctionProperty(context, console, "timelineEnd",
V8Console::timelineEndCallback);
createBoundFunctionProperty(context, console, "time",
V8Console::timeCallback);
createBoundFunctionProperty(context, console, "timeEnd",
V8Console::timeEndCallback);
createBoundFunctionProperty(context, console, "timeStamp",
V8Console::timeStampCallback);
if (hasMemoryAttribute)
console->SetAccessorProperty(
toV8StringInternalized(isolate, "memory"),
V8_FUNCTION_NEW_REMOVE_PROTOTYPE(
context, V8Console::memoryGetterCallback, console, 0)
.ToLocalChecked(),
V8_FUNCTION_NEW_REMOVE_PROTOTYPE(
context, V8Console::memorySetterCallback, v8::Local<v8::Value>(), 0)
.ToLocalChecked(),
static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT);
console->SetPrivate(context, inspectedContextPrivateKey(isolate),
v8::External::New(isolate, inspectedContext));
return console;
}
void V8Console::clearInspectedContextIfNeeded(v8::Local<v8::Context> context,
v8::Local<v8::Object> console) {
v8::Isolate* isolate = context->GetIsolate();
console->SetPrivate(context, inspectedContextPrivateKey(isolate),
v8::External::New(isolate, nullptr));
}
v8::Local<v8::Object> V8Console::createCommandLineAPI(
InspectedContext* inspectedContext) {
v8::Local<v8::Context> context = inspectedContext->context();
v8::Isolate* isolate = context->GetIsolate();
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate);
bool success =
commandLineAPI->SetPrototype(context, v8::Null(isolate)).FromMaybe(false);
DCHECK(success);
createBoundFunctionProperty(context, commandLineAPI, "dir",
V8Console::dirCallback,
"function dir(value) { [Command Line API] }");
createBoundFunctionProperty(context, commandLineAPI, "dirxml",
V8Console::dirxmlCallback,
"function dirxml(value) { [Command Line API] }");
createBoundFunctionProperty(context, commandLineAPI, "profile",
V8Console::profileCallback,
"function profile(title) { [Command Line API] }");
createBoundFunctionProperty(
context, commandLineAPI, "profileEnd", V8Console::profileEndCallback,
"function profileEnd(title) { [Command Line API] }");
createBoundFunctionProperty(context, commandLineAPI, "clear",
V8Console::clearCallback,
"function clear() { [Command Line API] }");
createBoundFunctionProperty(
context, commandLineAPI, "table", V8Console::tableCallback,
"function table(data, [columns]) { [Command Line API] }");
createBoundFunctionProperty(context, commandLineAPI, "keys",
V8Console::keysCallback,
"function keys(object) { [Command Line API] }");
createBoundFunctionProperty(context, commandLineAPI, "values",
V8Console::valuesCallback,
"function values(object) { [Command Line API] }");
createBoundFunctionProperty(
context, commandLineAPI, "debug", V8Console::debugFunctionCallback,
"function debug(function) { [Command Line API] }");
createBoundFunctionProperty(
context, commandLineAPI, "undebug", V8Console::undebugFunctionCallback,
"function undebug(function) { [Command Line API] }");
createBoundFunctionProperty(
context, commandLineAPI, "monitor", V8Console::monitorFunctionCallback,
"function monitor(function) { [Command Line API] }");
createBoundFunctionProperty(
context, commandLineAPI, "unmonitor",
V8Console::unmonitorFunctionCallback,
"function unmonitor(function) { [Command Line API] }");
createBoundFunctionProperty(
context, commandLineAPI, "inspect", V8Console::inspectCallback,
"function inspect(object) { [Command Line API] }");
createBoundFunctionProperty(context, commandLineAPI, "copy",
V8Console::copyCallback,
"function copy(value) { [Command Line API] }");
createBoundFunctionProperty(context, commandLineAPI, "$_",
V8Console::lastEvaluationResultCallback);
createBoundFunctionProperty(context, commandLineAPI, "$0",
V8Console::inspectedObject0);
createBoundFunctionProperty(context, commandLineAPI, "$1",
V8Console::inspectedObject1);
createBoundFunctionProperty(context, commandLineAPI, "$2",
V8Console::inspectedObject2);
createBoundFunctionProperty(context, commandLineAPI, "$3",
V8Console::inspectedObject3);
createBoundFunctionProperty(context, commandLineAPI, "$4",
V8Console::inspectedObject4);
inspectedContext->inspector()->client()->installAdditionalCommandLineAPI(
context, commandLineAPI);
commandLineAPI->SetPrivate(context, inspectedContextPrivateKey(isolate),
v8::External::New(isolate, inspectedContext));
return commandLineAPI;
}
static bool isCommandLineAPIGetter(const String16& name) {
if (name.length() != 2) return false;
// $0 ... $4, $_
return name[0] == '$' &&
((name[1] >= '0' && name[1] <= '4') || name[1] == '_');
}
void V8Console::CommandLineAPIScope::accessorGetterCallback(
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(
info.Data().As<v8::External>()->Value());
DCHECK(scope);
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
if (scope->m_cleanup) {
bool removed = info.Holder()->Delete(context, name).FromMaybe(false);
DCHECK(removed);
return;
}
v8::Local<v8::Object> commandLineAPI = scope->m_commandLineAPI;
v8::Local<v8::Value> value;
if (!commandLineAPI->Get(context, name).ToLocal(&value)) return;
if (isCommandLineAPIGetter(toProtocolStringWithTypeCheck(name))) {
DCHECK(value->IsFunction());
v8::MicrotasksScope microtasks(info.GetIsolate(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
if (value.As<v8::Function>()
->Call(context, commandLineAPI, 0, nullptr)
.ToLocal(&value))
info.GetReturnValue().Set(value);
} else {
info.GetReturnValue().Set(value);
}
}
void V8Console::CommandLineAPIScope::accessorSetterCallback(
v8::Local<v8::Name> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(
info.Data().As<v8::External>()->Value());
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
if (!info.Holder()->Delete(context, name).FromMaybe(false)) return;
if (!info.Holder()->CreateDataProperty(context, name, value).FromMaybe(false))
return;
bool removed =
scope->m_installedMethods->Delete(context, name).FromMaybe(false);
DCHECK(removed);
}
V8Console::CommandLineAPIScope::CommandLineAPIScope(
v8::Local<v8::Context> context, v8::Local<v8::Object> commandLineAPI,
v8::Local<v8::Object> global)
: m_context(context),
m_commandLineAPI(commandLineAPI),
m_global(global),
m_installedMethods(v8::Set::New(context->GetIsolate())),
m_cleanup(false) {
v8::Local<v8::Array> names;
if (!m_commandLineAPI->GetOwnPropertyNames(context).ToLocal(&names)) return;
v8::Local<v8::External> externalThis =
v8::External::New(context->GetIsolate(), this);
for (size_t i = 0; i < names->Length(); ++i) {
v8::Local<v8::Value> name;
if (!names->Get(context, i).ToLocal(&name) || !name->IsName()) continue;
if (m_global->Has(context, name).FromMaybe(true)) continue;
if (!m_installedMethods->Add(context, name).ToLocal(&m_installedMethods))
continue;
if (!m_global
->SetAccessor(context, v8::Local<v8::Name>::Cast(name),
CommandLineAPIScope::accessorGetterCallback,
CommandLineAPIScope::accessorSetterCallback,
externalThis, v8::DEFAULT, v8::DontEnum)
.FromMaybe(false)) {
bool removed = m_installedMethods->Delete(context, name).FromMaybe(false);
DCHECK(removed);
continue;
}
}
}
V8Console::CommandLineAPIScope::~CommandLineAPIScope() {
m_cleanup = true;
v8::Local<v8::Array> names = m_installedMethods->AsArray();
for (size_t i = 0; i < names->Length(); ++i) {
v8::Local<v8::Value> name;
if (!names->Get(m_context, i).ToLocal(&name) || !name->IsName()) continue;
if (name->IsString()) {
v8::Local<v8::Value> descriptor;
bool success = m_global
->GetOwnPropertyDescriptor(
m_context, v8::Local<v8::String>::Cast(name))
.ToLocal(&descriptor);
DCHECK(success);
}
}
}
} // namespace v8_inspector

119
src/inspector/V8Console.h Normal file
View File

@ -0,0 +1,119 @@
// 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_V8CONSOLE_H_
#define V8_INSPECTOR_V8CONSOLE_H_
#include "src/inspector/Allocator.h"
#include <v8.h>
namespace v8_inspector {
class InspectedContext;
// Console API
// https://console.spec.whatwg.org/#console-interface
class V8Console {
public:
static v8::Local<v8::Object> createConsole(InspectedContext*,
bool hasMemoryAttribute);
static void clearInspectedContextIfNeeded(v8::Local<v8::Context>,
v8::Local<v8::Object> console);
static v8::Local<v8::Object> createCommandLineAPI(InspectedContext*);
class CommandLineAPIScope {
V8_INSPECTOR_DISALLOW_COPY(CommandLineAPIScope);
public:
CommandLineAPIScope(v8::Local<v8::Context>,
v8::Local<v8::Object> commandLineAPI,
v8::Local<v8::Object> global);
~CommandLineAPIScope();
private:
static void accessorGetterCallback(
v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>&);
static void accessorSetterCallback(v8::Local<v8::Name>,
v8::Local<v8::Value>,
const v8::PropertyCallbackInfo<void>&);
v8::Local<v8::Context> m_context;
v8::Local<v8::Object> m_commandLineAPI;
v8::Local<v8::Object> m_global;
v8::Local<v8::Set> m_installedMethods;
bool m_cleanup;
};
private:
static void debugCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void errorCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void infoCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void logCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void warnCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void dirCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void tableCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void traceCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void groupCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void groupCollapsedCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void clearCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void countCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void assertCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void profileCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void profileEndCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void timelineCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void timeCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void timeStampCallback(const v8::FunctionCallbackInfo<v8::Value>&);
// TODO(foolip): There is no spec for the Memory Info API, see blink-dev:
// https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ
static void memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
// CommandLineAPI
static void keysCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void valuesCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void undebugFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void monitorFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void unmonitorFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void lastEvaluationResultCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void inspectCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void copyCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void inspectedObject(const v8::FunctionCallbackInfo<v8::Value>&,
unsigned num);
static void inspectedObject0(
const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 0);
}
static void inspectedObject1(
const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 1);
}
static void inspectedObject2(
const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 2);
}
static void inspectedObject3(
const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 3);
}
static void inspectedObject4(
const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 4);
}
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8CONSOLE_H_

View File

@ -0,0 +1,76 @@
// 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/V8ConsoleAgentImpl.h"
#include "src/inspector/V8ConsoleMessage.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/protocol/Protocol.h"
namespace v8_inspector {
namespace ConsoleAgentState {
static const char consoleEnabled[] = "consoleEnabled";
}
V8ConsoleAgentImpl::V8ConsoleAgentImpl(
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
protocol::DictionaryValue* state)
: m_session(session),
m_state(state),
m_frontend(frontendChannel),
m_enabled(false) {}
V8ConsoleAgentImpl::~V8ConsoleAgentImpl() {}
void V8ConsoleAgentImpl::enable(ErrorString* errorString) {
if (m_enabled) return;
m_state->setBoolean(ConsoleAgentState::consoleEnabled, true);
m_enabled = true;
m_session->inspector()->enableStackCapturingIfNeeded();
reportAllMessages();
}
void V8ConsoleAgentImpl::disable(ErrorString* errorString) {
if (!m_enabled) return;
m_session->inspector()->disableStackCapturingIfNeeded();
m_state->setBoolean(ConsoleAgentState::consoleEnabled, false);
m_enabled = false;
}
void V8ConsoleAgentImpl::clearMessages(ErrorString* errorString) {}
void V8ConsoleAgentImpl::restore() {
if (!m_state->booleanProperty(ConsoleAgentState::consoleEnabled, false))
return;
ErrorString ignored;
enable(&ignored);
}
void V8ConsoleAgentImpl::messageAdded(V8ConsoleMessage* message) {
if (m_enabled) reportMessage(message, true);
}
bool V8ConsoleAgentImpl::enabled() { return m_enabled; }
void V8ConsoleAgentImpl::reportAllMessages() {
V8ConsoleMessageStorage* storage =
m_session->inspector()->ensureConsoleMessageStorage(
m_session->contextGroupId());
for (const auto& message : storage->messages()) {
if (message->origin() == V8MessageOrigin::kConsole)
reportMessage(message.get(), false);
}
}
void V8ConsoleAgentImpl::reportMessage(V8ConsoleMessage* message,
bool generatePreview) {
DCHECK(message->origin() == V8MessageOrigin::kConsole);
message->reportToFrontend(&m_frontend);
m_frontend.flush();
}
} // namespace v8_inspector

View File

@ -0,0 +1,48 @@
// 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_V8CONSOLEAGENTIMPL_H_
#define V8_INSPECTOR_V8CONSOLEAGENTIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Console.h"
#include "src/inspector/protocol/Forward.h"
namespace v8_inspector {
class V8ConsoleMessage;
class V8InspectorSessionImpl;
using protocol::ErrorString;
class V8ConsoleAgentImpl : public protocol::Console::Backend {
V8_INSPECTOR_DISALLOW_COPY(V8ConsoleAgentImpl);
public:
V8ConsoleAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
protocol::DictionaryValue* state);
~V8ConsoleAgentImpl() override;
void enable(ErrorString*) override;
void disable(ErrorString*) override;
void clearMessages(ErrorString*) override;
void restore();
void messageAdded(V8ConsoleMessage*);
void reset();
bool enabled();
private:
void reportAllMessages();
void reportMessage(V8ConsoleMessage*, bool generatePreview);
V8InspectorSessionImpl* m_session;
protocol::DictionaryValue* m_state;
protocol::Console::Frontend m_frontend;
bool m_enabled;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8CONSOLEAGENTIMPL_H_

View File

@ -0,0 +1,458 @@
// 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/V8ConsoleMessage.h"
#include "src/inspector/InspectedContext.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8ConsoleAgentImpl.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8RuntimeAgentImpl.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
namespace {
String16 consoleAPITypeValue(ConsoleAPIType type) {
switch (type) {
case ConsoleAPIType::kLog:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log;
case ConsoleAPIType::kDebug:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug;
case ConsoleAPIType::kInfo:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Info;
case ConsoleAPIType::kError:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Error;
case ConsoleAPIType::kWarning:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Warning;
case ConsoleAPIType::kClear:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Clear;
case ConsoleAPIType::kDir:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dir;
case ConsoleAPIType::kDirXML:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dirxml;
case ConsoleAPIType::kTable:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Table;
case ConsoleAPIType::kTrace:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Trace;
case ConsoleAPIType::kStartGroup:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroup;
case ConsoleAPIType::kStartGroupCollapsed:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroupCollapsed;
case ConsoleAPIType::kEndGroup:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::EndGroup;
case ConsoleAPIType::kAssert:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Assert;
case ConsoleAPIType::kTimeEnd:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug;
case ConsoleAPIType::kCount:
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug;
}
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log;
}
const unsigned maxConsoleMessageCount = 1000;
const unsigned maxArrayItemsLimit = 10000;
const unsigned maxStackDepthLimit = 32;
class V8ValueStringBuilder {
public:
static String16 toString(v8::Local<v8::Value> value, v8::Isolate* isolate) {
V8ValueStringBuilder builder(isolate);
if (!builder.append(value)) return String16();
return builder.toString();
}
private:
enum {
IgnoreNull = 1 << 0,
IgnoreUndefined = 1 << 1,
};
V8ValueStringBuilder(v8::Isolate* isolate)
: m_arrayLimit(maxArrayItemsLimit),
m_isolate(isolate),
m_tryCatch(isolate) {}
bool append(v8::Local<v8::Value> value, unsigned ignoreOptions = 0) {
if (value.IsEmpty()) return true;
if ((ignoreOptions & IgnoreNull) && value->IsNull()) return true;
if ((ignoreOptions & IgnoreUndefined) && value->IsUndefined()) return true;
if (value->IsString()) return append(v8::Local<v8::String>::Cast(value));
if (value->IsStringObject())
return append(v8::Local<v8::StringObject>::Cast(value)->ValueOf());
if (value->IsSymbol()) return append(v8::Local<v8::Symbol>::Cast(value));
if (value->IsSymbolObject())
return append(v8::Local<v8::SymbolObject>::Cast(value)->ValueOf());
if (value->IsNumberObject()) {
m_builder.append(String16::fromDoublePrecision6(
v8::Local<v8::NumberObject>::Cast(value)->ValueOf()));
return true;
}
if (value->IsBooleanObject()) {
m_builder.append(v8::Local<v8::BooleanObject>::Cast(value)->ValueOf()
? "true"
: "false");
return true;
}
if (value->IsArray()) return append(v8::Local<v8::Array>::Cast(value));
if (value->IsProxy()) {
m_builder.append("[object Proxy]");
return true;
}
if (value->IsObject() && !value->IsDate() && !value->IsFunction() &&
!value->IsNativeError() && !value->IsRegExp()) {
v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
v8::Local<v8::String> stringValue;
if (object->ObjectProtoToString(m_isolate->GetCurrentContext())
.ToLocal(&stringValue))
return append(stringValue);
}
v8::Local<v8::String> stringValue;
if (!value->ToString(m_isolate->GetCurrentContext()).ToLocal(&stringValue))
return false;
return append(stringValue);
}
bool append(v8::Local<v8::Array> array) {
for (const auto& it : m_visitedArrays) {
if (it == array) return true;
}
uint32_t length = array->Length();
if (length > m_arrayLimit) return false;
if (m_visitedArrays.size() > maxStackDepthLimit) return false;
bool result = true;
m_arrayLimit -= length;
m_visitedArrays.push_back(array);
for (uint32_t i = 0; i < length; ++i) {
if (i) m_builder.append(',');
if (!append(array->Get(i), IgnoreNull | IgnoreUndefined)) {
result = false;
break;
}
}
m_visitedArrays.pop_back();
return result;
}
bool append(v8::Local<v8::Symbol> symbol) {
m_builder.append("Symbol(");
bool result = append(symbol->Name(), IgnoreUndefined);
m_builder.append(')');
return result;
}
bool append(v8::Local<v8::String> string) {
if (m_tryCatch.HasCaught()) return false;
if (!string.IsEmpty()) m_builder.append(toProtocolString(string));
return true;
}
String16 toString() {
if (m_tryCatch.HasCaught()) return String16();
return m_builder.toString();
}
uint32_t m_arrayLimit;
v8::Isolate* m_isolate;
String16Builder m_builder;
std::vector<v8::Local<v8::Array>> m_visitedArrays;
v8::TryCatch m_tryCatch;
};
} // namespace
V8ConsoleMessage::V8ConsoleMessage(V8MessageOrigin origin, double timestamp,
const String16& message)
: m_origin(origin),
m_timestamp(timestamp),
m_message(message),
m_lineNumber(0),
m_columnNumber(0),
m_scriptId(0),
m_contextId(0),
m_type(ConsoleAPIType::kLog),
m_exceptionId(0),
m_revokedExceptionId(0) {}
V8ConsoleMessage::~V8ConsoleMessage() {}
void V8ConsoleMessage::setLocation(const String16& url, unsigned lineNumber,
unsigned columnNumber,
std::unique_ptr<V8StackTraceImpl> stackTrace,
int scriptId) {
m_url = url;
m_lineNumber = lineNumber;
m_columnNumber = columnNumber;
m_stackTrace = std::move(stackTrace);
m_scriptId = scriptId;
}
void V8ConsoleMessage::reportToFrontend(
protocol::Console::Frontend* frontend) const {
DCHECK(m_origin == V8MessageOrigin::kConsole);
String16 level = protocol::Console::ConsoleMessage::LevelEnum::Log;
if (m_type == ConsoleAPIType::kDebug || m_type == ConsoleAPIType::kCount ||
m_type == ConsoleAPIType::kTimeEnd)
level = protocol::Console::ConsoleMessage::LevelEnum::Debug;
else if (m_type == ConsoleAPIType::kError ||
m_type == ConsoleAPIType::kAssert)
level = protocol::Console::ConsoleMessage::LevelEnum::Error;
else if (m_type == ConsoleAPIType::kWarning)
level = protocol::Console::ConsoleMessage::LevelEnum::Warning;
else if (m_type == ConsoleAPIType::kInfo)
level = protocol::Console::ConsoleMessage::LevelEnum::Info;
std::unique_ptr<protocol::Console::ConsoleMessage> result =
protocol::Console::ConsoleMessage::create()
.setSource(protocol::Console::ConsoleMessage::SourceEnum::ConsoleApi)
.setLevel(level)
.setText(m_message)
.build();
result->setLine(static_cast<int>(m_lineNumber));
result->setColumn(static_cast<int>(m_columnNumber));
result->setUrl(m_url);
frontend->messageAdded(std::move(result));
}
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>>
V8ConsoleMessage::wrapArguments(V8InspectorSessionImpl* session,
bool generatePreview) const {
if (!m_arguments.size() || !m_contextId) return nullptr;
InspectedContext* inspectedContext =
session->inspector()->getContext(session->contextGroupId(), m_contextId);
if (!inspectedContext) return nullptr;
v8::Isolate* isolate = inspectedContext->isolate();
v8::HandleScope handles(isolate);
v8::Local<v8::Context> context = inspectedContext->context();
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> args =
protocol::Array<protocol::Runtime::RemoteObject>::create();
if (m_type == ConsoleAPIType::kTable && generatePreview) {
v8::Local<v8::Value> table = m_arguments[0]->Get(isolate);
v8::Local<v8::Value> columns = m_arguments.size() > 1
? m_arguments[1]->Get(isolate)
: v8::Local<v8::Value>();
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped =
session->wrapTable(context, table, columns);
if (wrapped)
args->addItem(std::move(wrapped));
else
args = nullptr;
} else {
for (size_t i = 0; i < m_arguments.size(); ++i) {
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped =
session->wrapObject(context, m_arguments[i]->Get(isolate), "console",
generatePreview);
if (!wrapped) {
args = nullptr;
break;
}
args->addItem(std::move(wrapped));
}
}
return args;
}
void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend,
V8InspectorSessionImpl* session,
bool generatePreview) const {
if (m_origin == V8MessageOrigin::kException) {
std::unique_ptr<protocol::Runtime::RemoteObject> exception =
wrapException(session, generatePreview);
std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
protocol::Runtime::ExceptionDetails::create()
.setExceptionId(m_exceptionId)
.setText(exception ? m_message : m_detailedMessage)
.setLineNumber(m_lineNumber ? m_lineNumber - 1 : 0)
.setColumnNumber(m_columnNumber ? m_columnNumber - 1 : 0)
.build();
if (m_scriptId)
exceptionDetails->setScriptId(String16::fromInteger(m_scriptId));
if (!m_url.isEmpty()) exceptionDetails->setUrl(m_url);
if (m_stackTrace)
exceptionDetails->setStackTrace(m_stackTrace->buildInspectorObjectImpl());
if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId);
if (exception) exceptionDetails->setException(std::move(exception));
frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails));
return;
}
if (m_origin == V8MessageOrigin::kRevokedException) {
frontend->exceptionRevoked(m_message, m_revokedExceptionId);
return;
}
if (m_origin == V8MessageOrigin::kConsole) {
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>>
arguments = wrapArguments(session, generatePreview);
if (!arguments) {
arguments = protocol::Array<protocol::Runtime::RemoteObject>::create();
if (!m_message.isEmpty()) {
std::unique_ptr<protocol::Runtime::RemoteObject> messageArg =
protocol::Runtime::RemoteObject::create()
.setType(protocol::Runtime::RemoteObject::TypeEnum::String)
.build();
messageArg->setValue(protocol::StringValue::create(m_message));
arguments->addItem(std::move(messageArg));
}
}
frontend->consoleAPICalled(
consoleAPITypeValue(m_type), std::move(arguments), m_contextId,
m_timestamp,
m_stackTrace ? m_stackTrace->buildInspectorObjectImpl() : nullptr);
return;
}
NOTREACHED();
}
std::unique_ptr<protocol::Runtime::RemoteObject>
V8ConsoleMessage::wrapException(V8InspectorSessionImpl* session,
bool generatePreview) const {
if (!m_arguments.size() || !m_contextId) return nullptr;
DCHECK_EQ(1u, m_arguments.size());
InspectedContext* inspectedContext =
session->inspector()->getContext(session->contextGroupId(), m_contextId);
if (!inspectedContext) return nullptr;
v8::Isolate* isolate = inspectedContext->isolate();
v8::HandleScope handles(isolate);
// TODO(dgozman): should we use different object group?
return session->wrapObject(inspectedContext->context(),
m_arguments[0]->Get(isolate), "console",
generatePreview);
}
V8MessageOrigin V8ConsoleMessage::origin() const { return m_origin; }
ConsoleAPIType V8ConsoleMessage::type() const { return m_type; }
// static
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(
double timestamp, ConsoleAPIType type,
const std::vector<v8::Local<v8::Value>>& arguments,
std::unique_ptr<V8StackTraceImpl> stackTrace, InspectedContext* context) {
std::unique_ptr<V8ConsoleMessage> message = wrapUnique(
new V8ConsoleMessage(V8MessageOrigin::kConsole, timestamp, String16()));
if (stackTrace && !stackTrace->isEmpty()) {
message->m_url = toString16(stackTrace->topSourceURL());
message->m_lineNumber = stackTrace->topLineNumber();
message->m_columnNumber = stackTrace->topColumnNumber();
}
message->m_stackTrace = std::move(stackTrace);
message->m_type = type;
message->m_contextId = context->contextId();
for (size_t i = 0; i < arguments.size(); ++i)
message->m_arguments.push_back(wrapUnique(
new v8::Global<v8::Value>(context->isolate(), arguments.at(i))));
if (arguments.size())
message->m_message =
V8ValueStringBuilder::toString(arguments[0], context->isolate());
V8ConsoleAPIType clientType = V8ConsoleAPIType::kLog;
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;
context->inspector()->client()->consoleAPIMessage(
context->contextGroupId(), clientType, toStringView(message->m_message),
toStringView(message->m_url), message->m_lineNumber,
message->m_columnNumber, message->m_stackTrace.get());
return message;
}
// static
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException(
double timestamp, const String16& detailedMessage, const String16& url,
unsigned lineNumber, unsigned columnNumber,
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(
new V8ConsoleMessage(V8MessageOrigin::kException, timestamp, message));
consoleMessage->setLocation(url, lineNumber, columnNumber,
std::move(stackTrace), scriptId);
consoleMessage->m_exceptionId = exceptionId;
consoleMessage->m_detailedMessage = detailedMessage;
if (contextId && !exception.IsEmpty()) {
consoleMessage->m_contextId = contextId;
consoleMessage->m_arguments.push_back(
wrapUnique(new v8::Global<v8::Value>(isolate, exception)));
}
return consoleMessage;
}
// static
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException(
double timestamp, const String16& messageText,
unsigned revokedExceptionId) {
std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(
V8MessageOrigin::kRevokedException, timestamp, messageText));
message->m_revokedExceptionId = revokedExceptionId;
return message;
}
void V8ConsoleMessage::contextDestroyed(int contextId) {
if (contextId != m_contextId) return;
m_contextId = 0;
if (m_message.isEmpty()) m_message = "<message collected>";
Arguments empty;
m_arguments.swap(empty);
}
// ------------------------ V8ConsoleMessageStorage ----------------------------
V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector,
int contextGroupId)
: m_inspector(inspector),
m_contextGroupId(contextGroupId),
m_expiredCount(0) {}
V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); }
void V8ConsoleMessageStorage::addMessage(
std::unique_ptr<V8ConsoleMessage> message) {
if (message->type() == ConsoleAPIType::kClear) clear();
V8InspectorSessionImpl* session =
m_inspector->sessionForContextGroup(m_contextGroupId);
if (session) {
if (message->origin() == V8MessageOrigin::kConsole)
session->consoleAgent()->messageAdded(message.get());
session->runtimeAgent()->messageAdded(message.get());
}
DCHECK(m_messages.size() <= maxConsoleMessageCount);
if (m_messages.size() == maxConsoleMessageCount) {
++m_expiredCount;
m_messages.pop_front();
}
m_messages.push_back(std::move(message));
}
void V8ConsoleMessageStorage::clear() {
m_messages.clear();
m_expiredCount = 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_messages[i]->contextDestroyed(contextId);
}
} // namespace v8_inspector

View File

@ -0,0 +1,120 @@
// 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_V8CONSOLEMESSAGE_H_
#define V8_INSPECTOR_V8CONSOLEMESSAGE_H_
#include <v8.h>
#include <deque>
#include "src/inspector/protocol/Console.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
namespace v8_inspector {
class InspectedContext;
class V8InspectorImpl;
class V8InspectorSessionImpl;
class V8StackTraceImpl;
enum class V8MessageOrigin { kConsole, kException, kRevokedException };
enum class ConsoleAPIType {
kLog,
kDebug,
kInfo,
kError,
kWarning,
kDir,
kDirXML,
kTable,
kTrace,
kStartGroup,
kStartGroupCollapsed,
kEndGroup,
kClear,
kAssert,
kTimeEnd,
kCount
};
class V8ConsoleMessage {
public:
~V8ConsoleMessage();
static std::unique_ptr<V8ConsoleMessage> createForConsoleAPI(
double timestamp, ConsoleAPIType,
const std::vector<v8::Local<v8::Value>>& arguments,
std::unique_ptr<V8StackTraceImpl>, InspectedContext*);
static std::unique_ptr<V8ConsoleMessage> createForException(
double timestamp, const String16& detailedMessage, const String16& url,
unsigned lineNumber, unsigned columnNumber,
std::unique_ptr<V8StackTraceImpl>, int scriptId, v8::Isolate*,
const String16& message, int contextId, v8::Local<v8::Value> exception,
unsigned exceptionId);
static std::unique_ptr<V8ConsoleMessage> createForRevokedException(
double timestamp, const String16& message, unsigned revokedExceptionId);
V8MessageOrigin origin() const;
void reportToFrontend(protocol::Console::Frontend*) const;
void reportToFrontend(protocol::Runtime::Frontend*, V8InspectorSessionImpl*,
bool generatePreview) const;
ConsoleAPIType type() const;
void contextDestroyed(int contextId);
private:
V8ConsoleMessage(V8MessageOrigin, double timestamp, const String16& message);
using Arguments = std::vector<std::unique_ptr<v8::Global<v8::Value>>>;
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>>
wrapArguments(V8InspectorSessionImpl*, bool generatePreview) const;
std::unique_ptr<protocol::Runtime::RemoteObject> wrapException(
V8InspectorSessionImpl*, bool generatePreview) const;
void setLocation(const String16& url, unsigned lineNumber,
unsigned columnNumber, std::unique_ptr<V8StackTraceImpl>,
int scriptId);
V8MessageOrigin m_origin;
double m_timestamp;
String16 m_message;
String16 m_url;
unsigned m_lineNumber;
unsigned m_columnNumber;
std::unique_ptr<V8StackTraceImpl> m_stackTrace;
int m_scriptId;
int m_contextId;
ConsoleAPIType m_type;
unsigned m_exceptionId;
unsigned m_revokedExceptionId;
Arguments m_arguments;
String16 m_detailedMessage;
};
class V8ConsoleMessageStorage {
public:
V8ConsoleMessageStorage(V8InspectorImpl*, int contextGroupId);
~V8ConsoleMessageStorage();
int contextGroupId() { return m_contextGroupId; }
int expiredCount() { return m_expiredCount; }
const std::deque<std::unique_ptr<V8ConsoleMessage>>& messages() const {
return m_messages;
}
void addMessage(std::unique_ptr<V8ConsoleMessage>);
void contextDestroyed(int contextId);
void clear();
private:
V8InspectorImpl* m_inspector;
int m_contextGroupId;
int m_expiredCount;
std::deque<std::unique_ptr<V8ConsoleMessage>> m_messages;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8CONSOLEMESSAGE_H_

View File

@ -0,0 +1,917 @@
// 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/V8Debugger.h"
#include "src/inspector/DebuggerScript.h"
#include "src/inspector/ScriptBreakpoint.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8DebuggerAgentImpl.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InternalValueType.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/V8ValueCopier.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
namespace {
const char stepIntoV8MethodName[] = "stepIntoStatement";
const char stepOutV8MethodName[] = "stepOutOfFunction";
static const char v8AsyncTaskEventEnqueue[] = "enqueue";
static const char v8AsyncTaskEventWillHandle[] = "willHandle";
static const char v8AsyncTaskEventDidHandle[] = "didHandle";
inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
return value ? v8::True(isolate) : v8::False(isolate);
}
} // namespace
static bool inLiveEditScope = false;
v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod(
const char* functionName, int argc, v8::Local<v8::Value> argv[]) {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
debuggerScript->Get(toV8StringInternalized(m_isolate, functionName)));
DCHECK(m_isolate->InContext());
return function->Call(m_isolate->GetCurrentContext(), 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_maxAsyncCallStackDepth(0) {}
V8Debugger::~V8Debugger() {}
void V8Debugger::enable() {
if (m_enableCount++) return;
DCHECK(!enabled());
v8::HandleScope scope(m_isolate);
v8::Debug::SetDebugEventListener(m_isolate, &V8Debugger::v8DebugEventCallback,
v8::External::New(m_isolate, this));
m_debuggerContext.Reset(m_isolate, v8::Debug::GetDebugContext(m_isolate));
compileDebuggerScript();
}
void V8Debugger::disable() {
if (--m_enableCount) return;
DCHECK(enabled());
clearBreakpoints();
m_debuggerScript.Reset();
m_debuggerContext.Reset();
allAsyncTasksCanceled();
v8::Debug::SetDebugEventListener(m_isolate, nullptr);
}
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::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
DCHECK(!debuggerScript->IsUndefined());
v8::Local<v8::Function> getScriptsFunction = v8::Local<v8::Function>::Cast(
debuggerScript->Get(toV8StringInternalized(m_isolate, "getScripts")));
v8::Local<v8::Value> argv[] = {v8::Integer::New(m_isolate, contextGroupId)};
v8::Local<v8::Value> value;
if (!getScriptsFunction
->Call(debuggerContext(), debuggerScript,
V8_INSPECTOR_ARRAY_LENGTH(argv), argv)
.ToLocal(&value))
return;
DCHECK(value->IsArray());
v8::Local<v8::Array> scriptsArray = v8::Local<v8::Array>::Cast(value);
result.reserve(scriptsArray->Length());
for (unsigned i = 0; i < scriptsArray->Length(); ++i) {
v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(
scriptsArray->Get(v8::Integer::New(m_isolate, i)));
result.push_back(wrapUnique(
new V8DebuggerScript(m_isolate, scriptObject, inLiveEditScope)));
}
}
String16 V8Debugger::setBreakpoint(const String16& sourceID,
const ScriptBreakpoint& scriptBreakpoint,
int* actualLineNumber,
int* actualColumnNumber) {
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::Object> info = v8::Object::New(m_isolate);
info->Set(toV8StringInternalized(m_isolate, "sourceID"),
toV8String(m_isolate, sourceID));
info->Set(toV8StringInternalized(m_isolate, "lineNumber"),
v8::Integer::New(m_isolate, scriptBreakpoint.lineNumber));
info->Set(toV8StringInternalized(m_isolate, "columnNumber"),
v8::Integer::New(m_isolate, scriptBreakpoint.columnNumber));
info->Set(toV8StringInternalized(m_isolate, "condition"),
toV8String(m_isolate, scriptBreakpoint.condition));
v8::Local<v8::Function> setBreakpointFunction =
v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(
toV8StringInternalized(m_isolate, "setBreakpoint")));
v8::Local<v8::Value> breakpointId =
v8::Debug::Call(debuggerContext(), setBreakpointFunction, info)
.ToLocalChecked();
if (!breakpointId->IsString()) return "";
*actualLineNumber =
info->Get(toV8StringInternalized(m_isolate, "lineNumber"))->Int32Value();
*actualColumnNumber =
info->Get(toV8StringInternalized(m_isolate, "columnNumber"))
->Int32Value();
return toProtocolString(breakpointId.As<v8::String>());
}
void V8Debugger::removeBreakpoint(const String16& breakpointId) {
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::Object> info = v8::Object::New(m_isolate);
info->Set(toV8StringInternalized(m_isolate, "breakpointId"),
toV8String(m_isolate, breakpointId));
v8::Local<v8::Function> removeBreakpointFunction =
v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(
toV8StringInternalized(m_isolate, "removeBreakpoint")));
v8::Debug::Call(debuggerContext(), removeBreakpointFunction, info)
.ToLocalChecked();
}
void V8Debugger::clearBreakpoints() {
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::Function> clearBreakpoints =
v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(
toV8StringInternalized(m_isolate, "clearBreakpoints")));
v8::Debug::Call(debuggerContext(), clearBreakpoints).ToLocalChecked();
}
void V8Debugger::setBreakpointsActivated(bool activated) {
if (!enabled()) {
NOTREACHED();
return;
}
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::Object> info = v8::Object::New(m_isolate);
info->Set(toV8StringInternalized(m_isolate, "enabled"),
v8::Boolean::New(m_isolate, activated));
v8::Local<v8::Function> setBreakpointsActivated =
v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(
toV8StringInternalized(m_isolate, "setBreakpointsActivated")));
v8::Debug::Call(debuggerContext(), setBreakpointsActivated, info)
.ToLocalChecked();
m_breakpointsActivated = activated;
}
V8Debugger::PauseOnExceptionsState V8Debugger::getPauseOnExceptionsState() {
DCHECK(enabled());
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::Value> argv[] = {v8::Undefined(m_isolate)};
v8::Local<v8::Value> result =
callDebuggerMethod("pauseOnExceptionsState", 0, argv).ToLocalChecked();
return static_cast<V8Debugger::PauseOnExceptionsState>(result->Int32Value());
}
void V8Debugger::setPauseOnExceptionsState(
PauseOnExceptionsState pauseOnExceptionsState) {
DCHECK(enabled());
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::Value> argv[] = {
v8::Int32::New(m_isolate, pauseOnExceptionsState)};
callDebuggerMethod("setPauseOnExceptionsState", 1, argv);
}
void V8Debugger::setPauseOnNextStatement(bool pause) {
if (m_runningNestedMessageLoop) return;
if (pause)
v8::Debug::DebugBreak(m_isolate);
else
v8::Debug::CancelDebugBreak(m_isolate);
}
bool V8Debugger::canBreakProgram() {
if (!m_breakpointsActivated) return false;
return m_isolate->InContext();
}
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;
}
if (!canBreakProgram()) return;
v8::HandleScope scope(m_isolate);
v8::Local<v8::Function> breakFunction;
if (!V8_FUNCTION_NEW_REMOVE_PROTOTYPE(m_isolate->GetCurrentContext(),
&V8Debugger::breakProgramCallback,
v8::External::New(m_isolate, this), 0)
.ToLocal(&breakFunction))
return;
v8::Debug::Call(debuggerContext(), breakFunction).ToLocalChecked();
}
void V8Debugger::continueProgram() {
if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
m_pausedContext.Clear();
m_executionState.Clear();
}
void V8Debugger::stepIntoStatement() {
DCHECK(isPaused());
DCHECK(!m_executionState.IsEmpty());
v8::HandleScope handleScope(m_isolate);
v8::Local<v8::Value> argv[] = {m_executionState};
callDebuggerMethod(stepIntoV8MethodName, 1, argv);
continueProgram();
}
void V8Debugger::stepOverStatement() {
DCHECK(isPaused());
DCHECK(!m_executionState.IsEmpty());
v8::HandleScope handleScope(m_isolate);
v8::Local<v8::Value> argv[] = {m_executionState};
callDebuggerMethod("stepOverStatement", 1, argv);
continueProgram();
}
void V8Debugger::stepOutOfFunction() {
DCHECK(isPaused());
DCHECK(!m_executionState.IsEmpty());
v8::HandleScope handleScope(m_isolate);
v8::Local<v8::Value> argv[] = {m_executionState};
callDebuggerMethod(stepOutV8MethodName, 1, argv);
continueProgram();
}
void V8Debugger::clearStepping() {
DCHECK(enabled());
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::Value> argv[] = {v8::Undefined(m_isolate)};
callDebuggerMethod("clearStepping", 0, argv);
}
bool V8Debugger::setScriptSource(
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
ErrorString* error,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged) {
class EnableLiveEditScope {
public:
explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) {
v8::Debug::SetLiveEditEnabled(m_isolate, true);
inLiveEditScope = true;
}
~EnableLiveEditScope() {
v8::Debug::SetLiveEditEnabled(m_isolate, false);
inLiveEditScope = false;
}
private:
v8::Isolate* m_isolate;
};
DCHECK(enabled());
v8::HandleScope scope(m_isolate);
std::unique_ptr<v8::Context::Scope> contextScope;
if (!isPaused())
contextScope = wrapUnique(new v8::Context::Scope(debuggerContext()));
v8::Local<v8::Value> argv[] = {toV8String(m_isolate, sourceID), newSource,
v8Boolean(dryRun, m_isolate)};
v8::Local<v8::Value> v8result;
{
EnableLiveEditScope enableLiveEditScope(m_isolate);
v8::TryCatch tryCatch(m_isolate);
tryCatch.SetVerbose(false);
v8::MaybeLocal<v8::Value> maybeResult =
callDebuggerMethod("liveEditScriptSource", 3, argv);
if (tryCatch.HasCaught()) {
v8::Local<v8::Message> message = tryCatch.Message();
if (!message.IsEmpty())
*error = toProtocolStringWithTypeCheck(message->Get());
else
*error = "Unknown error.";
return false;
}
v8result = maybeResult.ToLocalChecked();
}
DCHECK(!v8result.IsEmpty());
v8::Local<v8::Object> resultTuple = v8result->ToObject(m_isolate);
int code =
static_cast<int>(resultTuple->Get(0)->ToInteger(m_isolate)->Value());
switch (code) {
case 0: {
*stackChanged = resultTuple->Get(1)->BooleanValue();
// Call stack may have changed after if the edited function was on the
// stack.
if (!dryRun && isPaused()) {
JavaScriptCallFrames frames = currentCallFrames();
newCallFrames->swap(frames);
}
return true;
}
// Compile error.
case 1: {
*exceptionDetails =
protocol::Runtime::ExceptionDetails::create()
.setExceptionId(m_inspector->nextExceptionId())
.setText(toProtocolStringWithTypeCheck(resultTuple->Get(2)))
.setLineNumber(
resultTuple->Get(3)->ToInteger(m_isolate)->Value() - 1)
.setColumnNumber(
resultTuple->Get(4)->ToInteger(m_isolate)->Value() - 1)
.build();
return false;
}
}
*error = "Unknown error.";
return false;
}
JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) {
if (!m_isolate->InContext()) 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(
toV8StringInternalized(m_isolate, "currentCallFrames")));
currentCallFramesV8 =
v8::Debug::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", V8_INSPECTOR_ARRAY_LENGTH(argv),
argv)
.ToLocalChecked();
}
DCHECK(!currentCallFramesV8.IsEmpty());
if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames();
v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
JavaScriptCallFrames callFrames;
for (size_t i = 0; i < callFramesArray->Length(); ++i) {
v8::Local<v8::Value> callFrameValue;
if (!callFramesArray->Get(debuggerContext(), i).ToLocal(&callFrameValue))
return JavaScriptCallFrames();
if (!callFrameValue->IsObject()) return JavaScriptCallFrames();
v8::Local<v8::Object> callFrameObject = callFrameValue.As<v8::Object>();
callFrames.push_back(JavaScriptCallFrame::create(
debuggerContext(), v8::Local<v8::Object>::Cast(callFrameObject)));
}
return callFrames;
}
static V8Debugger* toV8Debugger(v8::Local<v8::Value> data) {
void* p = v8::Local<v8::External>::Cast(data)->Value();
return static_cast<V8Debugger*>(p);
}
void V8Debugger::breakProgramCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK_EQ(info.Length(), 2);
V8Debugger* thisPtr = toV8Debugger(info.Data());
if (!thisPtr->enabled()) return;
v8::Local<v8::Context> pausedContext =
thisPtr->m_isolate->GetCurrentContext();
v8::Local<v8::Value> exception;
v8::Local<v8::Array> hitBreakpoints;
thisPtr->handleProgramBreak(pausedContext,
v8::Local<v8::Object>::Cast(info[0]), exception,
hitBreakpoints);
}
void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
v8::Local<v8::Object> executionState,
v8::Local<v8::Value> exception,
v8::Local<v8::Array> hitBreakpointNumbers,
bool isPromiseRejection) {
// Don't allow nested breaks.
if (m_runningNestedMessageLoop) return;
V8DebuggerAgentImpl* agent =
m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext));
if (!agent) return;
std::vector<String16> breakpointIds;
if (!hitBreakpointNumbers.IsEmpty()) {
breakpointIds.reserve(hitBreakpointNumbers->Length());
for (size_t i = 0; i < hitBreakpointNumbers->Length(); i++) {
v8::Local<v8::Value> hitBreakpointNumber = hitBreakpointNumbers->Get(i);
DCHECK(!hitBreakpointNumber.IsEmpty() && hitBreakpointNumber->IsInt32());
breakpointIds.push_back(
String16::fromInteger(hitBreakpointNumber->Int32Value()));
}
}
m_pausedContext = pausedContext;
m_executionState = executionState;
V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause(
pausedContext, exception, breakpointIds, isPromiseRejection);
if (result == V8DebuggerAgentImpl::RequestNoSkip) {
m_runningNestedMessageLoop = true;
int groupId = getGroupId(pausedContext);
DCHECK(groupId);
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;
}
m_pausedContext.Clear();
m_executionState.Clear();
if (result == V8DebuggerAgentImpl::RequestStepFrame) {
v8::Local<v8::Value> argv[] = {executionState};
callDebuggerMethod("stepFrameStatement", 1, argv);
} else if (result == V8DebuggerAgentImpl::RequestStepInto) {
v8::Local<v8::Value> argv[] = {executionState};
callDebuggerMethod(stepIntoV8MethodName, 1, argv);
} else if (result == V8DebuggerAgentImpl::RequestStepOut) {
v8::Local<v8::Value> argv[] = {executionState};
callDebuggerMethod(stepOutV8MethodName, 1, argv);
}
}
void V8Debugger::v8DebugEventCallback(
const v8::Debug::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(toV8StringInternalized(m_isolate, functionName));
DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction());
return v8::Local<v8::Function>::Cast(getterValue)
->Call(m_isolate->GetCurrentContext(), object, 0, 0)
.ToLocalChecked();
}
void V8Debugger::handleV8DebugEvent(
const v8::Debug::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());
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::Context::Scope contextScope(debuggerContext());
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);
agent->didParseSource(wrapUnique(new V8DebuggerScript(
m_isolate, scriptObject, inLiveEditScope)),
event == v8::AfterCompile);
} else if (event == v8::Exception) {
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();
handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
exception, v8::Local<v8::Array>(), isPromiseRejection);
} 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>());
}
}
}
void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Context> context,
v8::Local<v8::Object> executionState,
v8::Local<v8::Object> eventData) {
if (!m_maxAsyncCallStackDepth) return;
String16 type = toProtocolStringWithTypeCheck(
callInternalGetterFunction(eventData, "type"));
String16 name = toProtocolStringWithTypeCheck(
callInternalGetterFunction(eventData, "name"));
int id = callInternalGetterFunction(eventData, "id")
->ToInteger(m_isolate)
->Value();
// The scopes for the ids are defined by the eventData.name namespaces. There
// are currently two namespaces: "Object." and "Promise.".
void* ptr = reinterpret_cast<void*>(id * 4 + (name[0] == 'P' ? 2 : 0) + 1);
if (type == v8AsyncTaskEventEnqueue)
asyncTaskScheduled(name, ptr, false);
else if (type == v8AsyncTaskEventWillHandle)
asyncTaskStarted(ptr);
else if (type == v8AsyncTaskEventDidHandle)
asyncTaskFinished(ptr);
else
NOTREACHED();
}
V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
if (!m_currentStacks.size()) return nullptr;
return m_currentStacks.back().get();
}
void V8Debugger::compileDebuggerScript() {
if (!m_debuggerScript.IsEmpty()) {
NOTREACHED();
return;
}
v8::HandleScope scope(m_isolate);
v8::Context::Scope contextScope(debuggerContext());
v8::Local<v8::String> scriptValue =
v8::String::NewFromUtf8(m_isolate, DebuggerScript_js,
v8::NewStringType::kInternalized,
sizeof(DebuggerScript_js))
.ToLocalChecked();
v8::Local<v8::Value> value;
if (!m_inspector->compileAndRunInternalScript(debuggerContext(), scriptValue)
.ToLocal(&value)) {
NOTREACHED();
return;
}
DCHECK(value->IsObject());
m_debuggerScript.Reset(m_isolate, value.As<v8::Object>());
}
v8::Local<v8::Context> V8Debugger::debuggerContext() const {
DCHECK(!m_debuggerContext.IsEmpty());
return m_debuggerContext.Get(m_isolate);
}
v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
if (!enabled()) {
NOTREACHED();
return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
}
v8::Local<v8::Value> argv[] = {function};
v8::Local<v8::Value> scopesValue;
if (!callDebuggerMethod("getFunctionScopes", 1, argv).ToLocal(&scopesValue))
return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Value> copied;
if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
scopesValue)
.ToLocal(&copied) ||
!copied->IsArray())
return v8::MaybeLocal<v8::Value>();
if (!markAsInternal(context, v8::Local<v8::Array>::Cast(copied),
V8InternalValueType::kScopeList))
return v8::MaybeLocal<v8::Value>();
if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied),
V8InternalValueType::kScope))
return v8::MaybeLocal<v8::Value>();
return copied;
}
v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
v8::Local<v8::Array> 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()) {
createDataProperty(
context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[FunctionLocation]]"));
createDataProperty(context, properties, properties->Length(), location);
}
if (function->IsGeneratorFunction()) {
createDataProperty(context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[IsGenerator]]"));
createDataProperty(context, properties, properties->Length(),
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);
}
}
if (value->IsGeneratorObject()) {
v8::Local<v8::Value> location =
generatorObjectLocation(context, v8::Local<v8::Object>::Cast(value));
if (location->IsObject()) {
createDataProperty(
context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
createDataProperty(context, properties, properties->Length(), location);
}
}
if (value->IsFunction()) {
v8::Local<v8::Function> function = value.As<v8::Function>();
v8::Local<v8::Value> boundFunction = function->GetBoundFunction();
v8::Local<v8::Value> scopes;
if (boundFunction->IsUndefined() &&
functionScopes(context, function).ToLocal(&scopes)) {
createDataProperty(context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[Scopes]]"));
createDataProperty(context, properties, properties->Length(), scopes);
}
}
return properties;
}
v8::Local<v8::Value> V8Debugger::collectionEntries(
v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
if (!enabled()) {
NOTREACHED();
return v8::Undefined(m_isolate);
}
v8::Local<v8::Value> argv[] = {object};
v8::Local<v8::Value> entriesValue =
callDebuggerMethod("getCollectionEntries", 1, argv).ToLocalChecked();
v8::Local<v8::Value> copied;
if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
entriesValue)
.ToLocal(&copied) ||
!copied->IsArray())
return v8::Undefined(m_isolate);
if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied),
V8InternalValueType::kEntry))
return v8::Undefined(m_isolate);
return copied;
}
v8::Local<v8::Value> V8Debugger::generatorObjectLocation(
v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
if (!enabled()) {
NOTREACHED();
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;
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);
else
m_maxAsyncCallStackDepthMap[agent] = depth;
int maxAsyncCallStackDepth = 0;
for (const auto& pair : m_maxAsyncCallStackDepthMap) {
if (pair.second > maxAsyncCallStackDepth)
maxAsyncCallStackDepth = pair.second;
}
if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
}
void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
bool recurring) {
if (!m_maxAsyncCallStackDepth) return;
asyncTaskScheduled(toString16(taskName), task, recurring);
}
void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task,
bool recurring) {
if (!m_maxAsyncCallStackDepth) return;
v8::HandleScope scope(m_isolate);
int contextGroupId =
m_isolate->InContext() ? getGroupId(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);
}
}
void V8Debugger::asyncTaskCanceled(void* task) {
if (!m_maxAsyncCallStackDepth) return;
m_asyncTaskStacks.erase(task);
m_recurringTasks.erase(task);
}
void V8Debugger::asyncTaskStarted(void* task) {
if (!m_maxAsyncCallStackDepth) return;
m_currentTasks.push_back(task);
AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
// Needs to support following order of events:
// - asyncTaskScheduled
// <-- attached here -->
// - asyncTaskStarted
// - asyncTaskCanceled <-- canceled before finished
// <-- async stack requested here -->
// - asyncTaskFinished
std::unique_ptr<V8StackTraceImpl> stack;
if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
stack = stackIt->second->cloneImpl();
m_currentStacks.push_back(std::move(stack));
}
void V8Debugger::asyncTaskFinished(void* task) {
if (!m_maxAsyncCallStackDepth) return;
// We could start instrumenting half way and the stack is empty.
if (!m_currentStacks.size()) return;
DCHECK(m_currentTasks.back() == task);
m_currentTasks.pop_back();
m_currentStacks.pop_back();
if (m_recurringTasks.find(task) == m_recurringTasks.end())
m_asyncTaskStacks.erase(task);
}
void V8Debugger::allAsyncTasksCanceled() {
m_asyncTaskStacks.clear();
m_recurringTasks.clear();
m_currentStacks.clear();
m_currentTasks.clear();
}
void V8Debugger::muteScriptParsedEvents() {
++m_ignoreScriptParsedEventsCounter;
}
void V8Debugger::unmuteScriptParsedEvents() {
--m_ignoreScriptParsedEventsCounter;
DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
}
std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
bool fullStack) {
if (!m_isolate->InContext()) return nullptr;
v8::HandleScope handles(m_isolate);
int contextGroupId = getGroupId(m_isolate->GetCurrentContext());
if (!contextGroupId) return nullptr;
size_t stackSize =
fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1;
if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId))
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
}
} // namespace v8_inspector

159
src/inspector/V8Debugger.h Normal file
View File

@ -0,0 +1,159 @@
// 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_V8DEBUGGER_H_
#define V8_INSPECTOR_V8DEBUGGER_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/JavaScriptCallFrame.h"
#include "src/inspector/V8DebuggerScript.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
#include "src/inspector/public/StringView.h"
#include "src/inspector/public/V8ContextInfo.h"
#include <v8-debug.h>
#include <v8.h>
#include <vector>
namespace v8_inspector {
struct ScriptBreakpoint;
class V8DebuggerAgentImpl;
class V8InspectorImpl;
class V8StackTraceImpl;
using protocol::ErrorString;
class V8Debugger {
V8_INSPECTOR_DISALLOW_COPY(V8Debugger);
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);
void removeBreakpoint(const String16& breakpointId);
void setBreakpointsActivated(bool);
bool breakpointsActivated() const { return m_breakpointsActivated; }
enum PauseOnExceptionsState {
DontPauseOnExceptions,
PauseOnAllExceptions,
PauseOnUncaughtExceptions
};
PauseOnExceptionsState getPauseOnExceptionsState();
void setPauseOnExceptionsState(PauseOnExceptionsState);
void setPauseOnNextStatement(bool);
bool canBreakProgram();
void breakProgram();
void continueProgram();
void stepIntoStatement();
void stepOverStatement();
void stepOutOfFunction();
void clearStepping();
bool setScriptSource(const String16& sourceID,
v8::Local<v8::String> newSource, bool dryRun,
ErrorString*,
protocol::Maybe<protocol::Runtime::ExceptionDetails>*,
JavaScriptCallFrames* newCallFrames,
protocol::Maybe<bool>* stackChanged);
JavaScriptCallFrames currentCallFrames(int limit = 0);
// Each script inherits debug data from v8::Context where it has been
// compiled.
// Only scripts whose debug data matches |contextGroupId| will be reported.
// Passing 0 will result in reporting all scripts.
void getCompiledScripts(int contextGroupId,
std::vector<std::unique_ptr<V8DebuggerScript>>&);
void enable();
void disable();
bool isPaused();
v8::Local<v8::Context> pausedContext() { return m_pausedContext; }
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
V8StackTraceImpl* currentAsyncCallChain();
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>);
std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack);
v8::MaybeLocal<v8::Array> internalProperties(v8::Local<v8::Context>,
v8::Local<v8::Value>);
void asyncTaskScheduled(const StringView& taskName, void* task,
bool recurring);
void asyncTaskScheduled(const String16& taskName, void* task, bool recurring);
void asyncTaskCanceled(void* task);
void asyncTaskStarted(void* task);
void asyncTaskFinished(void* task);
void allAsyncTasksCanceled();
void muteScriptParsedEvents();
void unmuteScriptParsedEvents();
private:
void compileDebuggerScript();
v8::MaybeLocal<v8::Value> callDebuggerMethod(const char* functionName,
int argc,
v8::Local<v8::Value> argv[]);
v8::Local<v8::Context> debuggerContext() const;
void clearBreakpoints();
static void breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void handleProgramBreak(v8::Local<v8::Context> pausedContext,
v8::Local<v8::Object> executionState,
v8::Local<v8::Value> exception,
v8::Local<v8::Array> hitBreakpoints,
bool isPromiseRejection = false);
static void v8DebugEventCallback(const v8::Debug::EventDetails&);
v8::Local<v8::Value> callInternalGetterFunction(v8::Local<v8::Object>,
const char* functionName);
void handleV8DebugEvent(const v8::Debug::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>);
v8::MaybeLocal<v8::Value> functionScopes(v8::Local<v8::Context>,
v8::Local<v8::Function>);
v8::Isolate* m_isolate;
V8InspectorImpl* m_inspector;
int m_lastContextId;
int m_enableCount;
bool m_breakpointsActivated;
v8::Global<v8::Object> m_debuggerScript;
v8::Global<v8::Context> m_debuggerContext;
v8::Local<v8::Object> m_executionState;
v8::Local<v8::Context> m_pausedContext;
bool m_runningNestedMessageLoop;
int m_ignoreScriptParsedEventsCounter;
using AsyncTaskToStackTrace =
protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>;
AsyncTaskToStackTrace m_asyncTaskStacks;
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;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8DEBUGGER_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,224 @@
// Copyright 2015 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_V8DEBUGGERAGENTIMPL_H_
#define V8_INSPECTOR_V8DEBUGGERAGENTIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/JavaScriptCallFrame.h"
#include "src/inspector/protocol/Debugger.h"
#include "src/inspector/protocol/Forward.h"
#include <vector>
namespace v8_inspector {
struct ScriptBreakpoint;
class JavaScriptCallFrame;
class PromiseTracker;
class V8Debugger;
class V8DebuggerScript;
class V8InspectorImpl;
class V8InspectorSessionImpl;
class V8Regex;
class V8StackTraceImpl;
using protocol::ErrorString;
using protocol::Maybe;
class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
V8_INSPECTOR_DISALLOW_COPY(V8DebuggerAgentImpl);
public:
enum SkipPauseRequest {
RequestNoSkip,
RequestContinue,
RequestStepInto,
RequestStepOut,
RequestStepFrame
};
enum BreakpointSource {
UserBreakpointSource,
DebugCommandBreakpointSource,
MonitorCommandBreakpointSource
};
V8DebuggerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
protocol::DictionaryValue* state);
~V8DebuggerAgentImpl() override;
void restore();
// Part of the protocol.
void enable(ErrorString*) override;
void disable(ErrorString*) override;
void setBreakpointsActive(ErrorString*, bool active) override;
void setSkipAllPauses(ErrorString*, bool skip) override;
void setBreakpointByUrl(
ErrorString*, int lineNumber, const Maybe<String16>& optionalURL,
const Maybe<String16>& optionalURLRegex,
const Maybe<int>& optionalColumnNumber,
const Maybe<String16>& optionalCondition, String16*,
std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations)
override;
void setBreakpoint(
ErrorString*, std::unique_ptr<protocol::Debugger::Location>,
const Maybe<String16>& optionalCondition, String16*,
std::unique_ptr<protocol::Debugger::Location>* actualLocation) override;
void removeBreakpoint(ErrorString*, const String16& breakpointId) override;
void continueToLocation(
ErrorString*, std::unique_ptr<protocol::Debugger::Location>) override;
void searchInContent(
ErrorString*, const String16& scriptId, const String16& query,
const Maybe<bool>& optionalCaseSensitive,
const Maybe<bool>& optionalIsRegex,
std::unique_ptr<protocol::Array<protocol::Debugger::SearchMatch>>*)
override;
void setScriptSource(
ErrorString*, const String16& inScriptId, const String16& inScriptSource,
const Maybe<bool>& dryRun,
Maybe<protocol::Array<protocol::Debugger::CallFrame>>* optOutCallFrames,
Maybe<bool>* optOutStackChanged,
Maybe<protocol::Runtime::StackTrace>* optOutAsyncStackTrace,
Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) override;
void restartFrame(
ErrorString*, const String16& callFrameId,
std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>*
newCallFrames,
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) override;
void getScriptSource(ErrorString*, const String16& scriptId,
String16* scriptSource) override;
void pause(ErrorString*) override;
void resume(ErrorString*) override;
void stepOver(ErrorString*) override;
void stepInto(ErrorString*) override;
void stepOut(ErrorString*) override;
void setPauseOnExceptions(ErrorString*, const String16& pauseState) override;
void evaluateOnCallFrame(
ErrorString*, const String16& callFrameId, const String16& expression,
const Maybe<String16>& objectGroup,
const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& silent,
const Maybe<bool>& returnByValue, const Maybe<bool>& generatePreview,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>*) override;
void setVariableValue(
ErrorString*, int scopeNumber, const String16& variableName,
std::unique_ptr<protocol::Runtime::CallArgument> newValue,
const String16& callFrame) override;
void setAsyncCallStackDepth(ErrorString*, int depth) override;
void setBlackboxPatterns(
ErrorString*,
std::unique_ptr<protocol::Array<String16>> patterns) override;
void setBlackboxedRanges(
ErrorString*, const String16& scriptId,
std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
positions) override;
bool enabled();
void setBreakpointAt(const String16& scriptId, int lineNumber,
int columnNumber, BreakpointSource,
const String16& condition = String16());
void removeBreakpointAt(const String16& scriptId, int lineNumber,
int columnNumber, BreakpointSource);
void schedulePauseOnNextStatement(
const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data);
void cancelPauseOnNextStatement();
void breakProgram(const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data);
void breakProgramOnException(const String16& breakReason,
std::unique_ptr<protocol::DictionaryValue> data);
void reset();
// Interface for V8InspectorImpl
SkipPauseRequest didPause(v8::Local<v8::Context>,
v8::Local<v8::Value> exception,
const std::vector<String16>& hitBreakpoints,
bool isPromiseRejection);
void didContinue();
void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success);
void willExecuteScript(int scriptId);
void didExecuteScript();
v8::Isolate* isolate() { return m_isolate; }
private:
bool checkEnabled(ErrorString*);
void enable();
SkipPauseRequest shouldSkipExceptionPause(JavaScriptCallFrame* topCallFrame);
SkipPauseRequest shouldSkipStepPause(JavaScriptCallFrame* topCallFrame);
void schedulePauseOnNextStatementIfSteppingInto();
std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>
currentCallFrames(ErrorString*);
std::unique_ptr<protocol::Runtime::StackTrace> currentAsyncStackTrace();
void changeJavaScriptRecursionLevel(int step);
void setPauseOnExceptionsImpl(ErrorString*, int);
std::unique_ptr<protocol::Debugger::Location> resolveBreakpoint(
const String16& breakpointId, const String16& scriptId,
const ScriptBreakpoint&, BreakpointSource);
void removeBreakpoint(const String16& breakpointId);
bool assertPaused(ErrorString*);
void clearBreakDetails();
bool isCurrentCallStackEmptyOrBlackboxed();
bool isTopPausedCallFrameBlackboxed();
bool isCallFrameWithUnknownScriptOrBlackboxed(JavaScriptCallFrame*);
void internalSetAsyncCallStackDepth(int);
void increaseCachedSkipStackGeneration();
bool setBlackboxPattern(ErrorString*, const String16& pattern);
using ScriptsMap =
protocol::HashMap<String16, std::unique_ptr<V8DebuggerScript>>;
using BreakpointIdToDebuggerBreakpointIdsMap =
protocol::HashMap<String16, std::vector<String16>>;
using DebugServerBreakpointToBreakpointIdAndSourceMap =
protocol::HashMap<String16, std::pair<String16, BreakpointSource>>;
using MuteBreakpoins = protocol::HashMap<String16, std::pair<String16, int>>;
enum DebuggerStep { NoStep = 0, StepInto, StepOver, StepOut };
V8InspectorImpl* m_inspector;
V8Debugger* m_debugger;
V8InspectorSessionImpl* m_session;
bool m_enabled;
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;
int m_recursionLevelForStepOut;
int m_recursionLevelForStepFrame;
bool m_skipAllPauses;
std::unique_ptr<V8Regex> m_blackboxPattern;
protocol::HashMap<String16, std::vector<std::pair<int, int>>>
m_blackboxedPositions;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8DEBUGGERAGENTIMPL_H_

View File

@ -0,0 +1,134 @@
// Copyright 2014 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/V8DebuggerScript.h"
#include "src/inspector/ProtocolPlatform.h"
#include "src/inspector/StringUtil.h"
namespace v8_inspector {
static const char hexDigits[17] = "0123456789ABCDEF";
static void appendUnsignedAsHex(unsigned number, String16Builder* destination) {
for (size_t i = 0; i < 8; ++i) {
UChar c = hexDigits[number & 0xF];
destination->append(c);
number >>= 4;
}
}
// Hash algorithm for substrings is described in "Über die Komplexität der
// 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) {
static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35,
0x81ABE279};
static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
0xC3D2E1F0};
static uint32_t randomOdd[] = {0xB4663807, 0xCC322BF5, 0xD4F91BBD, 0xA7BEA11D,
0x8F462907};
uint64_t hashes[] = {0, 0, 0, 0, 0};
uint64_t zi[] = {1, 1, 1, 1, 1};
const size_t hashesSize = V8_INSPECTOR_ARRAY_LENGTH(hashes);
size_t current = 0;
const uint32_t* data = nullptr;
size_t sizeInBytes = sizeof(UChar) * str.length();
data = reinterpret_cast<const uint32_t*>(str.characters16());
for (size_t i = 0; i < sizeInBytes / 4; i += 4) {
uint32_t v = data[i];
uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
zi[current] = (zi[current] * random[current]) % prime[current];
current = current == hashesSize - 1 ? 0 : current + 1;
}
if (sizeInBytes % 4) {
uint32_t v = 0;
for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) {
v <<= 8;
v |= reinterpret_cast<const uint8_t*>(data)[i];
}
uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF;
hashes[current] = (hashes[current] + zi[current] * xi) % prime[current];
zi[current] = (zi[current] * random[current]) % prime[current];
current = current == hashesSize - 1 ? 0 : current + 1;
}
for (size_t i = 0; i < hashesSize; ++i)
hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];
String16Builder hash;
for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash);
return hash.toString();
}
V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate,
v8::Local<v8::Object> object,
bool isLiveEdit) {
v8::Local<v8::Value> idValue =
object->Get(toV8StringInternalized(isolate, "id"));
DCHECK(!idValue.IsEmpty() && idValue->IsInt32());
m_id = String16::fromInteger(idValue->Int32Value());
m_url = toProtocolStringWithTypeCheck(
object->Get(toV8StringInternalized(isolate, "name")));
m_sourceURL = toProtocolStringWithTypeCheck(
object->Get(toV8StringInternalized(isolate, "sourceURL")));
m_sourceMappingURL = toProtocolStringWithTypeCheck(
object->Get(toV8StringInternalized(isolate, "sourceMappingURL")));
m_startLine = object->Get(toV8StringInternalized(isolate, "startLine"))
->ToInteger(isolate)
->Value();
m_startColumn = object->Get(toV8StringInternalized(isolate, "startColumn"))
->ToInteger(isolate)
->Value();
m_endLine = object->Get(toV8StringInternalized(isolate, "endLine"))
->ToInteger(isolate)
->Value();
m_endColumn = object->Get(toV8StringInternalized(isolate, "endColumn"))
->ToInteger(isolate)
->Value();
m_executionContextAuxData = toProtocolStringWithTypeCheck(
object->Get(toV8StringInternalized(isolate, "executionContextAuxData")));
m_executionContextId =
object->Get(toV8StringInternalized(isolate, "executionContextId"))
->ToInteger(isolate)
->Value();
m_isLiveEdit = isLiveEdit;
v8::Local<v8::Value> sourceValue =
object->Get(toV8StringInternalized(isolate, "source"));
if (!sourceValue.IsEmpty() && sourceValue->IsString())
setSource(isolate, sourceValue.As<v8::String>());
}
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);
}
void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
m_sourceURL = sourceURL;
}
void V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) {
m_sourceMappingURL = sourceMappingURL;
}
void V8DebuggerScript::setSource(v8::Isolate* isolate,
v8::Local<v8::String> source) {
m_source.Reset(isolate, source);
m_hash = calculateHash(toProtocolString(source));
}
} // namespace v8_inspector

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef V8_INSPECTOR_V8DEBUGGERSCRIPT_H_
#define V8_INSPECTOR_V8DEBUGGERSCRIPT_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/String16.h"
#include <v8.h>
namespace v8_inspector {
class V8DebuggerScript {
V8_INSPECTOR_DISALLOW_COPY(V8DebuggerScript);
public:
V8DebuggerScript(v8::Isolate*, v8::Local<v8::Object>, bool isLiveEdit);
~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; }
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; }
void setSourceURL(const String16&);
void setSourceMappingURL(const String16&);
void setSource(v8::Isolate*, v8::Local<v8::String>);
private:
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;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8DEBUGGERSCRIPT_H_

View File

@ -0,0 +1,113 @@
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "src/inspector/V8FunctionCall.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/public/V8InspectorClient.h"
#include <v8.h>
namespace v8_inspector {
V8FunctionCall::V8FunctionCall(V8InspectorImpl* inspector,
v8::Local<v8::Context> context,
v8::Local<v8::Value> value, const String16& name)
: m_inspector(inspector),
m_context(context),
m_name(toV8String(context->GetIsolate(), name)),
m_value(value) {}
void V8FunctionCall::appendArgument(v8::Local<v8::Value> value) {
m_arguments.push_back(value);
}
void V8FunctionCall::appendArgument(const String16& argument) {
m_arguments.push_back(toV8String(m_context->GetIsolate(), argument));
}
void V8FunctionCall::appendArgument(int argument) {
m_arguments.push_back(v8::Number::New(m_context->GetIsolate(), argument));
}
void V8FunctionCall::appendArgument(bool argument) {
m_arguments.push_back(argument ? v8::True(m_context->GetIsolate())
: v8::False(m_context->GetIsolate()));
}
v8::Local<v8::Value> V8FunctionCall::call(bool& hadException,
bool reportExceptions) {
v8::TryCatch tryCatch(m_context->GetIsolate());
tryCatch.SetVerbose(reportExceptions);
v8::Local<v8::Value> result = callWithoutExceptionHandling();
hadException = tryCatch.HasCaught();
return result;
}
v8::Local<v8::Value> V8FunctionCall::callWithoutExceptionHandling() {
v8::Local<v8::Object> thisObject = v8::Local<v8::Object>::Cast(m_value);
v8::Local<v8::Value> value;
if (!thisObject->Get(m_context, m_name).ToLocal(&value))
return v8::Local<v8::Value>();
DCHECK(value->IsFunction());
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value);
std::unique_ptr<v8::Local<v8::Value>[]> info(
new v8::Local<v8::Value>[m_arguments.size()]);
for (size_t i = 0; i < m_arguments.size(); ++i) {
info[i] = m_arguments[i];
DCHECK(!info[i].IsEmpty());
}
int contextGroupId = V8Debugger::getGroupId(m_context);
if (contextGroupId) {
m_inspector->client()->muteMetrics(contextGroupId);
m_inspector->muteExceptions(contextGroupId);
}
v8::MicrotasksScope microtasksScope(m_context->GetIsolate(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::MaybeLocal<v8::Value> maybeResult =
function->Call(m_context, thisObject, m_arguments.size(), info.get());
if (contextGroupId) {
m_inspector->client()->unmuteMetrics(contextGroupId);
m_inspector->unmuteExceptions(contextGroupId);
}
v8::Local<v8::Value> result;
if (!maybeResult.ToLocal(&result)) return v8::Local<v8::Value>();
return result;
}
} // namespace v8_inspector

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef V8_INSPECTOR_V8FUNCTIONCALL_H_
#define V8_INSPECTOR_V8FUNCTIONCALL_H_
#include "src/inspector/String16.h"
#include <v8.h>
namespace v8_inspector {
class V8InspectorImpl;
class V8FunctionCall {
public:
V8FunctionCall(V8InspectorImpl*, v8::Local<v8::Context>, v8::Local<v8::Value>,
const String16& name);
void appendArgument(v8::Local<v8::Value>);
void appendArgument(const String16&);
void appendArgument(int);
void appendArgument(bool);
v8::Local<v8::Value> call(bool& hadException, bool reportExceptions = true);
v8::Local<v8::Value> callWithoutExceptionHandling();
protected:
V8InspectorImpl* m_inspector;
v8::Local<v8::Context> m_context;
std::vector<v8::Local<v8::Value>> m_arguments;
v8::Local<v8::String> m_name;
v8::Local<v8::Value> m_value;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8FUNCTIONCALL_H_

View File

@ -0,0 +1,424 @@
// 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/V8HeapProfilerAgentImpl.h"
#include "src/inspector/InjectedScript.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8InspectorClient.h"
#include <v8-profiler.h>
#include <v8-version.h>
namespace v8_inspector {
namespace {
namespace HeapProfilerAgentState {
static const char heapProfilerEnabled[] = "heapProfilerEnabled";
static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled";
static const char allocationTrackingEnabled[] = "allocationTrackingEnabled";
#if V8_MAJOR_VERSION >= 5
static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled";
static const char samplingHeapProfilerInterval[] =
"samplingHeapProfilerInterval";
#endif
}
class HeapSnapshotProgress final : public v8::ActivityControl {
public:
HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend)
: m_frontend(frontend) {}
ControlOption ReportProgressValue(int done, int total) override {
m_frontend->reportHeapSnapshotProgress(done, total,
protocol::Maybe<bool>());
if (done >= total) {
m_frontend->reportHeapSnapshotProgress(total, total, true);
}
m_frontend->flush();
return kContinue;
}
private:
protocol::HeapProfiler::Frontend* m_frontend;
};
class GlobalObjectNameResolver final
: public v8::HeapProfiler::ObjectNameResolver {
public:
explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session)
: m_offset(0), m_strings(10000), m_session(session) {}
const char* GetName(v8::Local<v8::Object> object) override {
InspectedContext* context = m_session->inspector()->getContext(
m_session->contextGroupId(),
V8Debugger::contextId(object->CreationContext()));
if (!context) return "";
String16 name = context->origin();
size_t length = name.length();
if (m_offset + length + 1 >= m_strings.size()) return "";
for (size_t i = 0; i < length; ++i) {
UChar ch = name[i];
m_strings[m_offset + i] = ch > 0xff ? '?' : static_cast<char>(ch);
}
m_strings[m_offset + length] = '\0';
char* result = &*m_strings.begin() + m_offset;
m_offset += length + 1;
return result;
}
private:
size_t m_offset;
std::vector<char> m_strings;
V8InspectorSessionImpl* m_session;
};
class HeapSnapshotOutputStream final : public v8::OutputStream {
public:
HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend)
: m_frontend(frontend) {}
void EndOfStream() override {}
int GetChunkSize() override { return 102400; }
WriteResult WriteAsciiChunk(char* data, int size) override {
m_frontend->addHeapSnapshotChunk(String16(data, size));
m_frontend->flush();
return kContinue;
}
private:
protocol::HeapProfiler::Frontend* m_frontend;
};
v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) {
v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
v8::Local<v8::Value> value = profiler->FindObjectById(id);
if (value.IsEmpty() || !value->IsObject()) return v8::Local<v8::Object>();
return value.As<v8::Object>();
}
class InspectableHeapObject final : public V8InspectorSession::Inspectable {
public:
explicit InspectableHeapObject(int heapObjectId)
: m_heapObjectId(heapObjectId) {}
v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId);
}
private:
int m_heapObjectId;
};
class HeapStatsStream final : public v8::OutputStream {
public:
HeapStatsStream(protocol::HeapProfiler::Frontend* frontend)
: m_frontend(frontend) {}
void EndOfStream() override {}
WriteResult WriteAsciiChunk(char* data, int size) override {
DCHECK(false);
return kAbort;
}
WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData,
int count) override {
DCHECK_GT(count, 0);
std::unique_ptr<protocol::Array<int>> statsDiff =
protocol::Array<int>::create();
for (int i = 0; i < count; ++i) {
statsDiff->addItem(updateData[i].index);
statsDiff->addItem(updateData[i].count);
statsDiff->addItem(updateData[i].size);
}
m_frontend->heapStatsUpdate(std::move(statsDiff));
return kContinue;
}
private:
protocol::HeapProfiler::Frontend* m_frontend;
};
} // namespace
V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
protocol::DictionaryValue* state)
: m_session(session),
m_isolate(session->inspector()->isolate()),
m_frontend(frontendChannel),
m_state(state),
m_hasTimer(false) {}
V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() {}
void V8HeapProfilerAgentImpl::restore() {
if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled,
false))
m_frontend.resetProfiles();
if (m_state->booleanProperty(
HeapProfilerAgentState::heapObjectsTrackingEnabled, false))
startTrackingHeapObjectsInternal(m_state->booleanProperty(
HeapProfilerAgentState::allocationTrackingEnabled, false));
#if V8_MAJOR_VERSION >= 5
if (m_state->booleanProperty(
HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
ErrorString error;
double samplingInterval = m_state->doubleProperty(
HeapProfilerAgentState::samplingHeapProfilerInterval, -1);
DCHECK_GE(samplingInterval, 0);
startSampling(&error, Maybe<double>(samplingInterval));
}
#endif
}
void V8HeapProfilerAgentImpl::collectGarbage(ErrorString*) {
m_isolate->LowMemoryNotification();
}
void V8HeapProfilerAgentImpl::startTrackingHeapObjects(
ErrorString*, const protocol::Maybe<bool>& trackAllocations) {
m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true);
bool allocationTrackingEnabled = trackAllocations.fromMaybe(false);
m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled,
allocationTrackingEnabled);
startTrackingHeapObjectsInternal(allocationTrackingEnabled);
}
void V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
ErrorString* error, const protocol::Maybe<bool>& reportProgress) {
requestHeapStatsUpdate();
takeHeapSnapshot(error, reportProgress);
stopTrackingHeapObjectsInternal();
}
void V8HeapProfilerAgentImpl::enable(ErrorString*) {
m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
}
void V8HeapProfilerAgentImpl::disable(ErrorString* error) {
stopTrackingHeapObjectsInternal();
#if V8_MAJOR_VERSION >= 5
if (m_state->booleanProperty(
HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (profiler) profiler->StopSamplingHeapProfiler();
}
#endif
m_isolate->GetHeapProfiler()->ClearObjectIds();
m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
}
void V8HeapProfilerAgentImpl::takeHeapSnapshot(
ErrorString* errorString, const protocol::Maybe<bool>& reportProgress) {
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (!profiler) {
*errorString = "Cannot access v8 heap profiler";
return;
}
std::unique_ptr<HeapSnapshotProgress> progress;
if (reportProgress.fromMaybe(false))
progress = wrapUnique(new HeapSnapshotProgress(&m_frontend));
GlobalObjectNameResolver resolver(m_session);
const v8::HeapSnapshot* snapshot =
profiler->TakeHeapSnapshot(progress.get(), &resolver);
if (!snapshot) {
*errorString = "Failed to take heap snapshot";
return;
}
HeapSnapshotOutputStream stream(&m_frontend);
snapshot->Serialize(&stream);
const_cast<v8::HeapSnapshot*>(snapshot)->Delete();
}
void V8HeapProfilerAgentImpl::getObjectByHeapObjectId(
ErrorString* error, const String16& heapSnapshotObjectId,
const protocol::Maybe<String16>& objectGroup,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
bool ok;
int id = heapSnapshotObjectId.toInteger(&ok);
if (!ok) {
*error = "Invalid heap snapshot object id";
return;
}
v8::HandleScope handles(m_isolate);
v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
if (heapObject.IsEmpty()) {
*error = "Object is not available";
return;
}
if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) {
*error = "Object is not available";
return;
}
*result = m_session->wrapObject(heapObject->CreationContext(), heapObject,
objectGroup.fromMaybe(""), false);
if (!result) *error = "Object is not available";
}
void V8HeapProfilerAgentImpl::addInspectedHeapObject(
ErrorString* errorString, const String16& inspectedHeapObjectId) {
bool ok;
int id = inspectedHeapObjectId.toInteger(&ok);
if (!ok) {
*errorString = "Invalid heap snapshot object id";
return;
}
v8::HandleScope handles(m_isolate);
v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
if (heapObject.IsEmpty()) {
*errorString = "Object is not available";
return;
}
if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) {
*errorString = "Object is not available";
return;
}
m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id)));
}
void V8HeapProfilerAgentImpl::getHeapObjectId(ErrorString* errorString,
const String16& objectId,
String16* heapSnapshotObjectId) {
v8::HandleScope handles(m_isolate);
v8::Local<v8::Value> value;
v8::Local<v8::Context> context;
if (!m_session->unwrapObject(errorString, objectId, &value, &context,
nullptr) ||
value->IsUndefined())
return;
v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value);
*heapSnapshotObjectId = String16::fromInteger(id);
}
void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() {
HeapStatsStream stream(&m_frontend);
v8::SnapshotObjectId lastSeenObjectId =
m_isolate->GetHeapProfiler()->GetHeapStats(&stream);
m_frontend.lastSeenObjectId(
lastSeenObjectId, m_session->inspector()->client()->currentTimeMS());
}
// static
void V8HeapProfilerAgentImpl::onTimer(void* data) {
reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate();
}
void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal(
bool trackAllocations) {
m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations);
if (!m_hasTimer) {
m_hasTimer = true;
m_session->inspector()->client()->startRepeatingTimer(
0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this));
}
}
void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() {
if (m_hasTimer) {
m_session->inspector()->client()->cancelTimer(
reinterpret_cast<void*>(this));
m_hasTimer = false;
}
m_isolate->GetHeapProfiler()->StopTrackingHeapObjects();
m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled,
false);
m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false);
}
void V8HeapProfilerAgentImpl::startSampling(
ErrorString* errorString, const Maybe<double>& samplingInterval) {
#if V8_MAJOR_VERSION >= 5
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (!profiler) {
*errorString = "Cannot access v8 heap profiler";
return;
}
const unsigned defaultSamplingInterval = 1 << 15;
double samplingIntervalValue =
samplingInterval.fromMaybe(defaultSamplingInterval);
m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval,
samplingIntervalValue);
m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
true);
#if V8_MAJOR_VERSION * 1000 + V8_MINOR_VERSION >= 5002
profiler->StartSamplingHeapProfiler(
static_cast<uint64_t>(samplingIntervalValue), 128,
v8::HeapProfiler::kSamplingForceGC);
#else
profiler->StartSamplingHeapProfiler(
static_cast<uint64_t>(samplingIntervalValue), 128);
#endif
#endif
}
#if V8_MAJOR_VERSION >= 5
namespace {
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode>
buildSampingHeapProfileNode(const v8::AllocationProfile::Node* node) {
auto children = protocol::Array<
protocol::HeapProfiler::SamplingHeapProfileNode>::create();
for (const auto* child : node->children)
children->addItem(buildSampingHeapProfileNode(child));
size_t selfSize = 0;
for (const auto& allocation : node->allocations)
selfSize += allocation.size * allocation.count;
std::unique_ptr<protocol::Runtime::CallFrame> callFrame =
protocol::Runtime::CallFrame::create()
.setFunctionName(toProtocolString(node->name))
.setScriptId(String16::fromInteger(node->script_id))
.setUrl(toProtocolString(node->script_name))
.setLineNumber(node->line_number - 1)
.setColumnNumber(node->column_number - 1)
.build();
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result =
protocol::HeapProfiler::SamplingHeapProfileNode::create()
.setCallFrame(std::move(callFrame))
.setSelfSize(selfSize)
.setChildren(std::move(children))
.build();
return result;
}
} // namespace
#endif
void V8HeapProfilerAgentImpl::stopSampling(
ErrorString* errorString,
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
#if V8_MAJOR_VERSION >= 5
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (!profiler) {
*errorString = "Cannot access v8 heap profiler";
return;
}
v8::HandleScope scope(
m_isolate); // Allocation profile contains Local handles.
std::unique_ptr<v8::AllocationProfile> v8Profile(
profiler->GetAllocationProfile());
profiler->StopSamplingHeapProfiler();
m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
false);
if (!v8Profile) {
*errorString = "Cannot access v8 sampled heap profile.";
return;
}
v8::AllocationProfile::Node* root = v8Profile->GetRootNode();
*profile = protocol::HeapProfiler::SamplingHeapProfile::create()
.setHead(buildSampingHeapProfileNode(root))
.build();
#endif
}
} // namespace v8_inspector

View File

@ -0,0 +1,73 @@
// 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_V8HEAPPROFILERAGENTIMPL_H_
#define V8_INSPECTOR_V8HEAPPROFILERAGENTIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/HeapProfiler.h"
#include <v8.h>
namespace v8_inspector {
class V8InspectorSessionImpl;
using protocol::ErrorString;
using protocol::Maybe;
class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
V8_INSPECTOR_DISALLOW_COPY(V8HeapProfilerAgentImpl);
public:
V8HeapProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
protocol::DictionaryValue* state);
~V8HeapProfilerAgentImpl() override;
void restore();
void collectGarbage(ErrorString*) override;
void enable(ErrorString*) override;
void startTrackingHeapObjects(ErrorString*,
const Maybe<bool>& trackAllocations) override;
void stopTrackingHeapObjects(ErrorString*,
const Maybe<bool>& reportProgress) override;
void disable(ErrorString*) override;
void takeHeapSnapshot(ErrorString*,
const Maybe<bool>& reportProgress) override;
void getObjectByHeapObjectId(
ErrorString*, const String16& heapSnapshotObjectId,
const Maybe<String16>& objectGroup,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) override;
void addInspectedHeapObject(ErrorString*,
const String16& inspectedHeapObjectId) override;
void getHeapObjectId(ErrorString*, const String16& objectId,
String16* heapSnapshotObjectId) override;
void startSampling(ErrorString*,
const Maybe<double>& samplingInterval) override;
void stopSampling(
ErrorString*,
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>*) override;
private:
void startTrackingHeapObjectsInternal(bool trackAllocations);
void stopTrackingHeapObjectsInternal();
void requestHeapStatsUpdate();
static void onTimer(void*);
V8InspectorSessionImpl* m_session;
v8::Isolate* m_isolate;
protocol::HeapProfiler::Frontend m_frontend;
protocol::DictionaryValue* m_state;
bool m_hasTimer;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8HEAPPROFILERAGENTIMPL_H_

View File

@ -0,0 +1,210 @@
// Copyright 2015 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/V8InjectedScriptHost.h"
#include "src/inspector/InjectedScriptNative.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InternalValueType.h"
#include "src/inspector/V8ValueCopier.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
namespace {
void setFunctionProperty(v8::Local<v8::Context> context,
v8::Local<v8::Object> obj, const char* name,
v8::FunctionCallback callback,
v8::Local<v8::External> external) {
v8::Local<v8::String> funcName =
toV8StringInternalized(context->GetIsolate(), name);
v8::Local<v8::Function> func;
if (!V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, callback, external, 0)
.ToLocal(&func))
return;
func->SetName(funcName);
createDataProperty(context, obj, funcName, func);
}
V8InspectorImpl* unwrapInspector(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(!info.Data().IsEmpty());
DCHECK(info.Data()->IsExternal());
V8InspectorImpl* inspector =
static_cast<V8InspectorImpl*>(info.Data().As<v8::External>()->Value());
DCHECK(inspector);
return inspector;
}
} // namespace
v8::Local<v8::Object> V8InjectedScriptHost::create(
v8::Local<v8::Context> context, V8InspectorImpl* inspector) {
v8::Isolate* isolate = inspector->isolate();
v8::Local<v8::Object> injectedScriptHost = v8::Object::New(isolate);
bool success = injectedScriptHost->SetPrototype(context, v8::Null(isolate))
.FromMaybe(false);
DCHECK(success);
v8::Local<v8::External> debuggerExternal =
v8::External::New(isolate, inspector);
setFunctionProperty(context, injectedScriptHost, "internalConstructorName",
V8InjectedScriptHost::internalConstructorNameCallback,
debuggerExternal);
setFunctionProperty(
context, injectedScriptHost, "formatAccessorsAsProperties",
V8InjectedScriptHost::formatAccessorsAsProperties, debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "subtype",
V8InjectedScriptHost::subtypeCallback, debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "getInternalProperties",
V8InjectedScriptHost::getInternalPropertiesCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "objectHasOwnProperty",
V8InjectedScriptHost::objectHasOwnPropertyCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "bind",
V8InjectedScriptHost::bindCallback, debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "proxyTargetValue",
V8InjectedScriptHost::proxyTargetValueCallback,
debuggerExternal);
return injectedScriptHost;
}
void V8InjectedScriptHost::internalConstructorNameCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 1 || !info[0]->IsObject()) return;
v8::Local<v8::Object> object = info[0].As<v8::Object>();
info.GetReturnValue().Set(object->GetConstructorName());
}
void V8InjectedScriptHost::formatAccessorsAsProperties(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK_EQ(info.Length(), 2);
info.GetReturnValue().Set(false);
if (!info[1]->IsFunction()) return;
// Check that function is user-defined.
if (info[1].As<v8::Function>()->ScriptId() != v8::UnboundScript::kNoScriptId)
return;
info.GetReturnValue().Set(
unwrapInspector(info)->client()->formatAccessorsAsProperties(info[0]));
}
void V8InjectedScriptHost::subtypeCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 1) return;
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Value> value = info[0];
if (value->IsObject()) {
v8::Local<v8::Value> internalType = v8InternalValueTypeFrom(
isolate->GetCurrentContext(), v8::Local<v8::Object>::Cast(value));
if (internalType->IsString()) {
info.GetReturnValue().Set(internalType);
return;
}
}
if (value->IsArray() || value->IsArgumentsObject()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "array"));
return;
}
if (value->IsTypedArray()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "typedarray"));
return;
}
if (value->IsDate()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "date"));
return;
}
if (value->IsRegExp()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "regexp"));
return;
}
if (value->IsMap() || value->IsWeakMap()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "map"));
return;
}
if (value->IsSet() || value->IsWeakSet()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "set"));
return;
}
if (value->IsMapIterator() || value->IsSetIterator()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "iterator"));
return;
}
if (value->IsGeneratorObject()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "generator"));
return;
}
if (value->IsNativeError()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "error"));
return;
}
if (value->IsProxy()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "proxy"));
return;
}
if (value->IsPromise()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "promise"));
return;
}
std::unique_ptr<StringBuffer> subtype =
unwrapInspector(info)->client()->valueSubtype(value);
if (subtype) {
info.GetReturnValue().Set(toV8String(isolate, subtype->string()));
return;
}
}
void V8InjectedScriptHost::getInternalPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 1) return;
v8::Local<v8::Array> properties;
if (unwrapInspector(info)
->debugger()
->internalProperties(info.GetIsolate()->GetCurrentContext(), info[0])
.ToLocal(&properties))
info.GetReturnValue().Set(properties);
}
void V8InjectedScriptHost::objectHasOwnPropertyCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 2 || !info[0]->IsObject() || !info[1]->IsString()) return;
bool result = info[0]
.As<v8::Object>()
->HasOwnProperty(info.GetIsolate()->GetCurrentContext(),
v8::Local<v8::String>::Cast(info[1]))
.FromMaybe(false);
info.GetReturnValue().Set(v8::Boolean::New(info.GetIsolate(), result));
}
void V8InjectedScriptHost::bindCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 2 || !info[1]->IsString()) return;
InjectedScriptNative* injectedScriptNative =
InjectedScriptNative::fromInjectedScriptHost(info.Holder());
if (!injectedScriptNative) return;
v8::Local<v8::String> v8groupName = info[1]->ToString(info.GetIsolate());
String16 groupName = toProtocolStringWithTypeCheck(v8groupName);
int id = injectedScriptNative->bind(info[0], groupName);
info.GetReturnValue().Set(id);
}
void V8InjectedScriptHost::proxyTargetValueCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() != 1 || !info[0]->IsProxy()) {
NOTREACHED();
return;
}
v8::Local<v8::Object> target = info[0].As<v8::Proxy>();
while (target->IsProxy())
target = v8::Local<v8::Proxy>::Cast(target)->GetTarget();
info.GetReturnValue().Set(target);
}
} // namespace v8_inspector

View File

@ -0,0 +1,46 @@
// Copyright 2015 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_V8INJECTEDSCRIPTHOST_H_
#define V8_INSPECTOR_V8INJECTEDSCRIPTHOST_H_
#include <v8.h>
namespace v8_inspector {
class V8InspectorImpl;
// SECURITY NOTE: Although the InjectedScriptHost is intended for use solely by
// the inspector,
// a reference to the InjectedScriptHost may be leaked to the page being
// inspected. Thus, the
// InjectedScriptHost must never implemment methods that have more power over
// the page than the
// page already has itself (e.g. origin restriction bypasses).
class V8InjectedScriptHost {
public:
// We expect that debugger outlives any JS context and thus
// V8InjectedScriptHost (owned by JS)
// is destroyed before inspector.
static v8::Local<v8::Object> create(v8::Local<v8::Context>, V8InspectorImpl*);
private:
static void internalConstructorNameCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void formatAccessorsAsProperties(
const v8::FunctionCallbackInfo<v8::Value>&);
static void subtypeCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void getInternalPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void objectHasOwnPropertyCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void bindCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void proxyTargetValueCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8INJECTEDSCRIPTHOST_H_

View File

@ -0,0 +1,362 @@
/*
* Copyright (c) 2010-2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "src/inspector/V8InspectorImpl.h"
#include <v8-profiler.h>
#include "src/inspector/InspectedContext.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8ConsoleAgentImpl.h"
#include "src/inspector/V8ConsoleMessage.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/V8DebuggerAgentImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8RuntimeAgentImpl.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate,
V8InspectorClient* client) {
return wrapUnique(new V8InspectorImpl(isolate, client));
}
V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate,
V8InspectorClient* client)
: m_isolate(isolate),
m_client(client),
m_debugger(new V8Debugger(isolate, this)),
m_capturingStackTracesCount(0),
m_lastExceptionId(0) {}
V8InspectorImpl::~V8InspectorImpl() {}
V8DebuggerAgentImpl* V8InspectorImpl::enabledDebuggerAgentForGroup(
int contextGroupId) {
V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId);
V8DebuggerAgentImpl* agent = session ? session->debuggerAgent() : nullptr;
return agent && agent->enabled() ? agent : nullptr;
}
V8RuntimeAgentImpl* V8InspectorImpl::enabledRuntimeAgentForGroup(
int contextGroupId) {
V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId);
V8RuntimeAgentImpl* agent = session ? session->runtimeAgent() : nullptr;
return agent && agent->enabled() ? agent : nullptr;
}
v8::MaybeLocal<v8::Value> V8InspectorImpl::runCompiledScript(
v8::Local<v8::Context> context, v8::Local<v8::Script> script) {
v8::MicrotasksScope microtasksScope(m_isolate,
v8::MicrotasksScope::kRunMicrotasks);
int groupId = V8Debugger::getGroupId(context);
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
agent->willExecuteScript(script->GetUnboundScript()->GetId());
v8::MaybeLocal<v8::Value> result = script->Run(context);
// Get agent from the map again, since it could have detached during script
// execution.
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
agent->didExecuteScript();
return result;
}
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);
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
agent->willExecuteScript(function->ScriptId());
v8::MaybeLocal<v8::Value> result =
function->Call(context, receiver, argc, info);
// Get agent from the map again, since it could have detached during script
// execution.
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId))
agent->didExecuteScript();
return result;
}
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::MicrotasksScope microtasksScope(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
return script->Run(context);
}
v8::Local<v8::Script> V8InspectorImpl::compileScript(
v8::Local<v8::Context> context, v8::Local<v8::String> code,
const String16& fileName, bool markAsInternal) {
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;
}
void V8InspectorImpl::enableStackCapturingIfNeeded() {
if (!m_capturingStackTracesCount)
V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
true);
++m_capturingStackTracesCount;
}
void V8InspectorImpl::disableStackCapturingIfNeeded() {
if (!(--m_capturingStackTracesCount))
V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
false);
}
void V8InspectorImpl::muteExceptions(int contextGroupId) {
m_muteExceptionsMap[contextGroupId]++;
}
void V8InspectorImpl::unmuteExceptions(int contextGroupId) {
m_muteExceptionsMap[contextGroupId]--;
}
V8ConsoleMessageStorage* V8InspectorImpl::ensureConsoleMessageStorage(
int contextGroupId) {
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;
return storageIt->second.get();
}
std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace(
v8::Local<v8::StackTrace> stackTrace) {
return m_debugger->createStackTrace(stackTrace);
}
std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
int contextGroupId, V8Inspector::Channel* channel,
const StringView& state) {
DCHECK(m_sessions.find(contextGroupId) == m_sessions.cend());
std::unique_ptr<V8InspectorSessionImpl> session =
V8InspectorSessionImpl::create(this, contextGroupId, channel, state);
m_sessions[contextGroupId] = session.get();
return std::move(session);
}
void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) {
DCHECK(m_sessions.find(session->contextGroupId()) != m_sessions.end());
m_sessions.erase(session->contextGroupId());
}
InspectedContext* V8InspectorImpl::getContext(int groupId,
int contextId) const {
if (!groupId || !contextId) return nullptr;
ContextsByGroupMap::const_iterator contextGroupIt = m_contexts.find(groupId);
if (contextGroupIt == m_contexts.end()) return nullptr;
ContextByIdMap::iterator contextIt = contextGroupIt->second->find(contextId);
if (contextIt == contextGroupIt->second->end()) return nullptr;
return contextIt->second.get();
}
void V8InspectorImpl::contextCreated(const V8ContextInfo& info) {
int contextId = m_debugger->markContext(info);
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())))
.first;
const auto& contextById = contextIt->second;
DCHECK(contextById->find(contextId) == contextById->cend());
InspectedContext* context = new InspectedContext(this, info, contextId);
(*contextById)[contextId] = wrapUnique(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);
ConsoleStorageMap::iterator storageIt =
m_consoleStorageMap.find(contextGroupId);
if (storageIt != m_consoleStorageMap.end())
storageIt->second->contextDestroyed(contextId);
InspectedContext* inspectedContext = getContext(contextGroupId, contextId);
if (!inspectedContext) return;
SessionMap::iterator iter = m_sessions.find(contextGroupId);
if (iter != m_sessions.end())
iter->second->runtimeAgent()->reportExecutionContextDestroyed(
inspectedContext);
discardInspectedContext(contextGroupId, contextId);
}
void V8InspectorImpl::resetContextGroup(int contextGroupId) {
m_consoleStorageMap.erase(contextGroupId);
m_muteExceptionsMap.erase(contextGroupId);
SessionMap::iterator session = m_sessions.find(contextGroupId);
if (session != m_sessions.end()) session->second->reset();
m_contexts.erase(contextGroupId);
}
void V8InspectorImpl::willExecuteScript(v8::Local<v8::Context> context,
int scriptId) {
if (V8DebuggerAgentImpl* agent =
enabledDebuggerAgentForGroup(V8Debugger::getGroupId(context)))
agent->willExecuteScript(scriptId);
}
void V8InspectorImpl::didExecuteScript(v8::Local<v8::Context> context) {
if (V8DebuggerAgentImpl* agent =
enabledDebuggerAgentForGroup(V8Debugger::getGroupId(context)))
agent->didExecuteScript();
}
void V8InspectorImpl::idleStarted() {
m_isolate->GetCpuProfiler()->SetIdle(true);
}
void V8InspectorImpl::idleFinished() {
m_isolate->GetCpuProfiler()->SetIdle(false);
}
unsigned V8InspectorImpl::exceptionThrown(
v8::Local<v8::Context> context, const StringView& message,
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()));
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));
return exceptionId;
}
void V8InspectorImpl::exceptionRevoked(v8::Local<v8::Context> context,
unsigned exceptionId,
const StringView& message) {
int contextGroupId = V8Debugger::getGroupId(context);
if (!contextGroupId) return;
std::unique_ptr<V8ConsoleMessage> consoleMessage =
V8ConsoleMessage::createForRevokedException(
m_client->currentTimeMS(), toString16(message), exceptionId);
ensureConsoleMessageStorage(contextGroupId)
->addMessage(std::move(consoleMessage));
}
std::unique_ptr<V8StackTrace> V8InspectorImpl::captureStackTrace(
bool fullStack) {
return m_debugger->captureStackTrace(fullStack);
}
void V8InspectorImpl::asyncTaskScheduled(const StringView& taskName, void* task,
bool recurring) {
m_debugger->asyncTaskScheduled(taskName, task, recurring);
}
void V8InspectorImpl::asyncTaskCanceled(void* task) {
m_debugger->asyncTaskCanceled(task);
}
void V8InspectorImpl::asyncTaskStarted(void* task) {
m_debugger->asyncTaskStarted(task);
}
void V8InspectorImpl::asyncTaskFinished(void* task) {
m_debugger->asyncTaskFinished(task);
}
void V8InspectorImpl::allAsyncTasksCanceled() {
m_debugger->allAsyncTasksCanceled();
}
v8::Local<v8::Context> V8InspectorImpl::regexContext() {
if (m_regexContext.IsEmpty())
m_regexContext.Reset(m_isolate, v8::Context::New(m_isolate));
return m_regexContext.Get(m_isolate);
}
void V8InspectorImpl::discardInspectedContext(int contextGroupId,
int contextId) {
if (!getContext(contextGroupId, contextId)) return;
m_contexts[contextGroupId]->erase(contextId);
if (m_contexts[contextGroupId]->empty()) m_contexts.erase(contextGroupId);
}
const V8InspectorImpl::ContextByIdMap* V8InspectorImpl::contextGroup(
int contextGroupId) {
ContextsByGroupMap::iterator iter = m_contexts.find(contextGroupId);
return iter == m_contexts.end() ? nullptr : iter->second.get();
}
V8InspectorSessionImpl* V8InspectorImpl::sessionForContextGroup(
int contextGroupId) {
if (!contextGroupId) return nullptr;
SessionMap::iterator iter = m_sessions.find(contextGroupId);
return iter == m_sessions.end() ? nullptr : iter->second;
}
} // namespace v8_inspector

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef V8_INSPECTOR_V8INSPECTORIMPL_H_
#define V8_INSPECTOR_V8INSPECTORIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8Inspector.h"
#include <v8-debug.h>
#include <v8.h>
#include <vector>
namespace v8_inspector {
class InspectedContext;
class V8ConsoleMessageStorage;
class V8Debugger;
class V8DebuggerAgentImpl;
class V8InspectorSessionImpl;
class V8RuntimeAgentImpl;
class V8StackTraceImpl;
class V8InspectorImpl : public V8Inspector {
V8_INSPECTOR_DISALLOW_COPY(V8InspectorImpl);
public:
V8InspectorImpl(v8::Isolate*, V8InspectorClient*);
~V8InspectorImpl() override;
v8::Isolate* isolate() const { return m_isolate; }
V8InspectorClient* client() { return m_client; }
V8Debugger* debugger() { return m_debugger.get(); }
v8::MaybeLocal<v8::Value> runCompiledScript(v8::Local<v8::Context>,
v8::Local<v8::Script>);
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::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::Local<v8::Context> regexContext();
// V8Inspector implementation.
std::unique_ptr<V8InspectorSession> connect(int contextGroupId,
V8Inspector::Channel*,
const StringView& state) override;
void contextCreated(const V8ContextInfo&) override;
void contextDestroyed(v8::Local<v8::Context>) override;
void resetContextGroup(int contextGroupId) override;
void willExecuteScript(v8::Local<v8::Context>, int scriptId) override;
void didExecuteScript(v8::Local<v8::Context>) override;
void idleStarted() override;
void idleFinished() override;
unsigned exceptionThrown(v8::Local<v8::Context>, const StringView& message,
v8::Local<v8::Value> exception,
const StringView& detailedMessage,
const StringView& url, unsigned lineNumber,
unsigned columnNumber, std::unique_ptr<V8StackTrace>,
int scriptId) override;
void exceptionRevoked(v8::Local<v8::Context>, unsigned exceptionId,
const StringView& message) override;
std::unique_ptr<V8StackTrace> createStackTrace(
v8::Local<v8::StackTrace>) override;
std::unique_ptr<V8StackTrace> captureStackTrace(bool fullStack) override;
void asyncTaskScheduled(const StringView& taskName, void* task,
bool recurring) override;
void asyncTaskCanceled(void* task) override;
void asyncTaskStarted(void* task) override;
void asyncTaskFinished(void* task) override;
void allAsyncTasksCanceled() override;
unsigned nextExceptionId() { return ++m_lastExceptionId; }
void enableStackCapturingIfNeeded();
void disableStackCapturingIfNeeded();
void muteExceptions(int contextGroupId);
void unmuteExceptions(int contextGroupId);
V8ConsoleMessageStorage* ensureConsoleMessageStorage(int contextGroupId);
using ContextByIdMap =
protocol::HashMap<int, std::unique_ptr<InspectedContext>>;
void discardInspectedContext(int contextGroupId, int contextId);
const ContextByIdMap* contextGroup(int contextGroupId);
void disconnect(V8InspectorSessionImpl*);
V8InspectorSessionImpl* sessionForContextGroup(int contextGroupId);
InspectedContext* getContext(int groupId, int contextId) const;
V8DebuggerAgentImpl* enabledDebuggerAgentForGroup(int contextGroupId);
V8RuntimeAgentImpl* enabledRuntimeAgentForGroup(int contextGroupId);
private:
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;
using MuteExceptionsMap = protocol::HashMap<int, int>;
MuteExceptionsMap m_muteExceptionsMap;
using ContextsByGroupMap =
protocol::HashMap<int, std::unique_ptr<ContextByIdMap>>;
ContextsByGroupMap m_contexts;
using SessionMap = protocol::HashMap<int, V8InspectorSessionImpl*>;
SessionMap m_sessions;
using ConsoleStorageMap =
protocol::HashMap<int, std::unique_ptr<V8ConsoleMessageStorage>>;
ConsoleStorageMap m_consoleStorageMap;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8INSPECTORIMPL_H_

View File

@ -0,0 +1,420 @@
// 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/V8InspectorSessionImpl.h"
#include "src/inspector/InjectedScript.h"
#include "src/inspector/InspectedContext.h"
#include "src/inspector/RemoteObjectId.h"
#include "src/inspector/SearchUtil.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8ConsoleAgentImpl.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/V8DebuggerAgentImpl.h"
#include "src/inspector/V8HeapProfilerAgentImpl.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8ProfilerAgentImpl.h"
#include "src/inspector/V8RuntimeAgentImpl.h"
#include "src/inspector/V8SchemaAgentImpl.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8ContextInfo.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
// static
bool V8InspectorSession::canDispatchMethod(const StringView& method) {
return stringViewStartsWith(method,
protocol::Runtime::Metainfo::commandPrefix) ||
stringViewStartsWith(method,
protocol::Debugger::Metainfo::commandPrefix) ||
stringViewStartsWith(method,
protocol::Profiler::Metainfo::commandPrefix) ||
stringViewStartsWith(
method, protocol::HeapProfiler::Metainfo::commandPrefix) ||
stringViewStartsWith(method,
protocol::Console::Metainfo::commandPrefix) ||
stringViewStartsWith(method,
protocol::Schema::Metainfo::commandPrefix);
}
std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(
V8InspectorImpl* inspector, int contextGroupId,
V8Inspector::Channel* channel, const StringView& state) {
return wrapUnique(
new V8InspectorSessionImpl(inspector, contextGroupId, channel, state));
}
V8InspectorSessionImpl::V8InspectorSessionImpl(V8InspectorImpl* inspector,
int contextGroupId,
V8Inspector::Channel* channel,
const StringView& savedState)
: m_contextGroupId(contextGroupId),
m_inspector(inspector),
m_channel(channel),
m_customObjectFormatterEnabled(false),
m_dispatcher(this),
m_state(nullptr),
m_runtimeAgent(nullptr),
m_debuggerAgent(nullptr),
m_heapProfilerAgent(nullptr),
m_profilerAgent(nullptr),
m_consoleAgent(nullptr),
m_schemaAgent(nullptr) {
if (savedState.length()) {
std::unique_ptr<protocol::Value> state =
protocol::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(
this, this, agentState(protocol::Runtime::Metainfo::domainName)));
protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get());
m_debuggerAgent = wrapUnique(new V8DebuggerAgentImpl(
this, this, agentState(protocol::Debugger::Metainfo::domainName)));
protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get());
m_profilerAgent = wrapUnique(new V8ProfilerAgentImpl(
this, this, agentState(protocol::Profiler::Metainfo::domainName)));
protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get());
m_heapProfilerAgent = wrapUnique(new V8HeapProfilerAgentImpl(
this, this, agentState(protocol::HeapProfiler::Metainfo::domainName)));
protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher,
m_heapProfilerAgent.get());
m_consoleAgent = wrapUnique(new V8ConsoleAgentImpl(
this, this, agentState(protocol::Console::Metainfo::domainName)));
protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get());
m_schemaAgent = wrapUnique(new V8SchemaAgentImpl(
this, this, agentState(protocol::Schema::Metainfo::domainName)));
protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get());
if (savedState.length()) {
m_runtimeAgent->restore();
m_debuggerAgent->restore();
m_heapProfilerAgent->restore();
m_profilerAgent->restore();
m_consoleAgent->restore();
}
}
V8InspectorSessionImpl::~V8InspectorSessionImpl() {
ErrorString errorString;
m_consoleAgent->disable(&errorString);
m_profilerAgent->disable(&errorString);
m_heapProfilerAgent->disable(&errorString);
m_debuggerAgent->disable(&errorString);
m_runtimeAgent->disable(&errorString);
discardInjectedScripts();
m_inspector->disconnect(this);
}
protocol::DictionaryValue* V8InspectorSessionImpl::agentState(
const String16& name) {
protocol::DictionaryValue* state = m_state->getObject(name);
if (!state) {
std::unique_ptr<protocol::DictionaryValue> newState =
protocol::DictionaryValue::create();
state = newState.get();
m_state->setObject(name, std::move(newState));
}
return state;
}
void V8InspectorSessionImpl::sendProtocolResponse(int callId,
const String16& message) {
m_channel->sendProtocolResponse(callId, toStringView(message));
}
void V8InspectorSessionImpl::sendProtocolNotification(const String16& message) {
m_channel->sendProtocolNotification(toStringView(message));
}
void V8InspectorSessionImpl::flushProtocolNotifications() {
m_channel->flushProtocolNotifications();
}
void V8InspectorSessionImpl::reset() {
m_debuggerAgent->reset();
m_runtimeAgent->reset();
discardInjectedScripts();
}
void V8InspectorSessionImpl::discardInjectedScripts() {
m_inspectedObjects.clear();
const V8InspectorImpl::ContextByIdMap* contexts =
m_inspector->contextGroup(m_contextGroupId);
if (!contexts) return;
std::vector<int> keys;
keys.reserve(contexts->size());
for (auto& idContext : *contexts) keys.push_back(idContext.first);
for (auto& key : keys) {
contexts = m_inspector->contextGroup(m_contextGroupId);
if (!contexts) continue;
auto contextIt = contexts->find(key);
if (contextIt != contexts->end())
contextIt->second
->discardInjectedScript(); // This may destroy some contexts.
}
}
InjectedScript* V8InspectorSessionImpl::findInjectedScript(
ErrorString* errorString, int contextId) {
if (!contextId) {
*errorString = "Cannot find context with specified id";
return nullptr;
}
const V8InspectorImpl::ContextByIdMap* contexts =
m_inspector->contextGroup(m_contextGroupId);
if (!contexts) {
*errorString = "Cannot find context with specified id";
return nullptr;
}
auto contextsIt = contexts->find(contextId);
if (contextsIt == contexts->end()) {
*errorString = "Cannot find context with specified id";
return nullptr;
}
const std::unique_ptr<InspectedContext>& context = contextsIt->second;
if (!context->getInjectedScript()) {
context->createInjectedScript();
if (!context->getInjectedScript()) {
*errorString = "Cannot access specified execution context";
return nullptr;
}
if (m_customObjectFormatterEnabled)
context->getInjectedScript()->setCustomObjectFormatterEnabled(true);
}
return context->getInjectedScript();
}
InjectedScript* V8InspectorSessionImpl::findInjectedScript(
ErrorString* errorString, RemoteObjectIdBase* objectId) {
return objectId ? findInjectedScript(errorString, objectId->contextId())
: nullptr;
}
void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) {
releaseObjectGroup(toString16(objectGroup));
}
void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) {
const V8InspectorImpl::ContextByIdMap* contexts =
m_inspector->contextGroup(m_contextGroupId);
if (!contexts) return;
std::vector<int> keys;
for (auto& idContext : *contexts) keys.push_back(idContext.first);
for (auto& key : keys) {
contexts = m_inspector->contextGroup(m_contextGroupId);
if (!contexts) continue;
auto contextsIt = contexts->find(key);
if (contextsIt == contexts->end()) continue;
InjectedScript* injectedScript = contextsIt->second->getInjectedScript();
if (injectedScript)
injectedScript->releaseObjectGroup(
objectGroup); // This may destroy some contexts.
}
}
bool V8InspectorSessionImpl::unwrapObject(
std::unique_ptr<StringBuffer>* error, const StringView& objectId,
v8::Local<v8::Value>* object, v8::Local<v8::Context>* context,
std::unique_ptr<StringBuffer>* objectGroup) {
ErrorString errorString;
String16 objectGroupString;
bool result =
unwrapObject(&errorString, toString16(objectId), object, context,
objectGroup ? &objectGroupString : nullptr);
if (error) *error = StringBufferImpl::adopt(errorString);
if (objectGroup) *objectGroup = StringBufferImpl::adopt(objectGroupString);
return result;
}
bool V8InspectorSessionImpl::unwrapObject(ErrorString* errorString,
const String16& objectId,
v8::Local<v8::Value>* object,
v8::Local<v8::Context>* context,
String16* objectGroup) {
std::unique_ptr<RemoteObjectId> remoteId =
RemoteObjectId::parse(errorString, objectId);
if (!remoteId) return false;
InjectedScript* injectedScript =
findInjectedScript(errorString, remoteId.get());
if (!injectedScript) return false;
if (!injectedScript->findObject(errorString, *remoteId, object)) return false;
*context = injectedScript->context()->context();
if (objectGroup) *objectGroup = injectedScript->objectGroupName(*remoteId);
return true;
}
std::unique_ptr<protocol::Runtime::API::RemoteObject>
V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
v8::Local<v8::Value> value,
const StringView& groupName) {
return wrapObject(context, value, toString16(groupName), false);
}
std::unique_ptr<protocol::Runtime::RemoteObject>
V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
v8::Local<v8::Value> value,
const String16& groupName,
bool generatePreview) {
ErrorString errorString;
InjectedScript* injectedScript =
findInjectedScript(&errorString, V8Debugger::contextId(context));
if (!injectedScript) return nullptr;
return injectedScript->wrapObject(&errorString, value, groupName, false,
generatePreview);
}
std::unique_ptr<protocol::Runtime::RemoteObject>
V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context,
v8::Local<v8::Value> table,
v8::Local<v8::Value> columns) {
ErrorString errorString;
InjectedScript* injectedScript =
findInjectedScript(&errorString, V8Debugger::contextId(context));
if (!injectedScript) return nullptr;
return injectedScript->wrapTable(table, columns);
}
void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) {
m_customObjectFormatterEnabled = enabled;
const V8InspectorImpl::ContextByIdMap* contexts =
m_inspector->contextGroup(m_contextGroupId);
if (!contexts) return;
for (auto& idContext : *contexts) {
InjectedScript* injectedScript = idContext.second->getInjectedScript();
if (injectedScript)
injectedScript->setCustomObjectFormatterEnabled(enabled);
}
}
void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) {
const V8InspectorImpl::ContextByIdMap* contexts =
m_inspector->contextGroup(m_contextGroupId);
if (!contexts) return;
for (auto& idContext : *contexts)
agent->reportExecutionContextCreated(idContext.second.get());
}
void V8InspectorSessionImpl::dispatchProtocolMessage(
const StringView& message) {
m_dispatcher.dispatch(protocol::parseJSON(message));
}
std::unique_ptr<StringBuffer> V8InspectorSessionImpl::stateJSON() {
String16 json = m_state->toJSONString();
return StringBufferImpl::adopt(json);
}
std::vector<std::unique_ptr<protocol::Schema::API::Domain>>
V8InspectorSessionImpl::supportedDomains() {
std::vector<std::unique_ptr<protocol::Schema::Domain>> domains =
supportedDomainsImpl();
std::vector<std::unique_ptr<protocol::Schema::API::Domain>> result;
for (size_t i = 0; i < domains.size(); ++i)
result.push_back(std::move(domains[i]));
return result;
}
std::vector<std::unique_ptr<protocol::Schema::Domain>>
V8InspectorSessionImpl::supportedDomainsImpl() {
std::vector<std::unique_ptr<protocol::Schema::Domain>> result;
result.push_back(protocol::Schema::Domain::create()
.setName(protocol::Runtime::Metainfo::domainName)
.setVersion(protocol::Runtime::Metainfo::version)
.build());
result.push_back(protocol::Schema::Domain::create()
.setName(protocol::Debugger::Metainfo::domainName)
.setVersion(protocol::Debugger::Metainfo::version)
.build());
result.push_back(protocol::Schema::Domain::create()
.setName(protocol::Profiler::Metainfo::domainName)
.setVersion(protocol::Profiler::Metainfo::version)
.build());
result.push_back(protocol::Schema::Domain::create()
.setName(protocol::HeapProfiler::Metainfo::domainName)
.setVersion(protocol::HeapProfiler::Metainfo::version)
.build());
result.push_back(protocol::Schema::Domain::create()
.setName(protocol::Schema::Metainfo::domainName)
.setVersion(protocol::Schema::Metainfo::version)
.build());
return result;
}
void V8InspectorSessionImpl::addInspectedObject(
std::unique_ptr<V8InspectorSession::Inspectable> inspectable) {
m_inspectedObjects.insert(m_inspectedObjects.begin(), std::move(inspectable));
if (m_inspectedObjects.size() > kInspectedObjectBufferSize)
m_inspectedObjects.resize(kInspectedObjectBufferSize);
}
V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(
unsigned num) {
if (num >= m_inspectedObjects.size()) return nullptr;
return m_inspectedObjects[num].get();
}
void V8InspectorSessionImpl::schedulePauseOnNextStatement(
const StringView& breakReason, const StringView& breakDetails) {
m_debuggerAgent->schedulePauseOnNextStatement(
toString16(breakReason),
protocol::DictionaryValue::cast(protocol::parseJSON(breakDetails)));
}
void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
m_debuggerAgent->cancelPauseOnNextStatement();
}
void V8InspectorSessionImpl::breakProgram(const StringView& breakReason,
const StringView& breakDetails) {
m_debuggerAgent->breakProgram(
toString16(breakReason),
protocol::DictionaryValue::cast(protocol::parseJSON(breakDetails)));
}
void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
ErrorString errorString;
m_debuggerAgent->setSkipAllPauses(&errorString, skip);
}
void V8InspectorSessionImpl::resume() {
ErrorString errorString;
m_debuggerAgent->resume(&errorString);
}
void V8InspectorSessionImpl::stepOver() {
ErrorString errorString;
m_debuggerAgent->stepOver(&errorString);
}
std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
V8InspectorSessionImpl::searchInTextByLines(const StringView& text,
const StringView& query,
bool caseSensitive, bool isRegex) {
// TODO(dgozman): search may operate on StringView and avoid copying |text|.
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
searchInTextByLinesImpl(this, toString16(text), toString16(query),
caseSensitive, isRegex);
std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> result;
for (size_t i = 0; i < matches.size(); ++i)
result.push_back(std::move(matches[i]));
return result;
}
} // namespace v8_inspector

View File

@ -0,0 +1,127 @@
// 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_V8INSPECTORSESSIONIMPL_H_
#define V8_INSPECTOR_V8INSPECTORSESSIONIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
#include "src/inspector/protocol/Schema.h"
#include "src/inspector/public/V8Inspector.h"
#include "src/inspector/public/V8InspectorSession.h"
#include <v8.h>
#include <vector>
namespace v8_inspector {
class InjectedScript;
class RemoteObjectIdBase;
class V8ConsoleAgentImpl;
class V8DebuggerAgentImpl;
class V8InspectorImpl;
class V8HeapProfilerAgentImpl;
class V8ProfilerAgentImpl;
class V8RuntimeAgentImpl;
class V8SchemaAgentImpl;
using protocol::ErrorString;
class V8InspectorSessionImpl : public V8InspectorSession,
public protocol::FrontendChannel {
V8_INSPECTOR_DISALLOW_COPY(V8InspectorSessionImpl);
public:
static std::unique_ptr<V8InspectorSessionImpl> create(
V8InspectorImpl*, int contextGroupId, V8Inspector::Channel*,
const StringView& state);
~V8InspectorSessionImpl();
V8InspectorImpl* inspector() const { return m_inspector; }
V8ConsoleAgentImpl* consoleAgent() { return m_consoleAgent.get(); }
V8DebuggerAgentImpl* debuggerAgent() { return m_debuggerAgent.get(); }
V8SchemaAgentImpl* schemaAgent() { return m_schemaAgent.get(); }
V8ProfilerAgentImpl* profilerAgent() { return m_profilerAgent.get(); }
V8RuntimeAgentImpl* runtimeAgent() { return m_runtimeAgent.get(); }
int contextGroupId() const { return m_contextGroupId; }
InjectedScript* findInjectedScript(ErrorString*, int contextId);
InjectedScript* findInjectedScript(ErrorString*, RemoteObjectIdBase*);
void reset();
void discardInjectedScripts();
void reportAllContexts(V8RuntimeAgentImpl*);
void setCustomObjectFormatterEnabled(bool);
std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject(
v8::Local<v8::Context>, v8::Local<v8::Value>, const String16& groupName,
bool generatePreview);
std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(
v8::Local<v8::Context>, v8::Local<v8::Value> table,
v8::Local<v8::Value> columns);
std::vector<std::unique_ptr<protocol::Schema::Domain>> supportedDomainsImpl();
bool unwrapObject(ErrorString*, const String16& objectId,
v8::Local<v8::Value>*, v8::Local<v8::Context>*,
String16* objectGroup);
void releaseObjectGroup(const String16& objectGroup);
// V8InspectorSession implementation.
void dispatchProtocolMessage(const StringView& message) override;
std::unique_ptr<StringBuffer> stateJSON() override;
std::vector<std::unique_ptr<protocol::Schema::API::Domain>> supportedDomains()
override;
void addInspectedObject(
std::unique_ptr<V8InspectorSession::Inspectable>) override;
void schedulePauseOnNextStatement(const StringView& breakReason,
const StringView& breakDetails) override;
void cancelPauseOnNextStatement() override;
void breakProgram(const StringView& breakReason,
const StringView& breakDetails) override;
void setSkipAllPauses(bool) override;
void resume() override;
void stepOver() override;
std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
searchInTextByLines(const StringView& text, const StringView& query,
bool caseSensitive, bool isRegex) override;
void releaseObjectGroup(const StringView& objectGroup) override;
bool unwrapObject(std::unique_ptr<StringBuffer>*, const StringView& objectId,
v8::Local<v8::Value>*, v8::Local<v8::Context>*,
std::unique_ptr<StringBuffer>* objectGroup) override;
std::unique_ptr<protocol::Runtime::API::RemoteObject> wrapObject(
v8::Local<v8::Context>, v8::Local<v8::Value>,
const StringView& groupName) override;
V8InspectorSession::Inspectable* inspectedObject(unsigned num);
static const unsigned kInspectedObjectBufferSize = 5;
private:
V8InspectorSessionImpl(V8InspectorImpl*, int contextGroupId,
V8Inspector::Channel*, const StringView& state);
protocol::DictionaryValue* agentState(const String16& name);
// protocol::FrontendChannel implementation.
void sendProtocolResponse(int callId, const String16& message) override;
void sendProtocolNotification(const String16& message) override;
void flushProtocolNotifications() override;
int m_contextGroupId;
V8InspectorImpl* m_inspector;
V8Inspector::Channel* m_channel;
bool m_customObjectFormatterEnabled;
protocol::UberDispatcher m_dispatcher;
std::unique_ptr<protocol::DictionaryValue> m_state;
std::unique_ptr<V8RuntimeAgentImpl> m_runtimeAgent;
std::unique_ptr<V8DebuggerAgentImpl> m_debuggerAgent;
std::unique_ptr<V8HeapProfilerAgentImpl> m_heapProfilerAgent;
std::unique_ptr<V8ProfilerAgentImpl> m_profilerAgent;
std::unique_ptr<V8ConsoleAgentImpl> m_consoleAgent;
std::unique_ptr<V8SchemaAgentImpl> m_schemaAgent;
std::vector<std::unique_ptr<V8InspectorSession::Inspectable>>
m_inspectedObjects;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8INSPECTORSESSIONIMPL_H_

View File

@ -0,0 +1,77 @@
// 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/V8InternalValueType.h"
#include "src/inspector/ProtocolPlatform.h"
#include "src/inspector/StringUtil.h"
namespace v8_inspector {
namespace {
v8::Local<v8::Private> internalSubtypePrivate(v8::Isolate* isolate) {
return v8::Private::ForApi(
isolate,
toV8StringInternalized(isolate, "V8InternalType#internalSubtype"));
}
v8::Local<v8::String> subtypeForInternalType(v8::Isolate* isolate,
V8InternalValueType type) {
switch (type) {
case V8InternalValueType::kEntry:
return toV8StringInternalized(isolate, "internal#entry");
case V8InternalValueType::kLocation:
return toV8StringInternalized(isolate, "internal#location");
case V8InternalValueType::kScope:
return toV8StringInternalized(isolate, "internal#scope");
case V8InternalValueType::kScopeList:
return toV8StringInternalized(isolate, "internal#scopeList");
}
NOTREACHED();
return v8::Local<v8::String>();
}
} // namespace
bool markAsInternal(v8::Local<v8::Context> context,
v8::Local<v8::Object> object, V8InternalValueType type) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Private> privateValue = internalSubtypePrivate(isolate);
v8::Local<v8::String> subtype = subtypeForInternalType(isolate, type);
return object->SetPrivate(context, privateValue, subtype).FromMaybe(false);
}
bool markArrayEntriesAsInternal(v8::Local<v8::Context> context,
v8::Local<v8::Array> array,
V8InternalValueType type) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Private> privateValue = internalSubtypePrivate(isolate);
v8::Local<v8::String> subtype = subtypeForInternalType(isolate, type);
for (size_t i = 0; i < array->Length(); ++i) {
v8::Local<v8::Value> entry;
if (!array->Get(context, i).ToLocal(&entry) || !entry->IsObject())
return false;
if (!entry.As<v8::Object>()
->SetPrivate(context, privateValue, subtype)
.FromMaybe(false))
return false;
}
return true;
}
v8::Local<v8::Value> v8InternalValueTypeFrom(v8::Local<v8::Context> context,
v8::Local<v8::Object> object) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Private> privateValue = internalSubtypePrivate(isolate);
if (!object->HasPrivate(context, privateValue).FromMaybe(false))
return v8::Null(isolate);
v8::Local<v8::Value> subtypeValue;
if (!object->GetPrivate(context, privateValue).ToLocal(&subtypeValue) ||
!subtypeValue->IsString())
return v8::Null(isolate);
return subtypeValue;
}
} // namespace v8_inspector

View File

@ -0,0 +1,23 @@
// 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_V8INTERNALVALUETYPE_H_
#define V8_INSPECTOR_V8INTERNALVALUETYPE_H_
#include <v8.h>
namespace v8_inspector {
enum class V8InternalValueType { kEntry, kLocation, kScope, kScopeList };
bool markAsInternal(v8::Local<v8::Context>, v8::Local<v8::Object>,
V8InternalValueType);
bool markArrayEntriesAsInternal(v8::Local<v8::Context>, v8::Local<v8::Array>,
V8InternalValueType);
v8::Local<v8::Value> v8InternalValueTypeFrom(v8::Local<v8::Context>,
v8::Local<v8::Object>);
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8INTERNALVALUETYPE_H_

View File

@ -0,0 +1,324 @@
// Copyright 2015 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/V8ProfilerAgentImpl.h"
#include <v8-profiler.h>
#include "src/inspector/Atomics.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/protocol/Protocol.h"
#include <vector>
#define ENSURE_V8_VERSION(major, minor) \
(V8_MAJOR_VERSION * 1000 + V8_MINOR_VERSION >= (major)*1000 + (minor))
namespace v8_inspector {
namespace ProfilerAgentState {
static const char samplingInterval[] = "samplingInterval";
static const char userInitiatedProfiling[] = "userInitiatedProfiling";
static const char profilerEnabled[] = "profilerEnabled";
}
namespace {
std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
unsigned lineCount = node->GetHitLineCount();
if (!lineCount) return nullptr;
auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create();
std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
if (node->GetLineTicks(&entries[0], lineCount)) {
for (unsigned i = 0; i < lineCount; i++) {
std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
protocol::Profiler::PositionTickInfo::create()
.setLine(entries[i].line)
.setTicks(entries[i].hit_count)
.build();
array->addItem(std::move(line));
}
}
return array;
}
std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
v8::Isolate* isolate, const v8::CpuProfileNode* node) {
v8::HandleScope handleScope(isolate);
auto callFrame =
protocol::Runtime::CallFrame::create()
.setFunctionName(toProtocolString(node->GetFunctionName()))
.setScriptId(String16::fromInteger(node->GetScriptId()))
.setUrl(toProtocolString(node->GetScriptResourceName()))
.setLineNumber(node->GetLineNumber() - 1)
.setColumnNumber(node->GetColumnNumber() - 1)
.build();
auto result = protocol::Profiler::ProfileNode::create()
.setCallFrame(std::move(callFrame))
.setHitCount(node->GetHitCount())
.setId(node->GetNodeId())
.build();
const int childrenCount = node->GetChildrenCount();
if (childrenCount) {
auto children = protocol::Array<int>::create();
for (int i = 0; i < childrenCount; i++)
children->addItem(node->GetChild(i)->GetNodeId());
result->setChildren(std::move(children));
}
const char* deoptReason = node->GetBailoutReason();
if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
result->setDeoptReason(deoptReason);
auto positionTicks = buildInspectorObjectForPositionTicks(node);
if (positionTicks) result->setPositionTicks(std::move(positionTicks));
return result;
}
std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
v8::CpuProfile* v8profile) {
auto array = protocol::Array<int>::create();
int count = v8profile->GetSamplesCount();
for (int i = 0; i < count; i++)
array->addItem(v8profile->GetSample(i)->GetNodeId());
return array;
}
std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
v8::CpuProfile* v8profile) {
auto array = protocol::Array<int>::create();
int count = v8profile->GetSamplesCount();
uint64_t lastTime = v8profile->GetStartTime();
for (int i = 0; i < count; i++) {
uint64_t ts = v8profile->GetSampleTimestamp(i);
array->addItem(static_cast<int>(ts - lastTime));
lastTime = ts;
}
return array;
}
void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node,
protocol::Array<protocol::Profiler::ProfileNode>* list) {
list->addItem(buildInspectorObjectFor(isolate, node));
const int childrenCount = node->GetChildrenCount();
for (int i = 0; i < childrenCount; i++)
flattenNodesTree(isolate, node->GetChild(i), list);
}
std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
v8::Isolate* isolate, v8::CpuProfile* v8profile) {
auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create();
flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get());
return protocol::Profiler::Profile::create()
.setNodes(std::move(nodes))
.setStartTime(static_cast<double>(v8profile->GetStartTime()))
.setEndTime(static_cast<double>(v8profile->GetEndTime()))
.setSamples(buildInspectorObjectForSamples(v8profile))
.setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
.build();
}
std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
V8InspectorImpl* inspector) {
std::unique_ptr<V8StackTraceImpl> callStack =
inspector->debugger()->captureStackTrace(false /* fullStack */);
auto location = protocol::Debugger::Location::create()
.setScriptId(toString16(callStack->topScriptId()))
.setLineNumber(callStack->topLineNumber())
.build();
location->setColumnNumber(callStack->topColumnNumber());
return location;
}
volatile int s_lastProfileId = 0;
} // namespace
class V8ProfilerAgentImpl::ProfileDescriptor {
public:
ProfileDescriptor(const String16& id, const String16& title)
: m_id(id), m_title(title) {}
String16 m_id;
String16 m_title;
};
V8ProfilerAgentImpl::V8ProfilerAgentImpl(
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
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) {}
V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
#if ENSURE_V8_VERSION(5, 4)
if (m_profiler) m_profiler->Dispose();
#endif
}
void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
if (!m_enabled) return;
String16 id = nextProfileId();
m_startedProfiles.push_back(ProfileDescriptor(id, title));
startProfiling(id);
m_frontend.consoleProfileStarted(
id, currentDebugLocation(m_session->inspector()), title);
}
void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
if (!m_enabled) return;
String16 id;
String16 resolvedTitle;
// Take last started profile if no title was passed.
if (title.isEmpty()) {
if (m_startedProfiles.empty()) return;
id = m_startedProfiles.back().m_id;
resolvedTitle = m_startedProfiles.back().m_title;
m_startedProfiles.pop_back();
} else {
for (size_t i = 0; i < m_startedProfiles.size(); i++) {
if (m_startedProfiles[i].m_title == title) {
resolvedTitle = title;
id = m_startedProfiles[i].m_id;
m_startedProfiles.erase(m_startedProfiles.begin() + i);
break;
}
}
if (id.isEmpty()) return;
}
std::unique_ptr<protocol::Profiler::Profile> profile =
stopProfiling(id, true);
if (!profile) return;
std::unique_ptr<protocol::Debugger::Location> location =
currentDebugLocation(m_session->inspector());
m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile),
resolvedTitle);
}
void V8ProfilerAgentImpl::enable(ErrorString*) {
if (m_enabled) return;
m_enabled = true;
#if ENSURE_V8_VERSION(5, 4)
DCHECK(!m_profiler);
m_profiler = v8::CpuProfiler::New(m_isolate);
#endif
m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
}
void V8ProfilerAgentImpl::disable(ErrorString* errorString) {
if (!m_enabled) return;
for (size_t i = m_startedProfiles.size(); i > 0; --i)
stopProfiling(m_startedProfiles[i - 1].m_id, false);
m_startedProfiles.clear();
stop(nullptr, nullptr);
#if ENSURE_V8_VERSION(5, 4)
m_profiler->Dispose();
m_profiler = nullptr;
#endif
m_enabled = false;
m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
}
void V8ProfilerAgentImpl::setSamplingInterval(ErrorString* error,
int interval) {
if (m_recordingCPUProfile) {
*error = "Cannot change sampling interval when profiling.";
return;
}
m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
profiler()->SetSamplingInterval(interval);
}
void V8ProfilerAgentImpl::restore() {
DCHECK(!m_enabled);
if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false))
return;
m_enabled = true;
#if ENSURE_V8_VERSION(5, 4)
DCHECK(!m_profiler);
m_profiler = v8::CpuProfiler::New(m_isolate);
#endif
int interval = 0;
m_state->getInteger(ProfilerAgentState::samplingInterval, &interval);
if (interval) profiler()->SetSamplingInterval(interval);
if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
false)) {
ErrorString error;
start(&error);
}
}
void V8ProfilerAgentImpl::start(ErrorString* error) {
if (m_recordingCPUProfile) return;
if (!m_enabled) {
*error = "Profiler is not enabled";
return;
}
m_recordingCPUProfile = true;
m_frontendInitiatedProfileId = nextProfileId();
startProfiling(m_frontendInitiatedProfileId);
m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
}
void V8ProfilerAgentImpl::stop(
ErrorString* errorString,
std::unique_ptr<protocol::Profiler::Profile>* profile) {
if (!m_recordingCPUProfile) {
if (errorString) *errorString = "No recording profiles found";
return;
}
m_recordingCPUProfile = false;
std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
stopProfiling(m_frontendInitiatedProfileId, !!profile);
if (profile) {
*profile = std::move(cpuProfile);
if (!profile->get() && errorString) *errorString = "Profile is not found";
}
m_frontendInitiatedProfileId = String16();
m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
}
String16 V8ProfilerAgentImpl::nextProfileId() {
return String16::fromInteger(atomicIncrement(&s_lastProfileId));
}
void V8ProfilerAgentImpl::startProfiling(const String16& title) {
v8::HandleScope handleScope(m_isolate);
profiler()->StartProfiling(toV8String(m_isolate, title), true);
}
std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
const String16& title, bool serialize) {
v8::HandleScope handleScope(m_isolate);
v8::CpuProfile* profile =
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();
return result;
}
bool V8ProfilerAgentImpl::isRecording() const {
return m_recordingCPUProfile || !m_startedProfiles.empty();
}
v8::CpuProfiler* V8ProfilerAgentImpl::profiler() {
#if ENSURE_V8_VERSION(5, 4)
return m_profiler;
#else
return m_isolate->GetCpuProfiler();
#endif
}
} // namespace v8_inspector

View File

@ -0,0 +1,70 @@
// Copyright 2015 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_V8PROFILERAGENTIMPL_H_
#define V8_INSPECTOR_V8PROFILERAGENTIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Profiler.h"
#include <vector>
namespace v8 {
class CpuProfiler;
class Isolate;
}
namespace v8_inspector {
class V8InspectorSessionImpl;
using protocol::ErrorString;
class V8ProfilerAgentImpl : public protocol::Profiler::Backend {
V8_INSPECTOR_DISALLOW_COPY(V8ProfilerAgentImpl);
public:
V8ProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
protocol::DictionaryValue* state);
~V8ProfilerAgentImpl() override;
bool enabled() const { return m_enabled; }
void restore();
void enable(ErrorString*) override;
void disable(ErrorString*) override;
void setSamplingInterval(ErrorString*, int) override;
void start(ErrorString*) override;
void stop(ErrorString*,
std::unique_ptr<protocol::Profiler::Profile>*) override;
void consoleProfile(const String16& title);
void consoleProfileEnd(const String16& title);
private:
String16 nextProfileId();
v8::CpuProfiler* profiler();
void startProfiling(const String16& title);
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;
protocol::DictionaryValue* m_state;
protocol::Profiler::Frontend m_frontend;
bool m_enabled;
bool m_recordingCPUProfile;
class ProfileDescriptor;
std::vector<ProfileDescriptor> m_startedProfiles;
String16 m_frontendInitiatedProfileId;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8PROFILERAGENTIMPL_H_

93
src/inspector/V8Regex.cpp Normal file
View File

@ -0,0 +1,93 @@
// 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/V8Regex.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/public/V8InspectorClient.h"
#include <limits.h>
namespace v8_inspector {
V8Regex::V8Regex(V8InspectorImpl* inspector, const String16& pattern,
bool caseSensitive, bool multiline)
: m_inspector(inspector) {
v8::Isolate* isolate = m_inspector->isolate();
v8::HandleScope handleScope(isolate);
v8::Local<v8::Context> context = m_inspector->regexContext();
v8::Context::Scope contextScope(context);
v8::TryCatch tryCatch(isolate);
unsigned flags = v8::RegExp::kNone;
if (!caseSensitive) flags |= v8::RegExp::kIgnoreCase;
if (multiline) flags |= v8::RegExp::kMultiline;
v8::Local<v8::RegExp> regex;
if (v8::RegExp::New(context, toV8String(isolate, pattern),
static_cast<v8::RegExp::Flags>(flags))
.ToLocal(&regex))
m_regex.Reset(isolate, regex);
else if (tryCatch.HasCaught())
m_errorMessage = toProtocolString(tryCatch.Message()->Get());
else
m_errorMessage = "Internal error";
}
int V8Regex::match(const String16& string, int startFrom,
int* matchLength) const {
if (matchLength) *matchLength = 0;
if (m_regex.IsEmpty() || string.isEmpty()) return -1;
// v8 strings are limited to int.
if (string.length() > INT_MAX) return -1;
v8::Isolate* isolate = m_inspector->isolate();
v8::HandleScope handleScope(isolate);
v8::Local<v8::Context> context = m_inspector->regexContext();
v8::MicrotasksScope microtasks(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::TryCatch tryCatch(isolate);
v8::Local<v8::RegExp> regex = m_regex.Get(isolate);
v8::Local<v8::Value> exec;
if (!regex->Get(context, toV8StringInternalized(isolate, "exec"))
.ToLocal(&exec))
return -1;
v8::Local<v8::Value> argv[] = {
toV8String(isolate, string.substring(startFrom))};
v8::Local<v8::Value> returnValue;
if (!exec.As<v8::Function>()
->Call(context, regex, V8_INSPECTOR_ARRAY_LENGTH(argv), argv)
.ToLocal(&returnValue))
return -1;
// RegExp#exec returns null if there's no match, otherwise it returns an
// Array of strings with the first being the whole match string and others
// being subgroups. The Array also has some random properties tacked on like
// "index" which is the offset of the match.
//
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec
DCHECK(!returnValue.IsEmpty());
if (!returnValue->IsArray()) return -1;
v8::Local<v8::Array> result = returnValue.As<v8::Array>();
v8::Local<v8::Value> matchOffset;
if (!result->Get(context, toV8StringInternalized(isolate, "index"))
.ToLocal(&matchOffset))
return -1;
if (matchLength) {
v8::Local<v8::Value> match;
if (!result->Get(context, 0).ToLocal(&match)) return -1;
*matchLength = match.As<v8::String>()->Length();
}
return matchOffset.As<v8::Int32>()->Value() + startFrom;
}
} // namespace v8_inspector

37
src/inspector/V8Regex.h Normal file
View File

@ -0,0 +1,37 @@
// 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_V8REGEX_H_
#define V8_INSPECTOR_V8REGEX_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/String16.h"
#include <v8.h>
namespace v8_inspector {
class V8InspectorImpl;
enum MultilineMode { MultilineDisabled, MultilineEnabled };
class V8Regex {
V8_INSPECTOR_DISALLOW_COPY(V8Regex);
public:
V8Regex(V8InspectorImpl*, const String16&, bool caseSensitive,
bool multiline = false);
int match(const String16&, int startFrom = 0, int* matchLength = 0) const;
bool isValid() const { return !m_regex.IsEmpty(); }
const String16& errorMessage() const { return m_errorMessage; }
private:
V8InspectorImpl* m_inspector;
v8::Global<v8::RegExp> m_regex;
String16 m_errorMessage;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8REGEX_H_

View File

@ -0,0 +1,726 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "src/inspector/V8RuntimeAgentImpl.h"
#include "src/inspector/InjectedScript.h"
#include "src/inspector/InspectedContext.h"
#include "src/inspector/RemoteObjectId.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Compat.h"
#include "src/inspector/V8ConsoleMessage.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/V8DebuggerAgentImpl.h"
#include "src/inspector/V8InspectorImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/V8StackTraceImpl.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/public/V8InspectorClient.h"
namespace v8_inspector {
namespace V8RuntimeAgentImplState {
static const char customObjectFormatterEnabled[] =
"customObjectFormatterEnabled";
static const char runtimeEnabled[] = "runtimeEnabled";
};
using protocol::Runtime::RemoteObject;
static bool hasInternalError(ErrorString* errorString, bool hasError) {
if (hasError) *errorString = "Internal error";
return hasError;
}
namespace {
template <typename Callback>
class ProtocolPromiseHandler {
public:
static void add(V8InspectorImpl* inspector, v8::Local<v8::Context> context,
v8::MaybeLocal<v8::Value> value,
const String16& notPromiseError, int contextGroupId,
int executionContextId, const String16& objectGroup,
bool returnByValue, bool generatePreview,
std::unique_ptr<Callback> callback) {
if (value.IsEmpty()) {
callback->sendFailure("Internal error");
return;
}
if (!value.ToLocalChecked()->IsPromise()) {
callback->sendFailure(notPromiseError);
return;
}
v8::Local<v8::Promise> promise =
v8::Local<v8::Promise>::Cast(value.ToLocalChecked());
Callback* rawCallback = callback.get();
ProtocolPromiseHandler<Callback>* handler = new ProtocolPromiseHandler(
inspector, contextGroupId, executionContextId, objectGroup,
returnByValue, generatePreview, std::move(callback));
v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate());
v8::Local<v8::Function> thenCallbackFunction =
V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, thenCallback, wrapper, 0)
.ToLocalChecked();
if (promise->Then(context, thenCallbackFunction).IsEmpty()) {
rawCallback->sendFailure("Internal error");
return;
}
v8::Local<v8::Function> catchCallbackFunction =
V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, catchCallback, wrapper, 0)
.ToLocalChecked();
if (promise->Catch(context, catchCallbackFunction).IsEmpty()) {
rawCallback->sendFailure("Internal error");
return;
}
}
private:
static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ProtocolPromiseHandler<Callback>* handler =
static_cast<ProtocolPromiseHandler<Callback>*>(
info.Data().As<v8::External>()->Value());
DCHECK(handler);
v8::Local<v8::Value> value =
info.Length() > 0
? info[0]
: v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate()));
handler->m_callback->sendSuccess(
handler->wrapObject(value),
Maybe<protocol::Runtime::ExceptionDetails>());
}
static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
ProtocolPromiseHandler<Callback>* handler =
static_cast<ProtocolPromiseHandler<Callback>*>(
info.Data().As<v8::External>()->Value());
DCHECK(handler);
v8::Local<v8::Value> value =
info.Length() > 0
? info[0]
: v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate()));
std::unique_ptr<V8StackTraceImpl> stack =
handler->m_inspector->debugger()->captureStackTrace(true);
std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
protocol::Runtime::ExceptionDetails::create()
.setExceptionId(handler->m_inspector->nextExceptionId())
.setText("Uncaught (in promise)")
.setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber()
: 0)
.setColumnNumber(
stack && !stack->isEmpty() ? stack->topColumnNumber() : 0)
.setException(handler->wrapObject(value))
.build();
if (stack)
exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl());
if (stack && !stack->isEmpty())
exceptionDetails->setScriptId(toString16(stack->topScriptId()));
handler->m_callback->sendSuccess(handler->wrapObject(value),
std::move(exceptionDetails));
}
ProtocolPromiseHandler(V8InspectorImpl* inspector, int contextGroupId,
int executionContextId, const String16& objectGroup,
bool returnByValue, bool generatePreview,
std::unique_ptr<Callback> callback)
: m_inspector(inspector),
m_contextGroupId(contextGroupId),
m_executionContextId(executionContextId),
m_objectGroup(objectGroup),
m_returnByValue(returnByValue),
m_generatePreview(generatePreview),
m_callback(std::move(callback)),
m_wrapper(inspector->isolate(),
v8::External::New(inspector->isolate(), this)) {
m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter);
}
static void cleanup(
const v8::WeakCallbackInfo<ProtocolPromiseHandler<Callback>>& data) {
if (!data.GetParameter()->m_wrapper.IsEmpty()) {
data.GetParameter()->m_wrapper.Reset();
data.SetSecondPassCallback(cleanup);
} else {
data.GetParameter()->m_callback->sendFailure("Promise was collected");
delete data.GetParameter();
}
}
std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject(
v8::Local<v8::Value> value) {
ErrorString errorString;
InjectedScript::ContextScope scope(&errorString, m_inspector,
m_contextGroupId, m_executionContextId);
if (!scope.initialize()) {
m_callback->sendFailure(errorString);
return nullptr;
}
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue =
scope.injectedScript()->wrapObject(&errorString, value, m_objectGroup,
m_returnByValue, m_generatePreview);
if (!wrappedValue) {
m_callback->sendFailure(errorString);
return nullptr;
}
return wrappedValue;
}
V8InspectorImpl* m_inspector;
int m_contextGroupId;
int m_executionContextId;
String16 m_objectGroup;
bool m_returnByValue;
bool m_generatePreview;
std::unique_ptr<Callback> m_callback;
v8::Global<v8::External> m_wrapper;
};
template <typename Callback>
bool wrapEvaluateResultAsync(InjectedScript* injectedScript,
v8::MaybeLocal<v8::Value> maybeResultValue,
const v8::TryCatch& tryCatch,
const String16& objectGroup, bool returnByValue,
bool generatePreview, Callback* callback) {
std::unique_ptr<RemoteObject> result;
Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
ErrorString errorString;
injectedScript->wrapEvaluateResult(
&errorString, maybeResultValue, tryCatch, objectGroup, returnByValue,
generatePreview, &result, &exceptionDetails);
if (errorString.isEmpty()) {
callback->sendSuccess(std::move(result), exceptionDetails);
return true;
}
callback->sendFailure(errorString);
return false;
}
int ensureContext(ErrorString* errorString, V8InspectorImpl* inspector,
int contextGroupId, const Maybe<int>& executionContextId) {
int contextId;
if (executionContextId.isJust()) {
contextId = executionContextId.fromJust();
} else {
v8::HandleScope handles(inspector->isolate());
v8::Local<v8::Context> defaultContext =
inspector->client()->ensureDefaultContextInGroup(contextGroupId);
if (defaultContext.IsEmpty()) {
*errorString = "Cannot find default execution context";
return 0;
}
contextId = V8Debugger::contextId(defaultContext);
}
return contextId;
}
} // namespace
V8RuntimeAgentImpl::V8RuntimeAgentImpl(
V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel,
protocol::DictionaryValue* state)
: m_session(session),
m_state(state),
m_frontend(FrontendChannel),
m_inspector(session->inspector()),
m_enabled(false) {}
V8RuntimeAgentImpl::~V8RuntimeAgentImpl() {}
void V8RuntimeAgentImpl::evaluate(
const String16& expression, const Maybe<String16>& objectGroup,
const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& silent,
const Maybe<int>& executionContextId, const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture,
const Maybe<bool>& awaitPromise,
std::unique_ptr<EvaluateCallback> callback) {
ErrorString errorString;
int contextId =
ensureContext(&errorString, m_inspector, m_session->contextGroupId(),
executionContextId);
if (!errorString.isEmpty()) {
callback->sendFailure(errorString);
return;
}
InjectedScript::ContextScope scope(&errorString, m_inspector,
m_session->contextGroupId(), contextId);
if (!scope.initialize()) {
callback->sendFailure(errorString);
return;
}
if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
if (userGesture.fromMaybe(false)) scope.pretendUserGesture();
if (includeCommandLineAPI.fromMaybe(false) &&
!scope.installCommandLineAPI()) {
callback->sendFailure(errorString);
return;
}
bool evalIsDisabled = !scope.context()->IsCodeGenerationFromStringsAllowed();
// Temporarily enable allow evals for inspector.
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())
maybeResultValue = m_inspector->runCompiledScript(scope.context(), script);
if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(false);
// Re-initialize after running client's code, as it could have destroyed
// context or session.
if (!scope.initialize()) {
callback->sendFailure(errorString);
return;
}
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
scope.tryCatch(), objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false),
generatePreview.fromMaybe(false), callback.get());
return;
}
ProtocolPromiseHandler<EvaluateCallback>::add(
m_inspector, scope.context(), maybeResultValue,
"Result of the evaluation is not a promise", m_session->contextGroupId(),
scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
std::move(callback));
}
void V8RuntimeAgentImpl::awaitPromise(
const String16& promiseObjectId, const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview,
std::unique_ptr<AwaitPromiseCallback> callback) {
ErrorString errorString;
InjectedScript::ObjectScope scope(
&errorString, m_inspector, m_session->contextGroupId(), promiseObjectId);
if (!scope.initialize()) {
callback->sendFailure(errorString);
return;
}
ProtocolPromiseHandler<AwaitPromiseCallback>::add(
m_inspector, scope.context(), scope.object(),
"Could not find promise with given id", m_session->contextGroupId(),
scope.injectedScript()->context()->contextId(), scope.objectGroupName(),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
std::move(callback));
}
void V8RuntimeAgentImpl::callFunctionOn(
const String16& objectId, const String16& expression,
const Maybe<protocol::Array<protocol::Runtime::CallArgument>>&
optionalArguments,
const Maybe<bool>& silent, const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture,
const Maybe<bool>& awaitPromise,
std::unique_ptr<CallFunctionOnCallback> callback) {
ErrorString errorString;
InjectedScript::ObjectScope scope(&errorString, m_inspector,
m_session->contextGroupId(), objectId);
if (!scope.initialize()) {
callback->sendFailure(errorString);
return;
}
std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr;
int argc = 0;
if (optionalArguments.isJust()) {
protocol::Array<protocol::Runtime::CallArgument>* arguments =
optionalArguments.fromJust();
argc = arguments->length();
argv.reset(new v8::Local<v8::Value>[argc]);
for (int i = 0; i < argc; ++i) {
v8::Local<v8::Value> argumentValue;
if (!scope.injectedScript()
->resolveCallArgument(&errorString, arguments->get(i))
.ToLocal(&argumentValue)) {
callback->sendFailure(errorString);
return;
}
argv[i] = argumentValue;
}
}
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 + ")"));
// Re-initialize after running client's code, as it could have destroyed
// context or session.
if (!scope.initialize()) {
callback->sendFailure(errorString);
return;
}
if (scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue,
scope.tryCatch(), scope.objectGroupName(), false,
false, callback.get());
return;
}
v8::Local<v8::Value> functionValue;
if (!maybeFunctionValue.ToLocal(&functionValue) ||
!functionValue->IsFunction()) {
callback->sendFailure("Given expression does not evaluate to a function");
return;
}
v8::MaybeLocal<v8::Value> maybeResultValue = m_inspector->callFunction(
functionValue.As<v8::Function>(), scope.context(), scope.object(), argc,
argv.get());
// Re-initialize after running client's code, as it could have destroyed
// context or session.
if (!scope.initialize()) {
callback->sendFailure(errorString);
return;
}
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
scope.tryCatch(), scope.objectGroupName(),
returnByValue.fromMaybe(false),
generatePreview.fromMaybe(false), callback.get());
return;
}
ProtocolPromiseHandler<CallFunctionOnCallback>::add(
m_inspector, scope.context(), maybeResultValue,
"Result of the function call is not a promise",
m_session->contextGroupId(),
scope.injectedScript()->context()->contextId(), scope.objectGroupName(),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
std::move(callback));
}
void V8RuntimeAgentImpl::getProperties(
ErrorString* errorString, const String16& objectId,
const Maybe<bool>& ownProperties, const Maybe<bool>& accessorPropertiesOnly,
const Maybe<bool>& generatePreview,
std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>*
result,
Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>*
internalProperties,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
using protocol::Runtime::InternalPropertyDescriptor;
InjectedScript::ObjectScope scope(errorString, m_inspector,
m_session->contextGroupId(), objectId);
if (!scope.initialize()) return;
scope.ignoreExceptionsAndMuteConsole();
if (!scope.object()->IsObject()) {
*errorString = "Value with given id is not an object";
return;
}
v8::Local<v8::Object> object = scope.object().As<v8::Object>();
scope.injectedScript()->getProperties(
errorString, object, scope.objectGroupName(),
ownProperties.fromMaybe(false), accessorPropertiesOnly.fromMaybe(false),
generatePreview.fromMaybe(false), result, exceptionDetails);
if (!errorString->isEmpty() || exceptionDetails->isJust() ||
accessorPropertiesOnly.fromMaybe(false))
return;
v8::Local<v8::Array> propertiesArray;
if (hasInternalError(errorString, !m_inspector->debugger()
->internalProperties(scope.context(),
scope.object())
.ToLocal(&propertiesArray)))
return;
std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>
propertiesProtocolArray =
protocol::Array<InternalPropertyDescriptor>::create();
for (uint32_t i = 0; i < propertiesArray->Length(); i += 2) {
v8::Local<v8::Value> name;
if (hasInternalError(
errorString,
!propertiesArray->Get(scope.context(), i).ToLocal(&name)) ||
!name->IsString())
return;
v8::Local<v8::Value> value;
if (hasInternalError(
errorString,
!propertiesArray->Get(scope.context(), i + 1).ToLocal(&value)))
return;
std::unique_ptr<RemoteObject> wrappedValue =
scope.injectedScript()->wrapObject(errorString, value,
scope.objectGroupName());
if (!wrappedValue) return;
propertiesProtocolArray->addItem(
InternalPropertyDescriptor::create()
.setName(toProtocolString(name.As<v8::String>()))
.setValue(std::move(wrappedValue))
.build());
}
if (!propertiesProtocolArray->length()) return;
*internalProperties = std::move(propertiesProtocolArray);
}
void V8RuntimeAgentImpl::releaseObject(ErrorString* errorString,
const String16& objectId) {
InjectedScript::ObjectScope scope(errorString, m_inspector,
m_session->contextGroupId(), objectId);
if (!scope.initialize()) return;
scope.injectedScript()->releaseObject(objectId);
}
void V8RuntimeAgentImpl::releaseObjectGroup(ErrorString*,
const String16& objectGroup) {
m_session->releaseObjectGroup(objectGroup);
}
void V8RuntimeAgentImpl::runIfWaitingForDebugger(ErrorString* errorString) {
m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId());
}
void V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(ErrorString*,
bool enabled) {
m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled,
enabled);
m_session->setCustomObjectFormatterEnabled(enabled);
}
void V8RuntimeAgentImpl::discardConsoleEntries(ErrorString*) {
V8ConsoleMessageStorage* storage =
m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
storage->clear();
}
void V8RuntimeAgentImpl::compileScript(
ErrorString* errorString, const String16& expression,
const String16& sourceURL, bool persistScript,
const Maybe<int>& executionContextId, Maybe<String16>* scriptId,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
if (!m_enabled) {
*errorString = "Runtime agent is not enabled";
return;
}
int contextId =
ensureContext(errorString, m_inspector, m_session->contextGroupId(),
executionContextId);
if (!errorString->isEmpty()) return;
InjectedScript::ContextScope scope(errorString, m_inspector,
m_session->contextGroupId(), contextId);
if (!scope.initialize()) return;
if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents();
v8::Local<v8::Script> script = m_inspector->compileScript(
scope.context(), toV8String(m_inspector->isolate(), expression),
sourceURL, false);
if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents();
if (script.IsEmpty()) {
if (scope.tryCatch().HasCaught())
*exceptionDetails = scope.injectedScript()->createExceptionDetails(
errorString, scope.tryCatch(), String16(), false);
else
*errorString = "Script compilation failed";
return;
}
if (!persistScript) return;
String16 scriptValueId =
String16::fromInteger(script->GetUnboundScript()->GetId());
std::unique_ptr<v8::Global<v8::Script>> global(
new v8::Global<v8::Script>(m_inspector->isolate(), script));
m_compiledScripts[scriptValueId] = std::move(global);
*scriptId = scriptValueId;
}
void V8RuntimeAgentImpl::runScript(
const String16& scriptId, const Maybe<int>& executionContextId,
const Maybe<String16>& objectGroup, const Maybe<bool>& silent,
const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview, const Maybe<bool>& awaitPromise,
std::unique_ptr<RunScriptCallback> callback) {
if (!m_enabled) {
callback->sendFailure("Runtime agent is not enabled");
return;
}
auto it = m_compiledScripts.find(scriptId);
if (it == m_compiledScripts.end()) {
callback->sendFailure("No script with given id");
return;
}
ErrorString errorString;
int contextId =
ensureContext(&errorString, m_inspector, m_session->contextGroupId(),
executionContextId);
if (!errorString.isEmpty()) {
callback->sendFailure(errorString);
return;
}
InjectedScript::ContextScope scope(&errorString, m_inspector,
m_session->contextGroupId(), contextId);
if (!scope.initialize()) {
callback->sendFailure(errorString);
return;
}
if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second);
m_compiledScripts.erase(it);
v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate());
if (script.IsEmpty()) {
callback->sendFailure("Script execution failed");
return;
}
if (includeCommandLineAPI.fromMaybe(false) && !scope.installCommandLineAPI())
return;
v8::MaybeLocal<v8::Value> maybeResultValue =
m_inspector->runCompiledScript(scope.context(), script);
// Re-initialize after running client's code, as it could have destroyed
// context or session.
if (!scope.initialize()) return;
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
scope.tryCatch(), objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false),
generatePreview.fromMaybe(false), callback.get());
return;
}
ProtocolPromiseHandler<RunScriptCallback>::add(
m_inspector, scope.context(), maybeResultValue.ToLocalChecked(),
"Result of the script execution is not a promise",
m_session->contextGroupId(),
scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
std::move(callback));
}
void V8RuntimeAgentImpl::restore() {
if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false))
return;
m_frontend.executionContextsCleared();
ErrorString error;
enable(&error);
if (m_state->booleanProperty(
V8RuntimeAgentImplState::customObjectFormatterEnabled, false))
m_session->setCustomObjectFormatterEnabled(true);
}
void V8RuntimeAgentImpl::enable(ErrorString* errorString) {
if (m_enabled) return;
m_inspector->client()->beginEnsureAllContextsInGroup(
m_session->contextGroupId());
m_enabled = true;
m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true);
m_inspector->enableStackCapturingIfNeeded();
m_session->reportAllContexts(this);
V8ConsoleMessageStorage* storage =
m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
for (const auto& message : storage->messages())
reportMessage(message.get(), false);
}
void V8RuntimeAgentImpl::disable(ErrorString* errorString) {
if (!m_enabled) return;
m_enabled = false;
m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false);
m_inspector->disableStackCapturingIfNeeded();
m_session->discardInjectedScripts();
reset();
m_inspector->client()->endEnsureAllContextsInGroup(
m_session->contextGroupId());
}
void V8RuntimeAgentImpl::reset() {
m_compiledScripts.clear();
if (m_enabled) {
if (const V8InspectorImpl::ContextByIdMap* contexts =
m_inspector->contextGroup(m_session->contextGroupId())) {
for (auto& idContext : *contexts) idContext.second->setReported(false);
}
m_frontend.executionContextsCleared();
}
}
void V8RuntimeAgentImpl::reportExecutionContextCreated(
InspectedContext* context) {
if (!m_enabled) return;
context->setReported(true);
std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description =
protocol::Runtime::ExecutionContextDescription::create()
.setId(context->contextId())
.setName(context->humanReadableName())
.setOrigin(context->origin())
.build();
if (!context->auxData().isEmpty())
description->setAuxData(protocol::DictionaryValue::cast(
protocol::parseJSON(context->auxData())));
m_frontend.executionContextCreated(std::move(description));
}
void V8RuntimeAgentImpl::reportExecutionContextDestroyed(
InspectedContext* context) {
if (m_enabled && context->isReported()) {
context->setReported(false);
m_frontend.executionContextDestroyed(context->contextId());
}
}
void V8RuntimeAgentImpl::inspect(
std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect,
std::unique_ptr<protocol::DictionaryValue> hints) {
if (m_enabled)
m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints));
}
void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) {
if (m_enabled) reportMessage(message, true);
}
void V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message,
bool generatePreview) {
message->reportToFrontend(&m_frontend, m_session, generatePreview);
m_frontend.flush();
}
} // namespace v8_inspector

View File

@ -0,0 +1,131 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef V8_INSPECTOR_V8RUNTIMEAGENTIMPL_H_
#define V8_INSPECTOR_V8RUNTIMEAGENTIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
#include <v8.h>
namespace v8_inspector {
class InjectedScript;
class InspectedContext;
class RemoteObjectIdBase;
class V8ConsoleMessage;
class V8InspectorImpl;
class V8InspectorSessionImpl;
using protocol::ErrorString;
using protocol::Maybe;
class V8RuntimeAgentImpl : public protocol::Runtime::Backend {
V8_INSPECTOR_DISALLOW_COPY(V8RuntimeAgentImpl);
public:
V8RuntimeAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
protocol::DictionaryValue* state);
~V8RuntimeAgentImpl() override;
void restore();
// Part of the protocol.
void enable(ErrorString*) override;
void disable(ErrorString*) override;
void evaluate(const String16& expression, const Maybe<String16>& objectGroup,
const Maybe<bool>& includeCommandLineAPI,
const Maybe<bool>& silent, const Maybe<int>& executionContextId,
const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview,
const Maybe<bool>& userGesture, const Maybe<bool>& awaitPromise,
std::unique_ptr<EvaluateCallback>) override;
void awaitPromise(const String16& promiseObjectId,
const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview,
std::unique_ptr<AwaitPromiseCallback>) override;
void callFunctionOn(
const String16& objectId, const String16& expression,
const Maybe<protocol::Array<protocol::Runtime::CallArgument>>&
optionalArguments,
const Maybe<bool>& silent, const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture,
const Maybe<bool>& awaitPromise,
std::unique_ptr<CallFunctionOnCallback>) override;
void releaseObject(ErrorString*, const String16& objectId) override;
void getProperties(
ErrorString*, const String16& objectId, const Maybe<bool>& ownProperties,
const Maybe<bool>& accessorPropertiesOnly,
const Maybe<bool>& generatePreview,
std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>*
result,
Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>*
internalProperties,
Maybe<protocol::Runtime::ExceptionDetails>*) override;
void releaseObjectGroup(ErrorString*, const String16& objectGroup) override;
void runIfWaitingForDebugger(ErrorString*) override;
void setCustomObjectFormatterEnabled(ErrorString*, bool) override;
void discardConsoleEntries(ErrorString*) override;
void compileScript(ErrorString*, const String16& expression,
const String16& sourceURL, bool persistScript,
const Maybe<int>& executionContextId, Maybe<String16>*,
Maybe<protocol::Runtime::ExceptionDetails>*) override;
void runScript(const String16&, const Maybe<int>& executionContextId,
const Maybe<String16>& objectGroup, const Maybe<bool>& silent,
const Maybe<bool>& includeCommandLineAPI,
const Maybe<bool>& returnByValue,
const Maybe<bool>& generatePreview,
const Maybe<bool>& awaitPromise,
std::unique_ptr<RunScriptCallback>) override;
void reset();
void reportExecutionContextCreated(InspectedContext*);
void reportExecutionContextDestroyed(InspectedContext*);
void inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect,
std::unique_ptr<protocol::DictionaryValue> hints);
void messageAdded(V8ConsoleMessage*);
bool enabled() const { return m_enabled; }
private:
void reportMessage(V8ConsoleMessage*, bool generatePreview);
V8InspectorSessionImpl* m_session;
protocol::DictionaryValue* m_state;
protocol::Runtime::Frontend m_frontend;
V8InspectorImpl* m_inspector;
bool m_enabled;
protocol::HashMap<String16, std::unique_ptr<v8::Global<v8::Script>>>
m_compiledScripts;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8RUNTIMEAGENTIMPL_H_

View File

@ -0,0 +1,29 @@
// 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/V8SchemaAgentImpl.h"
#include "src/inspector/V8InspectorSessionImpl.h"
#include "src/inspector/protocol/Protocol.h"
namespace v8_inspector {
V8SchemaAgentImpl::V8SchemaAgentImpl(V8InspectorSessionImpl* session,
protocol::FrontendChannel* frontendChannel,
protocol::DictionaryValue* state)
: m_session(session), m_frontend(frontendChannel) {}
V8SchemaAgentImpl::~V8SchemaAgentImpl() {}
void V8SchemaAgentImpl::getDomains(
ErrorString*,
std::unique_ptr<protocol::Array<protocol::Schema::Domain>>* result) {
std::vector<std::unique_ptr<protocol::Schema::Domain>> domains =
m_session->supportedDomainsImpl();
*result = protocol::Array<protocol::Schema::Domain>::create();
for (size_t i = 0; i < domains.size(); ++i)
(*result)->addItem(std::move(domains[i]));
}
} // namespace v8_inspector

View File

@ -0,0 +1,37 @@
// 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_V8SCHEMAAGENTIMPL_H_
#define V8_INSPECTOR_V8SCHEMAAGENTIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Schema.h"
namespace v8_inspector {
class V8InspectorSessionImpl;
using protocol::ErrorString;
class V8SchemaAgentImpl : public protocol::Schema::Backend {
V8_INSPECTOR_DISALLOW_COPY(V8SchemaAgentImpl);
public:
V8SchemaAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
protocol::DictionaryValue* state);
~V8SchemaAgentImpl() override;
void getDomains(
ErrorString*,
std::unique_ptr<protocol::Array<protocol::Schema::Domain>>*) override;
private:
V8InspectorSessionImpl* m_session;
protocol::Schema::Frontend m_frontend;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8SCHEMAAGENTIMPL_H_

View File

@ -0,0 +1,275 @@
// 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/V8StackTraceImpl.h"
#include "src/inspector/StringUtil.h"
#include "src/inspector/V8Debugger.h"
#include "src/inspector/protocol/Protocol.h"
#include <v8-debug.h>
#include <v8-profiler.h>
#include <v8-version.h>
namespace v8_inspector {
namespace {
static const v8::StackTrace::StackTraceOptions stackTraceOptions =
static_cast<v8::StackTrace::StackTraceOptions>(
v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset |
v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
v8::StackTrace::kFunctionName);
V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) {
String16 scriptId = String16::fromInteger(frame->GetScriptId());
String16 sourceName;
v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
if (!sourceNameValue.IsEmpty())
sourceName = toProtocolString(sourceNameValue);
String16 functionName;
v8::Local<v8::String> functionNameValue(frame->GetFunctionName());
if (!functionNameValue.IsEmpty())
functionName = toProtocolString(functionNameValue);
int sourceLineNumber = frame->GetLineNumber();
int sourceColumn = frame->GetColumn();
return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
sourceLineNumber, sourceColumn);
}
void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
std::vector<V8StackTraceImpl::Frame>& frames,
size_t maxStackSize, v8::Isolate* isolate) {
DCHECK(isolate->InContext());
int frameCount = stackTrace->GetFrameCount();
if (frameCount > static_cast<int>(maxStackSize)) frameCount = maxStackSize;
for (int i = 0; i < frameCount; i++) {
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
frames.push_back(toFrame(stackFrame));
}
}
} // namespace
V8StackTraceImpl::Frame::Frame()
: m_functionName("undefined"),
m_scriptId(""),
m_scriptName("undefined"),
m_lineNumber(0),
m_columnNumber(0) {}
V8StackTraceImpl::Frame::Frame(const String16& functionName,
const String16& scriptId,
const String16& scriptName, int lineNumber,
int column)
: m_functionName(functionName),
m_scriptId(scriptId),
m_scriptName(scriptName),
m_lineNumber(lineNumber),
m_columnNumber(column) {
DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo);
DCHECK(m_columnNumber != v8::Message::kNoColumnInfo);
}
V8StackTraceImpl::Frame::~Frame() {}
// buildInspectorObject() and SourceLocation's toTracedValue() should set the
// same fields.
// If either of them is modified, the other should be also modified.
std::unique_ptr<protocol::Runtime::CallFrame>
V8StackTraceImpl::Frame::buildInspectorObject() const {
return protocol::Runtime::CallFrame::create()
.setFunctionName(m_functionName)
.setScriptId(m_scriptId)
.setUrl(m_scriptName)
.setLineNumber(m_lineNumber - 1)
.setColumnNumber(m_columnNumber - 1)
.build();
}
V8StackTraceImpl::Frame V8StackTraceImpl::Frame::clone() const {
return Frame(m_functionName, m_scriptId, m_scriptName, m_lineNumber,
m_columnNumber);
}
// static
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
v8::Isolate* isolate, bool capture) {
isolate->SetCaptureStackTraceForUncaughtExceptions(
capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions);
}
// static
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
V8Debugger* debugger, int contextGroupId,
v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize,
const String16& description) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
std::vector<V8StackTraceImpl::Frame> frames;
if (!stackTrace.IsEmpty())
toFramesVector(stackTrace, frames, maxStackSize, isolate);
int maxAsyncCallChainDepth = 1;
V8StackTraceImpl* asyncCallChain = nullptr;
if (debugger && maxStackSize > 1) {
asyncCallChain = debugger->currentAsyncCallChain();
maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth();
}
// Do not accidentally append async call chain from another group. This should
// not
// happen if we have proper instrumentation, but let's double-check to be
// safe.
if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId &&
asyncCallChain->m_contextGroupId != contextGroupId) {
asyncCallChain = nullptr;
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())
asyncCallChain = asyncCallChain->m_parent.get();
if (stackTrace.IsEmpty() && !asyncCallChain) return nullptr;
std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(
contextGroupId, description, frames,
asyncCallChain ? asyncCallChain->cloneImpl() : nullptr));
// Crop to not exceed maxAsyncCallChainDepth.
V8StackTraceImpl* deepest = result.get();
while (deepest && maxAsyncCallChainDepth) {
deepest = deepest->m_parent.get();
maxAsyncCallChainDepth--;
}
if (deepest) deepest->m_parent.reset();
return result;
}
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
V8Debugger* debugger, int contextGroupId, size_t maxStackSize,
const String16& description) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
v8::Local<v8::StackTrace> stackTrace;
if (isolate->InContext()) {
#if V8_MAJOR_VERSION >= 5
isolate->GetCpuProfiler()->CollectSample();
#endif
stackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
stackTraceOptions);
}
return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace,
maxStackSize, description);
}
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() {
std::vector<Frame> framesCopy(m_frames);
return wrapUnique(
new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy,
m_parent ? m_parent->cloneImpl() : nullptr));
}
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(
new V8StackTraceImpl(m_contextGroupId, m_description, frames, nullptr));
}
V8StackTraceImpl::V8StackTraceImpl(int contextGroupId,
const String16& description,
std::vector<Frame>& frames,
std::unique_ptr<V8StackTraceImpl> parent)
: m_contextGroupId(contextGroupId),
m_description(description),
m_parent(std::move(parent)) {
m_frames.swap(frames);
}
V8StackTraceImpl::~V8StackTraceImpl() {}
StringView V8StackTraceImpl::topSourceURL() const {
DCHECK(m_frames.size());
return toStringView(m_frames[0].m_scriptName);
}
int V8StackTraceImpl::topLineNumber() const {
DCHECK(m_frames.size());
return m_frames[0].m_lineNumber;
}
int V8StackTraceImpl::topColumnNumber() const {
DCHECK(m_frames.size());
return m_frames[0].m_columnNumber;
}
StringView V8StackTraceImpl::topFunctionName() const {
DCHECK(m_frames.size());
return toStringView(m_frames[0].m_functionName);
}
StringView V8StackTraceImpl::topScriptId() const {
DCHECK(m_frames.size());
return toStringView(m_frames[0].m_scriptId);
}
std::unique_ptr<protocol::Runtime::StackTrace>
V8StackTraceImpl::buildInspectorObjectImpl() const {
std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames =
protocol::Array<protocol::Runtime::CallFrame>::create();
for (size_t i = 0; i < m_frames.size(); i++)
frames->addItem(m_frames.at(i).buildInspectorObject());
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
protocol::Runtime::StackTrace::create()
.setCallFrames(std::move(frames))
.build();
if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
if (m_parent) stackTrace->setParent(m_parent->buildInspectorObjectImpl());
return stackTrace;
}
std::unique_ptr<protocol::Runtime::StackTrace>
V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) const {
v8::HandleScope handleScope(v8::Isolate::GetCurrent());
// Next call collapses possible empty stack and ensures
// maxAsyncCallChainDepth.
std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create(
debugger, m_contextGroupId, v8::Local<v8::StackTrace>(),
V8StackTraceImpl::maxCallStackSizeToCapture);
if (!fullChain || !fullChain->m_parent) return nullptr;
return fullChain->m_parent->buildInspectorObjectImpl();
}
std::unique_ptr<protocol::Runtime::API::StackTrace>
V8StackTraceImpl::buildInspectorObject() const {
return buildInspectorObjectImpl();
}
std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
String16Builder stackTrace;
for (size_t i = 0; i < m_frames.size(); ++i) {
const Frame& frame = m_frames[i];
stackTrace.append("\n at " + (frame.functionName().length()
? frame.functionName()
: "(anonymous function)"));
stackTrace.append(" (");
stackTrace.append(frame.sourceURL());
stackTrace.append(':');
stackTrace.append(String16::fromInteger(frame.lineNumber()));
stackTrace.append(':');
stackTrace.append(String16::fromInteger(frame.columnNumber()));
stackTrace.append(')');
}
String16 string = stackTrace.toString();
return StringBufferImpl::adopt(string);
}
} // namespace v8_inspector

View File

@ -0,0 +1,98 @@
// 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_V8STACKTRACEIMPL_H_
#define V8_INSPECTOR_V8STACKTRACEIMPL_H_
#include "src/inspector/Allocator.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
#include "src/inspector/public/V8StackTrace.h"
#include <vector>
namespace v8_inspector {
class TracedValue;
class V8Debugger;
// Note: async stack trace may have empty top stack with non-empty tail to
// indicate
// that current native-only state had some async story.
// On the other hand, any non-top async stack is guaranteed to be non-empty.
class V8StackTraceImpl final : public V8StackTrace {
V8_INSPECTOR_DISALLOW_COPY(V8StackTraceImpl);
public:
static const size_t maxCallStackSizeToCapture = 200;
class Frame {
public:
Frame();
Frame(const String16& functionName, const String16& scriptId,
const String16& scriptName, int lineNumber, int column = 0);
~Frame();
const String16& functionName() const { return m_functionName; }
const String16& scriptId() const { return m_scriptId; }
const String16& sourceURL() const { return m_scriptName; }
int lineNumber() const { return m_lineNumber; }
int columnNumber() const { return m_columnNumber; }
Frame clone() const;
private:
friend class V8StackTraceImpl;
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const;
void toTracedValue(TracedValue*) const;
String16 m_functionName;
String16 m_scriptId;
String16 m_scriptName;
int m_lineNumber;
int m_columnNumber;
};
static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*,
bool capture);
static std::unique_ptr<V8StackTraceImpl> create(
V8Debugger*, int contextGroupId, v8::Local<v8::StackTrace>,
size_t maxStackSize, const String16& description = String16());
static std::unique_ptr<V8StackTraceImpl> capture(
V8Debugger*, int contextGroupId, size_t maxStackSize,
const String16& description = String16());
// This method drops the async chain. Use cloneImpl() instead.
std::unique_ptr<V8StackTrace> clone() override;
std::unique_ptr<V8StackTraceImpl> cloneImpl();
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectForTail(
V8Debugger*) const;
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
const;
~V8StackTraceImpl() override;
// V8StackTrace implementation.
bool isEmpty() const override { return !m_frames.size(); };
StringView topSourceURL() const override;
int topLineNumber() const override;
int topColumnNumber() const override;
StringView topScriptId() const override;
StringView topFunctionName() const override;
std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject()
const override;
std::unique_ptr<StringBuffer> toString() const override;
private:
V8StackTraceImpl(int contextGroupId, const String16& description,
std::vector<Frame>& frames,
std::unique_ptr<V8StackTraceImpl> parent);
int m_contextGroupId;
String16 m_description;
std::vector<Frame> m_frames;
std::unique_ptr<V8StackTraceImpl> m_parent;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8STACKTRACEIMPL_H_

View File

@ -0,0 +1,110 @@
// 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/V8ValueCopier.h"
namespace v8_inspector {
namespace {
static int kMaxDepth = 20;
static int kMaxCalls = 1000;
class V8ValueCopier {
public:
v8::MaybeLocal<v8::Value> copy(v8::Local<v8::Value> value, int depth) {
if (++m_calls > kMaxCalls || depth > kMaxDepth)
return v8::MaybeLocal<v8::Value>();
if (value.IsEmpty()) return v8::MaybeLocal<v8::Value>();
if (value->IsNull() || value->IsUndefined() || value->IsBoolean() ||
value->IsString() || value->IsNumber())
return value;
if (!value->IsObject()) return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Object> object = value.As<v8::Object>();
if (object->CreationContext() != m_from) return value;
if (object->IsArray()) {
v8::Local<v8::Array> array = object.As<v8::Array>();
v8::Local<v8::Array> result = v8::Array::New(m_isolate, array->Length());
if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false))
return v8::MaybeLocal<v8::Value>();
for (size_t i = 0; i < array->Length(); ++i) {
v8::Local<v8::Value> item;
if (!array->Get(m_from, i).ToLocal(&item))
return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Value> copied;
if (!copy(item, depth + 1).ToLocal(&copied))
return v8::MaybeLocal<v8::Value>();
if (!createDataProperty(m_to, result, i, copied).FromMaybe(false))
return v8::MaybeLocal<v8::Value>();
}
return result;
}
v8::Local<v8::Object> result = v8::Object::New(m_isolate);
if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false))
return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Array> properties;
if (!object->GetOwnPropertyNames(m_from).ToLocal(&properties))
return v8::MaybeLocal<v8::Value>();
for (size_t i = 0; i < properties->Length(); ++i) {
v8::Local<v8::Value> name;
if (!properties->Get(m_from, i).ToLocal(&name) || !name->IsString())
return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Value> property;
if (!object->Get(m_from, name).ToLocal(&property))
return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Value> copied;
if (!copy(property, depth + 1).ToLocal(&copied))
return v8::MaybeLocal<v8::Value>();
if (!createDataProperty(m_to, result, v8::Local<v8::String>::Cast(name),
copied)
.FromMaybe(false))
return v8::MaybeLocal<v8::Value>();
}
return result;
}
v8::Isolate* m_isolate;
v8::Local<v8::Context> m_from;
v8::Local<v8::Context> m_to;
int m_calls;
};
} // namespace
v8::MaybeLocal<v8::Value> copyValueFromDebuggerContext(
v8::Isolate* isolate, v8::Local<v8::Context> debuggerContext,
v8::Local<v8::Context> toContext, v8::Local<v8::Value> value) {
V8ValueCopier copier;
copier.m_isolate = isolate;
copier.m_from = debuggerContext;
copier.m_to = toContext;
copier.m_calls = 0;
return copier.copy(value, 0);
}
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
v8::Local<v8::Name> key,
v8::Local<v8::Value> value) {
v8::TryCatch tryCatch(context->GetIsolate());
v8::Isolate::DisallowJavascriptExecutionScope throwJs(
context->GetIsolate(),
v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
return object->CreateDataProperty(context, key, value);
}
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context,
v8::Local<v8::Array> array, int index,
v8::Local<v8::Value> value) {
v8::TryCatch tryCatch(context->GetIsolate());
v8::Isolate::DisallowJavascriptExecutionScope throwJs(
context->GetIsolate(),
v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
return array->CreateDataProperty(context, index, value);
}
} // namespace v8_inspector

View File

@ -0,0 +1,24 @@
// 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_V8VALUECOPIER_H_
#define V8_INSPECTOR_V8VALUECOPIER_H_
#include <v8.h>
namespace v8_inspector {
v8::MaybeLocal<v8::Value> copyValueFromDebuggerContext(
v8::Isolate*, v8::Local<v8::Context> debuggerContext,
v8::Local<v8::Context> toContext, v8::Local<v8::Value>);
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context>,
v8::Local<v8::Object>,
v8::Local<v8::Name> key,
v8::Local<v8::Value>);
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context>, v8::Local<v8::Array>,
int index, v8::Local<v8::Value>);
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8VALUECOPIER_H_

295
src/inspector/build/rjsmin.py Executable file
View File

@ -0,0 +1,295 @@
#!/usr/bin/env python
#
# Copyright 2011 - 2013
# Andr\xe9 Malo or his licensors, as applicable
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""
=====================
Javascript Minifier
=====================
rJSmin is a javascript minifier written in python.
The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\.
The module is a re-implementation aiming for speed, so it can be used at
runtime (rather than during a preprocessing step). Usually it produces the
same results as the original ``jsmin.c``. It differs in the following ways:
- there is no error detection: unterminated string, regex and comment
literals are treated as regular javascript code and minified as such.
- Control characters inside string and regex literals are left untouched; they
are not converted to spaces (nor to \n)
- Newline characters are not allowed inside string and regex literals, except
for line continuations in string literals (ECMA-5).
- "return /regex/" is recognized correctly.
- "+ +" and "- -" sequences are not collapsed to '++' or '--'
- Newlines before ! operators are removed more sensibly
- rJSmin does not handle streams, but only complete strings. (However, the
module provides a "streamy" interface).
Since most parts of the logic are handled by the regex engine it's way
faster than the original python port of ``jsmin.c`` by Baruch Even. The speed
factor varies between about 6 and 55 depending on input and python version
(it gets faster the more compressed the input already is). Compared to the
speed-refactored python port by Dave St.Germain the performance gain is less
dramatic but still between 1.2 and 7. See the docs/BENCHMARKS file for
details.
rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more.
Both python 2 and python 3 are supported.
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
"""
__author__ = "Andr\xe9 Malo"
__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
__docformat__ = "restructuredtext en"
__license__ = "Apache License, Version 2.0"
__version__ = '1.0.7'
__all__ = ['jsmin']
import re as _re
def _make_jsmin(python_only=False):
"""
Generate JS minifier based on `jsmin.c by Douglas Crockford`_
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
:Parameters:
`python_only` : ``bool``
Use only the python variant. If true, the c extension is not even
tried to be loaded.
:Return: Minifier
:Rtype: ``callable``
"""
# pylint: disable = R0912, R0914, W0612
if not python_only:
try:
import _rjsmin
except ImportError:
pass
else:
return _rjsmin.jsmin
try:
xrange
except NameError:
xrange = range # pylint: disable = W0622
space_chars = r'[\000-\011\013\014\016-\040]'
line_comment = r'(?://[^\r\n]*)'
space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
string1 = \
r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)'
string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")'
strings = r'(?:%s|%s)' % (string1, string2)
charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])'
nospecial = r'[^/\\\[\r\n]'
regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % (
nospecial, charclass, nospecial)
space = r'(?:%s|%s)' % (space_chars, space_comment)
newline = r'(?:%s?[\r\n])' % line_comment
def fix_charclass(result):
""" Fixup string of chars to fit into a regex char class """
pos = result.find('-')
if pos >= 0:
result = r'%s%s-' % (result[:pos], result[pos + 1:])
def sequentize(string):
"""
Notate consecutive characters as sequence
(1-4 instead of 1234)
"""
first, last, result = None, None, []
for char in map(ord, string):
if last is None:
first = last = char
elif last + 1 == char:
last = char
else:
result.append((first, last))
first = last = char
if last is not None:
result.append((first, last))
return ''.join(['%s%s%s' % (
chr(first),
last > first + 1 and '-' or '',
last != first and chr(last) or '') for first, last in result])
return _re.sub(r'([\000-\040\047])', # for better portability
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
.replace('\\', '\\\\')
.replace('[', '\\[')
.replace(']', '\\]')))
def id_literal_(what):
""" Make id_literal like char class """
match = _re.compile(what).match
result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))])
return '[^%s]' % fix_charclass(result)
def not_id_literal_(keep):
""" Make negated id_literal like char class """
match = _re.compile(id_literal_(keep)).match
result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))])
return r'[%s]' % fix_charclass(result)
not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]')
preregex1 = r'[(,=:\[!&|?{};\r\n]'
preregex2 = r'%(not_id_literal)sreturn' % locals()
id_literal = id_literal_(r'[a-zA-Z0-9_$]')
id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]')
id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]')
dull = r'[^\047"/\000-\040]'
space_sub = _re.compile((
r'(%(dull)s+)'
r'|(%(strings)s%(dull)s*)'
r'|(?<=%(preregex1)s)'
r'%(space)s*(?:%(newline)s%(space)s*)*'
r'(%(regex)s%(dull)s*)'
r'|(?<=%(preregex2)s)'
r'%(space)s*(?:%(newline)s%(space)s)*'
r'(%(regex)s%(dull)s*)'
r'|(?<=%(id_literal_close)s)'
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
r'(?=%(id_literal_open)s)'
r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
r'|(?<=\+)(%(space)s)+(?=\+)'
r'|(?<=-)(%(space)s)+(?=-)'
r'|%(space)s+'
r'|(?:%(newline)s%(space)s*)+') % locals()).sub
#print space_sub.__self__.pattern
def space_subber(match):
""" Substitution callback """
# pylint: disable = C0321, R0911
groups = match.groups()
if groups[0]:
return groups[0]
elif groups[1]:
return groups[1]
elif groups[2]:
return groups[2]
elif groups[3]:
return groups[3]
elif groups[4]:
return '\n'
elif groups[5] or groups[6] or groups[7]:
return ' '
else:
return ''
def jsmin(script): # pylint: disable = W0621
r"""
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
Instead of parsing the stream char by char, it uses a regular
expression approach which minifies the whole script with one big
substitution regex.
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
:Parameters:
`script` : ``str``
Script to minify
:Return: Minified script
:Rtype: ``str``
"""
return space_sub(space_subber, '\n%s\n' % script).strip()
return jsmin
jsmin = _make_jsmin()
def jsmin_for_posers(script):
r"""
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
Instead of parsing the stream char by char, it uses a regular
expression approach which minifies the whole script with one big
substitution regex.
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
:Warning: This function is the digest of a _make_jsmin() call. It just
utilizes the resulting regex. It's just for fun here and may
vanish any time. Use the `jsmin` function instead.
:Parameters:
`script` : ``str``
Script to minify
:Return: Minified script
:Rtype: ``str``
"""
def subber(match):
""" Substitution callback """
groups = match.groups()
return (
groups[0] or
groups[1] or
groups[2] or
groups[3] or
(groups[4] and '\n') or
(groups[5] and ' ') or
(groups[6] and ' ') or
(groups[7] and ' ') or
'')
return _re.sub(
r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?'
r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|'
r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?{};\r\n])(?'
r':[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*'
r'(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*'
r'[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:('
r'?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\['
r'\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[\000-#%-,./:-@\[-^`{-~-]return'
r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/'
r'))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:'
r'/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?'
r':(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/'
r'\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\^`{|'
r'~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)'
r'*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]'
r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./'
r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\01'
r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./:'
r'-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*'
r'\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013\014\016-'
r'\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\000-\011\013'
r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:(?:(?://[^'
r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^'
r'/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script).strip()
if __name__ == '__main__':
import sys as _sys
_sys.stdout.write(jsmin(_sys.stdin.read()))

View File

@ -0,0 +1,28 @@
# 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.
"""Represent a file as a C++ constant string.
Usage:
python xxd.py VAR SOURCE DEST
"""
import sys
import rjsmin
def main():
variable_name, input_filename, output_filename = sys.argv[1:]
with open(input_filename) as input_file:
input_text = input_file.read()
input_text = rjsmin.jsmin(input_text)
hex_values = ['0x{0:02x}'.format(ord(char)) for char in input_text]
const_declaration = 'const char %s[] = {\n%s\n};\n' % (
variable_name, ', '.join(hex_values))
with open(output_filename, 'w') as output_file:
output_file.write(const_declaration)
if __name__ == '__main__':
sys.exit(main())

View File

@ -0,0 +1,547 @@
// 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.
/** @typedef {{
type: string,
object: !Object,
name: (string|undefined),
startLocation: (!RawLocation|undefined),
endLocation: (!RawLocation|undefined)
}} */
var Scope;
/** @typedef {{
scriptId: string,
lineNumber: number,
columnNumber: number
}} */
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,
scopeChain: !Array<!Scope>,
functionLocation: (RawLocation|undefined),
returnValue: (*|undefined)
}} */
var JavaScriptCallFrameDetails;
/** @typedef {{
sourceID: function():(number|undefined),
line: function():number,
column: function():number,
thisObject: !Object,
evaluate: function(string):*,
restart: function():undefined,
setVariableValue: function(number, string, *):undefined,
isAtReturn: boolean,
details: function():!JavaScriptCallFrameDetails
}} */
var JavaScriptCallFrame;
/** @interface */
function DebugClass()
{
/** @type {!LiveEditClass} */
this.LiveEdit;
}
DebugClass.prototype.setBreakOnException = function() {}
DebugClass.prototype.clearBreakOnException = function() {}
DebugClass.prototype.setBreakOnUncaughtException = function() {}
DebugClass.prototype.clearBreakOnUncaughtException = function() {}
DebugClass.prototype.clearStepping = function() {}
DebugClass.prototype.clearAllBreakPoints = function() {}
/** @return {!Array<!Script>} */
DebugClass.prototype.scripts = function() {}
/**
* @param {number} scriptId
* @param {number=} line
* @param {number=} column
* @param {string=} condition
* @param {string=} groupId
* @param {Debug.BreakPositionAlignment=} positionAlignment
*/
DebugClass.prototype.setScriptBreakPointById = function(scriptId, line, column, condition, groupId, positionAlignment) {}
/**
* @param {number} breakId
* @return {!Array<!SourceLocation>}
*/
DebugClass.prototype.findBreakPointActualLocations = function(breakId) {}
/**
* @param {number} breakId
* @param {boolean} remove
* @return {!BreakPoint|undefined}
*/
DebugClass.prototype.findBreakPoint = function(breakId, remove) {}
/** @return {!DebuggerFlags} */
DebugClass.prototype.debuggerFlags = function() {}
/** @type {!DebugClass} */
var Debug;
/** @enum */
Debug.BreakPositionAlignment = {
Statement: 0,
BreakPosition: 1
};
/** @enum */
Debug.StepAction = { StepOut: 0,
StepNext: 1,
StepIn: 2,
StepFrame: 3 };
/** @enum */
Debug.ScriptCompilationType = { Host: 0,
Eval: 1,
JSON: 2 };
/** @interface */
function DebuggerFlag() {}
/** @param {boolean} value */
DebuggerFlag.prototype.setValue = function(value) {}
/** @interface */
function DebuggerFlags()
{
/** @type {!DebuggerFlag} */
this.breakPointsActive;
}
/** @interface */
function LiveEditClass() {}
/**
* @param {!Script} script
* @param {string} newSource
* @param {boolean} previewOnly
* @return {!{stack_modified: (boolean|undefined)}}
*/
LiveEditClass.prototype.SetScriptSource = function(script, newSource, previewOnly, change_log) {}
/** @interface */
function LiveEditErrorDetails()
{
/** @type {string} */
this.syntaxErrorMessage;
/** @type {!{start: !{line: number, column: number}}} */
this.position;
}
/** @interface */
function BreakpointInfo()
{
/** @type {number} */
this.breakpointId;
/** @type {number} */
this.sourceID;
/** @type {number|undefined} */
this.lineNumber;
/** @type {number|undefined} */
this.columnNumber;
/** @type {string|undefined} */
this.condition;
/** @type {boolean|undefined} */
this.interstatementLocation;
}
/** @interface */
function BreakPoint() {}
/** @return {!BreakPoint|undefined} */
BreakPoint.prototype.script_break_point = function() {}
/** @return {number} */
BreakPoint.prototype.number = function() {}
/** @interface */
function CompileEvent() {}
/** @return {!ScriptMirror} */
CompileEvent.prototype.script = function() {}
/** @interface */
function BreakEvent() {}
/** @return {!Array<!BreakPoint>|undefined} */
BreakEvent.prototype.breakPointsHit = function() {}
/** @interface */
function ExecutionState() {}
/** @param {!Debug.StepAction} action */
ExecutionState.prototype.prepareStep = function(action) {}
/**
* @param {string} source
* @param {boolean} disableBreak
* @param {*=} additionalContext
*/
ExecutionState.prototype.evaluateGlobal = function(source, disableBreak, additionalContext) {}
/** @return {number} */
ExecutionState.prototype.frameCount = function() {}
/**
* @param {number} index
* @return {!FrameMirror}
*/
ExecutionState.prototype.frame = function(index) {}
/** @param {number} index */
ExecutionState.prototype.setSelectedFrame = function(index) {}
/** @return {number} */
ExecutionState.prototype.selectedFrame = function() {}
/** @enum */
var ScopeType = { Global: 0,
Local: 1,
With: 2,
Closure: 3,
Catch: 4,
Block: 5,
Script: 6 };
/** @interface */
function SourceLocation()
{
/** @type {number} */
this.script;
/** @type {number} */
this.position;
/** @type {number} */
this.line;
/** @type {number} */
this.column;
/** @type {number} */
this.start;
/** @type {number} */
this.end;
}
/** @interface */
function Script()
{
/** @type {number} */
this.id;
/** @type {string|undefined} */
this.context_data;
/** @type {string|undefined} */
this.source_url;
/** @type {string|undefined} */
this.source_mapping_url;
/** @type {boolean} */
this.is_debugger_script;
/** @type {string} */
this.source;
/** @type {!Array<number>} */
this.line_ends;
/** @type {number} */
this.line_offset;
/** @type {number} */
this.column_offset;
}
/** @return {string} */
Script.prototype.nameOrSourceURL = function() {}
/** @return {!Debug.ScriptCompilationType} */
Script.prototype.compilationType = function() {}
/** @interface */
function ScopeDetails() {}
/** @return {!Object} */
ScopeDetails.prototype.object = function() {}
/** @return {string|undefined} */
ScopeDetails.prototype.name = function() {}
/** @interface */
function FrameDetails() {}
/** @return {!Object} */
FrameDetails.prototype.receiver = function() {}
/** @return {function()} */
FrameDetails.prototype.func = function() {}
/** @return {boolean} */
FrameDetails.prototype.isAtReturn = function() {}
/** @return {number} */
FrameDetails.prototype.sourcePosition = function() {}
/** @return {*} */
FrameDetails.prototype.returnValue = function() {}
/** @return {number} */
FrameDetails.prototype.scopeCount = function() {}
/** @param {boolean} value */
function ToggleMirrorCache(value) {}
/**
* @param {*} value
* @param {boolean=} transient
* @return {!Mirror}
*/
function MakeMirror(value, transient) {}
/** @interface */
function Mirror() {}
/** @return {boolean} */
Mirror.prototype.isFunction = function() {}
/** @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}
*/
function ObjectMirror() {}
/** @return {!Array<!PropertyMirror>} */
ObjectMirror.prototype.properties = function() {}
/**
* @interface
* @extends {ObjectMirror}
*/
function FunctionMirror () {}
/** @return {number} */
FunctionMirror.prototype.scopeCount = function() {}
/**
* @param {number} index
* @return {!ScopeMirror|undefined}
*/
FunctionMirror.prototype.scope = function(index) {}
/** @return {boolean} */
FunctionMirror.prototype.resolved = function() {}
/** @return {function()} */
FunctionMirror.prototype.value = function() {}
/** @return {string} */
FunctionMirror.prototype.debugName = function() {}
/** @return {!ScriptMirror|undefined} */
FunctionMirror.prototype.script = function() {}
/** @return {!SourceLocation|undefined} */
FunctionMirror.prototype.sourceLocation = function() {}
/** @return {!ContextMirror|undefined} */
FunctionMirror.prototype.context = function() {}
/**
* @constructor
* @param {*} value
*/
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 {!SourceLocation|undefined} */
GeneratorMirror.prototype.sourceLocation = function() {}
/** @return {!FunctionMirror} */
GeneratorMirror.prototype.func = function() {}
/**
* @interface
* @extends {Mirror}
*/
function PropertyMirror()
{
/** @type {*} */
this.value_;
}
/** @return {!Mirror} */
PropertyMirror.prototype.value = function() {}
/** @return {string} */
PropertyMirror.prototype.name = function() {}
/**
* @interface
* @extends {Mirror}
*/
function FrameMirror() {}
/**
* @param {boolean=} ignoreNestedScopes
* @return {!Array<!ScopeMirror>}
*/
FrameMirror.prototype.allScopes = function(ignoreNestedScopes) {}
/** @return {!FrameDetails} */
FrameMirror.prototype.details = function() {}
/**
* @param {string} source
* @param {boolean} disableBreak
*/
FrameMirror.prototype.evaluate = function(source, disableBreak) {}
FrameMirror.prototype.restart = function() {}
/** @param {number} index */
FrameMirror.prototype.scope = function(index) {}
/**
* @interface
* @extends {Mirror}
*/
function ScriptMirror() {}
/** @return {!Script} */
ScriptMirror.prototype.value = function() {}
/** @return {number} */
ScriptMirror.prototype.id = function() {}
/**
* @param {number} position
* @param {boolean=} includeResourceOffset
*/
ScriptMirror.prototype.locationFromPosition = function(position, includeResourceOffset) {}
/**
* @interface
* @extends {Mirror}
*/
function ScopeMirror() {}
/** @return {!ScopeDetails} */
ScopeMirror.prototype.details = function() {}
/**
* @param {string} name
* @param {*} newValue
*/
ScopeMirror.prototype.setVariableValue = function(name, newValue) {}
/**
* @interface
* @extends {Mirror}
*/
function ContextMirror() {}
/** @return {string|undefined} */
ContextMirror.prototype.data = function() {}

View File

@ -0,0 +1,66 @@
// Copyright 2015 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.
/** @interface */
function InjectedScriptHostClass()
{
}
/**
* @param {*} obj
* @return {string}
*/
InjectedScriptHostClass.prototype.internalConstructorName = function(obj) {}
/**
* @param {*} obj
* @param {function()|undefined} func
* @return {boolean}
*/
InjectedScriptHostClass.prototype.formatAccessorsAsProperties = function(obj, func) {}
/**
* @param {*} obj
* @return {string}
*/
InjectedScriptHostClass.prototype.subtype = function(obj) {}
/**
* @param {*} obj
* @return {boolean}
*/
InjectedScriptHostClass.prototype.isTypedArray = function(obj) {}
/**
* @param {*} obj
* @return {!Array.<*>}
*/
InjectedScriptHostClass.prototype.getInternalProperties = function(obj) {}
/**
* @param {!Object} object
* @param {string} propertyName
* @return {boolean}
*/
InjectedScriptHostClass.prototype.objectHasOwnProperty = function(object, propertyName) {}
/**
* @param {*} value
* @param {string} groupName
* @return {number}
*/
InjectedScriptHostClass.prototype.bind = function(value, groupName) {}
/**
* @param {!Object} object
* @return {!Object}
*/
InjectedScriptHostClass.prototype.proxyTargetValue = function(object) {}
/** @type {!InjectedScriptHostClass} */
var InjectedScriptHost;
/** @type {!Window} */
var inspectedGlobalObject;
/** @type {number} */
var injectedScriptId;

View File

@ -0,0 +1,29 @@
{
"protocol": {
"path": "js_protocol.json",
"package": "src/inspector/protocol",
"output": "v8_inspector/protocol",
"namespace": ["v8_inspector", "protocol"]
},
"exported": {
"package": "src/inspector/public/protocol",
"output": "v8_inspector/public/protocol",
"string_header": "src/inspector/public/StringBuffer.h",
"string_in": "StringView",
"string_out": "std::unique_ptr<StringBuffer>",
"to_string_out": "StringBufferImpl::adopt(%s)"
},
"lib": {
"package": "src/inspector/protocol",
"output": "v8_inspector/protocol",
"string_header": "src/inspector/StringUtil.h",
"platform_header": "src/inspector/ProtocolPlatform.h"
},
"class_export": {
"macro": "PLATFORM_EXPORT",
"header": "platform/PlatformExport.h"
}
}

View File

@ -0,0 +1,997 @@
{
"version": { "major": "1", "minor": "2" },
"domains": [
{
"domain": "Schema",
"description": "Provides information about the protocol schema.",
"types": [
{
"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." }
]
}
],
"commands": [
{
"name": "getDomains",
"description": "Returns supported domains.",
"handlers": ["browser", "renderer"],
"returns": [
{ "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." }
]
}
]
},
{
"domain": "Runtime",
"description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.",
"types": [
{
"id": "ScriptId",
"type": "string",
"description": "Unique script identifier."
},
{
"id": "RemoteObjectId",
"type": "string",
"description": "Unique object identifier."
},
{
"id": "UnserializableValue",
"type": "string",
"enum": ["Infinity", "NaN", "-Infinity", "-0"],
"description": "Primitive value which cannot be JSON-stringified."
},
{
"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." },
{ "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." },
{ "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." },
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have <code>value</code>, but gets this property." },
{ "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." },
{ "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "experimental": true },
{ "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true}
]
},
{
"id": "CustomPreview",
"type": "object",
"experimental": true,
"properties": [
{ "name": "header", "type": "string"},
{ "name": "hasBody", "type": "boolean"},
{ "name": "formatterObjectId", "$ref": "RemoteObjectId"},
{ "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" },
{ "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true }
]
},
{
"id": "ObjectPreview",
"type": "object",
"experimental": true,
"description": "Object containing abbreviated remote object value.",
"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"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
{ "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
{ "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." },
{ "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." },
{ "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." }
]
},
{
"id": "PropertyPreview",
"type": "object",
"experimental": true,
"properties": [
{ "name": "name", "type": "string", "description": "Property name." },
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." },
{ "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." },
{ "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." },
{ "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }
]
},
{
"id": "EntryPreview",
"type": "object",
"experimental": true,
"properties": [
{ "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." },
{ "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." }
]
},
{
"id": "PropertyDescriptor",
"type": "object",
"description": "Object property descriptor.",
"properties": [
{ "name": "name", "type": "string", "description": "Property name or symbol description." },
{ "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." },
{ "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." },
{ "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." },
{ "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." },
{ "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
{ "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." },
{ "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." },
{ "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type." }
]
},
{
"id": "InternalPropertyDescriptor",
"type": "object",
"description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.",
"properties": [
{ "name": "name", "type": "string", "description": "Conventional property name." },
{ "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }
]
},
{
"id": "CallArgument",
"type": "object",
"description": "Represents function call argument. Either remote object id <code>objectId</code>, primitive <code>value</code>, unserializable primitive value or neither of (for undefined) them should be specified.",
"properties": [
{ "name": "value", "type": "any", "optional": true, "description": "Primitive value." },
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." },
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }
]
},
{
"id": "ExecutionContextId",
"type": "integer",
"description": "Id of an execution context."
},
{
"id": "ExecutionContextDescription",
"type": "object",
"description": "Description of an isolated world.",
"properties": [
{ "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." },
{ "name": "origin", "type": "string", "description": "Execution context origin." },
{ "name": "name", "type": "string", "description": "Human readable name describing given context." },
{ "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }
]
},
{
"id": "ExceptionDetails",
"type": "object",
"description": "Detailed information about exception (or error) that was thrown during script compilation or execution.",
"properties": [
{ "name": "exceptionId", "type": "integer", "description": "Exception id." },
{ "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." },
{ "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." },
{ "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." },
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." },
{ "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." },
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." },
{ "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." }
]
},
{
"id": "Timestamp",
"type": "number",
"description": "Number of milliseconds since epoch."
},
{
"id": "CallFrame",
"type": "object",
"description": "Stack entry for runtime errors and assertions.",
"properties": [
{ "name": "functionName", "type": "string", "description": "JavaScript function name." },
{ "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." },
{ "name": "url", "type": "string", "description": "JavaScript script name or url." },
{ "name": "lineNumber", "type": "integer", "description": "JavaScript script line number (0-based)." },
{ "name": "columnNumber", "type": "integer", "description": "JavaScript script column number (0-based)." }
]
},
{
"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." }
]
}
],
"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." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." },
{ "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": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." },
{ "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": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." },
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Evaluates expression on global object."
},
{
"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." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."}
],
"description": "Add handler to promise with given promise object id."
},
{
"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." },
{ "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." },
{ "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 which should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." },
{ "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." },
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Call result." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object."
},
{
"name": "getProperties",
"parameters": [
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
{ "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." },
{ "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." }
],
"returns": [
{ "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." },
{ "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Returns properties of a given object. Object group of the result is inherited from the target object."
},
{
"name": "releaseObject",
"parameters": [
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." }
],
"description": "Releases remote object with given id."
},
{
"name": "releaseObjectGroup",
"parameters": [
{ "name": "objectGroup", "type": "string", "description": "Symbolic object group name." }
],
"description": "Releases all remote objects that belong to a given group."
},
{
"name": "runIfWaitingForDebugger",
"description": "Tells inspected instance to run if it was waiting for debugger to attach."
},
{
"name": "enable",
"description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context."
},
{
"name": "disable",
"description": "Disables reporting of execution contexts creation."
},
{
"name": "discardConsoleEntries",
"description": "Discards collected exceptions and console API calls."
},
{
"name": "setCustomObjectFormatterEnabled",
"parameters": [
{
"name": "enabled",
"type": "boolean"
}
],
"experimental": true
},
{
"name": "compileScript",
"parameters": [
{ "name": "expression", "type": "string", "description": "Expression to compile." },
{ "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." },
{ "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." },
{ "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." }
],
"returns": [
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Compiles expression."
},
{
"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." },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
{ "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": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." },
{ "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Run result." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Runs script with given id in a given context."
}
],
"events": [
{
"name": "executionContextCreated",
"parameters": [
{ "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." }
],
"description": "Issued when new execution context is created."
},
{
"name": "executionContextDestroyed",
"parameters": [
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" }
],
"description": "Issued when execution context is destroyed."
},
{
"name": "executionContextsCleared",
"description": "Issued when all executionContexts were cleared in browser"
},
{
"name": "exceptionThrown",
"description": "Issued when exception was thrown and unhandled.",
"parameters": [
{ "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails" }
]
},
{
"name": "exceptionRevoked",
"description": "Issued when unhandled exception was revoked.",
"parameters": [
{ "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." },
{ "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in <code>exceptionUnhandled</code>." }
]
},
{
"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": "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." },
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." }
]
},
{
"name": "inspectRequested",
"description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).",
"parameters": [
{ "name": "object", "$ref": "RemoteObject" },
{ "name": "hints", "type": "object" }
]
}
]
},
{
"domain": "Debugger",
"description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.",
"dependencies": ["Runtime"],
"types": [
{
"id": "BreakpointId",
"type": "string",
"description": "Breakpoint identifier."
},
{
"id": "CallFrameId",
"type": "string",
"description": "Call frame identifier."
},
{
"id": "Location",
"type": "object",
"properties": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." },
{ "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." },
{ "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." }
],
"description": "Location in the source code."
},
{
"id": "ScriptPosition",
"experimental": true,
"type": "object",
"properties": [
{ "name": "lineNumber", "type": "integer" },
{ "name": "columnNumber", "type": "integer" }
],
"description": "Location in the source code."
},
{
"id": "CallFrame",
"type": "object",
"properties": [
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." },
{ "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." },
{ "name": "functionLocation", "$ref": "Location", "optional": true, "experimental": true, "description": "Location in the source code." },
{ "name": "location", "$ref": "Location", "description": "Location in the source code." },
{ "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." },
{ "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." },
{ "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." }
],
"description": "JavaScript call frame. Array of call frames form the call stack."
},
{
"id": "Scope",
"type": "object",
"properties": [
{ "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "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" },
{ "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" }
],
"description": "Scope description."
},
{
"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." }
],
"experimental": true
}
],
"commands": [
{
"name": "enable",
"description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received."
},
{
"name": "disable",
"description": "Disables debugger for given page."
},
{
"name": "setBreakpointsActive",
"parameters": [
{ "name": "active", "type": "boolean", "description": "New value for breakpoints active state." }
],
"description": "Activates / deactivates all breakpoints on the page."
},
{
"name": "setSkipAllPauses",
"parameters": [
{ "name": "skip", "type": "boolean", "description": "New value for skip pauses state." }
],
"description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)."
},
{
"name": "setBreakpointByUrl",
"parameters": [
{ "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." },
{ "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." },
{ "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." },
{ "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." },
{ "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
],
"returns": [
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
{ "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." }
],
"description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads."
},
{
"name": "setBreakpoint",
"parameters": [
{ "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." },
{ "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }
],
"returns": [
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." },
{ "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." }
],
"description": "Sets JavaScript breakpoint at a given location."
},
{
"name": "removeBreakpoint",
"parameters": [
{ "name": "breakpointId", "$ref": "BreakpointId" }
],
"description": "Removes JavaScript breakpoint."
},
{
"name": "continueToLocation",
"parameters": [
{ "name": "location", "$ref": "Location", "description": "Location to continue to." }
],
"description": "Continues execution until specific location is reached."
},
{
"name": "stepOver",
"description": "Steps over the statement."
},
{
"name": "stepInto",
"description": "Steps into the function call."
},
{
"name": "stepOut",
"description": "Steps out of the function call."
},
{
"name": "pause",
"description": "Stops on the next JavaScript statement."
},
{
"name": "resume",
"description": "Resumes JavaScript execution."
},
{
"name": "searchInContent",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." },
{ "name": "query", "type": "string", "description": "String to search for." },
{ "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." },
{ "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." }
],
"returns": [
{ "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." }
],
"experimental": true,
"description": "Searches for given string in script content."
},
{
"name": "setScriptSource",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." },
{ "name": "scriptSource", "type": "string", "description": "New content of the script." },
{ "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." }
],
"returns": [
{ "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." },
{ "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." },
{ "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." }
],
"description": "Edits JavaScript source live."
},
{
"name": "restartFrame",
"parameters": [
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }
],
"returns": [
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }
],
"description": "Restarts particular call frame from the beginning."
},
{
"name": "getScriptSource",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." }
],
"returns": [
{ "name": "scriptSource", "type": "string", "description": "Script source." }
],
"description": "Returns source for the script with given id."
},
{
"name": "setPauseOnExceptions",
"parameters": [
{ "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." }
],
"description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>."
},
{
"name": "evaluateOnCallFrame",
"parameters": [
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." },
{ "name": "expression", "type": "string", "description": "Expression to evaluate." },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." },
{ "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." }
],
"returns": [
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
{ "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Evaluates expression on a given call frame."
},
{
"name": "setVariableValue",
"parameters": [
{ "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." },
{ "name": "variableName", "type": "string", "description": "Variable name." },
{ "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." },
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." }
],
"description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually."
},
{
"name": "setAsyncCallStackDepth",
"parameters": [
{ "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to <code>0</code> will effectively disable collecting async call stacks (default)." }
],
"description": "Enables or disables async call stacks tracking."
},
{
"name": "setBlackboxPatterns",
"parameters": [
{ "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." }
],
"experimental": true,
"description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful."
},
{
"name": "setBlackboxedRanges",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." },
{ "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } }
],
"experimental": true,
"description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted."
}
],
"events": [
{
"name": "scriptParsed",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." },
{ "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." },
{ "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." },
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
{ "name": "endLine", "type": "integer", "description": "Last line of the script." },
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." },
{ "name": "hash", "type": "string", "description": "Content hash of the script."},
{ "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 }
],
"description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
},
{
"name": "scriptFailedToParse",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." },
{ "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." },
{ "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." },
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
{ "name": "endLine", "type": "integer", "description": "Last line of the script." },
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." },
{ "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 }
],
"description": "Fired when virtual machine fails to parse the script."
},
{
"name": "breakpointResolved",
"parameters": [
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." },
{ "name": "location", "$ref": "Location", "description": "Actual breakpoint location." }
],
"description": "Fired when breakpoint is resolved to an actual script and location."
},
{
"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": "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." }
],
"description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria."
},
{
"name": "resumed",
"description": "Fired when the virtual machine resumed execution."
}
]
},
{
"domain": "Console",
"description": "This domain is deprecated - use Runtime or Log instead.",
"dependencies": ["Runtime"],
"deprecated": true,
"types": [
{
"id": "ConsoleMessage",
"type": "object",
"description": "Console message.",
"properties": [
{ "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation", "worker"], "description": "Message source." },
{ "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." },
{ "name": "text", "type": "string", "description": "Message text." },
{ "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." },
{ "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message (1-based)." },
{ "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message (1-based)." }
]
}
],
"commands": [
{
"name": "enable",
"description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification."
},
{
"name": "disable",
"description": "Disables console domain, prevents further console messages from being reported to the client."
},
{
"name": "clearMessages",
"description": "Does nothing."
}
],
"events": [
{
"name": "messageAdded",
"parameters": [
{ "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." }
],
"description": "Issued when new console message is added."
}
]
},
{
"domain": "Profiler",
"dependencies": ["Runtime", "Debugger"],
"types": [
{
"id": "ProfileNode",
"type": "object",
"description": "Profile node. Holds callsite information, execution statistics and child nodes.",
"properties": [
{ "name": "id", "type": "integer", "description": "Unique id of the node." },
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." },
{ "name": "hitCount", "type": "integer", "optional": true, "experimental": true, "description": "Number of samples where this node was on top of the call stack." },
{ "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." },
{ "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."},
{ "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "experimental": true, "description": "An array of source position ticks." }
]
},
{
"id": "Profile",
"type": "object",
"description": "Profile.",
"properties": [
{ "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." },
{ "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." },
{ "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." },
{ "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." },
{ "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." }
]
},
{
"id": "PositionTickInfo",
"type": "object",
"experimental": true,
"description": "Specifies a number of samples attributed to a certain source position.",
"properties": [
{ "name": "line", "type": "integer", "description": "Source line number (1-based)." },
{ "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." }
]
}
],
"commands": [
{
"name": "enable"
},
{
"name": "disable"
},
{
"name": "setSamplingInterval",
"parameters": [
{ "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." }
],
"description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started."
},
{
"name": "start"
},
{
"name": "stop",
"returns": [
{ "name": "profile", "$ref": "Profile", "description": "Recorded profile." }
]
}
],
"events": [
{
"name": "consoleProfileStarted",
"parameters": [
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
],
"description": "Sent when new profile recodring is started using console.profile() call."
},
{
"name": "consoleProfileFinished",
"parameters": [
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." },
{ "name": "profile", "$ref": "Profile" },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
]
}
]
},
{
"domain": "HeapProfiler",
"dependencies": ["Runtime"],
"experimental": true,
"types": [
{
"id": "HeapSnapshotObjectId",
"type": "string",
"description": "Heap snapshot object id."
},
{
"id": "SamplingHeapProfileNode",
"type": "object",
"description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.",
"properties": [
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." },
{ "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." },
{ "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." }
]
},
{
"id": "SamplingHeapProfile",
"type": "object",
"description": "Profile.",
"properties": [
{ "name": "head", "$ref": "SamplingHeapProfileNode" }
]
}
],
"commands": [
{
"name": "enable"
},
{
"name": "disable"
},
{
"name": "startTrackingHeapObjects",
"parameters": [
{ "name": "trackAllocations", "type": "boolean", "optional": true }
]
},
{
"name": "stopTrackingHeapObjects",
"parameters": [
{ "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." }
]
},
{
"name": "takeHeapSnapshot",
"parameters": [
{ "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." }
]
},
{
"name": "collectGarbage"
},
{
"name": "getObjectByHeapObjectId",
"parameters": [
{ "name": "objectId", "$ref": "HeapSnapshotObjectId" },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }
],
"returns": [
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." }
]
},
{
"name": "addInspectedHeapObject",
"parameters": [
{ "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." }
],
"description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)."
},
{
"name": "getHeapObjectId",
"parameters": [
{ "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." }
],
"returns": [
{ "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." }
]
},
{
"name": "startSampling",
"parameters": [
{ "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." }
]
},
{
"name": "stopSampling",
"returns": [
{ "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." }
]
}
],
"events": [
{
"name": "addHeapSnapshotChunk",
"parameters": [
{ "name": "chunk", "type": "string" }
]
},
{
"name": "resetProfiles"
},
{
"name": "reportHeapSnapshotProgress",
"parameters": [
{ "name": "done", "type": "integer" },
{ "name": "total", "type": "integer" },
{ "name": "finished", "type": "boolean", "optional": true }
]
},
{
"name": "lastSeenObjectId",
"description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.",
"parameters": [
{ "name": "lastSeenObjectId", "type": "integer" },
{ "name": "timestamp", "type": "number" }
]
},
{
"name": "heapStatsUpdate",
"description": "If heap objects tracking has been started then backend may send update for one or more fragments",
"parameters": [
{ "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."}
]
}
]
}]
}

View File

@ -1,6 +1,33 @@
{
"version": { "major": "1", "minor": "1" },
"domains": [{
"version": { "major": "1", "minor": "2" },
"domains": [
{
"domain": "Schema",
"description": "Provides information about the protocol schema.",
"types": [
{
"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." }
]
}
],
"commands": [
{
"name": "getDomains",
"description": "Returns supported domains.",
"handlers": ["browser", "renderer"],
"returns": [
{ "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." }
]
}
]
},
{
"domain": "Runtime",
"description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.",
"types": [
@ -14,6 +41,12 @@
"type": "string",
"description": "Unique object identifier."
},
{
"id": "UnserializableValue",
"type": "string",
"enum": ["Infinity", "NaN", "-Infinity", "-0"],
"description": "Primitive value which cannot be JSON-stringified."
},
{
"id": "RemoteObject",
"type": "object",
@ -21,19 +54,20 @@
"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"], "description": "Object subtype hint. Specified for <code>object</code> type values only." },
{ "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." },
{ "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." },
{ "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested), or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." },
{ "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." },
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have <code>value</code>, but gets this property." },
{ "name": "description", "type": "string", "optional": true, "description": "String representation of the object." },
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." },
{ "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "hidden": true },
{ "name": "customPreview", "$ref": "CustomPreview", "optional": true, "hidden": true}
{ "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "experimental": true },
{ "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true}
]
},
{
"id": "CustomPreview",
"type": "object",
"hidden": true,
"experimental": true,
"properties": [
{ "name": "header", "type": "string"},
{ "name": "hasBody", "type": "boolean"},
@ -45,7 +79,7 @@
{
"id": "ObjectPreview",
"type": "object",
"hidden": true,
"experimental": true,
"description": "Object containing abbreviated remote object value.",
"properties": [
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." },
@ -59,7 +93,7 @@
{
"id": "PropertyPreview",
"type": "object",
"hidden": true,
"experimental": true,
"properties": [
{ "name": "name", "type": "string", "description": "Property name." },
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." },
@ -71,7 +105,7 @@
{
"id": "EntryPreview",
"type": "object",
"hidden": true,
"experimental": true,
"properties": [
{ "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." },
{ "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." }
@ -90,8 +124,8 @@
{ "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." },
{ "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." },
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." },
{ "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object.", "hidden": true },
{ "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type.", "hidden": true }
{ "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." },
{ "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type." }
]
},
{
@ -101,17 +135,16 @@
"properties": [
{ "name": "name", "type": "string", "description": "Conventional property name." },
{ "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }
],
"hidden": true
]
},
{
"id": "CallArgument",
"type": "object",
"description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.",
"description": "Represents function call argument. Either remote object id <code>objectId</code>, primitive <code>value</code>, unserializable primitive value or neither of (for undefined) them should be specified.",
"properties": [
{ "name": "value", "type": "any", "optional": true, "description": "Primitive value, or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." },
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." },
{ "name": "type", "optional": true, "hidden": true, "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }
{ "name": "value", "type": "any", "optional": true, "description": "Primitive value." },
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." },
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }
]
},
{
@ -125,31 +158,31 @@
"description": "Description of an isolated world.",
"properties": [
{ "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." },
{ "name": "isDefault", "type": "boolean", "description": "Whether context is the default page context (as opposite to e.g. context of content script).", "hidden": true },
{ "name": "origin", "type": "string", "description": "Execution context origin.", "hidden": true},
{ "name": "name", "type": "string", "description": "Human readable name describing given context.", "hidden": true},
{ "name": "frameId", "type": "string", "description": "Id of the owning frame. May be an empty string if the context is not associated with a frame." }
{ "name": "origin", "type": "string", "description": "Execution context origin." },
{ "name": "name", "type": "string", "description": "Human readable name describing given context." },
{ "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }
]
},
{
"id": "ExceptionDetails",
"type": "object",
"hidden": true,
"description": "Detailed information about exception (or error) that was thrown during script compilation or execution.",
"properties": [
{ "name": "text", "type": "string", "description": "Exception text." },
{ "name": "scriptId", "$ref": "ScriptId", "description": "Script ID of the exception location." },
{ "name": "exceptionId", "type": "integer", "description": "Exception id." },
{ "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." },
{ "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." },
{ "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." },
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." },
{ "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." },
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." }
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." },
{ "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." }
]
},
{
"id": "Timestamp",
"type": "number",
"description": "Number of milliseconds since epoch.",
"hidden": true
"description": "Number of milliseconds since epoch."
},
{
"id": "CallFrame",
@ -171,7 +204,7 @@
"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, "hidden": 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." }
]
}
],
@ -182,24 +215,22 @@
"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." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true },
{ "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true },
{ "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." },
{ "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": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." },
{ "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, "hidden": true, "description": "Whether preview should be generated for the result." },
{ "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." },
{ "name": "awaitPromise", "type": "boolean", "optional":true, "hidden": true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." },
{ "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." },
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." },
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."}
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Evaluates expression on global object."
},
{
"name": "awaitPromise",
"hidden": true,
"async": true,
"parameters": [
{ "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." },
@ -208,25 +239,26 @@
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." },
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the promise was rejected." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."}
],
"description": "Add handler to promise with given promise object id."
},
{
"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." },
{ "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." },
{ "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true },
{ "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 which should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." },
{ "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." }
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." },
{ "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." },
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Call result." },
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object."
},
@ -235,13 +267,13 @@
"parameters": [
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." },
{ "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." },
{ "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "hidden": true },
{ "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the results." }
{ "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true },
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." }
],
"returns": [
{ "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." },
{ "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself).", "hidden": true },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."}
{ "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." },
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Returns properties of a given object. Object group of the result is inherited from the target object."
},
@ -260,9 +292,8 @@
"description": "Releases all remote objects that belong to a given group."
},
{
"name": "run",
"hidden": true,
"description": "Tells inspected instance(worker or page) that it can run in case it was started paused."
"name": "runIfWaitingForDebugger",
"description": "Tells inspected instance to run if it was waiting for debugger to attach."
},
{
"name": "enable",
@ -270,12 +301,10 @@
},
{
"name": "disable",
"hidden": true,
"description": "Disables reporting of execution contexts creation."
},
{
"name": "discardConsoleEntries",
"hidden": true,
"description": "Discards collected exceptions and console API calls."
},
{
@ -286,16 +315,15 @@
"type": "boolean"
}
],
"hidden": true
"experimental": true
},
{
"name": "compileScript",
"hidden": true,
"parameters": [
{ "name": "expression", "type": "string", "description": "Expression to compile." },
{ "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." },
{ "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." }
{ "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." }
],
"returns": [
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." },
@ -305,13 +333,16 @@
},
{
"name": "runScript",
"hidden": true,
"async": true,
"parameters": [
{ "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." },
{ "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." },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." },
{ "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }
{ "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": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." },
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." },
{ "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." },
{ "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." }
],
"returns": [
{ "name": "result", "$ref": "RemoteObject", "description": "Run result." },
@ -343,22 +374,17 @@
"name": "exceptionThrown",
"description": "Issued when exception was thrown and unhandled.",
"parameters": [
{ "name": "exceptionId", "type": "integer", "description": "Exception id." },
{ "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." },
{ "name": "details", "$ref": "ExceptionDetails" },
{ "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object." },
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." }
],
"hidden": true
{ "name": "exceptionDetails", "$ref": "ExceptionDetails" }
]
},
{
"name": "exceptionRevoked",
"description": "Issued when unhandled exception was revoked.",
"parameters": [
{ "name": "message", "type": "string", "description": "Message describing why exception was revoked." },
{ "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." },
{ "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in <code>exceptionUnhandled</code>." }
],
"hidden": true
]
},
{
"name": "consoleAPICalled",
@ -369,16 +395,15 @@
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." },
{ "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." },
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." }
],
"hidden": true
]
},
{
"name": "inspectRequested",
"description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).",
"parameters": [
{ "name": "object", "$ref": "RemoteObject" },
{ "name": "hints", "type": "object" }
],
"hidden": true
]
}
]
},
@ -409,7 +434,7 @@
},
{
"id": "ScriptPosition",
"hidden": true,
"experimental": true,
"type": "object",
"properties": [
{ "name": "lineNumber", "type": "integer" },
@ -423,11 +448,11 @@
"properties": [
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." },
{ "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." },
{ "name": "functionLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code." },
{ "name": "functionLocation", "$ref": "Location", "optional": true, "experimental": true, "description": "Location in the source code." },
{ "name": "location", "$ref": "Location", "description": "Location in the source code." },
{ "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." },
{ "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." },
{ "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "hidden": true, "description": "The value being returned, if the function is at return point." }
{ "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." }
],
"description": "JavaScript call frame. Array of call frames form the call stack."
},
@ -437,9 +462,9 @@
"properties": [
{ "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "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, "hidden": true },
{ "name": "startLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope starts" },
{ "name": "endLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope ends" }
{ "name": "name", "type": "string", "optional": true },
{ "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" },
{ "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" }
],
"description": "Scope description."
},
@ -452,7 +477,7 @@
{ "name": "lineNumber", "type": "number", "description": "Line number in resource content." },
{ "name": "lineContent", "type": "string", "description": "Line with match content." }
],
"hidden": true
"experimental": true
}
],
"commands": [
@ -473,9 +498,8 @@
},
{
"name": "setSkipAllPauses",
"hidden": true,
"parameters": [
{ "name": "skipped", "type": "boolean", "description": "New value for skip pauses state." }
{ "name": "skip", "type": "boolean", "description": "New value for skip pauses state." }
],
"description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)."
},
@ -516,8 +540,7 @@
{
"name": "continueToLocation",
"parameters": [
{ "name": "location", "$ref": "Location", "description": "Location to continue to." },
{ "name": "interstatementLocation", "type": "boolean", "optional": true, "hidden": true, "description": "Allows breakpoints at the intemediate positions inside statements." }
{ "name": "location", "$ref": "Location", "description": "Location to continue to." }
],
"description": "Continues execution until specific location is reached."
},
@ -552,27 +575,21 @@
"returns": [
{ "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." }
],
"experimental": true,
"description": "Searches for given string in script content."
},
{
"name": "canSetScriptSource",
"returns": [
{ "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." }
],
"description": "Always returns true."
},
{
"name": "setScriptSource",
"parameters": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." },
{ "name": "scriptSource", "type": "string", "description": "New content of the script." },
{ "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true }
{ "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." }
],
"returns": [
{ "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." },
{ "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes.", "hidden": true },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true },
{ "name": "compileError", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Error data if any." }
{ "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." },
{ "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." }
],
"description": "Edits JavaScript source live."
},
@ -585,7 +602,6 @@
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }
],
"hidden": true,
"description": "Restarts particular call frame from the beginning."
},
{
@ -611,15 +627,14 @@
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." },
{ "name": "expression", "type": "string", "description": "Expression to evaluate." },
{ "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." },
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true },
{ "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true },
{ "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, "hidden": 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." }
],
"returns": [
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." },
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." },
{ "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."}
{ "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."}
],
"description": "Evaluates expression on a given call frame."
},
@ -631,24 +646,13 @@
{ "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." },
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." }
],
"hidden": true,
"description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually."
},
{
"name": "getBacktrace",
"returns": [
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }
],
"hidden": true,
"description": "Returns call stack including variables changed since VM was paused. VM must be paused."
},
{
"name": "setAsyncCallStackDepth",
"parameters": [
{ "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to <code>0</code> will effectively disable collecting async call stacks (default)." }
],
"hidden": true,
"description": "Enables or disables async call stacks tracking."
},
{
@ -656,7 +660,7 @@
"parameters": [
{ "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." }
],
"hidden": true,
"experimental": true,
"description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful."
},
{
@ -665,7 +669,7 @@
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." },
{ "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } }
],
"hidden": true,
"experimental": true,
"description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted."
}
],
@ -679,14 +683,12 @@
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
{ "name": "endLine", "type": "integer", "description": "Last line of the script." },
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true },
{ "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."},
{ "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." },
{ "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true },
{ "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "hidden": true },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." },
{ "name": "hash", "type": "string", "description": "Content hash of the script."},
{ "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.", "hidden": true },
{ "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."}
{ "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true }
],
"description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
},
@ -699,13 +701,11 @@
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." },
{ "name": "endLine", "type": "integer", "description": "Last line of the script." },
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true },
{ "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."},
{ "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." },
{ "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true },
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." },
{ "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.", "hidden": true },
{ "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."}
{ "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true }
],
"description": "Fired when virtual machine fails to parse the script."
},
@ -723,8 +723,8 @@
{ "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": "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", "hidden": true },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true }
{ "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." }
],
"description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria."
},
@ -775,56 +775,42 @@
{ "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." }
],
"description": "Issued when new console message is added."
},
{
"name": "messageRepeatCountUpdated",
"parameters": [
{ "name": "count", "type": "integer", "description": "New repeat count value." },
{ "name": "timestamp", "$ref": "Runtime.Timestamp", "description": "Timestamp of most recent message in batch.", "hidden": true }
],
"description": "Not issued.",
"deprecated": true
},
{
"name": "messagesCleared",
"description": "Not issued.",
"deprecated": true
}
]
},
{
"domain": "Profiler",
"dependencies": ["Runtime", "Debugger"],
"hidden": true,
"types": [
{
"id": "CPUProfileNode",
"id": "ProfileNode",
"type": "object",
"description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.",
"description": "Profile node. Holds callsite information, execution statistics and child nodes.",
"properties": [
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." },
{ "name": "hitCount", "type": "integer", "description": "Number of samples where this node was on top of the call stack." },
{ "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." },
{ "name": "deoptReason", "type": "string", "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."},
{ "name": "id", "type": "integer", "description": "Unique id of the node." },
{ "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "description": "An array of source position ticks." }
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." },
{ "name": "hitCount", "type": "integer", "optional": true, "experimental": true, "description": "Number of samples where this node was on top of the call stack." },
{ "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." },
{ "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."},
{ "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "experimental": true, "description": "An array of source position ticks." }
]
},
{
"id": "CPUProfile",
"id": "Profile",
"type": "object",
"description": "Profile.",
"properties": [
{ "name": "head", "$ref": "CPUProfileNode" },
{ "name": "startTime", "type": "number", "description": "Profiling start time in seconds." },
{ "name": "endTime", "type": "number", "description": "Profiling end time in seconds." },
{ "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." },
{ "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." },
{ "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." },
{ "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." },
{ "name": "timestamps", "optional": true, "type": "array", "items": { "type": "number" }, "description": "Timestamps of the samples in microseconds." }
{ "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." }
]
},
{
"id": "PositionTickInfo",
"type": "object",
"experimental": true,
"description": "Specifies a number of samples attributed to a certain source position.",
"properties": [
{ "name": "line", "type": "integer", "description": "Source line number (1-based)." },
@ -852,7 +838,7 @@
{
"name": "stop",
"returns": [
{ "name": "profile", "$ref": "CPUProfile", "description": "Recorded profile." }
{ "name": "profile", "$ref": "Profile", "description": "Recorded profile." }
]
}
],
@ -862,7 +848,7 @@
"parameters": [
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argument to console.profile()." }
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
],
"description": "Sent when new profile recodring is started using console.profile() call."
},
@ -871,8 +857,8 @@
"parameters": [
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." },
{ "name": "profile", "$ref": "CPUProfile" },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argunet to console.profile()." }
{ "name": "profile", "$ref": "Profile" },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
]
}
]
@ -880,7 +866,7 @@
{
"domain": "HeapProfiler",
"dependencies": ["Runtime"],
"hidden": true,
"experimental": true,
"types": [
{
"id": "HeapSnapshotObjectId",

View File

@ -0,0 +1,24 @@
// 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_PUBLIC_STRINGBUFFER_H_
#define V8_INSPECTOR_PUBLIC_STRINGBUFFER_H_
#include "src/inspector/public/StringView.h"
#include <memory>
namespace v8_inspector {
class PLATFORM_EXPORT StringBuffer {
public:
virtual ~StringBuffer() {}
virtual const StringView& string() = 0;
// This method copies contents.
static std::unique_ptr<StringBuffer> create(const StringView&);
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_PUBLIC_STRINGBUFFER_H_

View File

@ -0,0 +1,42 @@
// 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_PUBLIC_STRINGVIEW_H_
#define V8_INSPECTOR_PUBLIC_STRINGVIEW_H_
#include <stdint.h>
#include <cctype>
namespace v8_inspector {
class PLATFORM_EXPORT StringView {
public:
StringView() : m_is8Bit(true), m_length(0), m_characters8(nullptr) {}
StringView(const uint8_t* characters, unsigned length)
: m_is8Bit(true), m_length(length), m_characters8(characters) {}
StringView(const uint16_t* characters, unsigned length)
: m_is8Bit(false), m_length(length), m_characters16(characters) {}
bool is8Bit() const { return m_is8Bit; }
unsigned length() const { return m_length; }
// TODO(dgozman): add DCHECK(m_is8Bit) to accessors once platform can be used
// here.
const uint8_t* characters8() const { return m_characters8; }
const uint16_t* characters16() const { return m_characters16; }
private:
bool m_is8Bit;
unsigned m_length;
union {
const uint8_t* m_characters8;
const uint16_t* m_characters16;
};
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_PUBLIC_STRINGVIEW_H_

View File

@ -0,0 +1,46 @@
// 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_PUBLIC_V8CONTEXTINFO_H_
#define V8_INSPECTOR_PUBLIC_V8CONTEXTINFO_H_
#include "src/inspector/public/StringView.h"
#include <v8.h>
namespace v8_inspector {
class V8ContextInfo {
public:
V8ContextInfo(v8::Local<v8::Context> context, int contextGroupId,
const StringView& humanReadableName)
: context(context),
contextGroupId(contextGroupId),
humanReadableName(humanReadableName),
hasMemoryOnConsole(false) {}
v8::Local<v8::Context> context;
// Each v8::Context is a part of a group. The group id is used to find
// appropriate
// V8DebuggerAgent to notify about events in the context.
// |contextGroupId| must be non-0.
int contextGroupId;
StringView humanReadableName;
StringView origin;
StringView auxData;
bool hasMemoryOnConsole;
private:
// Disallow copying and allocating this one.
enum NotNullTagEnum { NotNullLiteral };
void* operator new(size_t) = delete;
void* operator new(size_t, NotNullTagEnum, void*) = delete;
void* operator new(size_t, void*) = delete;
V8ContextInfo(const V8ContextInfo&) = delete;
V8ContextInfo& operator=(const V8ContextInfo&) = delete;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_PUBLIC_V8CONTEXTINFO_H_

View File

@ -0,0 +1,72 @@
// Copyright 2015 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_PUBLIC_V8INSPECTOR_H_
#define V8_INSPECTOR_PUBLIC_V8INSPECTOR_H_
#include "src/inspector/public/StringView.h"
#include "src/inspector/public/V8ContextInfo.h"
#include <v8.h>
namespace v8_inspector {
class V8InspectorClient;
class V8InspectorSession;
class V8StackTrace;
class PLATFORM_EXPORT V8Inspector {
public:
static std::unique_ptr<V8Inspector> create(v8::Isolate*, V8InspectorClient*);
virtual ~V8Inspector() {}
// Contexts instrumentation.
virtual void contextCreated(const V8ContextInfo&) = 0;
virtual void contextDestroyed(v8::Local<v8::Context>) = 0;
virtual void resetContextGroup(int contextGroupId) = 0;
// Various instrumentation.
virtual void willExecuteScript(v8::Local<v8::Context>, int scriptId) = 0;
virtual void didExecuteScript(v8::Local<v8::Context>) = 0;
virtual void idleStarted() = 0;
virtual void idleFinished() = 0;
// Async stack traces instrumentation.
virtual void asyncTaskScheduled(const StringView& taskName, void* task,
bool recurring) = 0;
virtual void asyncTaskCanceled(void* task) = 0;
virtual void asyncTaskStarted(void* task) = 0;
virtual void asyncTaskFinished(void* task) = 0;
virtual void allAsyncTasksCanceled() = 0;
// Exceptions instrumentation.
virtual unsigned exceptionThrown(
v8::Local<v8::Context>, const StringView& message,
v8::Local<v8::Value> exception, const StringView& detailedMessage,
const StringView& url, unsigned lineNumber, unsigned columnNumber,
std::unique_ptr<V8StackTrace>, int scriptId) = 0;
virtual void exceptionRevoked(v8::Local<v8::Context>, unsigned exceptionId,
const StringView& message) = 0;
// Connection.
class PLATFORM_EXPORT Channel {
public:
virtual ~Channel() {}
virtual void sendProtocolResponse(int callId,
const StringView& message) = 0;
virtual void sendProtocolNotification(const StringView& message) = 0;
virtual void flushProtocolNotifications() = 0;
};
virtual std::unique_ptr<V8InspectorSession> connect(
int contextGroupId, Channel*, const StringView& state) = 0;
// API methods.
virtual std::unique_ptr<V8StackTrace> createStackTrace(
v8::Local<v8::StackTrace>) = 0;
virtual std::unique_ptr<V8StackTrace> captureStackTrace(bool fullStack) = 0;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_PUBLIC_V8INSPECTOR_H_

View File

@ -0,0 +1,74 @@
// Copyright 2015 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_PUBLIC_V8INSPECTORCLIENT_H_
#define V8_INSPECTOR_PUBLIC_V8INSPECTORCLIENT_H_
#include "src/inspector/public/StringBuffer.h"
#include "src/inspector/public/StringView.h"
#include <v8.h>
namespace v8_inspector {
class V8StackTrace;
enum class V8ConsoleAPIType { kClear, kDebug, kLog, kInfo, kWarning, kError };
class PLATFORM_EXPORT V8InspectorClient {
public:
virtual ~V8InspectorClient() {}
virtual void runMessageLoopOnPause(int contextGroupId) {}
virtual void quitMessageLoopOnPause() {}
virtual void runIfWaitingForDebugger(int contextGroupId) {}
virtual void muteMetrics(int contextGroupId) {}
virtual void unmuteMetrics(int contextGroupId) {}
virtual void beginUserGesture() {}
virtual void endUserGesture() {}
virtual std::unique_ptr<StringBuffer> valueSubtype(v8::Local<v8::Value>) {
return nullptr;
}
virtual bool formatAccessorsAsProperties(v8::Local<v8::Value>) {
return false;
}
virtual bool isInspectableHeapObject(v8::Local<v8::Object>) { return true; }
virtual v8::Local<v8::Context> ensureDefaultContextInGroup(
int contextGroupId) {
return v8::Local<v8::Context>();
}
virtual void beginEnsureAllContextsInGroup(int contextGroupId) {}
virtual void endEnsureAllContextsInGroup(int contextGroupId) {}
virtual void installAdditionalCommandLineAPI(v8::Local<v8::Context>,
v8::Local<v8::Object>) {}
virtual void consoleAPIMessage(int contextGroupId, V8ConsoleAPIType,
const StringView& message,
const StringView& url, unsigned lineNumber,
unsigned columnNumber, V8StackTrace*) {}
virtual v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*,
v8::Local<v8::Context>) {
return v8::MaybeLocal<v8::Value>();
}
virtual void consoleTime(const StringView& title) {}
virtual void consoleTimeEnd(const StringView& title) {}
virtual void consoleTimeStamp(const StringView& title) {}
virtual double currentTimeMS() { return 0; }
typedef void (*TimerCallback)(void*);
virtual void startRepeatingTimer(double, TimerCallback, void* data) {}
virtual void cancelTimer(void* data) {}
// TODO(dgozman): this was added to support service worker shadow page. We
// should not connect at all.
virtual bool canExecuteScripts(int contextGroupId) { return true; }
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_PUBLIC_V8INSPECTORCLIENT_H_

View File

@ -0,0 +1,63 @@
// 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_PUBLIC_V8INSPECTORSESSION_H_
#define V8_INSPECTOR_PUBLIC_V8INSPECTORSESSION_H_
#include "src/inspector/public/StringBuffer.h"
#include "src/inspector/public/StringView.h"
#include "src/inspector/public/protocol/Debugger.h"
#include "src/inspector/public/protocol/Runtime.h"
#include "src/inspector/public/protocol/Schema.h"
#include <v8.h>
namespace v8_inspector {
class PLATFORM_EXPORT V8InspectorSession {
public:
virtual ~V8InspectorSession() {}
// Cross-context inspectable values (DOM nodes in different worlds, etc.).
class Inspectable {
public:
virtual v8::Local<v8::Value> get(v8::Local<v8::Context>) = 0;
virtual ~Inspectable() {}
};
virtual void addInspectedObject(std::unique_ptr<Inspectable>) = 0;
// Dispatching protocol messages.
static bool canDispatchMethod(const StringView& method);
virtual void dispatchProtocolMessage(const StringView& message) = 0;
virtual std::unique_ptr<StringBuffer> stateJSON() = 0;
virtual std::vector<std::unique_ptr<protocol::Schema::API::Domain>>
supportedDomains() = 0;
// Debugger actions.
virtual void schedulePauseOnNextStatement(const StringView& breakReason,
const StringView& breakDetails) = 0;
virtual void cancelPauseOnNextStatement() = 0;
virtual void breakProgram(const StringView& breakReason,
const StringView& breakDetails) = 0;
virtual void setSkipAllPauses(bool) = 0;
virtual void resume() = 0;
virtual void stepOver() = 0;
virtual std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
searchInTextByLines(const StringView& text, const StringView& query,
bool caseSensitive, bool isRegex) = 0;
// Remote objects.
virtual std::unique_ptr<protocol::Runtime::API::RemoteObject> wrapObject(
v8::Local<v8::Context>, v8::Local<v8::Value>,
const StringView& groupName) = 0;
virtual bool unwrapObject(std::unique_ptr<StringBuffer>* error,
const StringView& objectId, v8::Local<v8::Value>*,
v8::Local<v8::Context>*,
std::unique_ptr<StringBuffer>* objectGroup) = 0;
virtual void releaseObjectGroup(const StringView&) = 0;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_PUBLIC_V8INSPECTORSESSION_H_

View File

@ -0,0 +1,34 @@
// 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_PUBLIC_V8STACKTRACE_H_
#define V8_INSPECTOR_PUBLIC_V8STACKTRACE_H_
#include "src/inspector/public/StringBuffer.h"
#include "src/inspector/public/StringView.h"
#include "src/inspector/public/protocol/Runtime.h"
namespace v8_inspector {
class V8StackTrace {
public:
virtual bool isEmpty() const = 0;
virtual StringView topSourceURL() const = 0;
virtual int topLineNumber() const = 0;
virtual int topColumnNumber() const = 0;
virtual StringView topScriptId() const = 0;
virtual StringView topFunctionName() const = 0;
virtual ~V8StackTrace() {}
virtual std::unique_ptr<protocol::Runtime::API::StackTrace>
buildInspectorObject() const = 0;
virtual std::unique_ptr<StringBuffer> toString() const = 0;
// Safe to pass between threads, drops async chain.
virtual std::unique_ptr<V8StackTrace> clone() = 0;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_PUBLIC_V8STACKTRACE_H_

View File

@ -192,8 +192,9 @@ class CppLintProcessor(SourceFileProcessor):
return name.endswith('.cc') or name.endswith('.h')
def IgnoreDir(self, name):
# TODO(dgozman): remove inspector after fixing the issues.
return (super(CppLintProcessor, self).IgnoreDir(name)
or (name == 'third_party'))
or (name == 'third_party') or (name == 'inspector'))
IGNORE_LINT = ['flag-definitions.h']
@ -299,9 +300,15 @@ class SourceProcessor(SourceFileProcessor):
'corrections.js',
'crypto.js',
'daemon.py',
'DebuggerScript.js',
'earley-boyer.js',
'fannkuch.js',
'fasta.js',
'InjectedScript.cpp',
'InjectedScript.h',
'InjectedScriptSource.js',
'JavaScriptCallFrame.cpp',
'JavaScriptCallFrame.h',
'jsmin.py',
'libraries.cc',
'libraries-empty.cc',
@ -311,10 +318,19 @@ class SourceProcessor(SourceFileProcessor):
'primes.js',
'raytrace.js',
'regexp-pcre.js',
'rjsmin.py',
'ScriptBreakpoint.h',
'sqlite.js',
'sqlite-change-heap.js',
'sqlite-pointer-masking.js',
'sqlite-safe-heap.js',
'V8DebuggerScript.h',
'V8FunctionCall.cpp',
'V8FunctionCall.h',
'V8InspectorImpl.cpp',
'V8InspectorImpl.h',
'V8RuntimeAgentImpl.cpp',
'V8RuntimeAgentImpl.h',
'gnuplot-4.6.3-emscripten.js',
'zlib.js']
IGNORE_TABS = IGNORE_COPYRIGHTS + ['unicode-test.js', 'html-comments.js']