Last active
December 20, 2015 01:58
-
-
Save tuchida/6052515 to your computer and use it in GitHub Desktop.
Rhino GOTO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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