| /* |
| * 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. |
| */ |
| |
| export default class RemoteObject { |
| /** |
| * This may not be an interface due to "instanceof SDK.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.match(_descriptionLengthParenRegex); |
| const squareMatches = object.description.match(_descriptionLengthSquareRegex); |
| return parenMatches ? parseInt(parenMatches[1], 10) : (squareMatches ? parseInt(squareMatches[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. |
| if (object instanceof RemoteObject) { |
| const unserializableValue = object.unserializableValue(); |
| if (unserializableValue !== undefined) { |
| return {unserializableValue: unserializableValue}; |
| } |
| } else if (object.unserializableValue !== undefined) { |
| return {unserializableValue: object.unserializableValue}; |
| } |
| |
| if (typeof object.objectId !== 'undefined') { |
| return {objectId: object.objectId}; |
| } |
| |
| return {value: object.value}; |
| } |
| |
| /** |
| * @param {!RemoteObject} object |
| * @param {boolean} generatePreview |
| * @return {!Promise<!SDK.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 {!SDK.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.valuesArray().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'; |
| } |
| |
| /** @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'; |
| } |
| |
| /** |
| * @param {boolean} generatePreview |
| * @return {!Promise<!SDK.GetPropertiesResult>} |
| */ |
| getOwnProperties(generatePreview) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @return {!Promise<!SDK.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, ...)} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>=} args |
| * @return {!Promise<!SDK.CallFunctionResult>} |
| */ |
| 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 {!SDK.DebuggerModel} |
| */ |
| debuggerModel() { |
| throw new Error('DebuggerModel-less object'); |
| } |
| |
| /** |
| * @return {!SDK.RuntimeModel} |
| */ |
| runtimeModel() { |
| throw new Error('RuntimeModel-less object'); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isNode() { |
| return false; |
| } |
| } |
| |
| export class RemoteObjectImpl extends RemoteObject { |
| /** |
| * @param {!SDK.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 = 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 |
| * @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<!SDK.GetPropertiesResult>} |
| */ |
| getOwnProperties(generatePreview) { |
| return this.doGetProperties(true, false, generatePreview); |
| } |
| |
| /** |
| * @override |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @return {!Promise<!SDK.GetPropertiesResult>} |
| */ |
| getAllProperties(accessorPropertiesOnly, generatePreview) { |
| return this.doGetProperties(false, accessorPropertiesOnly, generatePreview); |
| } |
| |
| /** |
| * @param {boolean} ownProperties |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @return {!Promise<!SDK.GetPropertiesResult>} |
| */ |
| async doGetProperties(ownProperties, accessorPropertiesOnly, generatePreview) { |
| if (!this._objectId) { |
| return /** @type {!SDK.GetPropertiesResult} */ ({properties: null, internalProperties: null}); |
| } |
| |
| const response = await this._runtimeAgent.invoke_getProperties( |
| {objectId: this._objectId, ownProperties, accessorPropertiesOnly, generatePreview}); |
| if (response[Protocol.Error]) { |
| return /** @type {!SDK.GetPropertiesResult} */ ({properties: null, internalProperties: null}); |
| } |
| if (response.exceptionDetails) { |
| this._runtimeModel.exceptionThrown(Date.now(), response.exceptionDetails); |
| return /** @type {!SDK.GetPropertiesResult} */ ({properties: null, internalProperties: null}); |
| } |
| const {result: properties = [], internalProperties = [], privateProperties = []} = response; |
| const result = []; |
| for (const property of properties) { |
| const propertyValue = property.value ? this._runtimeModel.createRemoteObject(property.value) : null; |
| const propertySymbol = property.symbol ? this._runtimeModel.createRemoteObject(property.symbol) : null; |
| const remoteProperty = new RemoteObjectProperty( |
| property.name, propertyValue, !!property.enumerable, !!property.writable, !!property.isOwn, |
| !!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(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[Protocol.Error] || response.exceptionDetails) { |
| return response[Protocol.Error] || |
| (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.releaseObject(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[Protocol.Error]; |
| 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[Protocol.Error] || response.exceptionDetails) { |
| return response[Protocol.Error] || response.result.description; |
| } |
| |
| if (!response.result.value) { |
| return 'Failed to delete property.'; |
| } |
| } |
| |
| /** |
| * @override |
| * @param {function(this:Object, ...)} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>=} args |
| * @return {!Promise<!SDK.CallFunctionResult>} |
| */ |
| async callFunction(functionDeclaration, args) { |
| const response = await this._runtimeAgent.invoke_callFunctionOn( |
| {objectId: this._objectId, functionDeclaration: functionDeclaration.toString(), arguments: args, silent: true}); |
| if (response[Protocol.Error]) { |
| return {object: null, wasThrown: false}; |
| } |
| // TODO: release exceptionDetails object |
| return {object: this._runtimeModel.createRemoteObject(response.result), wasThrown: !!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 |
| }); |
| return response[Protocol.Error] || response.exceptionDetails ? null : response.result.value; |
| } |
| |
| /** |
| * @override |
| */ |
| release() { |
| if (!this._objectId) { |
| return; |
| } |
| this._runtimeAgent.releaseObject(this._objectId); |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| arrayLength() { |
| return RemoteObject.arrayLength(this); |
| } |
| |
| /** |
| * @override |
| * @return {!SDK.DebuggerModel} |
| */ |
| debuggerModel() { |
| return this._runtimeModel.debuggerModel(); |
| } |
| |
| /** |
| * @override |
| * @return {!SDK.RuntimeModel} |
| */ |
| runtimeModel() { |
| return this._runtimeModel; |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| isNode() { |
| return !!this._objectId && this.type === 'object' && this.subtype === 'node'; |
| } |
| } |
| |
| export class ScopeRemoteObject extends RemoteObjectImpl { |
| /** |
| * @param {!SDK.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<!SDK.GetPropertiesResult>} |
| */ |
| async doGetProperties(ownProperties, accessorPropertiesOnly, generatePreview) { |
| if (accessorPropertiesOnly) { |
| return /** @type {!SDK.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), 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); |
| } |
| } |
| } |
| } |
| } |
| |
| export class ScopeRef { |
| /** |
| * @param {number} number |
| * @param {string=} callFrameId |
| */ |
| constructor(number, callFrameId) { |
| this.number = number; |
| this.callFrameId = callFrameId; |
| } |
| } |
| |
| /** |
| * @unrestricted |
| */ |
| 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 || !!syntheticSetter; |
| this.writable = typeof writable !== 'undefined' ? writable : isNonSyntheticOrSyntheticWritable; |
| this.isOwn = !!isOwn; |
| this.wasThrown = !!wasThrown; |
| if (symbol) { |
| this.symbol = symbol; |
| } |
| this.synthetic = !!synthetic; |
| if (syntheticSetter) { |
| this.syntheticSetter = syntheticSetter; |
| } |
| this.private = !!isPrivate; |
| } |
| |
| /** |
| * @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 !!result; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isAccessorProperty() { |
| return !!(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); |
| } |
| |
| /** |
| * @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); |
| } |
| |
| if (this.type === 'object') { |
| switch (this.subtype) { |
| case 'array': |
| this._cachedDescription = this._concatenate('[', ']', formatArrayItem.bind(this)); |
| break; |
| case 'date': |
| this._cachedDescription = '' + 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)} 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 += ',\u2026'; |
| 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'; |
| } |
| |
| return undefined; |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| get hasChildren() { |
| if ((typeof this._value !== 'object') || (this._value === null)) { |
| return false; |
| } |
| return !!Object.keys(/** @type {!Object} */ (this._value)).length; |
| } |
| |
| /** |
| * @override |
| * @param {boolean} generatePreview |
| * @return {!Promise<!SDK.GetPropertiesResult>} |
| */ |
| getOwnProperties(generatePreview) { |
| return Promise.resolve( |
| /** @type {!SDK.GetPropertiesResult} */ ({properties: this._children(), internalProperties: null})); |
| } |
| |
| /** |
| * @override |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @return {!Promise<!SDK.GetPropertiesResult>} |
| */ |
| getAllProperties(accessorPropertiesOnly, generatePreview) { |
| if (accessorPropertiesOnly) { |
| return Promise.resolve(/** @type {!SDK.GetPropertiesResult} */ ({properties: [], internalProperties: null})); |
| } else { |
| return Promise.resolve( |
| /** @type {!SDK.GetPropertiesResult} */ ({properties: this._children(), internalProperties: null})); |
| } |
| } |
| |
| /** |
| * @return {!Array.<!RemoteObjectProperty>} |
| */ |
| _children() { |
| if (!this.hasChildren) { |
| return []; |
| } |
| const value = /** @type {!Object} */ (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(value).map(buildProperty); |
| } |
| return this._cachedChildren; |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| arrayLength() { |
| return Array.isArray(this._value) ? this._value.length : 0; |
| } |
| |
| /** |
| * @override |
| * @param {function(this:Object, ...)} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>=} args |
| * @return {!Promise<!SDK.CallFunctionResult>} |
| */ |
| 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 Promise.resolve( |
| /** @type {!SDK.CallFunctionResult} */ ({object, wasThrown})); |
| } |
| |
| /** |
| * @override |
| * @param {function(this:Object, ...):T} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>|undefined} args |
| * @return {!Promise<T>} |
| * @template T |
| */ |
| 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 Promise.resolve(result); |
| } |
| } |
| |
| 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 {!SDK.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); |
| |
| /** |
| * @suppressReceiverCheck |
| * @param {number} index |
| * @return {*} |
| * @this {!Object} |
| */ |
| function at(index) { |
| return this[index]; |
| } |
| |
| /** |
| * @param {!SDK.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 {!SDK.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 property.value; |
| } |
| } |
| return this._object; |
| } |
| } |
| |
| /** |
| * @return {!Promise<?SDK.DebuggerModel.FunctionDetails>} |
| */ |
| targetFunctionDetails() { |
| return this.targetFunction().then(functionDetails.bind(this)); |
| |
| /** |
| * @param {!RemoteObject} targetFunction |
| * @return {!Promise<?SDK.DebuggerModel.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 {?SDK.DebuggerModel.FunctionDetails} functionDetails |
| * @return {?SDK.DebuggerModel.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') |
| }; |
| |
| /* Legacy exported object */ |
| self.SDK = self.SDK || {}; |
| |
| /* Legacy exported object */ |
| SDK = SDK || {}; |
| |
| /** @constructor */ |
| SDK.RemoteObject = RemoteObject; |
| |
| /** @constructor */ |
| SDK.RemoteObjectImpl = RemoteObjectImpl; |
| |
| /** @constructor */ |
| SDK.ScopeRemoteObject = ScopeRemoteObject; |
| |
| /** @constructor */ |
| SDK.ScopeRef = ScopeRef; |
| |
| /** @constructor */ |
| SDK.RemoteObjectProperty = RemoteObjectProperty; |
| |
| /** @constructor */ |
| SDK.LocalJSONObject = LocalJSONObject; |
| |
| /** @constructor */ |
| SDK.RemoteArray = RemoteArray; |
| |
| /** @constructor */ |
| SDK.RemoteFunction = RemoteFunction; |
| |
| /** |
| * @typedef {{object: ?RemoteObject, wasThrown: (boolean|undefined)}} |
| */ |
| SDK.CallFunctionResult; |
| |
| /** |
| * @typedef {{properties: ?Array<!RemoteObjectProperty>, internalProperties: ?Array<!RemoteObjectProperty>}} |
| */ |
| SDK.GetPropertiesResult; |