mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-09 13:18:29 +08:00
This cleans up the (unnecessary) use of `Promise.resolve()` in the following files: - sdk/RemoteObject.js - sdk/RuntimeModel.js - sdk/Script.js - sdk/SDKModel.js - sdk/CompilerSourceMappingContentProvider.js - sdk/Connections.js - sdk/NetworkManager.js Bug: chromium:1161501 Change-Id: I404e3e175a3b41a2b4ca4cac1178c4d7254f4d11 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2653092 Auto-Submit: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Mathias Bynens <mathias@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org>
1481 lines
40 KiB
JavaScript
1481 lines
40 KiB
JavaScript
/*
|
||
* 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.
|
||
*/
|
||
|
||
import {DebuggerModel, FunctionDetails} from './DebuggerModel.js'; // eslint-disable-line no-unused-vars
|
||
import {RuntimeModel} from './RuntimeModel.js'; // eslint-disable-line no-unused-vars
|
||
|
||
export class RemoteObject {
|
||
/**
|
||
* This may not be an interface due to "instanceof RemoteObject" checks in the code.
|
||
*/
|
||
|
||
/**
|
||
* @param {*} value
|
||
* @return {!RemoteObject}
|
||
*/
|
||
static fromLocalObject(value) {
|
||
return new LocalJSONObject(value);
|
||
}
|
||
|
||
/**
|
||
* @param {!RemoteObject} remoteObject
|
||
* @return {string}
|
||
*/
|
||
static type(remoteObject) {
|
||
if (remoteObject === null) {
|
||
return 'null';
|
||
}
|
||
|
||
const type = typeof remoteObject;
|
||
if (type !== 'object' && type !== 'function') {
|
||
return type;
|
||
}
|
||
|
||
return remoteObject.type;
|
||
}
|
||
|
||
/**
|
||
* @param {string} description
|
||
* @return {string}
|
||
*/
|
||
static arrayNameFromDescription(description) {
|
||
return description.replace(_descriptionLengthParenRegex, '').replace(_descriptionLengthSquareRegex, '');
|
||
}
|
||
|
||
/**
|
||
* @param {!RemoteObject|!Protocol.Runtime.RemoteObject|!Protocol.Runtime.ObjectPreview} object
|
||
* @return {number}
|
||
*/
|
||
static arrayLength(object) {
|
||
if (object.subtype !== 'array' && object.subtype !== 'typedarray') {
|
||
return 0;
|
||
}
|
||
// Array lengths in V8-generated descriptions switched from square brackets to parentheses.
|
||
// Both formats are checked in case the front end is dealing with an old version of V8.
|
||
const parenMatches = object.description && object.description.match(_descriptionLengthParenRegex);
|
||
const squareMatches = object.description && object.description.match(_descriptionLengthSquareRegex);
|
||
return parenMatches ? parseInt(parenMatches[1], 10) : (squareMatches ? parseInt(squareMatches[1], 10) : 0);
|
||
}
|
||
|
||
/**
|
||
* @param {!RemoteObject|!Protocol.Runtime.RemoteObject|!Protocol.Runtime.ObjectPreview} object
|
||
* @return {number}
|
||
*/
|
||
static arrayBufferByteLength(object) {
|
||
if (object.subtype !== 'arraybuffer') {
|
||
return 0;
|
||
}
|
||
const matches = object.description && object.description.match(_descriptionLengthParenRegex);
|
||
return matches ? parseInt(matches[1], 10) : 0;
|
||
}
|
||
|
||
/**
|
||
* @param {*} object
|
||
* @return {?string}
|
||
*/
|
||
static unserializableDescription(object) {
|
||
const type = typeof object;
|
||
if (type === 'number') {
|
||
const description = String(object);
|
||
if (object === 0 && 1 / object < 0) {
|
||
return UnserializableNumber.Negative0;
|
||
}
|
||
if (description === UnserializableNumber.NaN || description === UnserializableNumber.Infinity ||
|
||
description === UnserializableNumber.NegativeInfinity) {
|
||
return description;
|
||
}
|
||
}
|
||
if (type === 'bigint') {
|
||
return object + 'n';
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @param {!Protocol.Runtime.RemoteObject|!RemoteObject|number|string|boolean|undefined|null|bigint} object
|
||
* @return {!Protocol.Runtime.CallArgument}
|
||
*/
|
||
static toCallArgument(object) {
|
||
const type = typeof object;
|
||
if (type === 'undefined') {
|
||
return {};
|
||
}
|
||
const unserializableDescription = RemoteObject.unserializableDescription(object);
|
||
if (type === 'number') {
|
||
if (unserializableDescription !== null) {
|
||
return {unserializableValue: unserializableDescription};
|
||
}
|
||
return {value: object};
|
||
}
|
||
if (type === 'bigint') {
|
||
return {unserializableValue: /** @type {!Protocol.Runtime.UnserializableValue} */ (unserializableDescription)};
|
||
}
|
||
if (type === 'string' || type === 'boolean') {
|
||
return {value: object};
|
||
}
|
||
|
||
if (!object) {
|
||
return {value: null};
|
||
}
|
||
|
||
// The unserializableValue is a function on RemoteObject's and a simple property on
|
||
// Protocol.Runtime.RemoteObject's.
|
||
const objectAsProtocolRemoteObject = /** @type {!Protocol.Runtime.RemoteObject} */ (object);
|
||
if (object instanceof RemoteObject) {
|
||
const unserializableValue = object.unserializableValue();
|
||
if (unserializableValue !== undefined) {
|
||
return {unserializableValue: unserializableValue};
|
||
}
|
||
} else if (objectAsProtocolRemoteObject.unserializableValue !== undefined) {
|
||
return {unserializableValue: objectAsProtocolRemoteObject.unserializableValue};
|
||
}
|
||
|
||
if (typeof objectAsProtocolRemoteObject.objectId !== 'undefined') {
|
||
return {objectId: objectAsProtocolRemoteObject.objectId};
|
||
}
|
||
|
||
return {value: objectAsProtocolRemoteObject.value};
|
||
}
|
||
|
||
/**
|
||
* @param {!RemoteObject} object
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
static async loadFromObjectPerProto(object, generatePreview) {
|
||
const result = await Promise.all([
|
||
object.getAllProperties(true /* accessorPropertiesOnly */, generatePreview),
|
||
object.getOwnProperties(generatePreview)
|
||
]);
|
||
const accessorProperties = result[0].properties;
|
||
const ownProperties = result[1].properties;
|
||
const internalProperties = result[1].internalProperties;
|
||
if (!ownProperties || !accessorProperties) {
|
||
return /** @type {!GetPropertiesResult} */ ({properties: null, internalProperties: null});
|
||
}
|
||
const propertiesMap = new Map();
|
||
const propertySymbols = [];
|
||
for (let i = 0; i < accessorProperties.length; i++) {
|
||
const property = accessorProperties[i];
|
||
if (property.symbol) {
|
||
propertySymbols.push(property);
|
||
} else {
|
||
propertiesMap.set(property.name, property);
|
||
}
|
||
}
|
||
for (let i = 0; i < ownProperties.length; i++) {
|
||
const property = ownProperties[i];
|
||
if (property.isAccessorProperty()) {
|
||
continue;
|
||
}
|
||
if (property.symbol) {
|
||
propertySymbols.push(property);
|
||
} else {
|
||
propertiesMap.set(property.name, property);
|
||
}
|
||
}
|
||
return {
|
||
properties: [...propertiesMap.values()].concat(propertySymbols),
|
||
internalProperties: internalProperties ? internalProperties : null
|
||
};
|
||
}
|
||
|
||
/**
|
||
* @return {?Protocol.Runtime.CustomPreview}
|
||
*/
|
||
customPreview() {
|
||
return null;
|
||
}
|
||
|
||
/** @return {!Protocol.Runtime.RemoteObjectId|undefined} */
|
||
get objectId() {
|
||
return 'Not implemented';
|
||
}
|
||
|
||
/** @return {string} */
|
||
get type() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/** @return {string|undefined} */
|
||
get subtype() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/** @return {*} */
|
||
get value() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/** @return {string|undefined} */
|
||
unserializableValue() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/** @return {string|undefined} */
|
||
get description() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/** @param {string|undefined} description*/
|
||
set description(description) {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/** @return {boolean} */
|
||
get hasChildren() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @return {!Protocol.Runtime.ObjectPreview|undefined}
|
||
*/
|
||
get preview() {
|
||
return undefined;
|
||
}
|
||
|
||
/**
|
||
* @return {?string}
|
||
*/
|
||
get className() {
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @return {number}
|
||
*/
|
||
arrayLength() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @return {number}
|
||
*/
|
||
arrayBufferByteLength() {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
getOwnProperties(generatePreview) {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @param {boolean} accessorPropertiesOnly
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
getAllProperties(accessorPropertiesOnly, generatePreview) {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @param {!Protocol.Runtime.CallArgument} name
|
||
* @return {!Promise<string|undefined>}
|
||
*/
|
||
async deleteProperty(name) {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @param {string|!Protocol.Runtime.CallArgument} name
|
||
* @param {string} value
|
||
* @return {!Promise<string|undefined>}
|
||
*/
|
||
async setPropertyValue(name, value) {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @param {function(this:Object, ...?):T} functionDeclaration
|
||
* @param {!Array<!Protocol.Runtime.CallArgument>=} args
|
||
* @return {!Promise<!CallFunctionResult>}
|
||
* @template T
|
||
*/
|
||
callFunction(functionDeclaration, args) {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
/**
|
||
* @param {function(this:Object, ...?):T} functionDeclaration
|
||
* @param {!Array<!Protocol.Runtime.CallArgument>|undefined} args
|
||
* @return {!Promise<T>}
|
||
* @template T
|
||
*/
|
||
callFunctionJSON(functionDeclaration, args) {
|
||
throw 'Not implemented';
|
||
}
|
||
|
||
release() {
|
||
}
|
||
|
||
/**
|
||
* @return {!DebuggerModel}
|
||
*/
|
||
debuggerModel() {
|
||
throw new Error('DebuggerModel-less object');
|
||
}
|
||
|
||
/**
|
||
* @return {!RuntimeModel}
|
||
*/
|
||
runtimeModel() {
|
||
throw new Error('RuntimeModel-less object');
|
||
}
|
||
|
||
/**
|
||
* @return {boolean}
|
||
*/
|
||
isNode() {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
export class RemoteObjectImpl extends RemoteObject {
|
||
/**
|
||
* @param {!RuntimeModel} runtimeModel
|
||
* @param {string|undefined} objectId
|
||
* @param {string} type
|
||
* @param {string|undefined} subtype
|
||
* @param {*} value
|
||
* @param {!Protocol.Runtime.UnserializableValue=} unserializableValue
|
||
* @param {string=} description
|
||
* @param {!Protocol.Runtime.ObjectPreview=} preview
|
||
* @param {!Protocol.Runtime.CustomPreview=} customPreview
|
||
* @param {string=} className
|
||
*/
|
||
constructor(
|
||
runtimeModel, objectId, type, subtype, value, unserializableValue, description, preview, customPreview,
|
||
className) {
|
||
super();
|
||
|
||
this._runtimeModel = runtimeModel;
|
||
this._runtimeAgent = runtimeModel.target().runtimeAgent();
|
||
|
||
this._type = type;
|
||
this._subtype = subtype;
|
||
if (objectId) {
|
||
// handle
|
||
this._objectId = objectId;
|
||
this._description = description;
|
||
this._hasChildren = (type !== 'symbol');
|
||
this._preview = preview;
|
||
} else {
|
||
this._description = description;
|
||
if (!this.description && unserializableValue) {
|
||
this._description = unserializableValue;
|
||
}
|
||
if (!this._description && (typeof value !== 'object' || value === null)) {
|
||
this._description = String(value);
|
||
}
|
||
this._hasChildren = false;
|
||
if (typeof unserializableValue === 'string') {
|
||
this._unserializableValue = unserializableValue;
|
||
if (unserializableValue === UnserializableNumber.Infinity ||
|
||
unserializableValue === UnserializableNumber.NegativeInfinity ||
|
||
unserializableValue === UnserializableNumber.Negative0 ||
|
||
unserializableValue === UnserializableNumber.NaN) {
|
||
this._value = Number(unserializableValue);
|
||
} else if (type === 'bigint' && unserializableValue.endsWith('n')) {
|
||
this._value = BigInt(unserializableValue.substring(0, unserializableValue.length - 1));
|
||
} else {
|
||
this._value = unserializableValue;
|
||
}
|
||
|
||
} else {
|
||
this._value = value;
|
||
}
|
||
}
|
||
this._customPreview = customPreview || null;
|
||
this._className = typeof className === 'string' ? className : null;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {?Protocol.Runtime.CustomPreview}
|
||
*/
|
||
customPreview() {
|
||
return this._customPreview;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {!Protocol.Runtime.RemoteObjectId|undefined}
|
||
*/
|
||
get objectId() {
|
||
return this._objectId;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string}
|
||
*/
|
||
get type() {
|
||
return this._type;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string|undefined}
|
||
*/
|
||
get subtype() {
|
||
return this._subtype;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {*}
|
||
*/
|
||
get value() {
|
||
return this._value;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string|undefined}
|
||
*/
|
||
unserializableValue() {
|
||
return this._unserializableValue;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string|undefined}
|
||
*/
|
||
get description() {
|
||
return this._description;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {string|undefined} description
|
||
*/
|
||
set description(description) {
|
||
this._description = description;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {boolean}
|
||
*/
|
||
get hasChildren() {
|
||
return this._hasChildren;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {!Protocol.Runtime.ObjectPreview|undefined}
|
||
*/
|
||
get preview() {
|
||
return this._preview;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {?string}
|
||
*/
|
||
get className() {
|
||
return this._className;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
getOwnProperties(generatePreview) {
|
||
return this.doGetProperties(true, false, generatePreview);
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {boolean} accessorPropertiesOnly
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
getAllProperties(accessorPropertiesOnly, generatePreview) {
|
||
return this.doGetProperties(false, accessorPropertiesOnly, generatePreview);
|
||
}
|
||
|
||
/**
|
||
* @param {!Protocol.Runtime.RemoteObject} object
|
||
* @return {!Promise<!RemoteObject>}
|
||
*/
|
||
async _createRemoteObject(object) {
|
||
return this._runtimeModel.createRemoteObject(object);
|
||
}
|
||
|
||
/**
|
||
* @param {boolean} ownProperties
|
||
* @param {boolean} accessorPropertiesOnly
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
async doGetProperties(ownProperties, accessorPropertiesOnly, generatePreview) {
|
||
if (!this._objectId) {
|
||
return /** @type {!GetPropertiesResult} */ ({properties: null, internalProperties: null});
|
||
}
|
||
|
||
const response = await this._runtimeAgent.invoke_getProperties(
|
||
{objectId: this._objectId, ownProperties, accessorPropertiesOnly, generatePreview});
|
||
if (response.getError()) {
|
||
return /** @type {!GetPropertiesResult} */ ({properties: null, internalProperties: null});
|
||
}
|
||
if (response.exceptionDetails) {
|
||
this._runtimeModel.exceptionThrown(Date.now(), response.exceptionDetails);
|
||
return /** @type {!GetPropertiesResult} */ ({properties: null, internalProperties: null});
|
||
}
|
||
const {result: properties = [], internalProperties = [], privateProperties = []} = response;
|
||
const result = [];
|
||
for (const property of properties) {
|
||
const propertyValue = property.value ? await this._createRemoteObject(property.value) : null;
|
||
const propertySymbol = property.symbol ? this._runtimeModel.createRemoteObject(property.symbol) : null;
|
||
const remoteProperty = new RemoteObjectProperty(
|
||
property.name, propertyValue, Boolean(property.enumerable), Boolean(property.writable),
|
||
Boolean(property.isOwn), Boolean(property.wasThrown), propertySymbol);
|
||
|
||
if (typeof property.value === 'undefined') {
|
||
if (property.get && property.get.type !== 'undefined') {
|
||
remoteProperty.getter = this._runtimeModel.createRemoteObject(property.get);
|
||
}
|
||
if (property.set && property.set.type !== 'undefined') {
|
||
remoteProperty.setter = this._runtimeModel.createRemoteObject(property.set);
|
||
}
|
||
}
|
||
result.push(remoteProperty);
|
||
}
|
||
for (const property of privateProperties) {
|
||
const propertyValue =
|
||
this._runtimeModel.createRemoteObject(/** @type {!Protocol.Runtime.RemoteObject} */ (property.value));
|
||
const remoteProperty = new RemoteObjectProperty(
|
||
property.name, propertyValue, true, true, true, false, undefined, false, undefined, true);
|
||
result.push(remoteProperty);
|
||
}
|
||
|
||
const internalPropertiesResult = [];
|
||
for (const property of internalProperties) {
|
||
if (!property.value) {
|
||
continue;
|
||
}
|
||
if (property.name === '[[StableObjectId]]') {
|
||
continue;
|
||
}
|
||
const propertyValue = this._runtimeModel.createRemoteObject(property.value);
|
||
internalPropertiesResult.push(
|
||
new RemoteObjectProperty(property.name, propertyValue, true, false, undefined, undefined, undefined, true));
|
||
}
|
||
return {properties: result, internalProperties: internalPropertiesResult};
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {string|!Protocol.Runtime.CallArgument} name
|
||
* @param {string} value
|
||
* @return {!Promise<string|undefined>}
|
||
*/
|
||
async setPropertyValue(name, value) {
|
||
if (!this._objectId) {
|
||
return 'Can’t set a property of non-object.';
|
||
}
|
||
|
||
const response = await this._runtimeAgent.invoke_evaluate({expression: value, silent: true});
|
||
if (response.getError() || response.exceptionDetails) {
|
||
return response.getError() ||
|
||
(response.result.type !== 'string' ? response.result.description :
|
||
/** @type {string} */ (response.result.value));
|
||
}
|
||
|
||
if (typeof name === 'string') {
|
||
name = RemoteObject.toCallArgument(name);
|
||
}
|
||
|
||
const resultPromise = this.doSetObjectPropertyValue(response.result, name);
|
||
|
||
if (response.result.objectId) {
|
||
this._runtimeAgent.invoke_releaseObject({objectId: response.result.objectId});
|
||
}
|
||
|
||
return resultPromise;
|
||
}
|
||
|
||
/**
|
||
* @param {!Protocol.Runtime.RemoteObject} result
|
||
* @param {!Protocol.Runtime.CallArgument} name
|
||
* @return {!Promise<string|undefined>}
|
||
*/
|
||
async doSetObjectPropertyValue(result, name) {
|
||
// This assignment may be for a regular (data) property, and for an accessor property (with getter/setter).
|
||
// Note the sensitive matter about accessor property: the property may be physically defined in some proto object,
|
||
// but logically it is bound to the object in question. JavaScript passes this object to getters/setters, not the object
|
||
// where property was defined; so do we.
|
||
const setPropertyValueFunction = 'function(a, b) { this[a] = b; }';
|
||
|
||
const argv = [name, RemoteObject.toCallArgument(result)];
|
||
const response = await this._runtimeAgent.invoke_callFunctionOn(
|
||
{objectId: this._objectId, functionDeclaration: setPropertyValueFunction, arguments: argv, silent: true});
|
||
const error = response.getError();
|
||
return error || response.exceptionDetails ? error || response.result.description : undefined;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {!Protocol.Runtime.CallArgument} name
|
||
* @return {!Promise<string|undefined>}
|
||
*/
|
||
async deleteProperty(name) {
|
||
if (!this._objectId) {
|
||
return 'Can’t delete a property of non-object.';
|
||
}
|
||
|
||
const deletePropertyFunction = 'function(a) { delete this[a]; return !(a in this); }';
|
||
const response = await this._runtimeAgent.invoke_callFunctionOn(
|
||
{objectId: this._objectId, functionDeclaration: deletePropertyFunction, arguments: [name], silent: true});
|
||
|
||
if (response.getError() || response.exceptionDetails) {
|
||
return response.getError() || response.result.description;
|
||
}
|
||
|
||
if (!response.result.value) {
|
||
return 'Failed to delete property.';
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {function(this:Object, ...?):T} functionDeclaration
|
||
* @param {!Array<!Protocol.Runtime.CallArgument>=} args
|
||
* @return {!Promise<!CallFunctionResult>}
|
||
* @template T
|
||
*/
|
||
async callFunction(functionDeclaration, args) {
|
||
const response = await this._runtimeAgent.invoke_callFunctionOn(
|
||
{objectId: this._objectId, functionDeclaration: functionDeclaration.toString(), arguments: args, silent: true});
|
||
if (response.getError()) {
|
||
return {object: null, wasThrown: false};
|
||
}
|
||
// TODO: release exceptionDetails object
|
||
return {
|
||
object: this._runtimeModel.createRemoteObject(response.result),
|
||
wasThrown: Boolean(response.exceptionDetails)
|
||
};
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {function(this:Object, ...?):T} functionDeclaration
|
||
* @param {!Array<!Protocol.Runtime.CallArgument>|undefined} args
|
||
* @return {!Promise<T>}
|
||
* @template T
|
||
*/
|
||
async callFunctionJSON(functionDeclaration, args) {
|
||
const response = await this._runtimeAgent.invoke_callFunctionOn({
|
||
objectId: this._objectId,
|
||
functionDeclaration: functionDeclaration.toString(),
|
||
arguments: args,
|
||
silent: true,
|
||
returnByValue: true,
|
||
});
|
||
|
||
if (!this._objectId) {
|
||
return this.value;
|
||
}
|
||
return response.getError() || response.exceptionDetails ? null : response.result.value;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
*/
|
||
release() {
|
||
if (!this._objectId) {
|
||
return;
|
||
}
|
||
this._runtimeAgent.invoke_releaseObject({objectId: this._objectId});
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {number}
|
||
*/
|
||
arrayLength() {
|
||
return RemoteObject.arrayLength(this);
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {number}
|
||
*/
|
||
arrayBufferByteLength() {
|
||
return RemoteObject.arrayBufferByteLength(this);
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {!DebuggerModel}
|
||
*/
|
||
debuggerModel() {
|
||
return this._runtimeModel.debuggerModel();
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {!RuntimeModel}
|
||
*/
|
||
runtimeModel() {
|
||
return this._runtimeModel;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {boolean}
|
||
*/
|
||
isNode() {
|
||
return Boolean(this._objectId) && this.type === 'object' && this.subtype === 'node';
|
||
}
|
||
}
|
||
|
||
export class ScopeRemoteObject extends RemoteObjectImpl {
|
||
/**
|
||
* @param {!RuntimeModel} runtimeModel
|
||
* @param {string|undefined} objectId
|
||
* @param {!ScopeRef} scopeRef
|
||
* @param {string} type
|
||
* @param {string|undefined} subtype
|
||
* @param {*} value
|
||
* @param {!Protocol.Runtime.UnserializableValue=} unserializableValue
|
||
* @param {string=} description
|
||
* @param {!Protocol.Runtime.ObjectPreview=} preview
|
||
*/
|
||
constructor(runtimeModel, objectId, scopeRef, type, subtype, value, unserializableValue, description, preview) {
|
||
super(runtimeModel, objectId, type, subtype, value, unserializableValue, description, preview);
|
||
this._scopeRef = scopeRef;
|
||
this._savedScopeProperties = undefined;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {boolean} ownProperties
|
||
* @param {boolean} accessorPropertiesOnly
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
async doGetProperties(ownProperties, accessorPropertiesOnly, generatePreview) {
|
||
if (accessorPropertiesOnly) {
|
||
return /** @type {!GetPropertiesResult} */ ({properties: [], internalProperties: []});
|
||
}
|
||
|
||
if (this._savedScopeProperties) {
|
||
// No need to reload scope variables, as the remote object never
|
||
// changes its properties. If variable is updated, the properties
|
||
// array is patched locally.
|
||
return {properties: this._savedScopeProperties.slice(), internalProperties: null};
|
||
}
|
||
|
||
const allProperties =
|
||
await super.doGetProperties(ownProperties, accessorPropertiesOnly, true /* generatePreview */);
|
||
if (this._scopeRef && Array.isArray(allProperties.properties)) {
|
||
this._savedScopeProperties = allProperties.properties.slice();
|
||
if (!this._scopeRef.callFrameId) {
|
||
for (const property of this._savedScopeProperties) {
|
||
property.writable = false;
|
||
}
|
||
}
|
||
}
|
||
return allProperties;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {!Protocol.Runtime.RemoteObject} result
|
||
* @param {!Protocol.Runtime.CallArgument} argumentName
|
||
* @return {!Promise<string|undefined>}
|
||
*/
|
||
async doSetObjectPropertyValue(result, argumentName) {
|
||
const name = /** @type {string} */ (argumentName.value);
|
||
const error = await this.debuggerModel().setVariableValue(
|
||
this._scopeRef.number, name, RemoteObject.toCallArgument(result),
|
||
/** @type {string} */ (this._scopeRef.callFrameId));
|
||
if (error) {
|
||
return error;
|
||
}
|
||
if (this._savedScopeProperties) {
|
||
for (const property of this._savedScopeProperties) {
|
||
if (property.name === name) {
|
||
property.value = this._runtimeModel.createRemoteObject(result);
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
export class ScopeRef {
|
||
/**
|
||
* @param {number} number
|
||
* @param {string=} callFrameId
|
||
*/
|
||
constructor(number, callFrameId) {
|
||
this.number = number;
|
||
this.callFrameId = callFrameId;
|
||
}
|
||
}
|
||
|
||
export class RemoteObjectProperty {
|
||
/**
|
||
* @param {string} name
|
||
* @param {?RemoteObject} value
|
||
* @param {boolean=} enumerable
|
||
* @param {boolean=} writable
|
||
* @param {boolean=} isOwn
|
||
* @param {boolean=} wasThrown
|
||
* @param {?RemoteObject=} symbol
|
||
* @param {boolean=} synthetic
|
||
* @param {function(string):!Promise<?RemoteObject>=} syntheticSetter
|
||
* @param {boolean=} isPrivate
|
||
*/
|
||
constructor(name, value, enumerable, writable, isOwn, wasThrown, symbol, synthetic, syntheticSetter, isPrivate) {
|
||
this.name = name;
|
||
if (value !== null) {
|
||
this.value = value;
|
||
}
|
||
this.enumerable = typeof enumerable !== 'undefined' ? enumerable : true;
|
||
const isNonSyntheticOrSyntheticWritable = !synthetic || Boolean(syntheticSetter);
|
||
this.writable = typeof writable !== 'undefined' ? writable : isNonSyntheticOrSyntheticWritable;
|
||
this.isOwn = Boolean(isOwn);
|
||
this.wasThrown = Boolean(wasThrown);
|
||
if (symbol) {
|
||
this.symbol = symbol;
|
||
}
|
||
this.synthetic = Boolean(synthetic);
|
||
if (syntheticSetter) {
|
||
this.syntheticSetter = syntheticSetter;
|
||
}
|
||
this.private = Boolean(isPrivate);
|
||
|
||
/** @type {(!RemoteObject|undefined)} */
|
||
this.getter;
|
||
/** @type {(!RemoteObject|undefined)} */
|
||
this.setter;
|
||
/** @type {(?RemoteObject|undefined)} */
|
||
this.value;
|
||
}
|
||
|
||
/**
|
||
* @param {string} expression
|
||
* @return {!Promise<boolean>}
|
||
*/
|
||
async setSyntheticValue(expression) {
|
||
if (!this.syntheticSetter) {
|
||
return false;
|
||
}
|
||
const result = await this.syntheticSetter(expression);
|
||
if (result) {
|
||
this.value = result;
|
||
}
|
||
return Boolean(result);
|
||
}
|
||
|
||
/**
|
||
* @return {boolean}
|
||
*/
|
||
isAccessorProperty() {
|
||
return Boolean(this.getter || this.setter);
|
||
}
|
||
}
|
||
|
||
// Below is a wrapper around a local object that implements the RemoteObject interface,
|
||
// which can be used by the UI code (primarily ObjectPropertiesSection).
|
||
// Note that only JSON-compliant objects are currently supported, as there's no provision
|
||
// for traversing prototypes, extracting class names via constructor, handling properties
|
||
// or functions.
|
||
|
||
export class LocalJSONObject extends RemoteObject {
|
||
/**
|
||
* @param {*} value
|
||
*/
|
||
constructor(value) {
|
||
super();
|
||
this._value = value;
|
||
/** @type {string} */
|
||
this._cachedDescription;
|
||
/** @type {!Array<!RemoteObjectProperty>} */
|
||
this._cachedChildren;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {!Protocol.Runtime.RemoteObjectId|undefined}
|
||
* */
|
||
get objectId() {
|
||
return undefined;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {*}
|
||
*/
|
||
get value() {
|
||
return this._value;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string|undefined}
|
||
*/
|
||
unserializableValue() {
|
||
const unserializableDescription = RemoteObject.unserializableDescription(this._value);
|
||
return unserializableDescription || undefined;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string}
|
||
*/
|
||
get description() {
|
||
if (this._cachedDescription) {
|
||
return this._cachedDescription;
|
||
}
|
||
|
||
/**
|
||
* @param {!RemoteObjectProperty} property
|
||
* @return {string}
|
||
* @this {LocalJSONObject}
|
||
*/
|
||
function formatArrayItem(property) {
|
||
return this._formatValue(property.value || null);
|
||
}
|
||
|
||
/**
|
||
* @param {!RemoteObjectProperty} property
|
||
* @return {string}
|
||
* @this {LocalJSONObject}
|
||
*/
|
||
function formatObjectItem(property) {
|
||
let name = property.name;
|
||
if (/^\s|\s$|^$|\n/.test(name)) {
|
||
name = '"' + name.replace(/\n/g, '\u21B5') + '"';
|
||
}
|
||
return name + ': ' + this._formatValue(property.value || null);
|
||
}
|
||
|
||
if (this.type === 'object') {
|
||
switch (this.subtype) {
|
||
case 'array':
|
||
this._cachedDescription = this._concatenate('[', ']', formatArrayItem.bind(this));
|
||
break;
|
||
case 'date':
|
||
this._cachedDescription = String(this._value);
|
||
break;
|
||
case 'null':
|
||
this._cachedDescription = 'null';
|
||
break;
|
||
default:
|
||
this._cachedDescription = this._concatenate('{', '}', formatObjectItem.bind(this));
|
||
}
|
||
} else {
|
||
this._cachedDescription = String(this._value);
|
||
}
|
||
|
||
return this._cachedDescription;
|
||
}
|
||
|
||
/**
|
||
* @param {?RemoteObject} value
|
||
* @return {string}
|
||
*/
|
||
_formatValue(value) {
|
||
if (!value) {
|
||
return 'undefined';
|
||
}
|
||
const description = value.description || '';
|
||
if (value.type === 'string') {
|
||
return '"' + description.replace(/\n/g, '\u21B5') + '"';
|
||
}
|
||
return description;
|
||
}
|
||
|
||
/**
|
||
* @param {string} prefix
|
||
* @param {string} suffix
|
||
* @param {function(!RemoteObjectProperty):string} formatProperty
|
||
* @return {string}
|
||
*/
|
||
_concatenate(prefix, suffix, formatProperty) {
|
||
const previewChars = 100;
|
||
|
||
let buffer = prefix;
|
||
const children = this._children();
|
||
for (let i = 0; i < children.length; ++i) {
|
||
const itemDescription = formatProperty(children[i]);
|
||
if (buffer.length + itemDescription.length > previewChars) {
|
||
buffer += ',…';
|
||
break;
|
||
}
|
||
if (i) {
|
||
buffer += ', ';
|
||
}
|
||
buffer += itemDescription;
|
||
}
|
||
buffer += suffix;
|
||
return buffer;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string}
|
||
*/
|
||
get type() {
|
||
return typeof this._value;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {string|undefined}
|
||
*/
|
||
get subtype() {
|
||
if (this._value === null) {
|
||
return 'null';
|
||
}
|
||
|
||
if (Array.isArray(this._value)) {
|
||
return 'array';
|
||
}
|
||
|
||
if (this._value instanceof Date) {
|
||
return 'date';
|
||
}
|
||
|
||
if (this._value instanceof ArrayBuffer || this._value instanceof SharedArrayBuffer) {
|
||
return 'arraybuffer';
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {boolean}
|
||
*/
|
||
get hasChildren() {
|
||
if ((typeof this._value !== 'object') || (this._value === null)) {
|
||
return false;
|
||
}
|
||
return Boolean(Object.keys(/** @type {!Object} */ (this._value)).length);
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
async getOwnProperties(generatePreview) {
|
||
return /** @type {!GetPropertiesResult} */ ({properties: this._children(), internalProperties: null});
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {boolean} accessorPropertiesOnly
|
||
* @param {boolean} generatePreview
|
||
* @return {!Promise<!GetPropertiesResult>}
|
||
*/
|
||
async getAllProperties(accessorPropertiesOnly, generatePreview) {
|
||
if (accessorPropertiesOnly) {
|
||
return /** @type {!GetPropertiesResult} */ ({properties: [], internalProperties: null});
|
||
}
|
||
return /** @type {!GetPropertiesResult} */ ({properties: this._children(), internalProperties: null});
|
||
}
|
||
|
||
/**
|
||
* @return {!Array.<!RemoteObjectProperty>}
|
||
*/
|
||
_children() {
|
||
if (!this.hasChildren) {
|
||
return [];
|
||
}
|
||
const value = /** @type {*} */ (this._value);
|
||
|
||
/**
|
||
* @param {string} propName
|
||
* @return {!RemoteObjectProperty}
|
||
*/
|
||
function buildProperty(propName) {
|
||
let propValue = value[propName];
|
||
if (!(propValue instanceof RemoteObject)) {
|
||
propValue = RemoteObject.fromLocalObject(propValue);
|
||
}
|
||
return new RemoteObjectProperty(propName, propValue);
|
||
}
|
||
if (!this._cachedChildren) {
|
||
this._cachedChildren = Object.keys(/** @type {!Object} */ (value)).map(buildProperty);
|
||
}
|
||
return this._cachedChildren;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @return {number}
|
||
*/
|
||
arrayLength() {
|
||
return Array.isArray(this._value) ? this._value.length : 0;
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {function(this:Object, ...?):T} functionDeclaration
|
||
* @param {!Array<!Protocol.Runtime.CallArgument>=} args
|
||
* @return {!Promise<!CallFunctionResult>}
|
||
* @template T
|
||
*/
|
||
async callFunction(functionDeclaration, args) {
|
||
const target = /** @type {!Object} */ (this._value);
|
||
const rawArgs = args ? args.map(arg => arg.value) : [];
|
||
|
||
let result;
|
||
let wasThrown = false;
|
||
try {
|
||
result = functionDeclaration.apply(target, rawArgs);
|
||
} catch (e) {
|
||
wasThrown = true;
|
||
}
|
||
|
||
const object = RemoteObject.fromLocalObject(result);
|
||
|
||
return /** @type {!CallFunctionResult} */ ({object, wasThrown});
|
||
}
|
||
|
||
/**
|
||
* @override
|
||
* @param {function(this:Object, ...?):T} functionDeclaration
|
||
* @param {!Array<!Protocol.Runtime.CallArgument>|undefined} args
|
||
* @return {!Promise<T>}
|
||
* @template T
|
||
*/
|
||
async callFunctionJSON(functionDeclaration, args) {
|
||
const target = /** @type {!Object} */ (this._value);
|
||
const rawArgs = args ? args.map(arg => arg.value) : [];
|
||
|
||
let result;
|
||
try {
|
||
result = functionDeclaration.apply(target, rawArgs);
|
||
} catch (e) {
|
||
result = null;
|
||
}
|
||
|
||
return /** @type {T} */ (result);
|
||
}
|
||
}
|
||
|
||
export class RemoteArrayBuffer {
|
||
/**
|
||
* @param {!RemoteObject} object
|
||
*/
|
||
constructor(object) {
|
||
if (object.type !== 'object' || object.subtype !== 'arraybuffer') {
|
||
throw new Error('Object is not an arraybuffer');
|
||
}
|
||
this._object = object;
|
||
}
|
||
|
||
/**
|
||
* @return {number}
|
||
*/
|
||
byteLength() {
|
||
return this._object.arrayBufferByteLength();
|
||
}
|
||
|
||
/**
|
||
* @param {*} start
|
||
* @param {*} end
|
||
* @return {!Promise<!Array<number>>}
|
||
*/
|
||
async bytes(start = 0, end = this.byteLength()) {
|
||
if (start < 0 || start >= this.byteLength()) {
|
||
throw new RangeError('start is out of range');
|
||
}
|
||
if (end < start || end > this.byteLength()) {
|
||
throw new RangeError('end is out of range');
|
||
}
|
||
|
||
return await this._object.callFunctionJSON(bytes, [{value: start}, {value: end - start}]);
|
||
|
||
/**
|
||
* @param {number} offset
|
||
* @param {number} length
|
||
* @return {!Array<number>}
|
||
* @this {*}
|
||
*/
|
||
function bytes(offset, length) {
|
||
return [...new Uint8Array(this, offset, length)];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return {!RemoteObject}
|
||
*/
|
||
object() {
|
||
return this._object;
|
||
}
|
||
}
|
||
|
||
export class RemoteArray {
|
||
/**
|
||
* @param {!RemoteObject} object
|
||
*/
|
||
constructor(object) {
|
||
this._object = object;
|
||
}
|
||
|
||
/**
|
||
* @param {?RemoteObject} object
|
||
* @return {!RemoteArray}
|
||
*/
|
||
static objectAsArray(object) {
|
||
if (!object || object.type !== 'object' || (object.subtype !== 'array' && object.subtype !== 'typedarray')) {
|
||
throw new Error('Object is empty or not an array');
|
||
}
|
||
return new RemoteArray(object);
|
||
}
|
||
|
||
/**
|
||
* @param {!Array<!RemoteObject>} objects
|
||
* @return {!Promise<!RemoteArray>}
|
||
*/
|
||
static createFromRemoteObjects(objects) {
|
||
if (!objects.length) {
|
||
throw new Error('Input array is empty');
|
||
}
|
||
const objectArguments = [];
|
||
for (let i = 0; i < objects.length; ++i) {
|
||
objectArguments.push(RemoteObject.toCallArgument(objects[i]));
|
||
}
|
||
return objects[0].callFunction(createArray, objectArguments).then(returnRemoteArray);
|
||
|
||
/**
|
||
* @return {!Array<*>}
|
||
*/
|
||
function createArray() {
|
||
if (arguments.length > 1) {
|
||
return new Array(arguments);
|
||
}
|
||
return [arguments[0]];
|
||
}
|
||
|
||
/**
|
||
* @param {!CallFunctionResult} result
|
||
* @return {!RemoteArray}
|
||
*/
|
||
function returnRemoteArray(result) {
|
||
if (result.wasThrown || !result.object) {
|
||
throw new Error('Call function throws exceptions or returns empty value');
|
||
}
|
||
return RemoteArray.objectAsArray(result.object);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param {number} index
|
||
* @return {!Promise<!RemoteObject>}
|
||
*/
|
||
at(index) {
|
||
if (index < 0 || index > this._object.arrayLength()) {
|
||
throw new Error('Out of range');
|
||
}
|
||
return this._object.callFunction(at, [RemoteObject.toCallArgument(index)]).then(assertCallFunctionResult);
|
||
|
||
/**
|
||
* @param {number} index
|
||
* @return {*}
|
||
* @this {*}
|
||
*/
|
||
function at(index) {
|
||
return this[index];
|
||
}
|
||
|
||
/**
|
||
* @param {!CallFunctionResult} result
|
||
* @return {!RemoteObject}
|
||
*/
|
||
function assertCallFunctionResult(result) {
|
||
if (result.wasThrown || !result.object) {
|
||
throw new Error('Exception in callFunction or result value is empty');
|
||
}
|
||
return result.object;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return {number}
|
||
*/
|
||
length() {
|
||
return this._object.arrayLength();
|
||
}
|
||
|
||
/**
|
||
* @param {function(!RemoteObject):!Promise<T>} func
|
||
* @return {!Promise<!Array<T>>}
|
||
* @template T
|
||
*/
|
||
map(func) {
|
||
const promises = [];
|
||
for (let i = 0; i < this.length(); ++i) {
|
||
promises.push(this.at(i).then(func));
|
||
}
|
||
return Promise.all(promises);
|
||
}
|
||
|
||
/**
|
||
* @return {!RemoteObject}
|
||
*/
|
||
object() {
|
||
return this._object;
|
||
}
|
||
}
|
||
|
||
export class RemoteFunction {
|
||
/**
|
||
* @param {!RemoteObject} object
|
||
*/
|
||
constructor(object) {
|
||
this._object = object;
|
||
}
|
||
|
||
/**
|
||
* @param {?RemoteObject} object
|
||
* @return {!RemoteFunction}
|
||
*/
|
||
static objectAsFunction(object) {
|
||
if (!object || object.type !== 'function') {
|
||
throw new Error('Object is empty or not a function');
|
||
}
|
||
return new RemoteFunction(object);
|
||
}
|
||
|
||
/**
|
||
* @return {!Promise<!RemoteObject>}
|
||
*/
|
||
targetFunction() {
|
||
return this._object.getOwnProperties(false /* generatePreview */).then(targetFunction.bind(this));
|
||
|
||
/**
|
||
* @param {!GetPropertiesResult} ownProperties
|
||
* @return {!RemoteObject}
|
||
* @this {RemoteFunction}
|
||
*/
|
||
function targetFunction(ownProperties) {
|
||
if (!ownProperties.internalProperties) {
|
||
return this._object;
|
||
}
|
||
const internalProperties = ownProperties.internalProperties;
|
||
for (const property of internalProperties) {
|
||
if (property.name === '[[TargetFunction]]') {
|
||
return /** @type {!RemoteObject} */ (property.value);
|
||
}
|
||
}
|
||
return this._object;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return {!Promise<?FunctionDetails>}
|
||
*/
|
||
targetFunctionDetails() {
|
||
return this.targetFunction().then(functionDetails.bind(this));
|
||
|
||
/**
|
||
* @param {!RemoteObject} targetFunction
|
||
* @return {!Promise<?FunctionDetails>}
|
||
* @this {RemoteFunction}
|
||
*/
|
||
function functionDetails(targetFunction) {
|
||
const boundReleaseFunctionDetails =
|
||
releaseTargetFunction.bind(null, this._object !== targetFunction ? targetFunction : null);
|
||
return targetFunction.debuggerModel().functionDetailsPromise(targetFunction).then(boundReleaseFunctionDetails);
|
||
}
|
||
|
||
/**
|
||
* @param {?RemoteObject} targetFunction
|
||
* @param {?FunctionDetails} functionDetails
|
||
* @return {?FunctionDetails}
|
||
*/
|
||
function releaseTargetFunction(targetFunction, functionDetails) {
|
||
if (targetFunction) {
|
||
targetFunction.release();
|
||
}
|
||
return functionDetails;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return {!RemoteObject}
|
||
*/
|
||
object() {
|
||
return this._object;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @const
|
||
* @type {!RegExp}
|
||
*/
|
||
const _descriptionLengthParenRegex = /\(([0-9]+)\)/;
|
||
|
||
/**
|
||
* @const
|
||
* @type {!RegExp}
|
||
*/
|
||
const _descriptionLengthSquareRegex = /\[([0-9]+)\]/;
|
||
|
||
/**
|
||
* @const
|
||
* @enum {!Protocol.Runtime.UnserializableValue}
|
||
*/
|
||
const UnserializableNumber = {
|
||
Negative0: /** @type {!Protocol.Runtime.UnserializableValue} */ ('-0'),
|
||
NaN: /** @type {!Protocol.Runtime.UnserializableValue} */ ('NaN'),
|
||
Infinity: /** @type {!Protocol.Runtime.UnserializableValue} */ ('Infinity'),
|
||
NegativeInfinity: /** @type {!Protocol.Runtime.UnserializableValue} */ ('-Infinity')
|
||
};
|
||
|
||
/**
|
||
* @typedef {{object: ?RemoteObject, wasThrown: (boolean|undefined)}}
|
||
*/
|
||
// @ts-ignore typedef
|
||
export let CallFunctionResult;
|
||
|
||
/**
|
||
* @typedef {{properties: ?Array<!RemoteObjectProperty>, internalProperties: ?Array<!RemoteObjectProperty>}}
|
||
*/
|
||
// @ts-ignore typedef
|
||
export let GetPropertiesResult;
|