From 8fd8135514b349baec28c8fd7179fa64b084a070 Mon Sep 17 00:00:00 2001
From: Antoine du Hamel <duhamelantoine1995@gmail.com>
Date: Tue, 27 Apr 2021 12:27:39 +0200
Subject: [PATCH] debugger: refactor `inspect_repl` to use primordials

---
 lib/internal/inspector/inspect_repl.js | 566 ++++++++++++++-----------
 1 file changed, 315 insertions(+), 251 deletions(-)

diff --git a/lib/internal/inspector/inspect_repl.js b/lib/internal/inspector/inspect_repl.js
index 0a1f3fcad5b153..e05c9225a89ed6 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 '<unknown>';
-  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 ? ' <native>' : ''}`;
         return `${isCurrent ? '*' : ' '} ${script.scriptId}: ${name}`;
-      })
-      .join('\n');
+      }), '\n');
   }
 
   function listScripts(displayNatives = false) {
@@ -371,21 +419,21 @@ 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;
       return stylize(
-        `[Profile ${endTime - startTime}${String.fromCharCode(956)}s]`,
+        `[Profile ${endTime - startTime}${StringFromCharCode(956)}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);
     }
@@ -393,68 +441,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);
           })
+        )
       );
     }
 
@@ -465,69 +520,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)) || '<unknown>';
-        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) {
@@ -541,11 +599,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);
     }
@@ -558,76 +621,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 });
     }
   }
 
@@ -643,9 +693,10 @@ 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);
   }
 
@@ -662,9 +713,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
@@ -673,8 +724,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') {
@@ -682,7 +734,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({
@@ -694,7 +746,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
       });
@@ -706,15 +758,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) {
@@ -728,19 +780,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,
@@ -752,41 +810,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;
       });
   }
@@ -812,13 +875,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}`);
         }));
   });
@@ -841,15 +904,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,
@@ -915,8 +978,8 @@ function createRepl(inspector) {
       },
 
       get profileEnd() {
-        return Profiler.stop()
-          .then(Profile.createAndRegister);
+        return PromisePrototypeThen(Profiler.stop(),
+                                    Profile.createAndRegister);
       },
 
       get profiles() {
@@ -965,8 +1028,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);
         });
       },
 
@@ -975,21 +1039,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;
@@ -997,7 +1062,7 @@ function createRepl(inspector) {
         exitDebugRepl = () => {
           // Restore all listeners
           process.nextTick(() => {
-            listeners.forEach((listener) => {
+            ArrayPrototypeForEach(listeners, (listener) => {
               repl.on('SIGINT', listener);
             });
           });
@@ -1040,11 +1105,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);
         });
       },
@@ -1069,18 +1134,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() {