blueshell
Advanced tools
Comparing version 3.6.0-btvBreakpoints.9 to 3.6.0-btvBreakpoints.10
import { BaseNode, BlueshellState } from '../models'; | ||
export declare class DuplicateNodeAdded extends Error { | ||
constructor(path: string); | ||
} | ||
export declare class APIFunctionNotFound extends Error { | ||
constructor(apiFunction: string); | ||
} | ||
export declare class NodeManager<S extends BlueshellState, E> { | ||
@@ -3,0 +9,0 @@ private nodePathMap; |
"use strict"; | ||
/* eslint-disable no-console */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NodeManager = void 0; | ||
exports.NodeManager = exports.APIFunctionNotFound = exports.DuplicateNodeAdded = void 0; | ||
const models_1 = require("../models"); | ||
const ws_1 = require("ws"); | ||
const inspector_1 = require("inspector"); | ||
const nodeManagerHelper_1 = require("./nodeManagerHelper"); | ||
class DuplicateNodeAdded extends Error { | ||
constructor(path) { | ||
super(`Key ${path} already exists! Cannot add new node.`); | ||
} | ||
} | ||
exports.DuplicateNodeAdded = DuplicateNodeAdded; | ||
class APIFunctionNotFound extends Error { | ||
constructor(apiFunction) { | ||
super(`Unknown request type: ${apiFunction}`); | ||
} | ||
} | ||
exports.APIFunctionNotFound = APIFunctionNotFound; | ||
// Manages information about what nodes are available in the BT for debugging (nodes must be registered | ||
@@ -24,91 +37,96 @@ // when they become available and unregistered when they are no longer available) | ||
this.session.post('Debugger.enable', () => { }); | ||
if (process.env.NODE_ENV !== 'test') { | ||
this.server = new ws_1.Server({ | ||
host: 'localhost', | ||
port: 8990, | ||
}); | ||
// should be empty but clear everything for good measure | ||
this.breakpointInfoMap.forEach((breakpointInfo, nodePathAndMethodName) => { | ||
this.session.post('Debugger.removeBreakpoint', { | ||
breakpointId: breakpointInfo.breakpointId, | ||
}, () => { | ||
this.breakpointInfoMap.delete(nodePathAndMethodName); | ||
this.server = new ws_1.Server({ | ||
host: 'localhost', | ||
port: 8990, | ||
}); | ||
// should be empty but clear everything for good measure | ||
this.breakpointInfoMap.forEach(async (breakpointInfo, nodePathAndMethodName) => { | ||
const success = await nodeManagerHelper_1.RuntimeWrappers.removeBreakpointFromFunction(this.session, breakpointInfo); | ||
if (success) { | ||
this.breakpointInfoMap.delete(nodePathAndMethodName); | ||
} | ||
}); | ||
this.breakpointInfoMap.clear(); | ||
global.breakpointMethods.clear(); | ||
// setup the connection handler | ||
this.server.on('connection', (clientSocket) => { | ||
// send the current cached breakpoints to the client if the client reconnects | ||
this.breakpointInfoMap.forEach((breakpointInfo) => { | ||
breakpointInfo.breakpoints.forEach((breakpoint) => { | ||
clientSocket.send(JSON.stringify({ | ||
request: 'placeBreakpoint', | ||
nodePath: breakpoint.nodePath, | ||
methodName: breakpointInfo.methodInfo.methodName, | ||
nodeName: breakpoint.nodeName, | ||
nodeParent: breakpoint.nodeParent, | ||
condition: breakpoint.condition, | ||
success: true, | ||
})); | ||
}); | ||
}); | ||
this.breakpointInfoMap.clear(); | ||
global.breakpointMethods.clear(); | ||
// setup the connection handler | ||
this.server.on('connection', (clientSocket) => { | ||
// send the current cached breakpoints to the client if the client reconnects | ||
this.breakpointInfoMap.forEach((breakpointInfo) => { | ||
breakpointInfo.breakpoints.forEach((breakpoint) => { | ||
clientSocket.on('message', async (data) => { | ||
const dataObj = JSON.parse(data); | ||
// message should always have a request and nodePath | ||
const request = dataObj.request; | ||
const nodePath = dataObj.nodePath; | ||
switch (request) { | ||
// client is requesting the methods for a given node path | ||
case 'getMethodsForNode': { | ||
let methodInfo; | ||
let success = true; | ||
try { | ||
methodInfo = this.getMethodsForNode(nodePath); | ||
} | ||
catch (_a) { | ||
success = false; | ||
} | ||
clientSocket.send(JSON.stringify({ | ||
request: 'getMethodsForNode', | ||
success, | ||
nodePath, | ||
...methodInfo, | ||
})); | ||
break; | ||
} | ||
// client is requesting to add (or modify) a breakpoint for a given node path/method | ||
case 'placeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
const condition = dataObj.condition; | ||
const success = await this.setBreakpoint(nodePath, methodName, condition); | ||
const node = this.nodePathMap.get(nodePath); | ||
const nodeName = node === null || node === void 0 ? void 0 : node.name; | ||
const nodeParent = node === null || node === void 0 ? void 0 : node.parent; | ||
clientSocket.send(JSON.stringify({ | ||
request: 'placeBreakpoint', | ||
nodePath: breakpoint.nodePath, | ||
methodName: breakpointInfo.methodInfo.methodName, | ||
nodeName: breakpoint.nodeName, | ||
nodeParent: breakpoint.nodeParent, | ||
condition: breakpoint.condition, | ||
success: true, | ||
nodePath: node === null || node === void 0 ? void 0 : node.path, | ||
methodName, | ||
nodeName, | ||
nodeParent, | ||
condition, | ||
success, | ||
})); | ||
}); | ||
}); | ||
clientSocket.on('message', (data) => { | ||
const dataObj = JSON.parse(data); | ||
// message should always have a request and nodePath | ||
const request = dataObj.request; | ||
const nodePath = dataObj.nodePath; | ||
switch (request) { | ||
// client is requesting the methods for a given node path | ||
case 'getMethodsForNode': { | ||
const methodInfo = this.getMethodsForNode(nodePath); | ||
clientSocket.send(JSON.stringify({ | ||
request: 'getMethodsForNode', | ||
nodePath, | ||
...methodInfo, | ||
})); | ||
break; | ||
} | ||
// client is requesting to add (or modify) a breakpoint for a given node path/method | ||
case 'placeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
const condition = dataObj.condition; | ||
this.setBreakpoint(nodePath, methodName, condition, (success) => { | ||
const node = this.nodePathMap.get(nodePath); | ||
const nodeName = node === null || node === void 0 ? void 0 : node.name; | ||
const nodeParent = node === null || node === void 0 ? void 0 : node.parent; | ||
clientSocket.send(JSON.stringify({ | ||
request: 'placeBreakpoint', | ||
nodePath: node === null || node === void 0 ? void 0 : node.path, | ||
methodName, | ||
nodeName, | ||
nodeParent, | ||
condition, | ||
success, | ||
})); | ||
}); | ||
break; | ||
} | ||
// client is requesting to remove a breakpoint by node path and method name | ||
case 'removeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
this.removeBreakpoint(nodePath, methodName, (success) => { | ||
const node = this.nodePathMap.get(nodePath); | ||
const nodeName = node === null || node === void 0 ? void 0 : node.name; | ||
const nodeParent = node === null || node === void 0 ? void 0 : node.parent; | ||
clientSocket.send(JSON.stringify({ | ||
request: 'removeBreakpoint', | ||
nodePath: node === null || node === void 0 ? void 0 : node.path, | ||
methodName, | ||
success, | ||
})); | ||
}); | ||
break; | ||
} | ||
default: | ||
throw new Error(`Unknown request type: ${dataObj.request}`); | ||
break; | ||
} | ||
}); | ||
// client is requesting to remove a breakpoint by node path and method name | ||
case 'removeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
const success = await this.removeBreakpoint(nodePath, methodName); | ||
const node = this.nodePathMap.get(nodePath); | ||
clientSocket.send(JSON.stringify({ | ||
request: 'removeBreakpoint', | ||
nodePath: node === null || node === void 0 ? void 0 : node.path, | ||
methodName, | ||
success, | ||
})); | ||
break; | ||
} | ||
default: | ||
clientSocket.send(JSON.stringify({ | ||
request: dataObj.request, | ||
success: false, | ||
err: new APIFunctionNotFound(dataObj.request).message | ||
})); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
@@ -122,38 +140,8 @@ // Returns the list of methods (and which class they are inherited from) for the | ||
else { | ||
let node = this.nodePathMap.get(nodePath); | ||
const node = this.nodePathMap.get(nodePath); | ||
const nodeName = node.name; | ||
const nodeParent = node.parent; | ||
const setOfMethods = new Set(); | ||
const methodsData = []; | ||
do { | ||
const methods = Object.getOwnPropertyNames(node).filter((prop) => { | ||
const nodePropDescriptor = Object.getOwnPropertyDescriptor(node, prop); | ||
// if the prop name is a getter or setter, if we simply just check that it's a function | ||
// that will end up invoking the getter or setter, which could lead to a crash | ||
if (nodePropDescriptor && (nodePropDescriptor.get || nodePropDescriptor.set)) { | ||
return true; | ||
} | ||
return typeof node[prop] === 'function'; | ||
}); | ||
const className = node.constructor.name; | ||
methods.forEach((methodName) => { | ||
// de-duplicate any inherited methods | ||
if (!setOfMethods.has(methodName)) { | ||
setOfMethods.add(methodName); | ||
methodsData.push({ methodName, className }); | ||
} | ||
}); | ||
// climb up the inheritance tree until we get to Object | ||
node = Object.getPrototypeOf(node); | ||
} while (!!node && node.constructor.name !== 'Object'); | ||
const listOfMethods = nodeManagerHelper_1.Utils.getMethodInfoForObject(node); | ||
return { | ||
listOfMethods: methodsData.sort((a, b) => { | ||
if (a.methodName < b.methodName) { | ||
return -1; | ||
} | ||
if (a.methodName > b.methodName) { | ||
return 1; | ||
} | ||
return 0; | ||
}), | ||
listOfMethods, | ||
nodeName, | ||
@@ -165,3 +153,3 @@ nodeParent | ||
// Uses the node inspector to set a breakpoint using the specified node and the details in breakpointInfo | ||
_setBreakpoint(key, node, breakpointInfo, callback) { | ||
async _setBreakpoint(key, node, breakpointInfo) { | ||
const nodeName = node.name; | ||
@@ -176,4 +164,3 @@ // find the class in the inheritance chain which contains the method or property | ||
console.error(`Could not find method ${breakpointInfo.methodInfo.methodName} in inheritance chain for ${nodeName}`); | ||
callback(false); | ||
return; | ||
return false; | ||
} | ||
@@ -191,60 +178,19 @@ const methodPropertyDescriptor = Object.getOwnPropertyDescriptor(node, breakpointInfo.methodInfo.methodName); | ||
} | ||
this.session.post('Runtime.evaluate', { expression: `global.breakpointMethods.get('${key}')` }, (err, { result }) => { | ||
if (err) { | ||
console.error(`NodeManager - set breakpoint - Error in Runtime.evaluate for: ${key}`, err); | ||
callback(false); | ||
return; | ||
} | ||
console.log(`NodeManager - set breakpoint - got result from Runtime.evaluate for: ${key}`); | ||
const objectId = result.objectId; | ||
this.session.post('Runtime.getProperties', { objectId }, (err, result) => { | ||
if (err) { | ||
console.error(`NodeManager - set breakpoint - Error in Runtime.getProperties for ${key}`, err); | ||
callback(false); | ||
return; | ||
} | ||
console.log(`NodeManager - set breakpoint - got result from Runtime.getProperties for: ${key}`); | ||
const funcObjId = result.internalProperties[0].value.objectId; | ||
// build up the condition for each node that has a breakpoint at this class/method | ||
let condition = ''; | ||
let first = true; | ||
[...breakpointInfo.breakpoints].forEach(([key, breakpointData]) => { | ||
if (first) { | ||
first = false; | ||
} | ||
else { | ||
condition = condition + ' || '; | ||
} | ||
condition = condition + | ||
`(this.path === '${breakpointData.nodePath}'` + | ||
(!!breakpointData.condition ? ` && ${breakpointData.condition}` : '') | ||
+ ')'; | ||
}); | ||
// do the magic! | ||
this.session.post('Debugger.setBreakpointOnFunctionCall', { | ||
objectId: funcObjId, | ||
condition, | ||
}, (err, result) => { | ||
if (err) { | ||
console.error(`NodeManager - set breakpoint - Error in \ | ||
Debugger.setBreakpointOnFunctionCall for: ${key}`, err); | ||
callback(false); | ||
return; | ||
} | ||
if (!result) { | ||
console.error(`NodeManager - set breakpoint - Got no result in \ | ||
Debugger.setBreakpointOnFunctionCall for: ${key}`); | ||
callback(false); | ||
return; | ||
} | ||
console.log(`NodeManager - set breakpoint - breakpoint set successfully: ${key}`); | ||
breakpointInfo.breakpointId = result.breakpointId; // HACK: types are not defined | ||
callback(true); | ||
}); | ||
}); | ||
}); | ||
const runtimeEvaluate = await nodeManagerHelper_1.RuntimeWrappers.getObjectIdFromRuntimeEvaluate(this.session, key); | ||
if (runtimeEvaluate.err) { | ||
return false; | ||
} | ||
const runtimeProperties = await nodeManagerHelper_1.RuntimeWrappers.getFunctionObjectIdFromRuntimeProperties(this.session, runtimeEvaluate.objectId); | ||
if (runtimeProperties.err) { | ||
return false; | ||
} | ||
// build up the condition for each node that has a breakpoint at this class/method | ||
const condition = nodeManagerHelper_1.Utils.createConditionString(Array.from(breakpointInfo.breakpoints.values())); | ||
const setBreakpointSuccess = await nodeManagerHelper_1.RuntimeWrappers.setBreakpointOnFunctionCall(this.session, runtimeProperties.functionObjectId, condition, breakpointInfo); | ||
return setBreakpointSuccess; | ||
} | ||
// Returns the NodeMethodInfo for the method in the specified node | ||
getNodeMethodInfo(node, methodName) { | ||
return this.getMethodsForNode(node.path).listOfMethods.find((method) => method.methodName === methodName); | ||
return this.getMethodsForNode(node.path) | ||
.listOfMethods.find((method) => method.methodName === methodName); | ||
} | ||
@@ -254,3 +200,3 @@ // Sets a breakpoint on the specified method for the specified node with an optional additional condition. | ||
// _setBreakpoint which will create the breakpoint with the new details provided as input here | ||
setBreakpoint(nodePath, methodName, breakpointCondition, callback) { | ||
async setBreakpoint(nodePath, methodName, breakpointCondition) { | ||
var _a; | ||
@@ -261,3 +207,3 @@ const debugString = `${nodePath}::${methodName}`; | ||
but node does not exist.`); | ||
callback(false); | ||
return false; | ||
} | ||
@@ -270,2 +216,3 @@ else { | ||
but method does not exist`); | ||
return false; | ||
} | ||
@@ -296,25 +243,17 @@ else { | ||
// breakpoint exists for this class/method, need to remove it and then re-create it | ||
this.session.post('Debugger.removeBreakpoint', { | ||
breakpointId: breakpointInfo.breakpointId, | ||
}, (err) => { | ||
if (err) { | ||
console.error(`NodeManager - remove breakpoint - error removing breakpoint for: ${key}`, err); | ||
callback(false); | ||
} | ||
else { | ||
console.log(`NodeManager - remove breakpoint - removed breakpoint successfully for: ${key}`); | ||
this._setBreakpoint(key, node, breakpointInfo, (success) => { | ||
callback(success); | ||
}); | ||
} | ||
}); | ||
const success = await nodeManagerHelper_1.RuntimeWrappers.removeBreakpointFromFunction(this.session, breakpointInfo); | ||
if (!success) { | ||
return false; | ||
} | ||
else { | ||
return await this._setBreakpoint(key, node, breakpointInfo); | ||
} | ||
} | ||
else { | ||
// breakpoint doesn't exist, so just create it | ||
this._setBreakpoint(key, node, breakpointInfo, (success) => { | ||
if (success) { | ||
this.breakpointInfoMap.set(key, breakpointInfo); | ||
} | ||
callback(success); | ||
}); | ||
const success = await this._setBreakpoint(key, node, breakpointInfo); | ||
if (success) { | ||
this.breakpointInfoMap.set(key, breakpointInfo); | ||
} | ||
return success; | ||
} | ||
@@ -324,3 +263,3 @@ } | ||
console.error(`NodeManager - set breakpoint - breakpoint already exists: ${key}`); | ||
callback(false); | ||
return false; | ||
} | ||
@@ -332,8 +271,7 @@ } | ||
// breakpoints set for the same method on the same class the node is inheriting the method from | ||
removeBreakpoint(nodePath, methodName, callback) { | ||
async removeBreakpoint(nodePath, methodName) { | ||
const node = this.nodePathMap.get(nodePath); | ||
if (!node) { | ||
console.error(`NodeManager - remove breakpoint - node does not exist ${nodePath}`); | ||
callback(false); | ||
return; | ||
return false; | ||
} | ||
@@ -343,4 +281,3 @@ const methodInfo = this.getNodeMethodInfo(node, methodName); | ||
console.error(`NodeManager - remove breakpoint - method ${methodName} does not exist on node ${nodePath}`); | ||
callback(false); | ||
return; | ||
return false; | ||
} | ||
@@ -355,27 +292,23 @@ const key = `${methodInfo.className}::${methodInfo.methodName}`; | ||
console.log(`NodeManager - remove breakpoint - found breakpoint id: ${breakpointInfo.breakpointId} for: ${keyAndPath}`); | ||
this.session.post('Debugger.removeBreakpoint', { | ||
breakpointId: breakpointInfo.breakpointId, | ||
}, (err) => { | ||
if (err) { | ||
console.error(`NodeManager - remove breakpoint - error removing breakpoint for: ${keyAndPath}`, err); | ||
callback(false); | ||
const success = await nodeManagerHelper_1.RuntimeWrappers.removeBreakpointFromFunction(this.session, breakpointInfo); | ||
if (!success) { | ||
return false; | ||
} | ||
else { | ||
console.log(`NodeManager - remove breakpoint - removed breakpoint successfully for: ${keyAndPath}`); | ||
breakpointInfo.breakpoints.delete(nodePath); | ||
if (breakpointInfo.breakpoints.size === 0) { | ||
// this was the only breakpoint set for the key | ||
this.breakpointInfoMap.delete(key); | ||
global.breakpointMethods.delete(key); | ||
return true; | ||
} | ||
else { | ||
console.log(`NodeManager - remove breakpoint - removed breakpoint successfully for: ${keyAndPath}`); | ||
breakpointInfo.breakpoints.delete(nodePath); | ||
if (breakpointInfo.breakpoints.size === 0) { | ||
// this was the only breakpoint set for the key | ||
this.breakpointInfoMap.delete(key); | ||
global.breakpointMethods.delete(key); | ||
} | ||
else { | ||
this._setBreakpoint(key, this.nodePathMap.get([...breakpointInfo.breakpoints][0][1].nodePath), breakpointInfo, () => { }); | ||
} | ||
callback(true); | ||
return await this._setBreakpoint(key, this.nodePathMap.get([...breakpointInfo.breakpoints][0][1].nodePath), breakpointInfo); | ||
} | ||
}); | ||
} | ||
} | ||
else { | ||
console.log(`NodeManager - remove breakpoint - did not find breakpoint for path: ${keyAndPath}`); | ||
callback(false); | ||
return false; | ||
} | ||
@@ -385,4 +318,4 @@ } | ||
console.log(`NodeManager - remove breakpoint - did not find breakpoint id at all: ${keyAndPath}`); | ||
callback(false); | ||
global.breakpointMethods.delete(key); | ||
return false; | ||
} | ||
@@ -401,3 +334,3 @@ } | ||
if (this.nodePathMap.has(path)) { | ||
throw new Error(`Key ${path} already exists! Cannot add new node.`); | ||
throw new DuplicateNodeAdded(path); | ||
} | ||
@@ -404,0 +337,0 @@ else { |
@@ -5,38 +5,24 @@ /* eslint-disable no-console */ | ||
import {Server} from 'ws'; | ||
import {Session, Debugger} from 'inspector'; | ||
import {Session} from 'inspector'; | ||
import {RuntimeWrappers, Utils} from './nodeManagerHelper'; | ||
import { | ||
NodePathKey, | ||
ClassMethodNameKey, | ||
NodeMethodInfo, | ||
BreakpointInfo, | ||
NodeMethodsInfo | ||
} from './nodeManagerTypes'; | ||
// Map key that is the path property of a bt node | ||
type NodePathKey = string; | ||
// Map key that is the class plus method name of a bt node method | ||
type ClassMethodNameKey = string; | ||
// the two parts of a ClassMethodNameKey - the className is the class the method comes from | ||
// which might be a super class | ||
interface NodeMethodInfo { | ||
className: string; | ||
methodName: string; | ||
export class DuplicateNodeAdded extends Error { | ||
constructor(path: string) { | ||
super(`Key ${path} already exists! Cannot add new node.`); | ||
} | ||
} | ||
// the information about a breakpoint from one node on a particular class/method | ||
interface BreakpointData { | ||
nodePath: string; | ||
condition?: string; | ||
nodeName?: string; | ||
nodeParent?: string; | ||
export class APIFunctionNotFound extends Error { | ||
constructor(apiFunction: string) { | ||
super(`Unknown request type: ${apiFunction}`); | ||
} | ||
} | ||
// the information about a breakpoint set on a particular class/method (for 1 or more nodes) | ||
interface BreakpointInfo { | ||
methodInfo: NodeMethodInfo, | ||
breakpointId?: Debugger.BreakpointId; | ||
breakpoints: Map<NodePathKey, BreakpointData>, | ||
} | ||
// the information for the methods available for a particuliar node as well as the node name and parent | ||
interface NodeMethodsInfo { | ||
listOfMethods: NodeMethodInfo[]; | ||
nodeName: string; | ||
nodeParent: string; | ||
} | ||
// Manages information about what nodes are available in the BT for debugging (nodes must be registered | ||
@@ -65,100 +51,104 @@ // when they become available and unregistered when they are no longer available) | ||
if (process.env.NODE_ENV !== 'test') { | ||
this.server = new Server({ | ||
host: 'localhost', | ||
port: 8990, | ||
}); | ||
this.server = new Server({ | ||
host: 'localhost', | ||
port: 8990, | ||
}); | ||
// should be empty but clear everything for good measure | ||
this.breakpointInfoMap.forEach((breakpointInfo, nodePathAndMethodName) => { | ||
this.session.post('Debugger.removeBreakpoint', { | ||
breakpointId: breakpointInfo.breakpointId, | ||
}, () => { | ||
this.breakpointInfoMap.delete(nodePathAndMethodName); | ||
// should be empty but clear everything for good measure | ||
this.breakpointInfoMap.forEach(async (breakpointInfo, nodePathAndMethodName) => { | ||
const success = await RuntimeWrappers.removeBreakpointFromFunction(this.session, breakpointInfo); | ||
if (success) { | ||
this.breakpointInfoMap.delete(nodePathAndMethodName); | ||
} | ||
}); | ||
this.breakpointInfoMap.clear(); | ||
(<any>global).breakpointMethods.clear(); | ||
// setup the connection handler | ||
this.server.on('connection', (clientSocket) => { | ||
// send the current cached breakpoints to the client if the client reconnects | ||
this.breakpointInfoMap.forEach((breakpointInfo) => { | ||
breakpointInfo.breakpoints.forEach((breakpoint) => { | ||
clientSocket.send(JSON.stringify({ | ||
request: 'placeBreakpoint', | ||
nodePath: breakpoint.nodePath, | ||
methodName: breakpointInfo.methodInfo.methodName, | ||
nodeName: breakpoint.nodeName, | ||
nodeParent: breakpoint.nodeParent, | ||
condition: breakpoint.condition, | ||
success: true, | ||
})); | ||
}); | ||
}); | ||
this.breakpointInfoMap.clear(); | ||
(<any>global).breakpointMethods.clear(); | ||
// setup the connection handler | ||
this.server.on('connection', (clientSocket) => { | ||
// send the current cached breakpoints to the client if the client reconnects | ||
this.breakpointInfoMap.forEach((breakpointInfo) => { | ||
breakpointInfo.breakpoints.forEach((breakpoint) => { | ||
clientSocket.send(JSON.stringify({ | ||
request: 'placeBreakpoint', | ||
nodePath: breakpoint.nodePath, | ||
methodName: breakpointInfo.methodInfo.methodName, | ||
nodeName: breakpoint.nodeName, | ||
nodeParent: breakpoint.nodeParent, | ||
condition: breakpoint.condition, | ||
success: true, | ||
})); | ||
}); | ||
}); | ||
clientSocket.on('message', async (data: string) => { | ||
const dataObj = JSON.parse(data); | ||
// message should always have a request and nodePath | ||
const request = dataObj.request; | ||
const nodePath = dataObj.nodePath; | ||
switch (request) { | ||
// client is requesting the methods for a given node path | ||
case 'getMethodsForNode': { | ||
let methodInfo; | ||
let success = true; | ||
try { | ||
methodInfo = this.getMethodsForNode(nodePath); | ||
} catch { | ||
success = false; | ||
} | ||
clientSocket.on('message', (data: string) => { | ||
const dataObj = JSON.parse(data); | ||
// message should always have a request and nodePath | ||
const request = dataObj.request; | ||
const nodePath = dataObj.nodePath; | ||
switch (request) { | ||
// client is requesting the methods for a given node path | ||
case 'getMethodsForNode': { | ||
const methodInfo = this.getMethodsForNode(nodePath); | ||
clientSocket.send(JSON.stringify({ | ||
request: 'getMethodsForNode', | ||
success, | ||
nodePath, | ||
...methodInfo, | ||
})); | ||
clientSocket.send(JSON.stringify({ | ||
request: 'getMethodsForNode', | ||
nodePath, | ||
...methodInfo, | ||
})); | ||
break; | ||
} | ||
// client is requesting to add (or modify) a breakpoint for a given node path/method | ||
case 'placeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
const condition = dataObj.condition; | ||
break; | ||
} | ||
// client is requesting to add (or modify) a breakpoint for a given node path/method | ||
case 'placeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
const condition = dataObj.condition; | ||
const success = await this.setBreakpoint(nodePath, methodName, condition); | ||
const node = this.nodePathMap.get(nodePath); | ||
const nodeName = node?.name; | ||
const nodeParent = node?.parent; | ||
this.setBreakpoint(nodePath, methodName, condition, (success) => { | ||
const node = this.nodePathMap.get(nodePath); | ||
const nodeName = node?.name; | ||
const nodeParent = node?.parent; | ||
clientSocket.send(JSON.stringify({ | ||
request: 'placeBreakpoint', | ||
nodePath: node?.path, | ||
methodName, | ||
nodeName, | ||
nodeParent, | ||
condition, | ||
success, | ||
})); | ||
clientSocket.send(JSON.stringify({ | ||
request: 'placeBreakpoint', | ||
nodePath: node?.path, | ||
methodName, | ||
nodeName, | ||
nodeParent, | ||
condition, | ||
success, | ||
})); | ||
}); | ||
break; | ||
} | ||
// client is requesting to remove a breakpoint by node path and method name | ||
case 'removeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
const success = await this.removeBreakpoint(nodePath, methodName); | ||
const node = this.nodePathMap.get(nodePath); | ||
break; | ||
} | ||
// client is requesting to remove a breakpoint by node path and method name | ||
case 'removeBreakpoint': { | ||
const methodName = dataObj.methodName; | ||
this.removeBreakpoint(nodePath, methodName, (success) => { | ||
const node = this.nodePathMap.get(nodePath); | ||
const nodeName = node?.name; | ||
const nodeParent = node?.parent; | ||
clientSocket.send(JSON.stringify({ | ||
request: 'removeBreakpoint', | ||
nodePath: node?.path, | ||
methodName, | ||
success, | ||
})); | ||
}); | ||
break; | ||
} | ||
default: | ||
throw new Error(`Unknown request type: ${dataObj.request}`); | ||
} | ||
}); | ||
clientSocket.send(JSON.stringify({ | ||
request: 'removeBreakpoint', | ||
nodePath: node?.path, | ||
methodName, | ||
success, | ||
})); | ||
break; | ||
} | ||
default: | ||
clientSocket.send(JSON.stringify({ | ||
request: dataObj.request, | ||
success: false, | ||
err: new APIFunctionNotFound(dataObj.request).message | ||
})); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
@@ -172,39 +162,9 @@ | ||
} else { | ||
let node = this.nodePathMap.get(nodePath)!; | ||
const node = this.nodePathMap.get(nodePath)!; | ||
const nodeName = node.name; | ||
const nodeParent = node.parent; | ||
const setOfMethods: Set<string> = new Set(); | ||
const methodsData: NodeMethodInfo[] = []; | ||
do { | ||
const methods = Object.getOwnPropertyNames(node).filter((prop) => { | ||
const nodePropDescriptor = Object.getOwnPropertyDescriptor(node, prop); | ||
// if the prop name is a getter or setter, if we simply just check that it's a function | ||
// that will end up invoking the getter or setter, which could lead to a crash | ||
if (nodePropDescriptor && (nodePropDescriptor.get || nodePropDescriptor.set)) { | ||
return true; | ||
} | ||
return typeof (node as any)[prop] === 'function'; | ||
}); | ||
const className = node.constructor.name; | ||
methods.forEach((methodName) => { | ||
// de-duplicate any inherited methods | ||
if (!setOfMethods.has(methodName)) { | ||
setOfMethods.add(methodName); | ||
methodsData.push({methodName, className}); | ||
} | ||
}); | ||
// climb up the inheritance tree until we get to Object | ||
node = Object.getPrototypeOf(node); | ||
} while (!!node && node.constructor.name !== 'Object'); | ||
const listOfMethods = Utils.getMethodInfoForObject(node); | ||
return { | ||
listOfMethods: methodsData.sort((a, b) => { | ||
if (a.methodName < b.methodName) { | ||
return -1; | ||
} | ||
if (a.methodName > b.methodName) { | ||
return 1; | ||
} | ||
return 0; | ||
}), | ||
listOfMethods, | ||
nodeName, | ||
@@ -217,8 +177,7 @@ nodeParent | ||
// Uses the node inspector to set a breakpoint using the specified node and the details in breakpointInfo | ||
private _setBreakpoint( | ||
private async _setBreakpoint( | ||
key: string, | ||
node: BaseNode<S, E>, | ||
breakpointInfo: BreakpointInfo, | ||
callback: (success: boolean) => void | ||
) { | ||
breakpointInfo: BreakpointInfo | ||
): Promise<boolean> { | ||
const nodeName = node.name; | ||
@@ -234,4 +193,3 @@ // find the class in the inheritance chain which contains the method or property | ||
`Could not find method ${breakpointInfo.methodInfo.methodName} in inheritance chain for ${nodeName}`); | ||
callback(false); | ||
return; | ||
return false; | ||
} | ||
@@ -249,60 +207,20 @@ | ||
this.session.post('Runtime.evaluate', {expression: `global.breakpointMethods.get('${key}')`}, | ||
(err, {result}) => { | ||
if (err) { | ||
console.error(`NodeManager - set breakpoint - Error in Runtime.evaluate for: ${key}`, err); | ||
callback(false); | ||
return; | ||
} | ||
console.log(`NodeManager - set breakpoint - got result from Runtime.evaluate for: ${key}`); | ||
const objectId = result.objectId; | ||
const runtimeEvaluate = await RuntimeWrappers.getObjectIdFromRuntimeEvaluate( | ||
this.session, key); | ||
if (runtimeEvaluate.err) { | ||
return false; | ||
} | ||
this.session.post('Runtime.getProperties', {objectId}, (err, result) => { | ||
if (err) { | ||
console.error(`NodeManager - set breakpoint - Error in Runtime.getProperties for ${key}`, err); | ||
callback(false); | ||
return; | ||
} | ||
console.log(`NodeManager - set breakpoint - got result from Runtime.getProperties for: ${key}`); | ||
const funcObjId = (<any>result).internalProperties[0].value.objectId; | ||
const runtimeProperties = await RuntimeWrappers.getFunctionObjectIdFromRuntimeProperties( | ||
this.session, runtimeEvaluate.objectId!); | ||
if (runtimeProperties.err) { | ||
return false; | ||
} | ||
// build up the condition for each node that has a breakpoint at this class/method | ||
let condition = ''; | ||
let first = true; | ||
[...breakpointInfo.breakpoints].forEach(([key, breakpointData]) => { | ||
if (first) { | ||
first = false; | ||
} else { | ||
condition = condition + ' || '; | ||
} | ||
condition = condition + | ||
`(this.path === '${breakpointData.nodePath}'` + | ||
(!!breakpointData.condition ? ` && ${breakpointData.condition}` : '') | ||
+ ')'; | ||
}); | ||
// build up the condition for each node that has a breakpoint at this class/method | ||
const condition = Utils.createConditionString(Array.from(breakpointInfo.breakpoints.values())); | ||
// do the magic! | ||
this.session.post('Debugger.setBreakpointOnFunctionCall', { | ||
objectId: funcObjId, | ||
condition, | ||
}, | ||
(err, result) => { | ||
if (err) { | ||
console.error(`NodeManager - set breakpoint - Error in \ | ||
Debugger.setBreakpointOnFunctionCall for: ${key}`, err); | ||
callback(false); | ||
return; | ||
} | ||
if (!result) { | ||
console.error(`NodeManager - set breakpoint - Got no result in \ | ||
Debugger.setBreakpointOnFunctionCall for: ${key}`); | ||
callback(false); | ||
return; | ||
} | ||
console.log(`NodeManager - set breakpoint - breakpoint set successfully: ${key}`); | ||
breakpointInfo.breakpointId = (result as any).breakpointId; // HACK: types are not defined | ||
callback(true); | ||
}); | ||
}); | ||
}); | ||
const setBreakpointSuccess = await RuntimeWrappers.setBreakpointOnFunctionCall( | ||
this.session, runtimeProperties.functionObjectId!, condition, breakpointInfo); | ||
return setBreakpointSuccess; | ||
} | ||
@@ -312,3 +230,4 @@ | ||
private getNodeMethodInfo(node: BaseNode<S, E>, methodName: string) { | ||
return this.getMethodsForNode(node.path).listOfMethods.find((method) => method.methodName === methodName); | ||
return this.getMethodsForNode(node.path) | ||
.listOfMethods.find((method) => method.methodName === methodName); | ||
} | ||
@@ -319,8 +238,7 @@ | ||
// _setBreakpoint which will create the breakpoint with the new details provided as input here | ||
private setBreakpoint( | ||
private async setBreakpoint( | ||
nodePath: string, | ||
methodName: string, | ||
breakpointCondition: string, | ||
callback: (success: boolean) => void | ||
) { | ||
): Promise<boolean> { | ||
const debugString = `${nodePath}::${methodName}`; | ||
@@ -330,3 +248,3 @@ if (!this.nodePathMap.has(nodePath)) { | ||
but node does not exist.`); | ||
callback(false); | ||
return false; | ||
} else { | ||
@@ -338,2 +256,3 @@ const node = this.nodePathMap.get(nodePath)!; | ||
but method does not exist`); | ||
return false; | ||
} else { | ||
@@ -364,27 +283,19 @@ const key = `${className}::${methodName}`; | ||
// breakpoint exists for this class/method, need to remove it and then re-create it | ||
this.session.post('Debugger.removeBreakpoint', { | ||
breakpointId: breakpointInfo.breakpointId, | ||
}, (err: Error|null) => { | ||
if (err) { | ||
console.error(`NodeManager - remove breakpoint - error removing breakpoint for: ${key}`, err); | ||
callback(false); | ||
} else { | ||
console.log(`NodeManager - remove breakpoint - removed breakpoint successfully for: ${key}`); | ||
this._setBreakpoint(key, node, breakpointInfo!, (success) => { | ||
callback(success); | ||
}); | ||
} | ||
}); | ||
const success = await RuntimeWrappers.removeBreakpointFromFunction(this.session, breakpointInfo); | ||
if (!success) { | ||
return false; | ||
} else { | ||
return await this._setBreakpoint(key, node, breakpointInfo!); | ||
} | ||
} else { | ||
// breakpoint doesn't exist, so just create it | ||
this._setBreakpoint(key, node, breakpointInfo!, (success) => { | ||
if (success) { | ||
this.breakpointInfoMap.set(key, breakpointInfo!); | ||
} | ||
callback(success); | ||
}); | ||
const success = await this._setBreakpoint(key, node, breakpointInfo!); | ||
if (success) { | ||
this.breakpointInfoMap.set(key, breakpointInfo!); | ||
} | ||
return success; | ||
} | ||
} else { | ||
console.error(`NodeManager - set breakpoint - breakpoint already exists: ${key}`); | ||
callback(false); | ||
return false; | ||
} | ||
@@ -397,8 +308,7 @@ } | ||
// breakpoints set for the same method on the same class the node is inheriting the method from | ||
private removeBreakpoint(nodePath: string, methodName: string, callback: (success: boolean) => void) { | ||
private async removeBreakpoint(nodePath: string, methodName: string): Promise<boolean> { | ||
const node = this.nodePathMap.get(nodePath); | ||
if (!node) { | ||
console.error(`NodeManager - remove breakpoint - node does not exist ${nodePath}`); | ||
callback(false); | ||
return; | ||
return false; | ||
} | ||
@@ -408,4 +318,3 @@ const methodInfo = this.getNodeMethodInfo(node, methodName); | ||
console.error(`NodeManager - remove breakpoint - method ${methodName} does not exist on node ${nodePath}`); | ||
callback(false); | ||
return; | ||
return false; | ||
} | ||
@@ -423,30 +332,26 @@ const key = `${methodInfo.className}::${methodInfo.methodName}`; | ||
`NodeManager - remove breakpoint - found breakpoint id: ${breakpointInfo.breakpointId} for: ${keyAndPath}`); | ||
this.session.post('Debugger.removeBreakpoint', { | ||
breakpointId: breakpointInfo.breakpointId, | ||
}, (err: Error|null) => { | ||
if (err) { | ||
console.error(`NodeManager - remove breakpoint - error removing breakpoint for: ${keyAndPath}`, err); | ||
callback(false); | ||
const success = await RuntimeWrappers.removeBreakpointFromFunction(this.session, breakpointInfo); | ||
if (!success) { | ||
return false; | ||
} else { | ||
console.log(`NodeManager - remove breakpoint - removed breakpoint successfully for: ${keyAndPath}`); | ||
breakpointInfo.breakpoints.delete(nodePath); | ||
if (breakpointInfo.breakpoints.size === 0) { | ||
// this was the only breakpoint set for the key | ||
this.breakpointInfoMap.delete(key); | ||
(<any>global).breakpointMethods.delete(key); | ||
return true; | ||
} else { | ||
console.log(`NodeManager - remove breakpoint - removed breakpoint successfully for: ${keyAndPath}`); | ||
breakpointInfo.breakpoints.delete(nodePath); | ||
if (breakpointInfo.breakpoints.size === 0) { | ||
// this was the only breakpoint set for the key | ||
this.breakpointInfoMap.delete(key); | ||
(<any>global).breakpointMethods.delete(key); | ||
} else { | ||
this._setBreakpoint( | ||
key, this.nodePathMap.get([...breakpointInfo.breakpoints][0][1].nodePath)!, breakpointInfo, () => {}); | ||
} | ||
callback(true); | ||
return await this._setBreakpoint( | ||
key, this.nodePathMap.get([...breakpointInfo.breakpoints][0][1].nodePath)!, breakpointInfo); | ||
} | ||
}); | ||
} | ||
} else { | ||
console.log(`NodeManager - remove breakpoint - did not find breakpoint for path: ${keyAndPath}`); | ||
callback(false); | ||
return false; | ||
} | ||
} else { | ||
console.log(`NodeManager - remove breakpoint - did not find breakpoint id at all: ${keyAndPath}`); | ||
callback(false); | ||
(<any>global).breakpointMethods.delete(key); | ||
return false; | ||
} | ||
@@ -467,3 +372,3 @@ } | ||
if (this.nodePathMap.has(path)) { | ||
throw new Error(`Key ${path} already exists! Cannot add new node.`); | ||
throw new DuplicateNodeAdded(path); | ||
} else { | ||
@@ -470,0 +375,0 @@ this.nodePathMap.set(path, node); |
@@ -100,3 +100,3 @@ { | ||
"test:no-cover": "NODE_ENV=test mocha", | ||
"test:debug": "npm run test:no-cover -- --debug-brk test", | ||
"test:debug": "npm run test:no-cover -- --inspect-brk test", | ||
"coverage": "NODE_ENV=test nyc report --reporter=text-lcov \u003e coverage.lcov \u0026\u0026 codecov", | ||
@@ -106,3 +106,3 @@ "prepublish": "npm-run-all compile" | ||
"types": "dist/index.d.ts", | ||
"version": "3.6.0-btvBreakpoints.9" | ||
"version": "3.6.0-btvBreakpoints.10" | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
240354
179
4978
1