diff --git a/core/src/main/java/io/apigee/trireme/core/NodeScript.java b/core/src/main/java/io/apigee/trireme/core/NodeScript.java index 383d91ad..8035612b 100755 --- a/core/src/main/java/io/apigee/trireme/core/NodeScript.java +++ b/core/src/main/java/io/apigee/trireme/core/NodeScript.java @@ -58,6 +58,7 @@ public class NodeScript private String workingDir; private Map environment; private String nodeVersion = NodeEnvironment.DEFAULT_NODE_VERSION; + private boolean debugging = false; NodeScript(NodeEnvironment env, String scriptName, File script, String[] args) { @@ -83,8 +84,15 @@ public class NodeScript this.env = env; this.forceRepl = forceRepl; this.sandbox = env.getSandbox(); - } + } + + public boolean isDebugging() { + return debugging; + } + public void setDebugging(boolean debugging) { + this.debugging = debugging; + } /** * Run the script and return a Future denoting its status. The script is treated exactly as any other * Node.js program -- that is, it runs in a separate thread, and the returned future may be used to diff --git a/core/src/main/java/io/apigee/trireme/core/Utils.java b/core/src/main/java/io/apigee/trireme/core/Utils.java index 8ff1892e..5f7fc9f5 100644 --- a/core/src/main/java/io/apigee/trireme/core/Utils.java +++ b/core/src/main/java/io/apigee/trireme/core/Utils.java @@ -21,15 +21,11 @@ */ package io.apigee.trireme.core; -import io.apigee.trireme.kernel.Charsets; import io.apigee.trireme.core.internal.NodeOSException; +import io.apigee.trireme.kernel.Charsets; import io.apigee.trireme.kernel.ErrorCodes; import io.apigee.trireme.kernel.util.BufferUtils; import io.apigee.trireme.kernel.util.StringUtils; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.JavaScriptException; -import org.mozilla.javascript.RhinoException; -import org.mozilla.javascript.Scriptable; import java.io.File; import java.io.FileInputStream; @@ -43,6 +39,12 @@ import java.util.ArrayList; import java.util.List; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.JavaScriptException; +import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.Script; +import org.mozilla.javascript.Scriptable; + /** * A few utility functions, mainly for Rhino, that are useful when writing Node modules in Java. */ @@ -293,11 +295,64 @@ public static ByteBuffer duplicateBuffer(ByteBuffer b) return BufferUtils.duplicateBuffer(b); } - /** - * Remove leading and trailing strings from a quoted string that has both leading and trailing quotes on it. - */ - public static String unquote(String s) - { - return StringUtils.unquote(s); - } + /** + * Remove leading and trailing strings from a quoted string that has both + * leading and trailing quotes on it. + */ + public static String unquote(String s) { + return StringUtils.unquote(s); + } + + /** + * + * Find script source by the fullname of script class. the script source + * + * must ended with '.js' extension. + * + * + * + * @param script + * + * script instance + * + * @return if found script source, return source string. if not found, + * + * return null. + */ + + public static String getScriptSource(Script script) { + + Class clazz = script.getClass(); + + String name = clazz.getSimpleName(); + + InputStream is = clazz.getResourceAsStream(name + ".js"); + + try { + + if (is != null) { + + String src = Utils.readStream(is); + + return src; + + } + + } catch (IOException e) { + + } finally { + + try { + + is.close(); + + } catch (IOException ignore) { + + } + + } + + return null; + + } } diff --git a/core/src/main/java/io/apigee/trireme/core/internal/ScriptRunner.java b/core/src/main/java/io/apigee/trireme/core/internal/ScriptRunner.java index 89d578c2..e2afad7a 100755 --- a/core/src/main/java/io/apigee/trireme/core/internal/ScriptRunner.java +++ b/core/src/main/java/io/apigee/trireme/core/internal/ScriptRunner.java @@ -40,20 +40,6 @@ import io.apigee.trireme.kernel.PathTranslator; import io.apigee.trireme.kernel.net.NetworkPolicy; import io.apigee.trireme.kernel.net.SelectorHandler; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.ContextAction; -import org.mozilla.javascript.EcmaError; -import org.mozilla.javascript.EvaluatorException; -import org.mozilla.javascript.Function; -import org.mozilla.javascript.JavaScriptException; -import org.mozilla.javascript.RhinoException; -import org.mozilla.javascript.Script; -import org.mozilla.javascript.ScriptRuntime; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Undefined; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.File; @@ -79,6 +65,23 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextAction; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.EcmaError; +import org.mozilla.javascript.EvaluatorException; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.JavaScriptException; +import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.Script; +import org.mozilla.javascript.ScriptRuntime; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Undefined; +import org.mozilla.javascript.tools.debugger.Main; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * This class actually runs the script. */ @@ -662,19 +665,36 @@ private void closeCloseables(Context cx) * Execute the script. */ @Override - public ScriptStatus call() - throws NodeException - { - Object ret = env.getContextFactory().call(new ContextAction() - { - @Override - public Object run(Context cx) - { - return runScript(cx); - } - }); - return (ScriptStatus)ret; - } + public ScriptStatus call() throws NodeException { + ContextFactory contextFactory = env.getContextFactory(); + contextFactory.call(new ContextAction() { + @Override + public Object run(Context cx) { + // All scripts get their own global scope. This is a lot safer + // than sharing them in case a script wants + // to add to the prototype of String or Date or whatever (as + // they often do) + // This uses a bit more memory and in theory slows down script + // startup but in practice it is + // a drop in the bucket. + scope = cx.initStandardObjects(); + return null; + } + }); + // debugger does not work when Main.mainEmbedded(...) is put in to + // contextFactory.call(...). + // I don't know why. + if (getScriptObject().isDebugging()) { + Main.mainEmbedded(env.getContextFactory(), scope, "trireme debug"); + } + + Object ret = contextFactory.call(new ContextAction() { + public Object run(Context cx) { + return runScript(cx); + } + }); + return (ScriptStatus) ret; + } protected ScriptStatus runScript(Context cx) { @@ -691,11 +711,7 @@ protected ScriptStatus runScript(Context cx) now = System.currentTimeMillis(); try { - // All scripts get their own global scope. This is a lot safer than sharing them in case a script wants - // to add to the prototype of String or Date or whatever (as they often do) - // This uses a bit more memory and in theory slows down script startup but in practice it is - // a drop in the bucket. - scope = cx.initStandardObjects(); + // Lazy first-time init of the node version. registry.loadRoot(cx); @@ -726,7 +742,20 @@ protected ScriptStatus runScript(Context cx) // Run "trireme.js," which is our equivalent of "node.js". It returns a function that takes // "process". When done, we may have ticks to execute. Script mainScript = registry.getMainScript(); - Function main = (Function)mainScript.exec(cx, scope); + Function main = null; + if (getScriptObject().isDebugging()) { + // try to find script source + String src = Utils.getScriptSource(mainScript); + if (src != null) { + Object ret = cx.evaluateString(scope, src, mainScript + .getClass().getName(), 1, null); + + main = (Function) ret; + } + } + if (main == null) { + main = (Function) mainScript.exec(cx, scope); + } boolean timing = startTiming(cx); try { diff --git a/core/src/main/java/io/apigee/trireme/core/modules/NativeModule.java b/core/src/main/java/io/apigee/trireme/core/modules/NativeModule.java index 2fcfab15..e4fb62d5 100644 --- a/core/src/main/java/io/apigee/trireme/core/modules/NativeModule.java +++ b/core/src/main/java/io/apigee/trireme/core/modules/NativeModule.java @@ -198,22 +198,50 @@ public Scriptable internalRequire(String name, Context cx) return null; } - private void runCompiledModule(Script compiled, Context cx, ModuleImpl mod) - { - Function requireFunc = new FunctionObject("require", - Utils.findMethod(NativeImpl.class, "require"), - this); - - // The script code found in src/main/javascript is wrapped with a function by the Rhino compiler - // (see the pom.xml for the wrapper code). What we actually - // need to do here is to invoke the wrapper function after running the script. - - Object ret = compiled.exec(cx, runner.getScriptScope()); - Function fn = (Function)ret; - fn.call(cx, runner.getScriptScope(), null, new Object[] { mod.getExports(), requireFunc, - mod, mod.getFileName() }); - mod.setLoaded(true); - } + private void runCompiledModule(Script compiled, Context cx, + ModuleImpl mod) { + + Function requireFunc = new FunctionObject("require", + Utils.findMethod(NativeImpl.class, "require"), this); + + Function fn = null; + + if (runner.getScriptObject().isDebugging()) { + // try to find script source + String src = Utils.getScriptSource(compiled); + if (src != null) { + String finalSource = NativeImpl.WRAP_PREFIX + src + + NativeImpl.WRAP_POSTFIX; + Object ret = cx + .evaluateString(runner.getScriptScope(), + finalSource, compiled.getClass().getName(), + 1, null); + + fn = (Function) ret; + } else { + // TODO how to find module script source defined by + // NodeScriptModule + } + + } + if (fn == null) { + // The script code found in src/main/javascript is wrapped with + // a function by the Rhino compiler + // (see the pom.xml for the wrapper code). What we actually + // need to do here is to invoke the wrapper function after + // running the script. + + Object ret = compiled.exec(cx, runner.getScriptScope()); + fn = (Function) ret; + } + fn.call(cx, + runner.getScriptScope(), + null, + new Object[] { mod.getExports(), requireFunc, mod, + mod.getFileName() }); + + mod.setLoaded(true); + } @JSFunction public static Object getCached(Context cx, Scriptable thisObj, Object[] args, Function func)