From b70fed48a7fb0ac884e6574253a89db6158b2f60 Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 17 Oct 2011 18:24:12 -0700 Subject: [PATCH] Don't use a separate context for the repl. Fix #1484 Fix #1834 Fix #1482 Fix #771 It's been a while now, and we've seen how this separate context thing works. It constantly confuses people, and no one actually uses '.clear' anyway, so the benefit of that feature does not justify the constant WTFery. This makes repl.context actually be a getter that returns the global object, and prints a deprecation warning. The '.clear' command is gone, and will report that it's an invalid repl keyword. Tests updated to allow the require, module, and exports globals, which are still available in the repl just like they were before, by making them global. --- doc/api/repl.markdown | 18 +------ lib/repl.js | 114 +++++++++++------------------------------- test/common.js | 11 ++++ 3 files changed, 40 insertions(+), 103 deletions(-) diff --git a/doc/api/repl.markdown b/doc/api/repl.markdown index c0b9789e1b4..7bce1f7e50f 100644 --- a/doc/api/repl.markdown +++ b/doc/api/repl.markdown @@ -83,28 +83,12 @@ The special variable `_` (underscore) contains the result of the last expression > _ += 1 4 -The REPL provides access to any variables in the global scope. You can expose -a variable to the REPL explicitly by assigning it to the `context` object -associated with each `REPLServer`. For example: - - // repl_test.js - var repl = require("repl"), - msg = "message"; - - repl.start().context.m = msg; - -Things in the `context` object appear as local within the REPL: - - mjr:~$ node repl_test.js - > m - 'message' +The REPL provides access to any variables in the global scope. There are a few special REPL commands: - `.break` - While inputting a multi-line expression, sometimes you get lost or just don't care about completing it. `.break` will start over. - - `.clear` - Resets the `context` object to an empty object and clears any - multi-line expression. - `.exit` - Close the I/O stream, which will cause the REPL to exit. - `.help` - Show this list of special commands. diff --git a/lib/repl.js b/lib/repl.js index 3da7b61e4ff..0ac6bc5217d 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -46,6 +46,10 @@ var path = require('path'); var fs = require('fs'); var rl = require('readline'); +global.module = module; +global.exports = exports; +global.require = require; + // If obj.hasOwnProperty has been overridden, then calling // obj.hasOwnProperty(prop) will break. // See: https://github.com/joyent/node/issues/1707 @@ -53,9 +57,6 @@ function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } - -var context; - exports.disableColors = process.env.NODE_DISABLE_COLORS ? true : false; // hack for require.resolve("./relative") to work properly. @@ -71,16 +72,27 @@ exports.writer = util.inspect; function REPLServer(prompt, stream, eval) { var self = this; - self.eval = eval || function(code, context, file, cb) { + var contextWarning; + Object.defineProperty(this, 'context', { + get: function() { + if (!contextWarning) { + contextWarning = 'repl.context is deprecated.'; + console.error(contextWarning); + } + return global; + } + }); + + + self.eval = eval || function(code, file, cb) { try { - var err, result = vm.runInContext(code, context, file); + var err, result = vm.runInThisContext(code, file); } catch (e) { err = e; } cb(err, result); }; - self.resetContext(); self.bufferedCommand = ''; if (stream) { @@ -173,14 +185,13 @@ function REPLServer(prompt, stream, eval) { // First we attempt to eval as expression with parens. // This catches '{a : 1}' properly. self.eval('(' + evalCmd + ')', - self.context, 'repl', function(e, ret) { if (e && !isSyntaxError(e)) return finish(e); if (typeof ret === 'function' || e) { // Now as statement without parens. - self.eval(evalCmd, self.context, 'repl', finish); + self.eval(evalCmd, 'repl', finish); } else { finish(null, ret); } @@ -218,7 +229,7 @@ function REPLServer(prompt, stream, eval) { // If we got any output - print it (if no error) if (!e && ret !== undefined) { - self.context._ = ret; + global._ = ret; self.outputStream.write(exports.writer(ret) + '\n'); } @@ -245,25 +256,12 @@ exports.start = function(prompt, source, eval) { }; -REPLServer.prototype.createContext = function() { - var context = vm.createContext(); - - for (var i in global) context[i] = global[i]; - context.module = module; - context.require = require; - context.global = context; - context.global.global = context; - - return context; -}; - +var resetWarning; REPLServer.prototype.resetContext = function(force) { - if (!context || force) { - context = this.createContext(); - for (var i in require.cache) delete require.cache[i]; + if (!resetWarning) { + resetWarning = 'REPLServer.resetContext is deprecated.'; + console.error(resetWarning); } - - this.context = context; }; REPLServer.prototype.displayPrompt = function() { @@ -413,26 +411,9 @@ REPLServer.prototype.complete = function(line, callback) { if (!expr) { // If context is instance of vm.ScriptContext // Get global vars synchronously - if (this.context.constructor.name === 'Context') { - completionGroups.push(Object.getOwnPropertyNames(this.context)); - addStandardGlobals(); - completionGroupsLoaded(); - } else { - this.eval('.scope', this.context, 'repl', function(err, globals) { - if (err || !globals) { - addStandardGlobals(); - } else if (Array.isArray(globals[0])) { - // Add grouped globals - globals.forEach(function(group) { - completionGroups.push(group); - }); - } else { - completionGroups.push(globals); - addStandardGlobals(); - } - completionGroupsLoaded(); - }); - } + completionGroups.push(Object.getOwnPropertyNames(global)); + addStandardGlobals(); + completionGroupsLoaded(); function addStandardGlobals() { // Global object properties @@ -457,7 +438,7 @@ REPLServer.prototype.complete = function(line, callback) { } } else { - this.eval(expr, this.context, 'repl', function(e, obj) { + this.eval(expr, 'repl', function(e, obj) { // if (e) console.log(e); if (obj != null) { @@ -584,16 +565,6 @@ function defineDefaultCommands(repl) { } }); - repl.defineCommand('clear', { - help: 'Break, and also clear the local context', - action: function() { - this.outputStream.write('Clearing context...\n'); - this.bufferedCommand = ''; - this.resetContext(true); - this.displayPrompt(); - } - }); - repl.defineCommand('exit', { help: 'Exit the repl', action: function() { @@ -628,32 +599,3 @@ function trimWhitespace(cmd) { function regexpEscape(s) { return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); } - - -/** - * Converts commands that use var and function () to use the - * local exports.context when evaled. This provides a local context - * on the REPL. - * - * @param {String} cmd The cmd to convert. - * @return {String} The converted command. - */ -REPLServer.prototype.convertToContext = function(cmd) { - var self = this, matches, - scopeVar = /^\s*var\s*([_\w\$]+)(.*)$/m, - scopeFunc = /^\s*function\s*([_\w\$]+)/; - - // Replaces: var foo = "bar"; with: self.context.foo = bar; - matches = scopeVar.exec(cmd); - if (matches && matches.length === 3) { - return 'self.context.' + matches[1] + matches[2]; - } - - // Replaces: function foo() {}; with: foo = function foo() {}; - matches = scopeFunc.exec(self.bufferedCommand); - if (matches && matches.length === 2) { - return matches[1] + ' = ' + self.bufferedCommand; - } - - return cmd; -}; diff --git a/test/common.js b/test/common.js index a906217823c..a8ebb871f96 100644 --- a/test/common.js +++ b/test/common.js @@ -123,6 +123,17 @@ process.on('exit', function() { knownGlobals.push(DataView); } + // repl pollution + if (global.hasOwnProperty('module')) { + knownGlobals.push(global.module); + } + if (global.hasOwnProperty('require')) { + knownGlobals.push(global.require); + } + if (global.hasOwnProperty('exports')) { + knownGlobals.push(global.exports); + } + for (var x in global) { var found = false;