From c3352ddc80ad33dc609fa80cc84057bc10884148 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com>
Date: Tue, 10 Dec 2019 15:37:32 +0100
Subject: [PATCH] repl: fix autocomplete when useGlobal is false

This fixes two issues in the REPL when it is started with a new context
(useGlobal option set to `false`):
- The `primordials` object does not contain all builtins, so the
  filtering based on property names from `primordials` was wrong.
- The autocompleter did not take builtin names into account because
  they are not properties of the context object.

A list of all global builtin names is created lazily when needed. It is
used for filtering for the copy and for adding those names to the
autocompleter list.

Fixes: https://github.com/nodejs/node/issues/30792
---
 lib/repl.js | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/lib/repl.js b/lib/repl.js
index 98b0d2415d4f70..4d40da4daabd95 100644
--- a/lib/repl.js
+++ b/lib/repl.js
@@ -119,6 +119,9 @@ const { setImmediate } = require('timers');
 // Lazy-loaded.
 let processTopLevelAwait;
 
+const globalBuiltins =
+  new Set(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)'));
+
 const parentModule = module;
 const replMap = new WeakMap();
 const domainSet = new WeakSet();
@@ -935,8 +938,8 @@ REPLServer.prototype.createContext = function() {
       context = vm.createContext();
     });
     for (const name of ObjectGetOwnPropertyNames(global)) {
-      // Only set properties on the context that do not exist as primordial.
-      if (!(name in primordials)) {
+      // Only set properties that do not already exist as a global builtin.
+      if (!globalBuiltins.has(name)) {
         ObjectDefineProperty(context, name,
                              ObjectGetOwnPropertyDescriptor(global, name));
       }
@@ -1283,8 +1286,14 @@ function complete(line, callback) {
             completionGroups.push(
               filteredOwnPropertyNames.call(this, contextProto));
           }
-          completionGroups.push(
-            filteredOwnPropertyNames.call(this, this.context));
+          const contextOwnNames =
+            filteredOwnPropertyNames.call(this, this.context);
+          if (!this.useGlobal) {
+            // When the context is not `global`, builtins are not own
+            // properties of it.
+            contextOwnNames.push(...globalBuiltins);
+          }
+          completionGroups.push(contextOwnNames);
           if (filter !== '') addCommonWords(completionGroups);
           completionGroupsLoaded();
         } else {