[jrunscript][Rhino] jrunscript/Rhino: スタックトレース取得

普通に throw new Error("msg") だと出力が寂しい。。。
デバッグのとき切ない。。。

// foo.js

load("bar.js");

function foo_func(){
  println("foo_func");
  bar_func();
}

try{
  foo_func();
}catch(ex){
  println("* something wrong *");
  println(ex);
  for(var k in ex){
    println(k + " (" + ex[k] + ")");
  }
}
// bar.js

function bar_func(){
  println("bar_func");
  throw new Error("{msg}");
}

結果:

$ jrunscript foo.js
foo_func
bar_func
* something wrong *
Error: {msg}
message ({msg})
name (Error)
fileName ()
lineNumber (0)

ので、stack trace(Java) とか call stack とか back trace(Ruby)とか呼ばれるものが出るようにしてみた。

// foo.js

function getCallStack(){
  var stack = [];
  try{
    eval(".");
  }catch(e){
    var lines = e.stack.split("\n");
    for(var i=lines.length; i>=1; i--){
      var line = lines[i];
      if(typeof line === "undefined"
         || line.match(/^\s*$/))
      {
        // skip
      }else{
        stack.push(line.replace(/^\s*/, ""));
      }
    }
  }
  return stack;
}

function dumpError(ex){
  var s = "";
  s += ex.name + ": ";
  s += ex.message + "\n";
  if(typeof ex.stack === "undefined"){
    s += "(no stack)";
  }else if(typeof ex.stack === "object" && ex.stack.length){
    for(var i=ex.stack.length-1; i>=0; i--){
      s += ex.stack[i] + "\n";
    }
  }else{
    s += ex.stack;
  }

  println(s);
}

function MyError(msg){
  this.name = "MyError";
  this.message = msg;

  var stack = getCallStack();
  this.stack = [];
  for(var i=0; i<stack.length-1; i++){
    this.stack.push(stack[i]);
  }
}

////////////////////////////////

function foo_func(){
  println("foo_func");
  bar_func();
}

try{
  load("bar.js");
  foo_func();
}catch(ex){
  println("* something wrong *");
  dumpError(ex);
}
// bar.js

function bar_func(){
  println("bar_func");
  throw new MyError("{msg}");
}

結果:

$ jrunscript foo.js
foo_func
bar_func
* something wrong *
MyError: {msg}
at bar.js:5 (bar_func)
at foo.js:55 (foo_func)
at foo.js:60

それっぽい感じになったけど小手先感ある。
もっとちゃんとしたやり方がありそう。