vscode-chrome-debug-core
Version:
A library for building VS Code debug adapters for targets that support the Chrome Remote Debug Protocol
354 lines (352 loc) • 13.9 kB
JavaScript
"use strict";
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
const vscode_debugadapter_1 = require("vscode-debugadapter");
const utils = require("../utils");
const ChromeUtils = require("./chromeUtils");
class BaseVariableContainer {
constructor(objectId, evaluateName) {
this.objectId = objectId;
this.evaluateName = evaluateName;
}
expand(variablesManager, filter, start, count) {
return variablesManager.getVariablesForObjectId(this.objectId, this.evaluateName, filter, start, count);
}
setValue(variablesManager, name, value) {
return utils.errP('setValue not supported by this variable type');
}
}
exports.BaseVariableContainer = BaseVariableContainer;
class PropertyContainer extends BaseVariableContainer {
setValue(variablesManager, name, value) {
return variablesManager.setPropertyValue(this.objectId, name, value);
}
}
exports.PropertyContainer = PropertyContainer;
class LoggedObjects extends BaseVariableContainer {
constructor(args) {
super(undefined);
this.args = args;
}
expand(variablesManager, filter, start, count) {
return Promise.all(this.args.map((arg, i) => variablesManager.remoteObjectToVariable('' + i, arg, undefined, /*stringify=*/ false, 'repl')));
}
}
exports.LoggedObjects = LoggedObjects;
class ScopeContainer extends BaseVariableContainer {
constructor(frameId, origScopeIndex, objectId, thisObj, returnValue) {
super(objectId, '');
this._thisObj = thisObj;
this._returnValue = returnValue;
this._frameId = frameId;
this._origScopeIndex = origScopeIndex;
}
/**
* Call super then insert the 'this' object if needed
*/
expand(variablesManager, filter, start, count) {
// No filtering in scopes right now
return super.expand(variablesManager, 'all', start, count).then(variables => {
if (this._thisObj) {
// If this is a scope that should have the 'this', prop, insert it at the top of the list
return this.insertRemoteObject(variablesManager, variables, 'this', this._thisObj);
}
return variables;
}).then(variables => {
if (this._returnValue) {
return this.insertRemoteObject(variablesManager, variables, 'Return value', this._returnValue);
}
return variables;
});
}
setValue(variablesManager, name, value) {
return variablesManager.setVariableValue(this._frameId, this._origScopeIndex, name, value);
}
insertRemoteObject(variablesManager, variables, name, obj) {
return variablesManager.remoteObjectToVariable(name, obj).then(variable => {
variables.unshift(variable);
return variables;
});
}
}
exports.ScopeContainer = ScopeContainer;
class ExceptionContainer extends PropertyContainer {
constructor(objectId, exception) {
super(exception.objectId, undefined);
this._exception = exception;
}
/**
* Expand the exception as if it were a Scope
*/
static create(exception) {
return exception.objectId ?
new ExceptionContainer(exception.objectId, exception) :
new ExceptionValueContainer(exception);
}
}
exports.ExceptionContainer = ExceptionContainer;
/**
* For when a value is thrown instead of an object
*/
class ExceptionValueContainer extends ExceptionContainer {
constructor(exception) {
super('EXCEPTION_ID', exception);
}
/**
* Make up a fake 'Exception' property to hold the thrown value, displayed under the Exception Scope
*/
expand(variablesManager, filter, start, count) {
const excValuePropDescriptor = { name: 'Exception', value: this._exception };
return variablesManager.propertyDescriptorToVariable(excValuePropDescriptor)
.then(variable => [variable]);
}
}
exports.ExceptionValueContainer = ExceptionValueContainer;
function isIndexedPropName(name) {
return !!name.match(/^\d+$/);
}
exports.isIndexedPropName = isIndexedPropName;
const PREVIEW_PROPS_DEFAULT = 3;
const PREVIEW_PROPS_CONSOLE = 8;
const PREVIEW_PROP_LENGTH = 50;
const ELLIPSIS = '…';
function getArrayPreview(object, context) {
let value = object.description;
if (object.preview) {
const numProps = context === 'repl' ? PREVIEW_PROPS_CONSOLE : PREVIEW_PROPS_DEFAULT;
const indexedProps = object.preview.properties
.filter(prop => isIndexedPropName(prop.name));
// Take the first 3 props, and parse the indexes
const propsWithIdx = indexedProps.slice(0, numProps)
.map((prop, i) => {
return {
idx: parseInt(prop.name, 10),
value: propertyPreviewToString(prop)
};
});
// Insert ... where there are undefined indexes
const propValues = [];
for (let i = 0; i < propsWithIdx.length; i++) {
const prop = propsWithIdx[i];
const prevIdx = i === 0 ? -1 : propsWithIdx[i - 1].idx;
if (prop.idx > prevIdx + 1) {
propValues.push(ELLIPSIS);
}
propValues.push(prop.value);
}
let propsPreview = propValues.join(', ');
if (object.preview.overflow || indexedProps.length > numProps) {
propsPreview += ', ' + ELLIPSIS;
}
value += ` [${propsPreview}]`;
}
return value;
}
function getObjectPreview(object, context) {
let value = object.description;
if (object.preview) {
const numProps = context === 'repl' ? PREVIEW_PROPS_CONSOLE : PREVIEW_PROPS_DEFAULT;
const props = object.preview.properties.slice(0, numProps);
let propsPreview = props
.map(prop => {
const name = prop.name || `""`;
return `${name}: ${propertyPreviewToString(prop)}`;
})
.join(', ');
if (object.preview.overflow || object.preview.properties.length > numProps) {
propsPreview += ', …';
}
value += ` {${propsPreview}}`;
}
return value;
}
function propertyPreviewToString(prop) {
const value = typeof prop.value === 'undefined' ?
`<${prop.type}>` :
trimProperty(prop.value);
return prop.type === 'string' ?
`"${value}"` :
value;
}
function trimProperty(value) {
return (value !== undefined && value !== null && value.length > PREVIEW_PROP_LENGTH) ?
value.substr(0, PREVIEW_PROP_LENGTH) + ELLIPSIS :
value;
}
function getRemoteObjectPreview(object, stringify = true, context) {
if (object) {
if (object.type === 'object') {
return getRemoteObjectPreview_object(object, context);
}
else if (object.type === 'function') {
return getRemoteObjectPreview_function(object, context);
}
else {
return getRemoteObjectPreview_primitive(object, stringify);
}
}
return '';
}
exports.getRemoteObjectPreview = getRemoteObjectPreview;
function getRemoteObjectPreview_object(object, context) {
const objectDescription = object.description || '';
if (object.subtype === 'internal#location') {
// Could format this nicely later, see #110
return 'internal#location';
}
else if (object.subtype === 'null') {
return 'null';
}
else if (object.subtype === 'array' || object.subtype === 'typedarray') {
return getArrayPreview(object, context);
}
else if (object.subtype === 'error') {
// The Error's description contains the whole stack which is not a nice description.
// Up to the first newline is just the error name/message.
const firstNewlineIdx = objectDescription.indexOf('\n');
return firstNewlineIdx >= 0 ?
objectDescription.substr(0, firstNewlineIdx) :
objectDescription;
}
else if (object.subtype === 'promise' && object.preview) {
const promiseStatus = object.preview.properties.filter(prop => prop.name === '[[PromiseStatus]]')[0];
return promiseStatus ?
objectDescription + ' { ' + promiseStatus.value + ' }' :
objectDescription;
}
else if (object.subtype === 'generator' && object.preview) {
const generatorStatus = object.preview.properties.filter(prop => prop.name === '[[GeneratorStatus]]')[0];
return generatorStatus ?
objectDescription + ' { ' + generatorStatus.value + ' }' :
objectDescription;
}
else if (object.type === 'object' && object.preview) {
return getObjectPreview(object, context);
}
else {
return objectDescription;
}
}
exports.getRemoteObjectPreview_object = getRemoteObjectPreview_object;
function getRemoteObjectPreview_primitive(object, stringify) {
// The value is a primitive value, or something that has a description (not object, primitive, or undefined). And force to be string
if (typeof object.value === 'undefined') {
return object.description + '';
}
else if (object.type === 'number') {
// .value is truncated, so use .description, the full string representation
// Should be like '3' or 'Infinity'.
return object.description || '' + object.value;
}
else if (object.type === 'boolean') {
// Never stringified
return '' + object.value;
}
else {
return stringify ? `"${object.value}"` : object.value;
}
}
exports.getRemoteObjectPreview_primitive = getRemoteObjectPreview_primitive;
function getRemoteObjectPreview_function(object, context) {
const firstBraceIdx = object.description.indexOf('{');
if (firstBraceIdx >= 0) {
return object.description.substring(0, firstBraceIdx) + '{ … }';
}
else {
const firstArrowIdx = object.description.indexOf('=>');
return firstArrowIdx >= 0 ?
object.description.substring(0, firstArrowIdx + 2) + ' …' :
object.description;
}
}
exports.getRemoteObjectPreview_function = getRemoteObjectPreview_function;
class VariableHandles {
constructor() {
this._variableHandles = new vscode_debugadapter_1.Handles(1);
this._consoleVariableHandles = new vscode_debugadapter_1.Handles(1e5);
}
onPaused() {
// Only reset the variableHandles, the console vars are still visible
this._variableHandles.reset();
}
create(value, context = 'variables') {
return this.getHandles(context).create(value);
}
get(handle) {
return this._variableHandles.get(handle) || this._consoleVariableHandles.get(handle);
}
getHandles(context) {
return context === 'repl' ?
this._consoleVariableHandles :
this._variableHandles;
}
}
exports.VariableHandles = VariableHandles;
function getCollectionNumPropsByPreview(object) {
let indexedVariables = 0;
let namedVariables = object.preview.properties.length + 1; // +1 for [[Entries]];
return { indexedVariables, namedVariables };
}
exports.getCollectionNumPropsByPreview = getCollectionNumPropsByPreview;
function getArrayNumPropsByPreview(object) {
let indexedVariables = 0;
const indexedProps = object.preview.properties
.filter(prop => isIndexedPropName(prop.name));
if (indexedProps.length) {
// +1 because (last index=0) => 1 prop
indexedVariables = parseInt(indexedProps[indexedProps.length - 1].name, 10) + 1;
}
const namedVariables = object.preview.properties.length - indexedProps.length + 2; // 2 for __proto__ and length
return { indexedVariables, namedVariables };
}
exports.getArrayNumPropsByPreview = getArrayNumPropsByPreview;
function createPrimitiveVariableWithValue(name, value, parentEvaluateName) {
return {
name,
value,
variablesReference: 0,
evaluateName: ChromeUtils.getEvaluateName(parentEvaluateName, name)
};
}
exports.createPrimitiveVariableWithValue = createPrimitiveVariableWithValue;
function createPropertyContainer(object, evaluateName) {
return new PropertyContainer(object.objectId, evaluateName);
}
exports.createPropertyContainer = createPropertyContainer;
function createPrimitiveVariable(name, object, parentEvaluateName, stringify) {
const value = getRemoteObjectPreview_primitive(object, stringify);
const variable = createPrimitiveVariableWithValue(name, value, parentEvaluateName);
variable.type = object.type;
return variable;
}
exports.createPrimitiveVariable = createPrimitiveVariable;
function createFunctionVariable(name, object, context, handles, parentEvaluateName) {
let value;
if (object.description) {
const firstBraceIdx = object.description.indexOf('{');
if (firstBraceIdx >= 0) {
value = object.description.substring(0, firstBraceIdx) + '{ … }';
}
else {
const firstArrowIdx = object.description.indexOf('=>');
value = firstArrowIdx >= 0 ?
object.description.substring(0, firstArrowIdx + 2) + ' …' :
object.description;
}
}
else {
value = 'function() { … }';
}
const evaluateName = ChromeUtils.getEvaluateName(parentEvaluateName, name);
return {
name,
value,
type: utils.uppercaseFirstLetter(object.type),
variablesReference: handles.create(new PropertyContainer(object.objectId, evaluateName), context),
evaluateName
};
}
exports.createFunctionVariable = createFunctionVariable;
//# sourceMappingURL=variables.js.map