Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
Revert "Don't use a separate context for the repl."
Browse files Browse the repository at this point in the history
This reverts commit b70fed4.
  • Loading branch information
isaacs committed Oct 21, 2011
1 parent 0ae98df commit caf70f5
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 41 deletions.
18 changes: 17 additions & 1 deletion doc/api/repl.markdown
Expand Up @@ -83,12 +83,28 @@ The special variable `_` (underscore) contains the result of the last expression
> _ += 1
4

The REPL provides access to any variables in the global scope.
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'

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.

Expand Down
116 changes: 87 additions & 29 deletions lib/repl.js
Expand Up @@ -46,17 +46,16 @@ 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
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.
Expand All @@ -72,27 +71,16 @@ exports.writer = util.inspect;
function REPLServer(prompt, stream, eval) {
var self = this;

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) {
self.eval = eval || function(code, context, file, cb) {
try {
var err, result = vm.runInThisContext(code, file);
var err, result = vm.runInContext(code, context, file);
} catch (e) {
err = e;
}
cb(err, result);
};

self.resetContext();
self.bufferedCommand = '';

if (stream) {
Expand Down Expand Up @@ -185,13 +173,14 @@ 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, 'repl', finish);
self.eval(evalCmd, self.context, 'repl', finish);
} else {
finish(null, ret);
}
Expand Down Expand Up @@ -228,8 +217,8 @@ function REPLServer(prompt, stream, eval) {
self.bufferedCommand = '';

// If we got any output - print it (if no error)
if (!e) {
global._ = ret;
if (!e && ret !== undefined) {

This comment has been minimized.

Copy link
@TooTallNate

TooTallNate Oct 22, 2011

This blew away 95d530f, so it's not in v0.5.10

This comment has been minimized.

Copy link
@koichik

koichik Oct 22, 2011

I applied your patch again (bdb9d09).

This comment has been minimized.

Copy link
@TooTallNate

TooTallNate Oct 22, 2011

Thanks @koichik

self.context._ = ret;
self.outputStream.write(exports.writer(ret) + '\n');
}

Expand All @@ -256,12 +245,25 @@ exports.start = function(prompt, source, eval) {
};


var resetWarning;
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;
};

REPLServer.prototype.resetContext = function(force) {
if (!resetWarning) {
resetWarning = 'REPLServer.resetContext is deprecated.';
console.error(resetWarning);
if (!context || force) {
context = this.createContext();
for (var i in require.cache) delete require.cache[i];

This comment has been minimized.

Copy link
@ncr

ncr Mar 6, 2012

What are the reasons for clearing the require cache?

}

this.context = context;
};

REPLServer.prototype.displayPrompt = function() {
Expand Down Expand Up @@ -411,9 +413,26 @@ REPLServer.prototype.complete = function(line, callback) {
if (!expr) {
// If context is instance of vm.ScriptContext
// Get global vars synchronously
completionGroups.push(Object.getOwnPropertyNames(global));
addStandardGlobals();
completionGroupsLoaded();
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();
});
}

function addStandardGlobals() {
// Global object properties
Expand All @@ -438,7 +457,7 @@ REPLServer.prototype.complete = function(line, callback) {
}

} else {
this.eval(expr, 'repl', function(e, obj) {
this.eval(expr, this.context, 'repl', function(e, obj) {
// if (e) console.log(e);

if (obj != null) {
Expand Down Expand Up @@ -565,6 +584,16 @@ 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() {
Expand Down Expand Up @@ -599,3 +628,32 @@ function trimWhitespace(cmd) {
function regexpEscape(s) {
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}


/**
* Converts commands that use var and function <name>() 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;
};
11 changes: 0 additions & 11 deletions test/common.js
Expand Up @@ -123,17 +123,6 @@ 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;

Expand Down

3 comments on commit caf70f5

@bnoordhuis
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this reverted? .clear is broken now according to #2034. Does this commit have something to do with that?

@koichik
Copy link

@koichik koichik commented on caf70f5 Nov 7, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, b70fed4 broke debugger. I think that 2d02e6a should have removed .clear again.

@isaacs
Copy link
Author

@isaacs isaacs commented on caf70f5 Nov 8, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The next commit adds a useGlobal flag to provide the same effect. See discussion on #2034

Please sign in to comment.