From 36bb7243ffd7a2d1c5a3252dff9af46800916c61 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 27 Apr 2021 12:27:39 +0200 Subject: [PATCH] debugger: refactor `inspect_repl` to use primordials PR-URL: https://github.com/nodejs/node/pull/38551 Reviewed-By: Rich Trott Reviewed-By: James M Snell --- lib/internal/inspector/inspect_repl.js | 567 ++++++++++++++----------- 1 file changed, 316 insertions(+), 251 deletions(-) diff --git a/lib/internal/inspector/inspect_repl.js b/lib/internal/inspector/inspect_repl.js index 1b23196b2138de..0fcde39bfc3e2c 100644 --- a/lib/internal/inspector/inspect_repl.js +++ b/lib/internal/inspector/inspect_repl.js @@ -21,10 +21,57 @@ */ // TODO(trott): enable ESLint -/* eslint-disable getter-return, no-restricted-syntax, - node-core/prefer-primordials */ +/* eslint-disable getter-return, no-restricted-syntax */ 'use strict'; + +const { + Array, + ArrayFrom, + ArrayPrototypeFilter, + ArrayPrototypeFind, + ArrayPrototypeForEach, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeJoin, + ArrayPrototypeMap, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSome, + ArrayPrototypeSplice, + Date, + Error, + FunctionPrototypeCall, + JSONStringify, + MathMax, + ObjectAssign, + ObjectDefineProperty, + ObjectKeys, + ObjectValues, + Promise, + PromiseAll, + PromisePrototypeCatch, + PromisePrototypeThen, + PromiseResolve, + ReflectGetOwnPropertyDescriptor, + ReflectOwnKeys, + RegExpPrototypeSymbolMatch, + RegExpPrototypeSymbolReplace, + SafeArrayIterator, + SafeMap, + String, + StringFromCharCode, + StringPrototypeEndsWith, + StringPrototypeIncludes, + StringPrototypeRepeat, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeStartsWith, + StringPrototypeToUpperCase, + StringPrototypeTrim, + TypeError, +} = primordials; + const FS = require('fs'); const Path = require('path'); const Repl = require('repl'); @@ -46,7 +93,7 @@ const SHORTCUTS = { run: 'r', }; -const HELP = ` +const HELP = StringPrototypeTrim(` run, restart, r Run the application or reconnect kill Kill a running application or disconnect @@ -83,88 +130,83 @@ profiles[n].save(filepath = 'node.cpuprofile') takeHeapSnapshot(filepath = 'node.heapsnapshot') Take a heap snapshot and save to disk as JSON. -`.trim(); +`); const FUNCTION_NAME_PATTERN = /^(?:function\*? )?([^(\s]+)\(/; function extractFunctionName(description) { - const fnNameMatch = description.match(FUNCTION_NAME_PATTERN); + const fnNameMatch = + RegExpPrototypeSymbolMatch(FUNCTION_NAME_PATTERN, description); return fnNameMatch ? `: ${fnNameMatch[1]}` : ''; } -const PUBLIC_BUILTINS = require('module').builtinModules; -const NATIVES = PUBLIC_BUILTINS ? internalBinding('natives') : {}; +const { + moduleIds: PUBLIC_BUILTINS, +} = internalBinding('native_module'); +const NATIVES = internalBinding('natives'); function isNativeUrl(url) { - url = url.replace(/\.js$/, ''); - if (PUBLIC_BUILTINS) { - if (url.startsWith('node:internal/') || PUBLIC_BUILTINS.includes(url)) - return true; - } + url = RegExpPrototypeSymbolReplace(/\.js$/, url, ''); - return url in NATIVES || url === 'bootstrap_node'; + return StringPrototypeStartsWith(url, 'node:internal/') || + ArrayPrototypeIncludes(PUBLIC_BUILTINS, url) || + url in NATIVES || url === 'bootstrap_node'; } function getRelativePath(filenameOrURL) { - const dir = Path.join(Path.resolve(), 'x').slice(0, -1); + const dir = StringPrototypeSlice(Path.join(Path.resolve(), 'x'), 0, -1); - const filename = filenameOrURL.startsWith('file://') ? + const filename = StringPrototypeStartsWith(filenameOrURL, 'file://') ? fileURLToPath(filenameOrURL) : filenameOrURL; // Change path to relative, if possible - if (filename.indexOf(dir) === 0) { - return filename.slice(dir.length); + if (StringPrototypeStartsWith(filename, dir)) { + return StringPrototypeSlice(filename, dir.length); } return filename; } -function toCallback(promise, callback) { - function forward(...args) { - process.nextTick(() => callback(...args)); - } - promise.then(forward.bind(null, null), forward); -} - // Adds spaces and prefix to number // maxN is a maximum number we should have space for function leftPad(n, prefix, maxN) { const s = n.toString(); - const nchars = Math.max(2, String(maxN).length) + 1; - const nspaces = nchars - s.length - 1; + const nchars = MathMax(2, String(maxN).length); + const nspaces = nchars - s.length; - return prefix + ' '.repeat(nspaces) + s; + return prefix + StringPrototypeRepeat(' ', nspaces) + s; } function markSourceColumn(sourceText, position, useColors) { if (!sourceText) return ''; - const head = sourceText.slice(0, position); - let tail = sourceText.slice(position); + const head = StringPrototypeSlice(sourceText, 0, position); + let tail = StringPrototypeSlice(sourceText, position); // Colourize char if stdout supports colours if (useColors) { - tail = tail.replace(/(.+?)([^\w]|$)/, '\u001b[32m$1\u001b[39m$2'); + tail = RegExpPrototypeSymbolReplace(/(.+?)([^\w]|$)/, tail, + '\u001b[32m$1\u001b[39m$2'); } // Return source line with coloured char at `position` - return [head, tail].join(''); + return head + tail; } function extractErrorMessage(stack) { if (!stack) return ''; - const m = stack.match(/^\w+: ([^\n]+)/); - return m ? m[1] : stack; + const m = RegExpPrototypeSymbolMatch(/^\w+: ([^\n]+)/, stack); + return m?.[1] ?? stack; } function convertResultToError(result) { const { className, description } = result; const err = new Error(extractErrorMessage(description)); err.stack = description; - Object.defineProperty(err, 'name', { value: className }); + ObjectDefineProperty(err, 'name', { value: className }); return err; } class RemoteObject { constructor(attributes) { - Object.assign(this, attributes); + ObjectAssign(this, attributes); if (this.type === 'number') { this.value = this.unserializableValue ? +this.unserializableValue : +this.value; @@ -227,18 +269,21 @@ class RemoteObject { break; } if (this.preview) { - const props = this.preview.properties - .map((prop, idx) => { + const props = ArrayPrototypeMap( + this.preview.properties, + (prop, idx) => { const value = formatProperty(prop); if (prop.name === `${idx}`) return value; return `${prop.name}: ${value}`; }); if (this.preview.overflow) { - props.push('...'); + ArrayPrototypePush(props, '...'); } - const singleLine = props.join(', '); + const singleLine = ArrayPrototypeJoin(props, ', '); const propString = - singleLine.length > 60 ? props.join(',\n ') : singleLine; + singleLine.length > 60 ? + ArrayPrototypeJoin(props, ',\n ') : + singleLine; return this.subtype === 'array' ? `[ ${propString} ]` : `{ ${propString} }`; @@ -258,34 +303,39 @@ class RemoteObject { class ScopeSnapshot { constructor(scope, properties) { - Object.assign(this, scope); - this.properties = new Map(properties.map((prop) => { + ObjectAssign(this, scope); + this.properties = new SafeMap(); + this.completionGroup = ArrayPrototypeMap(properties, (prop) => { const value = new RemoteObject(prop.value); - return [prop.name, value]; - })); - this.completionGroup = properties.map((prop) => prop.name); + this.properties.set(prop.name, value); + return prop.name; + }); } [customInspectSymbol](depth, opts) { - const type = `${this.type[0].toUpperCase()}${this.type.slice(1)}`; + const type = StringPrototypeToUpperCase(this.type[0]) + + StringPrototypeSlice(this.type, 1); const name = this.name ? `<${this.name}>` : ''; const prefix = `${type}${name} `; - return utilInspect(this.properties, opts) - .replace(/^Map /, prefix); + return RegExpPrototypeSymbolReplace(/^Map /, + utilInspect(this.properties, opts), + prefix); } } function copyOwnProperties(target, source) { - Object.getOwnPropertyNames(source).forEach((prop) => { - const descriptor = Object.getOwnPropertyDescriptor(source, prop); - Object.defineProperty(target, prop, descriptor); - }); + ArrayPrototypeForEach( + ReflectOwnKeys(source), + (prop) => { + const desc = ReflectGetOwnPropertyDescriptor(source, prop); + ObjectDefineProperty(target, prop, desc); + }); } function aliasProperties(target, mapping) { - Object.keys(mapping).forEach((key) => { - const descriptor = Object.getOwnPropertyDescriptor(target, key); - Object.defineProperty(target, mapping[key], descriptor); + ArrayPrototypeForEach(ObjectKeys(mapping), (key) => { + const desc = ReflectGetOwnPropertyDescriptor(target, key); + ObjectDefineProperty(target, mapping[key], desc); }); } @@ -344,16 +394,14 @@ function createRepl(inspector) { return !script.isNative || isCurrentScript(script); } - return Object.keys(knownScripts) - .map((scriptId) => knownScripts[scriptId]) - .filter(isVisible) - .map((script) => { + return ArrayPrototypeJoin(ArrayPrototypeMap( + ArrayPrototypeFilter(ObjectValues(knownScripts), isVisible), + (script) => { const isCurrent = isCurrentScript(script); const { isNative, url } = script; const name = `${getRelativePath(url)}${isNative ? ' ' : ''}`; return `${isCurrent ? '*' : ' '} ${script.scriptId}: ${name}`; - }) - .join('\n'); + }), '\n'); } function listScripts(displayNatives = false) { @@ -371,19 +419,19 @@ function createRepl(inspector) { static createAndRegister({ profile }) { const p = new Profile(profile); - profiles.push(p); + ArrayPrototypePush(profiles, p); return p; } [customInspectSymbol](depth, { stylize }) { const { startTime, endTime } = this.data; - const MU = String.fromChar(956); + const MU = StringFromCharCode(956); return stylize(`[Profile ${endTime - startTime}${MU}s]`, 'special'); } save(filename = 'node.cpuprofile') { const absoluteFile = Path.resolve(filename); - const json = JSON.stringify(this.data); + const json = JSONStringify(this.data); FS.writeFileSync(absoluteFile, json); print('Saved profile to ' + absoluteFile); } @@ -391,68 +439,75 @@ function createRepl(inspector) { class SourceSnippet { constructor(location, delta, scriptSource) { - Object.assign(this, location); + ObjectAssign(this, location); this.scriptSource = scriptSource; this.delta = delta; } [customInspectSymbol](depth, options) { const { scriptId, lineNumber, columnNumber, delta, scriptSource } = this; - const start = Math.max(1, lineNumber - delta + 1); + const start = MathMax(1, lineNumber - delta + 1); const end = lineNumber + delta + 1; - const lines = scriptSource.split('\n'); - return lines.slice(start - 1, end).map((lineText, offset) => { - const i = start + offset; - const isCurrent = i === (lineNumber + 1); - - const markedLine = isCurrent ? - markSourceColumn(lineText, columnNumber, options.colors) : - lineText; - - let isBreakpoint = false; - knownBreakpoints.forEach(({ location }) => { - if (!location) return; - if (scriptId === location.scriptId && + const lines = StringPrototypeSplit(scriptSource, '\n'); + return ArrayPrototypeJoin( + ArrayPrototypeMap( + ArrayPrototypeSlice(lines, start - 1, end), + (lineText, offset) => { + const i = start + offset; + const isCurrent = i === (lineNumber + 1); + + const markedLine = isCurrent ? + markSourceColumn(lineText, columnNumber, options.colors) : + lineText; + + let isBreakpoint = false; + ArrayPrototypeForEach(knownBreakpoints, ({ location }) => { + if (!location) return; + if (scriptId === location.scriptId && i === (location.lineNumber + 1)) { - isBreakpoint = true; - } - }); + isBreakpoint = true; + } + }); - let prefixChar = ' '; - if (isCurrent) { - prefixChar = '>'; - } else if (isBreakpoint) { - prefixChar = '*'; - } - return `${leftPad(i, prefixChar, end)} ${markedLine}`; - }).join('\n'); + let prefixChar = ' '; + if (isCurrent) { + prefixChar = '>'; + } else if (isBreakpoint) { + prefixChar = '*'; + } + return `${leftPad(i, prefixChar, end)} ${markedLine}`; + }), '\n'); } } - function getSourceSnippet(location, delta = 5) { + async function getSourceSnippet(location, delta = 5) { const { scriptId } = location; - return Debugger.getScriptSource({ scriptId }) - .then(({ scriptSource }) => - new SourceSnippet(location, delta, scriptSource)); + const { scriptSource } = await Debugger.getScriptSource({ scriptId }); + return new SourceSnippet(location, delta, scriptSource); } class CallFrame { constructor(callFrame) { - Object.assign(this, callFrame); + ObjectAssign(this, callFrame); } loadScopes() { - return Promise.all( - this.scopeChain - .filter((scope) => scope.type !== 'global') - .map((scope) => { + return PromiseAll( + new SafeArrayIterator(ArrayPrototypeMap( + ArrayPrototypeFilter( + this.scopeChain, + (scope) => scope.type !== 'global' + ), + async (scope) => { const { objectId } = scope.object; - return Runtime.getProperties({ + const { result } = await Runtime.getProperties({ objectId, generatePreview: true, - }).then(({ result }) => new ScopeSnapshot(scope, result)); + }); + return new ScopeSnapshot(scope, result); }) + ) ); } @@ -463,69 +518,72 @@ function createRepl(inspector) { class Backtrace extends Array { [customInspectSymbol]() { - return this.map((callFrame, idx) => { - const { - location: { scriptId, lineNumber, columnNumber }, - functionName - } = callFrame; - const name = functionName || '(anonymous)'; - - const script = knownScripts[scriptId]; - const relativeUrl = + return ArrayPrototypeJoin( + ArrayPrototypeMap(this, (callFrame, idx) => { + const { + location: { scriptId, lineNumber, columnNumber }, + functionName + } = callFrame; + const name = functionName || '(anonymous)'; + + const script = knownScripts[scriptId]; + const relativeUrl = (script && getRelativePath(script.url)) || ''; - const frameLocation = + const frameLocation = `${relativeUrl}:${lineNumber + 1}:${columnNumber}`; - return `#${idx} ${name} ${frameLocation}`; - }).join('\n'); + return `#${idx} ${name} ${frameLocation}`; + }), '\n'); } static from(callFrames) { - return super.from(Array.from(callFrames).map((callFrame) => { - if (callFrame instanceof CallFrame) { - return callFrame; - } - return new CallFrame(callFrame); - })); + return FunctionPrototypeCall( + ArrayFrom, + this, + callFrames, + (callFrame) => + (callFrame instanceof CallFrame ? + callFrame : + new CallFrame(callFrame)) + ); } } function prepareControlCode(input) { if (input === '\n') return lastCommand; // Add parentheses: exec process.title => exec("process.title"); - const match = input.match(/^\s*exec\s+([^\n]*)/); + const match = RegExpPrototypeSymbolMatch(/^\s*exec\s+([^\n]*)/, input); if (match) { - lastCommand = `exec(${JSON.stringify(match[1])})`; + lastCommand = `exec(${JSONStringify(match[1])})`; } else { lastCommand = input; } return lastCommand; } - function evalInCurrentContext(code) { + async function evalInCurrentContext(code) { // Repl asked for scope variables if (code === '.scope') { if (!selectedFrame) { - return Promise.reject(new Error('Requires execution to be paused')); + throw new Error('Requires execution to be paused'); } - return selectedFrame.loadScopes().then((scopes) => { - return scopes.map((scope) => scope.completionGroup); - }); + const scopes = await selectedFrame.loadScopes(); + return ArrayPrototypeMap(scopes, (scope) => scope.completionGroup); } if (selectedFrame) { - return Debugger.evaluateOnCallFrame({ + return PromisePrototypeThen(Debugger.evaluateOnCallFrame({ callFrameId: selectedFrame.callFrameId, expression: code, objectGroup: 'node-inspect', generatePreview: true, - }).then(RemoteObject.fromEvalResult); + }), RemoteObject.fromEvalResult); } - return Runtime.evaluate({ + return PromisePrototypeThen(Runtime.evaluate({ expression: code, objectGroup: 'node-inspect', generatePreview: true, - }).then(RemoteObject.fromEvalResult); + }), RemoteObject.fromEvalResult); } function controlEval(input, context, filename, callback) { @@ -539,11 +597,16 @@ function createRepl(inspector) { const code = prepareControlCode(input); const result = vm.runInContext(code, context, filename); - if (result && typeof result.then === 'function') { - toCallback(result, returnToCallback); - return; + const then = result?.then; + if (typeof then === 'function') { + FunctionPrototypeCall( + then, result, + (result) => returnToCallback(null, result), + returnToCallback + ); + } else { + returnToCallback(null, result); } - returnToCallback(null, result); } catch (e) { returnToCallback(e); } @@ -556,76 +619,63 @@ function createRepl(inspector) { callback(error, result); } - try { - const result = evalInCurrentContext(input); - - if (result && typeof result.then === 'function') { - toCallback(result, returnToCallback); - return; - } - returnToCallback(null, result); - } catch (e) { - returnToCallback(e); - } + PromisePrototypeThen(evalInCurrentContext(input), + (result) => returnToCallback(null, result), + returnToCallback + ); } - function formatWatchers(verbose = false) { + async function formatWatchers(verbose = false) { if (!watchedExpressions.length) { - return Promise.resolve(''); + return ''; } const inspectValue = (expr) => - evalInCurrentContext(expr) - // .then(formatValue) - .catch((error) => `<${error.message}>`); + PromisePrototypeCatch(evalInCurrentContext(expr), + (error) => `<${error.message}>`); const lastIndex = watchedExpressions.length - 1; - return Promise.all(watchedExpressions.map(inspectValue)) - .then((values) => { - const lines = watchedExpressions - .map((expr, idx) => { - const prefix = `${leftPad(idx, ' ', lastIndex)}: ${expr} =`; - const value = inspect(values[idx]); - if (value.indexOf('\n') === -1) { - return `${prefix} ${value}`; - } - return `${prefix}\n ${value.split('\n').join('\n ')}`; - }); - return lines.join('\n'); - }) - .then((valueList) => { - return verbose ? `Watchers:\n${valueList}\n` : valueList; - }); + const values = await PromiseAll(new SafeArrayIterator( + ArrayPrototypeMap(watchedExpressions, inspectValue))); + const lines = ArrayPrototypeMap(watchedExpressions, (expr, idx) => { + const prefix = `${leftPad(idx, ' ', lastIndex)}: ${expr} =`; + const value = inspect(values[idx], { colors: true }); + if (!StringPrototypeIncludes(value, '\n')) { + return `${prefix} ${value}`; + } + return `${prefix}\n ${RegExpPrototypeSymbolReplace(/\n/g, value, '\n ')}`; + }); + const valueList = ArrayPrototypeJoin(lines, '\n'); + return verbose ? `Watchers:\n${valueList}\n` : valueList; } function watchers(verbose = false) { - return formatWatchers(verbose).then(print); + return PromisePrototypeThen(formatWatchers(verbose), print); } // List source code function list(delta = 5) { - return selectedFrame.list(delta) - .then(null, (error) => { - print('You can\'t list source code right now'); - throw error; - }); + return selectedFrame.list(delta).then(null, (error) => { + print("You can't list source code right now"); + throw error; + }); } function handleBreakpointResolved({ breakpointId, location }) { const script = knownScripts[location.scriptId]; const scriptUrl = script && script.url; if (scriptUrl) { - Object.assign(location, { scriptUrl }); + ObjectAssign(location, { scriptUrl }); } - const isExisting = knownBreakpoints.some((bp) => { + const isExisting = ArrayPrototypeSome(knownBreakpoints, (bp) => { if (bp.breakpointId === breakpointId) { - Object.assign(bp, { location }); + ObjectAssign(bp, { location }); return true; } return false; }); if (!isExisting) { - knownBreakpoints.push({ breakpointId, location }); + ArrayPrototypePush(knownBreakpoints, { breakpointId, location }); } } @@ -641,9 +691,11 @@ function createRepl(inspector) { const scriptUrl = script ? script.url : location.scriptUrl; return `${getRelativePath(scriptUrl)}:${location.lineNumber + 1}`; } - const breaklist = knownBreakpoints - .map((bp, idx) => `#${idx} ${formatLocation(bp.location)}`) - .join('\n'); + const breaklist = ArrayPrototypeJoin( + ArrayPrototypeMap( + knownBreakpoints, + (bp, idx) => `#${idx} ${formatLocation(bp.location)}`), + '\n'); print(breaklist); } @@ -660,9 +712,9 @@ function createRepl(inspector) { // setBreakpoint(): set breakpoint at current location if (script === undefined) { - return Debugger - .setBreakpoint({ location: getCurrentLocation(), condition }) - .then(registerBreakpoint); + return PromisePrototypeThen( + Debugger.setBreakpoint({ location: getCurrentLocation(), condition }), + registerBreakpoint); } // setBreakpoint(line): set breakpoint in current script at specific line @@ -671,8 +723,9 @@ function createRepl(inspector) { scriptId: getCurrentLocation().scriptId, lineNumber: script - 1, }; - return Debugger.setBreakpoint({ location, condition }) - .then(registerBreakpoint); + return PromisePrototypeThen( + Debugger.setBreakpoint({ location, condition }), + registerBreakpoint); } if (typeof script !== 'string') { @@ -680,7 +733,7 @@ function createRepl(inspector) { } // setBreakpoint('fn()'): Break when a function is called - if (script.endsWith('()')) { + if (StringPrototypeEndsWith(script, '()')) { const debugExpr = `debug(${script.slice(0, -2)})`; const debugCall = selectedFrame ? Debugger.evaluateOnCallFrame({ @@ -692,7 +745,7 @@ function createRepl(inspector) { expression: debugExpr, includeCommandLineAPI: true, }); - return debugCall.then(({ result, wasThrown }) => { + return PromisePrototypeThen(debugCall, ({ result, wasThrown }) => { if (wasThrown) return convertResultToError(result); return undefined; // This breakpoint can't be removed the same way }); @@ -704,15 +757,15 @@ function createRepl(inspector) { if (knownScripts[script]) { scriptId = script; } else { - for (const id of Object.keys(knownScripts)) { + ArrayPrototypeForEach(ObjectKeys(knownScripts), (id) => { const scriptUrl = knownScripts[id].url; - if (scriptUrl && scriptUrl.indexOf(script) !== -1) { + if (scriptUrl && StringPrototypeIncludes(scriptUrl, script)) { if (scriptId !== null) { ambiguous = true; } scriptId = id; } - } + }); } if (ambiguous) { @@ -726,19 +779,25 @@ function createRepl(inspector) { if (scriptId !== null) { const location = { scriptId, lineNumber: line - 1 }; - return Debugger.setBreakpoint({ location, condition }) - .then(registerBreakpoint); + return PromisePrototypeThen( + Debugger.setBreakpoint({ location, condition }), + registerBreakpoint); } - const escapedPath = script.replace(/([/\\.?*()^${}|[\]])/g, '\\$1'); + const escapedPath = RegExpPrototypeSymbolReplace(/([/\\.?*()^${}|[\]])/g, + script, '\\$1'); const urlRegex = `^(.*[\\/\\\\])?${escapedPath}$`; - return Debugger - .setBreakpointByUrl({ urlRegex, lineNumber: line - 1, condition }) - .then((bp) => { + return PromisePrototypeThen( + Debugger.setBreakpointByUrl({ + urlRegex, + lineNumber: line - 1, + condition, + }), + (bp) => { // TODO: handle bp.locations in case the regex matches existing files if (!bp.location) { // Fake it for now. - Object.assign(bp, { + ObjectAssign(bp, { actualLocation: { scriptUrl: `.*/${script}$`, lineNumber: line - 1, @@ -750,41 +809,46 @@ function createRepl(inspector) { } function clearBreakpoint(url, line) { - const breakpoint = knownBreakpoints.find(({ location }) => { + const breakpoint = ArrayPrototypeFind(knownBreakpoints, ({ location }) => { if (!location) return false; const script = knownScripts[location.scriptId]; if (!script) return false; return ( - script.url.indexOf(url) !== -1 && (location.lineNumber + 1) === line + StringPrototypeIncludes(script.url, url) && + (location.lineNumber + 1) === line ); }); if (!breakpoint) { print(`Could not find breakpoint at ${url}:${line}`); - return Promise.resolve(); + return PromiseResolve(); } - return Debugger.removeBreakpoint({ breakpointId: breakpoint.breakpointId }) - .then(() => { - const idx = knownBreakpoints.indexOf(breakpoint); - knownBreakpoints.splice(idx, 1); + return PromisePrototypeThen( + Debugger.removeBreakpoint({ breakpointId: breakpoint.breakpointId }), + () => { + const idx = ArrayPrototypeIndexOf(knownBreakpoints, breakpoint); + ArrayPrototypeSplice(knownBreakpoints, idx, 1); }); } function restoreBreakpoints() { - const lastBreakpoints = knownBreakpoints.slice(); - knownBreakpoints.length = 0; - const newBreakpoints = lastBreakpoints - .filter(({ location }) => !!location.scriptUrl) - .map(({ location }) => - setBreakpoint(location.scriptUrl, location.lineNumber + 1)); - if (!newBreakpoints.length) return Promise.resolve(); - return Promise.all(newBreakpoints).then((results) => { - print(`${results.length} breakpoints restored.`); - }); + const lastBreakpoints = ArrayPrototypeSplice(knownBreakpoints, 0); + const newBreakpoints = ArrayPrototypeMap( + ArrayPrototypeFilter(lastBreakpoints, + ({ location }) => !!location.scriptUrl), + ({ location }) => setBreakpoint(location.scriptUrl, + location.lineNumber + 1)); + if (!newBreakpoints.length) return PromiseResolve(); + return PromisePrototypeThen( + PromiseAll(new SafeArrayIterator(newBreakpoints)), + (results) => { + print(`${results.length} breakpoints restored.`); + }); } function setPauseOnExceptions(state) { - return Debugger.setPauseOnExceptions({ state }) - .then(() => { + return PromisePrototypeThen( + Debugger.setPauseOnExceptions({ state }), + () => { pauseOnExceptionState = state; }); } @@ -810,13 +874,13 @@ function createRepl(inspector) { const header = `${breakType} in ${scriptUrl}:${lineNumber + 1}`; inspector.suspendReplWhile(() => - Promise.all([formatWatchers(true), selectedFrame.list(2)]) - .then(({ 0: watcherList, 1: context }) => { - if (watcherList) { - return `${watcherList}\n${inspect(context)}`; - } - return inspect(context); - }).then((breakContext) => { + PromisePrototypeThen( + PromiseAll(new SafeArrayIterator( + [formatWatchers(true), selectedFrame.list(2)])), + ({ 0: watcherList, 1: context }) => { + const breakContext = watcherList ? + `${watcherList}\n${inspect(context)}` : + inspect(context); print(`${header}\n${breakContext}`); })); }); @@ -839,15 +903,15 @@ function createRepl(inspector) { Profiler.on('consoleProfileFinished', ({ profile }) => { Profile.createAndRegister({ profile }); - print([ - 'Captured new CPU profile.', - `Access it with profiles[${profiles.length - 1}]`, - ].join('\n')); + print( + 'Captured new CPU profile.\n' + + `Access it with profiles[${profiles.length - 1}]` + ); }); function initializeContext(context) { - inspector.domainNames.forEach((domain) => { - Object.defineProperty(context, domain, { + ArrayPrototypeForEach(inspector.domainNames, (domain) => { + ObjectDefineProperty(context, domain, { value: inspector[domain], enumerable: true, configurable: true, @@ -913,8 +977,8 @@ function createRepl(inspector) { }, get profileEnd() { - return Profiler.stop() - .then(Profile.createAndRegister); + return PromisePrototypeThen(Profiler.stop(), + Profile.createAndRegister); }, get profiles() { @@ -963,8 +1027,9 @@ function createRepl(inspector) { HeapProfiler.on('addHeapSnapshotChunk', onChunk); print('Heap snapshot: 0/0', false); - HeapProfiler.takeHeapSnapshot({ reportProgress: true }) - .then(onResolve, onReject); + PromisePrototypeThen( + HeapProfiler.takeHeapSnapshot({ reportProgress: true }), + onResolve, onReject); }); }, @@ -973,21 +1038,22 @@ function createRepl(inspector) { }, watch(expr) { - watchedExpressions.push(expr); + ArrayPrototypePush(watchedExpressions, expr); }, unwatch(expr) { - const index = watchedExpressions.indexOf(expr); + const index = ArrayPrototypeIndexOf(watchedExpressions, expr); // Unwatch by expression // or // Unwatch by watcher number - watchedExpressions.splice(index !== -1 ? index : +expr, 1); + ArrayPrototypeSplice(watchedExpressions, + index !== -1 ? index : +expr, 1); }, get repl() { // Don't display any default messages - const listeners = repl.listeners('SIGINT').slice(0); + const listeners = ArrayPrototypeSlice(repl.listeners('SIGINT')); repl.removeAllListeners('SIGINT'); const oldContext = repl.context; @@ -995,7 +1061,7 @@ function createRepl(inspector) { exitDebugRepl = () => { // Restore all listeners process.nextTick(() => { - listeners.forEach((listener) => { + ArrayPrototypeForEach(listeners, (listener) => { repl.on('SIGINT', listener); }); }); @@ -1038,11 +1104,11 @@ function createRepl(inspector) { }, get version() { - return Runtime.evaluate({ + return PromisePrototypeThen(Runtime.evaluate({ expression: 'process.versions.v8', contextId: 1, returnByValue: true, - }).then(({ result }) => { + }), ({ result }) => { print(result.value); }); }, @@ -1067,18 +1133,17 @@ function createRepl(inspector) { aliasProperties(context, SHORTCUTS); } - function initAfterStart() { - return Runtime.enable() - .then(() => Profiler.enable()) - .then(() => Profiler.setSamplingInterval({ interval: 100 })) - .then(() => Debugger.enable()) - .then(() => Debugger.setPauseOnExceptions({ state: 'none' })) - .then(() => Debugger.setAsyncCallStackDepth({ maxDepth: 0 })) - .then(() => Debugger.setBlackboxPatterns({ patterns: [] })) - .then(() => - Debugger.setPauseOnExceptions({ state: pauseOnExceptionState })) - .then(() => restoreBreakpoints()) - .then(() => Runtime.runIfWaitingForDebugger()); + async function initAfterStart() { + await Runtime.enable(); + await Profiler.enable(); + await Profiler.setSamplingInterval({ interval: 100 }); + await Debugger.enable(); + await Debugger.setPauseOnExceptions({ state: 'none' }); + await Debugger.setAsyncCallStackDepth({ maxDepth: 0 }); + await Debugger.setBlackboxPatterns({ patterns: [] }); + await Debugger.setPauseOnExceptions({ state: pauseOnExceptionState }); + await restoreBreakpoints(); + return Runtime.runIfWaitingForDebugger(); } return function startRepl() {