Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Update how REPLServer uses contexts #851

Closed
wants to merge 1 commit into from

2 participants

@weaver

Hi, I was trying to subclass REPLServer to not share a context across instances. It turns out there's no way to do this conveniently because the current REPL implementation refers to a global context variable in several places. This patch does the following:

  • Always use this.context or self.context instead of context.
  • Move resetContext to REPLServer.createContext.
  • Add REPLServer.resetContext, memoize context here.
  • Memoize exports.repl in start instead of REPLServer constructor.

My use case is a local "administration" REPL for a web service. If several people are connected to the REPL, I don't want their context shared because they may clobber each others bindings by defining variables with the same name or using ".clear".

With this patch, the following works as expected (each repl has a unique local context):

util.inherits(REPL, repl.REPLServer);
function REPL(prompt, stream) {
  repl.REPLServer.call(this, prompt, stream);
}

REPL.prototype.resetContext = function() {
  this.context = this.createContext();
};
@weaver weaver Update how REPLServer uses contexts
* Always use `this.context` or `self.context`.
* Move `resetContext` to `REPLServer.createContext`.
* Add `REPLServer.resetContext`, memoize `context` here.
* Memoize `exports.repl` in `start`.
7ff173f
@ry
ry commented

Thanks Ben. Looks good. landed in master

@ry
ry commented

landed in d63a551

@ry ry closed this
@coolaj86 coolaj86 referenced this pull request from a commit
@weaver weaver Update how REPLServer uses contexts
* Always use `this.context` or `self.context`.
* Move `resetContext` to `REPLServer.createContext`.
* Add `REPLServer.resetContext`, memoize `context` here.
* Memoize `exports.repl` in `start`.

Closes GH-851.
d63a551
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 29, 2011
  1. @weaver

    Update how REPLServer uses contexts

    weaver authored
    * Always use `this.context` or `self.context`.
    * Move `resetContext` to `REPLServer.createContext`.
    * Add `REPLServer.resetContext`, memoize `context` here.
    * Memoize `exports.repl` in `start`.
This page is out of date. Refresh to see the latest.
Showing with 30 additions and 20 deletions.
  1. +30 −20 lib/repl.js
View
50 lib/repl.js
@@ -60,27 +60,14 @@ module.filename = process.cwd() + '/repl';
// hack for repl require to work properly with node_modules folders
module.paths = require('module')._nodeModulePaths(module.filename);
-
-function resetContext() {
- 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;
- for (var i in require.cache) delete require.cache[i];
-}
-
-
// Can overridden with custom print functions, such as `probe` or `eyes.js`
exports.writer = util.inspect;
function REPLServer(prompt, stream) {
var self = this;
- if (!context) resetContext();
- if (!exports.repl) exports.repl = this;
- self.context = context;
+
+ self.resetContext();
self.bufferedCommand = '';
if (stream) {
@@ -159,7 +146,7 @@ function REPLServer(prompt, stream) {
// First we attempt to eval as expression with parens.
// This catches '{a : 1}' properly.
ret = vm.runInContext('(' + self.bufferedCommand + ')',
- context,
+ self.context,
'repl');
if (typeof ret !== 'function') success = true;
} catch (e) {
@@ -168,11 +155,11 @@ function REPLServer(prompt, stream) {
if (!success) {
// Now as statement without parens.
- ret = vm.runInContext(self.bufferedCommand, context, 'repl');
+ ret = vm.runInContext(self.bufferedCommand, self.context, 'repl');
}
if (ret !== undefined) {
- context._ = ret;
+ self.context._ = ret;
self.outputStream.write(exports.writer(ret) + '\n');
}
@@ -215,10 +202,33 @@ exports.REPLServer = REPLServer;
// prompt is a string to print on each line for the prompt,
// source is a stream to use for I/O, defaulting to stdin/stdout.
exports.start = function(prompt, source) {
- return new REPLServer(prompt, source);
+ var repl = new REPLServer(prompt, source);
+ if (!exports.repl) exports.repl = repl;
+ return repl;
};
+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 (!context || force) {
+ context = this.createContext();
+ for (var i in require.cache) delete require.cache[i];
+ }
+
+ this.context = context;
+};
+
REPLServer.prototype.displayPrompt = function() {
this.rli.setPrompt(this.bufferedCommand.length ? '... ' : this.prompt);
this.rli.prompt();
@@ -503,7 +513,7 @@ function defineDefaultCommands(repl) {
action: function() {
this.outputStream.write('Clearing context...\n');
this.bufferedCommand = '';
- resetContext();
+ this.resetContext(true);
this.displayPrompt();
}
});
Something went wrong with that request. Please try again.