123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891 |
- /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
- *
- * jorendb - A toy command-line debugger for shell-js programs.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- /*
- * jorendb is a simple command-line debugger for shell-js programs. It is
- * intended as a demo of the Debugger object (as there are no shell js programs
- * to speak of).
- *
- * To run it: $JS -d path/to/this/file/jorendb.js
- * To run some JS code under it, try:
- * (jorendb) print load("my-script-to-debug.js")
- * Execution will stop at debugger statements and you'll get a jorendb prompt.
- */
- // Debugger state.
- var focusedFrame = null;
- var topFrame = null;
- var debuggeeValues = {};
- var nextDebuggeeValueIndex = 1;
- var lastExc = null;
- var todo = [];
- var activeTask;
- var options = { 'pretty': true,
- 'emacs': (os.getenv('EMACS') == 't') };
- var rerun = true;
- // Cleanup functions to run when we next re-enter the repl.
- var replCleanups = [];
- // Redirect debugger printing functions to go to the original output
- // destination, unaffected by any redirects done by the debugged script.
- var initialOut = os.file.redirect();
- var initialErr = os.file.redirectErr();
- function wrap(global, name) {
- var orig = global[name];
- global[name] = function(...args) {
- var oldOut = os.file.redirect(initialOut);
- var oldErr = os.file.redirectErr(initialErr);
- try {
- return orig.apply(global, args);
- } finally {
- os.file.redirect(oldOut);
- os.file.redirectErr(oldErr);
- }
- };
- }
- wrap(this, 'print');
- wrap(this, 'printErr');
- wrap(this, 'putstr');
- // Convert a debuggee value v to a string.
- function dvToString(v) {
- return (typeof v !== 'object' || v === null) ? uneval(v) : "[object " + v.class + "]";
- }
- function summaryObject(dv) {
- var obj = {};
- for (var name of dv.getOwnPropertyNames()) {
- var v = dv.getOwnPropertyDescriptor(name).value;
- if (v instanceof Debugger.Object) {
- v = "(...)";
- }
- obj[name] = v;
- }
- return obj;
- }
- function debuggeeValueToString(dv, style) {
- var dvrepr = dvToString(dv);
- if (!style.pretty || (typeof dv !== 'object'))
- return [dvrepr, undefined];
- if (dv.class == "Error") {
- let errval = debuggeeGlobalWrapper.executeInGlobalWithBindings("$" + i + ".toString()", debuggeeValues);
- return [dvrepr, errval.return];
- }
- if (style.brief)
- return [dvrepr, JSON.stringify(summaryObject(dv), null, 4)];
- let str = debuggeeGlobalWrapper.executeInGlobalWithBindings("JSON.stringify(v, null, 4)", {v: dv});
- if ('throw' in str) {
- if (style.noerror)
- return [dvrepr, undefined];
- let substyle = {};
- Object.assign(substyle, style);
- substyle.noerror = true;
- return [dvrepr, debuggeeValueToString(str.throw, substyle)];
- }
- return [dvrepr, str.return];
- }
- // Problem! Used to do [object Object] followed by details. Now just details?
- function showDebuggeeValue(dv, style={pretty: options.pretty}) {
- var i = nextDebuggeeValueIndex++;
- debuggeeValues["$" + i] = dv;
- let [brief, full] = debuggeeValueToString(dv, style);
- print("$" + i + " = " + brief);
- if (full !== undefined)
- print(full);
- }
- Object.defineProperty(Debugger.Frame.prototype, "num", {
- configurable: true,
- enumerable: false,
- get: function () {
- var i = 0;
- for (var f = topFrame; f && f !== this; f = f.older)
- i++;
- return f === null ? undefined : i;
- }
- });
- Debugger.Frame.prototype.frameDescription = function frameDescription() {
- if (this.type == "call")
- return ((this.callee.name || '<anonymous>') +
- "(" + this.arguments.map(dvToString).join(", ") + ")");
- else
- return this.type + " code";
- }
- Debugger.Frame.prototype.positionDescription = function positionDescription() {
- if (this.script) {
- var line = this.script.getOffsetLocation(this.offset).lineNumber;
- if (this.script.url)
- return this.script.url + ":" + line;
- return "line " + line;
- }
- return null;
- }
- Debugger.Frame.prototype.location = function () {
- if (this.script) {
- var { lineNumber, columnNumber, isEntryPoint } = this.script.getOffsetLocation(this.offset);
- if (this.script.url)
- return this.script.url + ":" + lineNumber;
- return null;
- }
- return null;
- }
- Debugger.Frame.prototype.fullDescription = function fullDescription() {
- var fr = this.frameDescription();
- var pos = this.positionDescription();
- if (pos)
- return fr + ", " + pos;
- return fr;
- }
- Object.defineProperty(Debugger.Frame.prototype, "line", {
- configurable: true,
- enumerable: false,
- get: function() {
- if (this.script)
- return this.script.getOffsetLocation(this.offset).lineNumber;
- else
- return null;
- }
- });
- function callDescription(f) {
- return ((f.callee.name || '<anonymous>') +
- "(" + f.arguments.map(dvToString).join(", ") + ")");
- }
- function showFrame(f, n) {
- if (f === undefined || f === null) {
- f = focusedFrame;
- if (f === null) {
- print("No stack.");
- return;
- }
- }
- if (n === undefined) {
- n = f.num;
- if (n === undefined)
- throw new Error("Internal error: frame not on stack");
- }
- print('#' + n + " " + f.fullDescription());
- }
- function saveExcursion(fn) {
- var tf = topFrame, ff = focusedFrame;
- try {
- return fn();
- } finally {
- topFrame = tf;
- focusedFrame = ff;
- }
- }
- function parseArgs(str) {
- return str.split(" ");
- }
- function describedRv(r, desc) {
- desc = "[" + desc + "] ";
- if (r === undefined) {
- print(desc + "Returning undefined");
- } else if (r === null) {
- print(desc + "Returning null");
- } else if (r.length === undefined) {
- print(desc + "Returning object " + JSON.stringify(r));
- } else {
- print(desc + "Returning length-" + r.length + " list");
- if (r.length > 0) {
- print(" " + r[0]);
- }
- }
- return r;
- }
- // Rerun the program (reloading it from the file)
- function runCommand(args) {
- print("Restarting program");
- if (args)
- activeTask.scriptArgs = parseArgs(args);
- rerun = true;
- for (var f = topFrame; f; f = f.older) {
- if (f.older) {
- f.onPop = () => null;
- } else {
- f.onPop = () => ({ 'return': 0 });
- }
- }
- //return describedRv([{ 'return': 0 }], "runCommand");
- return null;
- }
- // Evaluate an expression in the Debugger global
- function evalCommand(expr) {
- eval(expr);
- }
- function quitCommand() {
- dbg.enabled = false;
- quit(0);
- }
- function backtraceCommand() {
- if (topFrame === null)
- print("No stack.");
- for (var i = 0, f = topFrame; f; i++, f = f.older)
- showFrame(f, i);
- }
- function setCommand(rest) {
- var space = rest.indexOf(' ');
- if (space == -1) {
- print("Invalid set <option> <value> command");
- } else {
- var name = rest.substr(0, space);
- var value = rest.substr(space + 1);
- if (name == 'args') {
- activeTask.scriptArgs = parseArgs(value);
- } else {
- var yes = ["1", "yes", "true", "on"];
- var no = ["0", "no", "false", "off"];
- if (yes.indexOf(value) !== -1)
- options[name] = true;
- else if (no.indexOf(value) !== -1)
- options[name] = false;
- else
- options[name] = value;
- }
- }
- }
- function split_print_options(s, style) {
- var m = /^\/(\w+)/.exec(s);
- if (!m)
- return [ s, style ];
- if (m[1].indexOf("p") != -1)
- style.pretty = true;
- if (m[1].indexOf("b") != -1)
- style.brief = true;
- return [ s.substr(m[0].length).trimLeft(), style ];
- }
- function doPrint(expr, style) {
- // This is the real deal.
- var cv = saveExcursion(
- () => focusedFrame == null
- ? debuggeeGlobalWrapper.executeInGlobalWithBindings(expr, debuggeeValues)
- : focusedFrame.evalWithBindings(expr, debuggeeValues));
- if (cv === null) {
- if (!dbg.enabled)
- return [cv];
- print("Debuggee died.");
- } else if ('return' in cv) {
- if (!dbg.enabled)
- return [undefined];
- showDebuggeeValue(cv.return, style);
- } else {
- if (!dbg.enabled)
- return [cv];
- print("Exception caught. (To rethrow it, type 'throw'.)");
- lastExc = cv.throw;
- showDebuggeeValue(lastExc, style);
- }
- }
- function printCommand(rest) {
- var [expr, style] = split_print_options(rest, {pretty: options.pretty});
- return doPrint(expr, style);
- }
- function keysCommand(rest) { return doPrint("Object.keys(" + rest + ")"); }
- function detachCommand() {
- dbg.enabled = false;
- return [undefined];
- }
- function continueCommand(rest) {
- if (focusedFrame === null) {
- print("No stack.");
- return;
- }
- var match = rest.match(/^(\d+)$/);
- if (match) {
- return doStepOrNext({upto:true, stopLine:match[1]});
- }
- return [undefined];
- }
- function throwCommand(rest) {
- var v;
- if (focusedFrame !== topFrame) {
- print("To throw, you must select the newest frame (use 'frame 0').");
- return;
- } else if (focusedFrame === null) {
- print("No stack.");
- return;
- } else if (rest === '') {
- return [{throw: lastExc}];
- } else {
- var cv = saveExcursion(function () { return focusedFrame.eval(rest); });
- if (cv === null) {
- if (!dbg.enabled)
- return [cv];
- print("Debuggee died while determining what to throw. Stopped.");
- } else if ('return' in cv) {
- return [{throw: cv.return}];
- } else {
- if (!dbg.enabled)
- return [cv];
- print("Exception determining what to throw. Stopped.");
- showDebuggeeValue(cv.throw);
- }
- return;
- }
- }
- function frameCommand(rest) {
- var n, f;
- if (rest.match(/[0-9]+/)) {
- n = +rest;
- f = topFrame;
- if (f === null) {
- print("No stack.");
- return;
- }
- for (var i = 0; i < n && f; i++) {
- if (!f.older) {
- print("There is no frame " + rest + ".");
- return;
- }
- f.older.younger = f;
- f = f.older;
- }
- focusedFrame = f;
- updateLocation(focusedFrame);
- showFrame(f, n);
- } else if (rest === '') {
- if (topFrame === null) {
- print("No stack.");
- } else {
- updateLocation(focusedFrame);
- showFrame();
- }
- } else {
- print("do what now?");
- }
- }
- function upCommand() {
- if (focusedFrame === null)
- print("No stack.");
- else if (focusedFrame.older === null)
- print("Initial frame selected; you cannot go up.");
- else {
- focusedFrame.older.younger = focusedFrame;
- focusedFrame = focusedFrame.older;
- updateLocation(focusedFrame);
- showFrame();
- }
- }
- function downCommand() {
- if (focusedFrame === null)
- print("No stack.");
- else if (!focusedFrame.younger)
- print("Youngest frame selected; you cannot go down.");
- else {
- focusedFrame = focusedFrame.younger;
- updateLocation(focusedFrame);
- showFrame();
- }
- }
- function forcereturnCommand(rest) {
- var v;
- var f = focusedFrame;
- if (f !== topFrame) {
- print("To forcereturn, you must select the newest frame (use 'frame 0').");
- } else if (f === null) {
- print("Nothing on the stack.");
- } else if (rest === '') {
- return [{return: undefined}];
- } else {
- var cv = saveExcursion(function () { return f.eval(rest); });
- if (cv === null) {
- if (!dbg.enabled)
- return [cv];
- print("Debuggee died while determining what to forcereturn. Stopped.");
- } else if ('return' in cv) {
- return [{return: cv.return}];
- } else {
- if (!dbg.enabled)
- return [cv];
- print("Error determining what to forcereturn. Stopped.");
- showDebuggeeValue(cv.throw);
- }
- }
- }
- function printPop(f, c) {
- var fdesc = f.fullDescription();
- if (c.return) {
- print("frame returning (still selected): " + fdesc);
- showDebuggeeValue(c.return, {brief: true});
- } else if (c.throw) {
- print("frame threw exception: " + fdesc);
- showDebuggeeValue(c.throw);
- print("(To rethrow it, type 'throw'.)");
- lastExc = c.throw;
- } else {
- print("frame was terminated: " + fdesc);
- }
- }
- // Set |prop| on |obj| to |value|, but then restore its current value
- // when we next enter the repl.
- function setUntilRepl(obj, prop, value) {
- var saved = obj[prop];
- obj[prop] = value;
- replCleanups.push(function () { obj[prop] = saved; });
- }
- function updateLocation(frame) {
- if (options.emacs) {
- var loc = frame.location();
- if (loc)
- print("\032\032" + loc + ":1");
- }
- }
- function doStepOrNext(kind) {
- var startFrame = topFrame;
- var startLine = startFrame.line;
- // print("stepping in: " + startFrame.fullDescription());
- // print("starting line: " + uneval(startLine));
- function stepPopped(completion) {
- // Note that we're popping this frame; we need to watch for
- // subsequent step events on its caller.
- this.reportedPop = true;
- printPop(this, completion);
- topFrame = focusedFrame = this;
- if (kind.finish) {
- // We want to continue, but this frame is going to be invalid as
- // soon as this function returns, which will make the replCleanups
- // assert when it tries to access the dead frame's 'onPop'
- // property. So clear it out now while the frame is still valid,
- // and trade it for an 'onStep' callback on the frame we're popping to.
- preReplCleanups();
- setUntilRepl(this.older, 'onStep', stepStepped);
- return undefined;
- }
- updateLocation(this);
- return repl();
- }
- function stepEntered(newFrame) {
- print("entered frame: " + newFrame.fullDescription());
- updateLocation(newFrame);
- topFrame = focusedFrame = newFrame;
- return repl();
- }
- function stepStepped() {
- // print("stepStepped: " + this.fullDescription());
- updateLocation(this);
- var stop = false;
- if (kind.finish) {
- // 'finish' set a one-time onStep for stopping at the frame it
- // wants to return to
- stop = true;
- } else if (kind.upto) {
- // running until a given line is reached
- if (this.line == kind.stopLine)
- stop = true;
- } else {
- // regular step; stop whenever the line number changes
- if ((this.line != startLine) || (this != startFrame))
- stop = true;
- }
- if (stop) {
- topFrame = focusedFrame = this;
- if (focusedFrame != startFrame)
- print(focusedFrame.fullDescription());
- return repl();
- }
- // Otherwise, let execution continue.
- return undefined;
- }
- if (kind.step)
- setUntilRepl(dbg, 'onEnterFrame', stepEntered);
- // If we're stepping after an onPop, watch for steps and pops in the
- // next-older frame; this one is done.
- var stepFrame = startFrame.reportedPop ? startFrame.older : startFrame;
- if (!stepFrame || !stepFrame.script)
- stepFrame = null;
- if (stepFrame) {
- if (!kind.finish)
- setUntilRepl(stepFrame, 'onStep', stepStepped);
- setUntilRepl(stepFrame, 'onPop', stepPopped);
- }
- // Let the program continue!
- return [undefined];
- }
- function stepCommand() { return doStepOrNext({step:true}); }
- function nextCommand() { return doStepOrNext({next:true}); }
- function finishCommand() { return doStepOrNext({finish:true}); }
- // FIXME: DOES NOT WORK YET
- function breakpointCommand(where) {
- print("Sorry, breakpoints don't work yet.");
- var script = focusedFrame.script;
- var offsets = script.getLineOffsets(Number(where));
- if (offsets.length == 0) {
- print("Unable to break at line " + where);
- return;
- }
- for (var offset of offsets) {
- script.setBreakpoint(offset, { hit: handleBreakpoint });
- }
- print("Set breakpoint in " + script.url + ":" + script.startLine + " at line " + where + ", " + offsets.length);
- }
- // Build the table of commands.
- var commands = {};
- var commandArray = [
- backtraceCommand, "bt", "where",
- breakpointCommand, "b", "break",
- continueCommand, "c",
- detachCommand,
- downCommand, "d",
- evalCommand, "!",
- forcereturnCommand,
- frameCommand, "f",
- finishCommand, "fin",
- nextCommand, "n",
- printCommand, "p",
- keysCommand, "k",
- quitCommand, "q",
- runCommand, "run",
- stepCommand, "s",
- setCommand,
- throwCommand, "t",
- upCommand, "u",
- helpCommand, "h",
- ];
- var currentCmd = null;
- for (var i = 0; i < commandArray.length; i++) {
- var cmd = commandArray[i];
- if (typeof cmd === "string")
- commands[cmd] = currentCmd;
- else
- currentCmd = commands[cmd.name.replace(/Command$/, '')] = cmd;
- }
- function helpCommand(rest) {
- print("Available commands:");
- var printcmd = function(group) {
- print(" " + group.join(", "));
- }
- var group = [];
- for (var cmd of commandArray) {
- if (typeof cmd === "string") {
- group.push(cmd);
- } else {
- if (group.length) printcmd(group);
- group = [ cmd.name.replace(/Command$/, '') ];
- }
- }
- printcmd(group);
- }
- // Break cmd into two parts: its first word and everything else. If it begins
- // with punctuation, treat that as a separate word. The first word is
- // terminated with whitespace or the '/' character. So:
- //
- // print x => ['print', 'x']
- // print => ['print', '']
- // !print x => ['!', 'print x']
- // ?!wtf!? => ['?', '!wtf!?']
- // print/b x => ['print', '/b x']
- //
- function breakcmd(cmd) {
- cmd = cmd.trimLeft();
- if ("!@#$%^&*_+=/?.,<>:;'\"".indexOf(cmd.substr(0, 1)) != -1)
- return [cmd.substr(0, 1), cmd.substr(1).trimLeft()];
- var m = /\s+|(?=\/)/.exec(cmd);
- if (m === null)
- return [cmd, ''];
- return [cmd.slice(0, m.index), cmd.slice(m.index + m[0].length)];
- }
- function runcmd(cmd) {
- var pieces = breakcmd(cmd);
- if (pieces[0] === "")
- return undefined;
- var first = pieces[0], rest = pieces[1];
- if (!commands.hasOwnProperty(first)) {
- print("unrecognized command '" + first + "'");
- return undefined;
- }
- var cmd = commands[first];
- if (cmd.length === 0 && rest !== '') {
- print("this command cannot take an argument");
- return undefined;
- }
- return cmd(rest);
- }
- function preReplCleanups() {
- while (replCleanups.length > 0)
- replCleanups.pop()();
- }
- var prevcmd = undefined;
- function repl() {
- preReplCleanups();
- var cmd;
- for (;;) {
- putstr("\n" + prompt);
- cmd = readline();
- if (cmd === null)
- return null;
- else if (cmd === "")
- cmd = prevcmd;
- try {
- prevcmd = cmd;
- var result = runcmd(cmd);
- if (result === undefined)
- ; // do nothing, return to prompt
- else if (Array.isArray(result))
- return result[0];
- else if (result === null)
- return null;
- else
- throw new Error("Internal error: result of runcmd wasn't array or undefined: " + result);
- } catch (exc) {
- print("*** Internal error: exception in the debugger code.");
- print(" " + exc);
- print(exc.stack);
- }
- }
- }
- var dbg = new Debugger();
- dbg.onDebuggerStatement = function (frame) {
- return saveExcursion(function () {
- topFrame = focusedFrame = frame;
- print("'debugger' statement hit.");
- showFrame();
- updateLocation(focusedFrame);
- backtrace();
- return describedRv(repl(), "debugger.saveExc");
- });
- };
- dbg.onThrow = function (frame, exc) {
- return saveExcursion(function () {
- topFrame = focusedFrame = frame;
- print("Unwinding due to exception. (Type 'c' to continue unwinding.)");
- showFrame();
- print("Exception value is:");
- showDebuggeeValue(exc);
- return repl();
- });
- };
- function handleBreakpoint (frame) {
- print("Breakpoint hit!");
- return saveExcursion(() => {
- topFrame = focusedFrame = frame;
- print("breakpoint hit.");
- showFrame();
- updateLocation(focusedFrame);
- return repl();
- });
- };
- // The depth of jorendb nesting.
- var jorendbDepth;
- if (typeof jorendbDepth == 'undefined') jorendbDepth = 0;
- var debuggeeGlobal = newGlobal("new-compartment");
- debuggeeGlobal.jorendbDepth = jorendbDepth + 1;
- var debuggeeGlobalWrapper = dbg.addDebuggee(debuggeeGlobal);
- print("jorendb version -0.0");
- prompt = '(' + Array(jorendbDepth+1).join('meta-') + 'jorendb) ';
- var args = scriptArgs.slice(0);
- print("INITIAL ARGS: " + args);
- // Find the script to run and its arguments. The script may have been given as
- // a plain script name, in which case all remaining arguments belong to the
- // script. Or there may have been any number of arguments to the JS shell,
- // followed by -f scriptName, followed by additional arguments to the JS shell,
- // followed by the script arguments. There may be multiple -e or -f options in
- // the JS shell arguments, and we want to treat each one as a debuggable
- // script.
- //
- // The difficulty is that the JS shell has a mixture of
- //
- // --boolean
- //
- // and
- //
- // --value VAL
- //
- // parameters, and there's no way to know whether --option takes an argument or
- // not. We will assume that VAL will never end in .js, or rather that the first
- // argument that does not start with "-" but does end in ".js" is the name of
- // the script.
- //
- // If you need to pass other options and not have them given to the script,
- // pass them before the -f jorendb.js argument. Thus, the safe ways to pass
- // arguments are:
- //
- // js [JS shell options] -f jorendb.js (-e SCRIPT | -f FILE)+ -- [script args]
- // js [JS shell options] -f jorendb.js (-e SCRIPT | -f FILE)* script.js [script args]
- //
- // Additionally, if you want to run a script that is *NOT* debugged, put it in
- // as part of the leading [JS shell options].
- // Compute actualScriptArgs by finding the script to be run and grabbing every
- // non-script argument. The script may be given by -f scriptname or just plain
- // scriptname. In the latter case, it will be in the global variable
- // 'scriptPath' (and NOT in scriptArgs.)
- var actualScriptArgs = [];
- var scriptSeen;
- if (scriptPath !== undefined) {
- todo.push({
- 'action': 'load',
- 'script': scriptPath,
- });
- scriptSeen = true;
- }
- while(args.length > 0) {
- var arg = args.shift();
- print("arg: " + arg);
- if (arg == '-e') {
- print(" eval");
- todo.push({
- 'action': 'eval',
- 'code': args.shift()
- });
- } else if (arg == '-f') {
- var script = args.shift();
- print(" load -f " + script);
- scriptSeen = true;
- todo.push({
- 'action': 'load',
- 'script': script,
- });
- } else if (arg.indexOf("-") == 0) {
- if (arg == '--') {
- print(" pass remaining args to script");
- actualScriptArgs.push(...args);
- break;
- } else if ((args.length > 0) && (args[0].indexOf(".js") + 3 == args[0].length)) {
- // Ends with .js, assume we are looking at --boolean script.js
- print(" load script.js after --boolean");
- todo.push({
- 'action': 'load',
- 'script': args.shift(),
- });
- scriptSeen = true;
- } else {
- // Does not end with .js, assume we are looking at JS shell arg
- // --value VAL
- print(" ignore");
- args.shift();
- }
- } else {
- if (!scriptSeen) {
- print(" load general");
- actualScriptArgs.push(...args);
- todo.push({
- 'action': 'load',
- 'script': arg,
- });
- break;
- } else {
- print(" arg " + arg);
- actualScriptArgs.push(arg);
- }
- }
- }
- print("jorendb: scriptPath = " + scriptPath);
- print("jorendb: scriptArgs = " + scriptArgs);
- print("jorendb: actualScriptArgs = " + actualScriptArgs);
- for (var task of todo) {
- task['scriptArgs'] = actualScriptArgs;
- }
- // If nothing to run, just drop into a repl
- if (todo.length == 0) {
- todo.push({ 'action': 'repl' });
- }
- while (rerun) {
- print("Top of run loop");
- rerun = false;
- for (var task of todo) {
- activeTask = task;
- if (task.action == 'eval') {
- debuggeeGlobal.eval(task.code);
- } else if (task.action == 'load') {
- debuggeeGlobal['scriptArgs'] = task.scriptArgs;
- debuggeeGlobal['scriptPath'] = task.script;
- print("Loading JavaScript file " + task.script);
- debuggeeGlobal.evaluate(read(task.script), { 'fileName': task.script, 'lineNumber': 1 });
- } else if (task.action == 'repl') {
- repl();
- }
- if (rerun)
- break;
- }
- }
- quit(0);
|