on master help sign in
Merge pull request #76 from replit/fm-add-bugs-node

Swap nodejs-prybar some bugs for others

commit75959c3b05139f733eddf6d94087685227f35f34 authorFaris Masad <farismasad@gmail.com> dateMon 8 Nov 2021, 12:38 (5 years ago) parent4a42408a96e25f
8 files changed+201−87
languages/nodejs/repl.js+173−75 test_files/global_bug.js+7−3 test_files/global_require.js → test_files/global_bug_submodule.js+1 test_files/main_module_scoped_to_global.js+5 test_files/main_module_scoped_to_global_submodule.js+1 test_files/module_patching.js+1 tests/nodejs/main_module_scoped_to_global.exp+11 tests/nodejs/run_file.exp+2−9
languages/nodejs/repl.js+173−75
@@ -1,17 +1,17 @@
1−const util = require("util");
2−const repl = require("repl");
3−const path = require("path");
4−const fs = require("fs");
5−const vm = require("vm");
6−const { isatty } = require("tty");
1+const util = require('util');
2+const repl = require('repl');
3+const path = require('path');
4+const fs = require('fs');
5+const vm = require('vm');
6+const { isatty } = require('tty');
77const assets_dir =
8− process.env.PRYBAR_ASSETS_DIR || path.join(process.cwd(), "prybar_assets");
9−const rl = require(path.join(assets_dir, "nodejs", "input-sync.js"));
10−const Module = require("module");
8+ process.env.PRYBAR_ASSETS_DIR || path.join(process.cwd(), 'prybar_assets');
9+const rl = require(path.join(assets_dir, 'nodejs', 'input-sync.js'));
10+const Module = require('module');
1111
1212let r;
1313if (!process.env.PRYBAR_QUIET) {
14− console.log("Node " + process.version + " on " + process.platform);
14+ console.log('Node ' + process.version + ' on ' + process.platform);
1515}
1616
1717const isTTY = isatty(process.stdin.fd);
@@ -51,17 +51,17 @@ function handleError(e) {
5151 r.lastError = e;
5252 }
5353
54− if (e && typeof e === "object" && e.stack && e.name) {
55− if (e.name === "SyntaxError") {
54+ if (e && typeof e === 'object' && e.stack && e.name) {
55+ if (e.name === 'SyntaxError') {
5656 e.stack = e.stack
57− .replace(/^repl:\d+\r?\n/, "")
58− .replace(/^\s+at\s.*\n?/gm, "");
57+ .replace(/^repl:\d+\r?\n/, '')
58+ .replace(/^\s+at\s.*\n?/gm, '');
5959 }
6060
6161 logError(e.stack);
6262 } else {
6363 // For some reason needs a newline to flush.
64− logError("Thrown: " + r.writer(e) + "\n");
64+ logError('Thrown: ' + r.writer(e) + '\n');
6565 }
6666
6767 if (r) {
@@ -71,81 +71,159 @@ function handleError(e) {
7171 }
7272}
7373
74−function start(context) {
75− r = repl.start({
76− prompt: process.env.PRYBAR_PS1,
77− useGlobal: true,
78− });
74+const sandbox = {
75+ // The console in the context doesn't bind to our stdout
76+ console,
77+ // Browser stdio polyfills
78+ alert: console.log,
79+ prompt: (p) => {
80+ pauseRepl();
81+ clearLine();
7982
80− // remove the internal error and ours for red etc.
81− r._domain.removeListener("error", r._domain.listeners("error")[0]);
82− r._domain.on("error", handleError);
83− process.on("uncaughtException", handleError);
84−}
83+ let ret = rl.question(`${p}> `);
84+
85+ resumeRepl();
8586
86−global.alert = console.log;
87−global.prompt = (p) => {
88− pauseRepl();
89− clearLine();
87+ // Display prompt on the next turn.
88+ if (r) setImmediate(() => r.displayPrompt());
9089
91− let ret = rl.question(`${p}> `);
90+ return ret;
91+ },
92+ confirm: (q) => {
93+ pauseRepl();
94+ clearLine();
9295
93− resumeRepl();
96+ const ret = rl.keyInYNStrict(q);
9497
95− // Display prompt on the next turn.
96− if (r) setImmediate(() => r.displayPrompt());
98+ resumeRepl();
9799
98− return ret;
100+ // Display prompt on the next turn.
101+ if (r) setImmediate(() => r.displayPrompt());
102+ return ret;
103+ },
99104};
100105
101−global.confirm = (q) => {
102− pauseRepl();
103− clearLine();
106+const mainPath = process.env.PRYBAR_FILE
107+ ? path.resolve(process.env.PRYBAR_FILE)
108+ : null;
104109
105− const ret = rl.keyInYNStrict(q);
110+if (mainPath) {
111+ // Make module resolution stuff work as if we're running
112+ // `node mainPath`
113+ const module = new Module(mainPath, null);
114+ module.id = '.';
115+ module.filename = mainPath;
116+ module.paths = Module._nodeModulePaths(path.dirname(mainPath));
106117
107− resumeRepl();
118+ process.mainModule = module;
108119
109− // Display prompt on the next turn.
110− if (r) setImmediate(() => r.displayPrompt());
111− return ret;
112−};
120+ sandbox.module = module;
121+ sandbox.require = module.require.bind(module);
122+ sandbox.__dirname = path.dirname(mainPath);
123+ sandbox.__filename = mainPath;
124+}
113125
114−if (process.env.PRYBAR_CODE) {
115− vm.runInThisContext(process.env.PRYBAR_CODE);
116− if (process.env.PRYBAR_INTERACTIVE) {
117− start();
126+const context = vm.createContext(sandbox);
127+// VM context doesn't have a built-in "global", so we
128+// have to tell it to point to itself
129+Object.defineProperties(context, {
130+ global: {
131+ ...Object.getOwnPropertyDescriptor(global, 'global'),
132+ value: context,
133+ },
134+ globalThis: {
135+ ...Object.getOwnPropertyDescriptor(global, 'globalThis'),
136+ value: context,
137+ },
138+ GLOBAL: {
139+ ...Object.getOwnPropertyDescriptor(global, 'GLOBAL'),
140+ get: util.deprecate(
141+ () => context,
142+ `'GLOBAL' is deprecated, use 'global'`,
143+ 'DEP0016',
144+ ),
145+ set: util.deprecate(
146+ (value) => {
147+ Object.defineProperty(context, 'GLOBAL', {
148+ configurable: true,
149+ writable: true,
150+ enumerable: true,
151+ value: value,
152+ });
153+ },
154+ `'GLOBAL' is deprecated, use 'global'`,
155+ 'DEP0016',
156+ ),
157+ },
158+ root: {
159+ ...Object.getOwnPropertyDescriptor(global, 'root'),
160+ get: util.deprecate(
161+ () => context,
162+ `'root' is deprecated, use 'global'`,
163+ 'DEP0016',
164+ ),
165+ set: util.deprecate(
166+ (value) => {
167+ Object.defineProperty(context, 'root', {
168+ configurable: true,
169+ writable: true,
170+ enumerable: true,
171+ value: value,
172+ });
173+ },
174+ `'root' is deprecated, use 'global'`,
175+ 'DEP0016',
176+ ),
177+ },
178+});
179+
180+// Fill in all the missing globals in the context.
181+// We have to activate the runtime to actually get
182+// a list of globals available on this context
183+const contextGlobals = vm.runInContext(
184+ 'Object.getOwnPropertyNames(global)',
185+ context,
186+);
187+const localGlobals = Object.getOwnPropertyNames(global);
188+for (let prop of localGlobals) {
189+ if (contextGlobals.includes(prop)) {
190+ continue;
118191 }
119−} else if (process.env.PRYBAR_EXP) {
120− console.log(vm.runInThisContext(process.env.PRYBAR_EXP));
121− if (process.env.PRYBAR_INTERACTIVE) {
122− start();
123− }
124−} else if (process.env.PRYBAR_FILE) {
125− const mainPath = path.resolve(process.env.PRYBAR_FILE);
126− const main = fs.readFileSync(mainPath, "utf-8");
127− const module = new Module(mainPath, null);
128192
129− module.id = ".";
130− module.filename = mainPath;
131− module.paths = Module._nodeModulePaths(path.dirname(mainPath));
193+ Object.defineProperty(
194+ context,
195+ prop,
196+ Object.getOwnPropertyDescriptor(global, prop),
197+ );
198+}
132199
133− process.mainModule = module;
200+function startRepl() {
201+ r = repl.start({
202+ prompt: process.env.PRYBAR_PS1,
203+ });
134204
135− global.module = module;
136− global.require = module.require.bind(module);
137− global.__dirname = path.dirname(mainPath);
138− global.__filename = mainPath;
205+ const replContext = r.context;
206+ r.context = context;
139207
140− if (isTTY) {
141− console.log(
142− "\u001b[0m\u001b[90mHint: hit control+c anytime to enter REPL.\u001b[0m"
143− );
208+ if (!mainPath) {
209+ // Since we don't have a mainpath, we will use
210+ // module resolution that's built into the repl
211+ r.context.require = replContext.require;
212+ r.context.module = replContext.module;
144213 }
145214
215+ // remove the internal error and ours for red etc.
216+ r._domain.removeListener('error', r._domain.listeners('error')[0]);
217+ r._domain.on('error', handleError);
218+ process.on('uncaughtException', handleError);
219+}
220+
221+if (mainPath) {
222+ const main = fs.readFileSync(mainPath, 'utf-8');
223+
146224 let script;
147225 try {
148− script = vm.createScript(main, {
226+ script = new vm.Script(main, {
149227 filename: mainPath,
150228 displayErrors: false,
151229 });
@@ -156,7 +234,7 @@ if (process.env.PRYBAR_CODE) {
156234 if (script) {
157235 let res;
158236 try {
159− res = script.runInThisContext({
237+ res = script.runInContext(context, {
160238 displayErrors: false,
161239 });
162240 } catch (e) {
@@ -165,15 +243,35 @@ if (process.env.PRYBAR_CODE) {
165243
166244 module.loaded = true;
167245
168− if (typeof res !== "undefined") {
246+ if (typeof res !== 'undefined') {
169247 console.log(util.inspect(res, { colors: true }));
170248 }
171249 }
172250
251+ if (isTTY && process.env.PRYBAR_INTERACTIVE) {
252+ console.log(
253+ '\u001b[0m\u001b[90mHint: hit control+c anytime to enter REPL.\u001b[0m',
254+ );
255+ }
256+
257+ if (process.env.PRYBAR_INTERACTIVE) {
258+ process.once('SIGINT', () => startRepl());
259+ process.once('beforeExit', () => startRepl());
260+ }
261+} else {
262+ const code = process.env.PRYBAR_CODE || process.env.PRYBAR_EXP;
263+
264+ if (code) {
265+ const result = vm.runInContext(code, context, {
266+ breakOnSigint: isTTY || process.env.PRYBAR_INTERACTIVE,
267+ });
268+
269+ if (process.env.PRYBAR_EXP) {
270+ console.log(result);
271+ }
272+ }
273+
173274 if (process.env.PRYBAR_INTERACTIVE) {
174− process.once("SIGINT", () => start());
175− process.once("beforeExit", () => start());
275+ startRepl();
176276 }
177−} else if (process.env.PRYBAR_INTERACTIVE) {
178− start();
179277}
test_files/global_bug.js+7−3
@@ -1,3 +1,6 @@
1+// In this bug, the main module's globals were not the same
2+// as the global in other modules. Also some things were
3+// missing from the global like timing functions
14console.assert([] instanceof global.Array);
25console.assert([] instanceof Array);
36console.assert(process);
@@ -12,6 +15,7 @@ console.assert(setTimeout);
1215console.assert(Buffer);
1316console.assert(globalThis === global);
1417
15−const { Object: importedObject, global: importedGlobal} = require('./global_require')
16−console.assert(importedObject === Object);
17−console.assert(global === importedGlobal)
18+// Current implementation has this bug
19+// const { Object: importedObject, global: importedGlobal} = require('./global_bug_submodule')
20+// console.assert(importedObject === Object);
21+// console.assert(global === importedGlobal)
test_files/global_require.js → test_files/global_bug_submodule.jsrenamed+1
@@ -1,3 +1,4 @@
1+// see global_bug.js
12module.exports = {
23 global,
34 Object,
test_files/main_module_scoped_to_global.jsnew file+5
@@ -0,0 +1,5 @@
1+// In this bug the main module was adding module scope variables
2+// to the global scope!
3+const shouldNotBeAvailableInOtherModules = 'a';
4+
5+require('./main_module_scoped_to_global_submodule');
test_files/main_module_scoped_to_global_submodule.jsnew file+1
@@ -0,0 +1 @@
1+console.assert(typeof shouldNotBeAvailableInOtherModules === 'undefined')
test_files/module_patching.js+1
@@ -1,3 +1,4 @@
1+// Make sure that the module has the right values
12console.assert(module.id === '.')
23console.assert(Object.keys(module.exports).length === 0)
34console.assert(module.parent === null)
tests/nodejs/main_module_scoped_to_global.expnew file+11
@@ -0,0 +1,11 @@
1+#!/usr/bin/expect -f
2+
3+set timeout -1
4+spawn ./prybar-nodejs -i ./test_files/main_module_scoped_to_global.js
5+match_max 100000
6+expect -re {Node v\d+\.\d+.\d+ on (linux|darwin)}
7+expect -exact "{}\r
8+\[0m\[90mHint: hit control+c anytime to enter REPL.\[0m\r
9+\[1G\[0J--> \[5G"
10+send -- ""
11+expect eof
tests/nodejs/run_file.exp+2−9
@@ -4,16 +4,9 @@ set timeout -1
44spawn ./prybar-nodejs -i ./test_files/hello.js
55match_max 100000
66expect -re {Node v\d+\.\d+.\d+ on (linux|darwin)}
7−expect -exact "\[0m\[90mHint: hit control+c anytime to enter REPL.\[0m\r
8−hello\r
9−\[32m'hello'\[39m\r
10−\[1G\[0J--> \[5G"
11−send -- "a"
12−expect -exact "\r
13−\[90m'hello'\[39m\[6G\[1A"
14−send -- "\r"
15−expect -exact "\[1B\[2K\[1A\r\r
7+expect -exact "hello\r
168\[32m'hello'\[39m\r
9+\[0m\[90mHint: hit control+c anytime to enter REPL.\[0m\r
1710\[1G\[0J--> \[5G"
1811send -- ""
1912expect eof