Skip to content

Instantly share code, notes, and snippets.

@tuchida
Last active December 20, 2015 01:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuchida/6052515 to your computer and use it in GitHub Desktop.
Save tuchida/6052515 to your computer and use it in GitHub Desktop.
Rhino GOTO
function traverseFn(node, fn) {
var parents = [];
(function f(node) {
fn(node, parents);
parents.push(node);
for (var c in Iterator(node)) {
f(c);
}
parents.pop();
})(node);
}
function printNode(node, indent) {
var Token = org.mozilla.javascript.Token;
indent = indent || 0;
var space = Array(indent*2+1).join(' ');
print(space + Token.typeToName(node.type) + ' ' + node.getClass());
for (var c in Iterator(node)) {
printNode(c, indent+1);
}
}
function transform(node) {
var Token = org.mozilla.javascript.Token;
var labels = Object.create(null);
traverseFn(node, function(node, parents) {
if (node.type == Token.LABEL) {
var name = node.getName();
if (name.startsWith('$$')) {
labels[name] = node;
}
}
});
var gotos = [];
traverseFn(node, function(node, parents) {
if (node.type == Token.GETELEM) {
var target = node.getFirstChild();
var element = node.getLastChild();
if (target && target.type == Token.NAME && target.getIdentifier() == 'goto' &&
element && element.type == Token.NAME && element.getIdentifier().startsWith('$$')) {
var label = labels[element.getIdentifier()];
if (!label) {
throw new SyntaxError('not found label "' + element.getIdentifier() + '"');
}
gotos.push([label, parents[parents.length-1], parents[parents.length-2]]);
}
}
});
gotos.forEach(function([label, old, parent]) {
var l = new org.mozilla.javascript.ast.Jump(Token.GOTO);
var target = label.newTarget();
l.target = target;
label.addChildToBack(target);
parent.replaceChild(old, l);
});
}
function evalWithGoto(fn) {
var context = new org.mozilla.javascript.Context();
var compilerEnv = new org.mozilla.javascript.CompilerEnvirons();
compilerEnv.initFromContext(context);
var ast = new org.mozilla.javascript.Parser(compilerEnv).parse(fn.toSource().replace(/^\(function *\( *\) *{|}\)*$/g, ''), '', 1);
var irf = new org.mozilla.javascript.IRFactory(compilerEnv);
var tree = irf.transformTree(ast);
transform(tree);
var interpreter = new org.mozilla.javascript.Interpreter();
var bytecode = interpreter.compile(compilerEnv, tree, tree.getEncodedSource(), false);
return interpreter.createScriptObject(bytecode, null)();
}
evalWithGoto(function() {
var i = 0;
$$label1:
if (i < 5) {
print(i++);
goto [$$label1];
}
});
// 0
// 1
// 2
// 3
// 4
evalWithGoto(function() {
for (var i = 0; i < 100; i++) {
for (var j = 0; j < 100; j++) {
if (j == 3) {
goto [$$break1];
}
print('i: ' + i + ', j: ' + j);
}
$$break1:
if (i == 5) {
goto [$$break2];
}
}
$$break2:
0;
});
// i: 0, j: 0
// i: 0, j: 1
// i: 0, j: 2
// i: 1, j: 0
// i: 1, j: 1
// i: 1, j: 2
// i: 2, j: 0
// i: 2, j: 1
// i: 2, j: 2
// i: 3, j: 0
// i: 3, j: 1
// i: 3, j: 2
// i: 4, j: 0
// i: 4, j: 1
// i: 4, j: 2
// i: 5, j: 0
// i: 5, j: 1
// i: 5, j: 2
evalWithGoto(function() {
var i = 5;
goto [$$intoLoop];
while (i > 0) {
print(i);
$$intoLoop:
i--;
}
});
// 4
// 3
// 2
// 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment