diff --git a/build.xml b/build.xml
index 37cc361..2588b07 100644
--- a/build.xml
+++ b/build.xml
@@ -274,6 +274,7 @@
+
@@ -350,6 +351,7 @@
+
diff --git a/patches/lib/module.js.patch b/patches/lib/module.js.patch
index ade0e01..5e334c3 100644
--- a/patches/lib/module.js.patch
+++ b/patches/lib/module.js.patch
@@ -1,5 +1,5 @@
---- ../node/lib/module.js 2013-08-22 13:55:28.000000000 +0200
-+++ src/main/js/lib/module.js 2013-10-07 14:01:12.000000000 +0200
+--- ../nodejs/lib/module.js 2013-12-02 16:45:51.723090200 -0800
++++ src/main/js/lib/module.js 2013-12-05 16:49:59.264917800 -0800
@@ -271,13 +271,19 @@
return [id, [path.dirname(parent.filename)]];
};
@@ -21,6 +21,15 @@
var cachedModule = Module._cache[filename];
if (cachedModule) {
+@@ -288,7 +294,7 @@
+ // REPL is a special case, because it needs the real require.
+ if (filename == 'repl') {
+ var replModule = new Module('repl');
+- replModule._compile(NativeModule.getSource('repl'), 'repl.js');
++ replModule._compile(NativeModule.getSource('/lib/repl.js'), 'repl.js');
+ NativeModule._cache.repl = replModule;
+ return replModule.exports;
+ }
@@ -309,7 +315,11 @@
var hadException = true;
@@ -49,7 +58,7 @@
// Returns exception if any
Module.prototype._compile = function(content, filename) {
var self = this;
-+
++
// remove shebang
+ if(content != null) {
content = content.replace(/^\#\!.*/, '');
diff --git a/patches/lib/repl.js.patch b/patches/lib/repl.js.patch
new file mode 100644
index 0000000..6ad216e
--- /dev/null
+++ b/patches/lib/repl.js.patch
@@ -0,0 +1,24 @@
+--- ../nodejs/lib/repl.js 2013-12-05 16:43:37.738090700 -0800
++++ src/main/js/lib/repl.js 2013-12-05 16:44:53.691533100 -0800
+@@ -535,7 +535,7 @@
+ // Get global vars synchronously
+ if (this.useGlobal ||
+ this.context.constructor &&
+- this.context.constructor.name === 'Context') {
++ this.context.constructor.name === 'Object') {
+ var contextProto = this.context;
+ while (contextProto = Object.getPrototypeOf(contextProto)) {
+ completionGroups.push(Object.getOwnPropertyNames(contextProto));
+@@ -918,10 +918,10 @@
+ e = e && (e.stack || e.toString());
+ return e && e.match(/^SyntaxError/) &&
+ // RegExp syntax error
+- !e.match(/^SyntaxError: Invalid regular expression/) &&
++ !e.match(/^SyntaxError: repl/) &&
+ !e.match(/^SyntaxError: Invalid flags supplied to RegExp constructor/) &&
+ // "strict mode" syntax errors
+ !e.match(/^SyntaxError: .*strict mode.*/i) &&
+ // JSON.parse() error
+- !e.match(/\n {4}at Object.parse \(native\)\n/);
++ !e.match(/^SyntaxError: Invalid /);
+ }
diff --git a/project.properties b/project.properties
index de05644..a021e9c 100644
--- a/project.properties
+++ b/project.properties
@@ -28,6 +28,7 @@ source.lib.modules = \
path.js \
querystring.js \
readline.js \
+ repl.js \
stream.js \
string_decoder.js \
timers.js \
@@ -436,6 +437,12 @@ source.test.simple.list = \
test-regress-GH-877.js \
test-regress-GH-897.js \
test-regression-object-prototype.js \
+ test-repl-autolibs.js \
+ test-repl-console.js \
+ test-repl-end-emits-exit.js \
+ test-repl-options.js \
+ test-repl-require-cache.js \
+ test-repl-tab-complete.js \
test-require-cache.js \
test-require-cache-without-stat.js \
test-require-exceptions.js \
diff --git a/src/main/java/net/java/avatar/js/Loader.java b/src/main/java/net/java/avatar/js/Loader.java
index c4770d5..069dc7b 100644
--- a/src/main/java/net/java/avatar/js/Loader.java
+++ b/src/main/java/net/java/avatar/js/Loader.java
@@ -25,6 +25,7 @@
package net.java.avatar.js;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -125,6 +126,8 @@ public abstract class Loader {
*/
public abstract boolean exists(final String id);
+ public abstract String load(final String id);
+
/**
* Returns the value of a compiled-in property.
* @param key the key whose value is desired
@@ -192,6 +195,27 @@ public abstract class Loader {
return BUILD_PROPERTIES.getProperty(key);
}
+ @Override
+ public String load(final String id) {
+ final int BUFFERSIZE = 64 * 1024;
+ final byte[] data = new byte[BUFFERSIZE];
+ final ByteArrayOutputStream buffer = new ByteArrayOutputStream(BUFFERSIZE);
+ final InputStream is = this.getClass().getResourceAsStream(id);
+ assert is != null;
+
+ try {
+ int bytesRead = is.read(data, 0, data.length);
+ while (bytesRead != -1) {
+ buffer.write(data, 0, bytesRead);
+ bytesRead = is.read(data, 0, data.length);
+ }
+ buffer.flush();
+ } catch (final IOException ex) {
+ ex.printStackTrace();
+ }
+ return new String(buffer.toByteArray());
+ }
+
protected String pathFor(final String id) {
return MODULES_DIR + id + SCRIPT_EXTENSION;
}
diff --git a/src/main/java/net/java/avatar/js/Server.java b/src/main/java/net/java/avatar/js/Server.java
index 1012e7f..d8df761 100644
--- a/src/main/java/net/java/avatar/js/Server.java
+++ b/src/main/java/net/java/avatar/js/Server.java
@@ -100,9 +100,7 @@ public final class Server {
}
public static void main(final String... args) throws Exception {
- if (args != null && args.length > 0) {
- new Server().run(args);
- }
+ new Server().run(args);
}
public Server() throws Exception {
@@ -147,7 +145,11 @@ public final class Server {
checkPermission();
bindings.put(SECURE_HOLDER, holder);
try {
- runUserScripts(args);
+ if (args.length == 0) {
+ runREPL();
+ } else {
+ runUserScripts(args);
+ }
} catch (final ScriptException ex) {
if (!eventLoop.handleCallbackException(ex)) {
throw ex;
@@ -206,11 +208,21 @@ public final class Server {
}
final String[] userFiles = {userFile};
- holder.setArgs(avatarArgs.toArray(new String[avatarArgs.size()]),
- userArgs.toArray(new String[userArgs.size()]),
- userFiles);
+
+ runEventLoop(avatarArgs.toArray(new String[avatarArgs.size()]),
+ userArgs.toArray(new String[userArgs.size()]),
+ userFiles);
+ }
+
+ private void runREPL() throws Exception {
+ holder.setForceRepl(true);
+ runEventLoop(null, null, null);
+ }
+
+ private void runEventLoop(final String[] avatarArgs, final String[] userArgs, final String[] userFiles) throws Exception {
Exception rootCause = null;
- // First run the main script...
+ holder.setArgs(avatarArgs, userArgs, userFiles);
+
try {
runSystemScript(SYSTEM_INIT_SCRIPTS);
} catch(Exception ex) {
@@ -234,17 +246,15 @@ public final class Server {
throw ex;
}
} finally {
- if (args != null && args.length > 0) {
- try {
- // emit the process.exit event
- runSystemScript(SYSTEM_FINALIZATION_SCRIPTS);
- } catch (Exception ex) {
- if (rootCause != null) {
- rootCause.addSuppressed(ex);
- throw rootCause;
- }
- throw ex;
+ try {
+ // emit the process.exit event
+ runSystemScript(SYSTEM_FINALIZATION_SCRIPTS);
+ } catch (Exception ex) {
+ if (rootCause != null) {
+ rootCause.addSuppressed(ex);
+ throw rootCause;
}
+ throw ex;
}
}
}
@@ -266,6 +276,8 @@ public final class Server {
} else if ("--trace-deprecation".equals(arg)) {
holder.setTraceDeprecation(true);
holder.setThrowDeprecation(false);
+ } else if ("-i".equals(arg) || "--interactive".equals(arg)) {
+ holder.setForceRepl(true);
} else {
System.out.println(HELP);
throw new IllegalArgumentException(arg);
@@ -339,6 +351,7 @@ public final class Server {
private String[] userFiles;
private boolean throwDeprecation = true;
private boolean traceDeprecation;
+ private boolean forceRepl = false;
private int exitCode = 0;
private final Invocable invocable;
private Object nativeModule;
@@ -368,9 +381,9 @@ public final class Server {
}
private void setArgs(final String[] avatarArgs, final String[] userArgs, final String[] userFiles) {
- this.avatarArgs = avatarArgs.clone();
- this.userArgs = userArgs.clone();
- this.userFiles = userFiles.clone();
+ this.avatarArgs = avatarArgs != null ? avatarArgs.clone() : null;
+ this.userArgs = userArgs != null ? userArgs.clone() : null;
+ this.userFiles = userFiles != null ? userFiles.clone() : null;
}
public String[] getAvatarArgs() {
@@ -393,6 +406,10 @@ public final class Server {
this.traceDeprecation = traceDeprecation;
}
+ private void setForceRepl(boolean forceRepl) {
+ this.forceRepl = forceRepl;
+ }
+
public boolean getThrowDeprecation() {
return throwDeprecation;
}
@@ -401,6 +418,10 @@ public final class Server {
return traceDeprecation;
}
+ public boolean getForceRepl() {
+ return forceRepl;
+ }
+
private void setExitCode(int exitCode) {
this.exitCode = exitCode;
}
diff --git a/src/main/js/lib/process.js b/src/main/js/lib/process.js
index 0f3c3c8..7437fc4 100644
--- a/src/main/js/lib/process.js
+++ b/src/main/js/lib/process.js
@@ -77,6 +77,11 @@ Object.defineProperty(exports, 'traceDeprecation', {
value: __avatar.traceDeprecation
});
+Object.defineProperty(exports, '_forceRepl', {
+ enumerable: true,
+ value: __avatar.forceRepl
+});
+
var stdin;
Object.defineProperty(exports, 'stdin', {
enumerable: true,
@@ -278,12 +283,12 @@ Object.defineProperty(exports, 'argv', {
_argv = [];
_argv.push(exports.execPath);
var uf = __avatar.userFiles;
- var flen = uf.length;
+ var flen = uf ? uf.length : 0;
for (var i=0; i < flen; i++) {
_argv.push(uf[i]);
}
var ua = __avatar.userArgs;
- var alen = ua.length;
+ var alen = ua ? ua.length : 0;
for (var j=0; j < alen; j++) {
_argv.push(ua[j]);
}
@@ -302,7 +307,7 @@ Object.defineProperty(exports, 'execArgv', {
if (!_execArgv) {
_execArgv = [];
var aa = __avatar.avatarArgs;
- var alen = aa.length;
+ var alen = aa ? aa.length : 0;
for (var j=0; j < alen; j++) {
_execArgv.push(aa[j]);
}
diff --git a/src/main/js/net/java/avatar/js/init.js b/src/main/js/net/java/avatar/js/init.js
index 9a1e875..ad14c6c 100644
--- a/src/main/js/net/java/avatar/js/init.js
+++ b/src/main/js/net/java/avatar/js/init.js
@@ -300,6 +300,55 @@ var gc = global.gc;
return current.apply(this, args);
}
- NativeModule.require('module').runMain();
+ if (process.argv[1]) {
+ // make process.argv[1] into a full path
+ var path = NativeModule.require('path');
+ process.argv[1] = path.resolve(process.argv[1]);
+ // If this is a worker in cluster mode, start up the communiction
+ // channel.
+ if (process.env.NODE_UNIQUE_ID) {
+ var cluster = NativeModule.require('cluster');
+ cluster._setupWorker();
+
+ // Make sure it's not accidentally inherited by child processes.
+ delete process.env.NODE_UNIQUE_ID;
+ }
+
+ NativeModule.require('module').runMain();
+ } else {
+ var Module = NativeModule.require('module');
+
+ // If -i or --interactive were passed, or stdin is a TTY.
+ if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
+ // REPL
+ var opts = {
+ useGlobal: true,
+ ignoreUndefined: false
+ };
+ if (parseInt(process.env['NODE_NO_READLINE'], 10)) {
+ opts.terminal = false;
+ }
+ if (parseInt(process.env['NODE_DISABLE_COLORS'], 10)) {
+ opts.useColors = false;
+ }
+ var repl = Module.requireRepl().start(opts);
+ repl.on('exit', function() {
+ process.exit();
+ });
+ } else {
+ // Read all of stdin - execute it.
+ process.stdin.setEncoding('utf8');
+
+ var code = '';
+ process.stdin.on('data', function(d) {
+ code += d;
+ });
+
+ process.stdin.on('end', function() {
+ process._eval = code;
+ evalScript('[stdin]');
+ });
+ }
+ }
} )();