зеркало из https://github.com/mozilla/pjs.git
More switch parsing/code generation cleanups: now IRFactory constructs the proper parsed tree for the switch statement which removed the need for tree mutations in NodeTransformer and during code generation.
This commit is contained in:
Родитель
61215cbb33
Коммит
06080a4162
|
@ -86,21 +86,94 @@ final class IRFactory
|
|||
|
||||
Node createSwitch(Node expr, int lineno)
|
||||
{
|
||||
return new Node.Jump(Token.SWITCH, expr, lineno);
|
||||
//
|
||||
// The switch will be rewritten from:
|
||||
//
|
||||
// switch (expr) {
|
||||
// case test1: statements1;
|
||||
// ...
|
||||
// default: statementsDefault;
|
||||
// ...
|
||||
// case testN: statementsN;
|
||||
// }
|
||||
//
|
||||
// to:
|
||||
//
|
||||
// {
|
||||
// switch (expr) {
|
||||
// case test1: goto label1;
|
||||
// ...
|
||||
// case testN: goto labelN;
|
||||
// }
|
||||
// goto labelDefault;
|
||||
// label1:
|
||||
// statements1;
|
||||
// ...
|
||||
// labelDefault:
|
||||
// statementsDefault;
|
||||
// ...
|
||||
// labelN:
|
||||
// statementsN;
|
||||
// breakLabel:
|
||||
// }
|
||||
//
|
||||
// where inside switch each "break;" without label will be replaced
|
||||
// by "goto breakLabel".
|
||||
//
|
||||
// If the original switch does not have the default label, then
|
||||
// the transformed code would contain after the switch instead of
|
||||
// goto labelDefault;
|
||||
// the following goto:
|
||||
// goto breakLabel;
|
||||
//
|
||||
|
||||
Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno);
|
||||
Node block = new Node(Token.BLOCK, switchNode);
|
||||
return block;
|
||||
}
|
||||
|
||||
void addSwitchCase(Node switchNode, Node expression, Node statements)
|
||||
/**
|
||||
* If caseExpression argument is null it indicate default label.
|
||||
*/
|
||||
void addSwitchCase(Node switchBlock, Node caseExpression, Node statements)
|
||||
{
|
||||
if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
|
||||
Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
|
||||
if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
|
||||
Node caseNode = new Node(Token.CASE, expression, statements);
|
||||
switchNode.addChildToBack(caseNode);
|
||||
|
||||
Node.Target gotoTarget = new Node.Target();
|
||||
if (caseExpression != null) {
|
||||
Node.Jump caseNode = new Node.Jump(Token.CASE, caseExpression);
|
||||
caseNode.target = gotoTarget;
|
||||
switchNode.addChildToBack(caseNode);
|
||||
} else {
|
||||
switchNode.setDefault(gotoTarget);
|
||||
}
|
||||
switchBlock.addChildToBack(gotoTarget);
|
||||
switchBlock.addChildToBack(statements);
|
||||
}
|
||||
|
||||
void addSwitchDefault(Node switchNode, Node statements)
|
||||
void closeSwitch(Node switchBlock)
|
||||
{
|
||||
if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
|
||||
Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
|
||||
if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
|
||||
Node defaultNode = new Node(Token.DEFAULT, statements);
|
||||
switchNode.addChildToBack(defaultNode);
|
||||
|
||||
Node.Target switchBreakTarget = new Node.Target();
|
||||
// switchNode.target is only used by NodeTransformer
|
||||
// to detect switch end
|
||||
switchNode.target = switchBreakTarget;
|
||||
|
||||
Node.Jump defaultGoto = new Node.Jump(Token.GOTO);
|
||||
Node.Target defaultTarget = switchNode.getDefault();
|
||||
if (defaultTarget != null) {
|
||||
defaultGoto.target = defaultTarget;
|
||||
} else {
|
||||
defaultGoto.target = switchBreakTarget;
|
||||
}
|
||||
|
||||
switchBlock.addChildAfter(defaultGoto, switchNode);
|
||||
switchBlock.addChildToBack(switchBreakTarget);
|
||||
}
|
||||
|
||||
Node createVariables(int lineno)
|
||||
|
|
|
@ -496,11 +496,6 @@ public class Interpreter
|
|||
break;
|
||||
}
|
||||
|
||||
case Token.CASE:
|
||||
// Skip case condition
|
||||
child = child.getNext();
|
||||
// fallthrough
|
||||
case Token.DEFAULT:
|
||||
case Token.SCRIPT:
|
||||
case Token.LABEL:
|
||||
case Token.LOOP:
|
||||
|
@ -1039,43 +1034,32 @@ public class Interpreter
|
|||
|
||||
private void visitSwitch(Node.Jump switchNode)
|
||||
{
|
||||
Node child = switchNode.getFirstChild();
|
||||
// See comments in IRFactory.createSwitch() for description
|
||||
// of SWITCH node
|
||||
|
||||
updateLineNumber(switchNode);
|
||||
visitExpression(child);
|
||||
|
||||
ObjArray cases = (ObjArray) switchNode.getProp(Node.CASES_PROP);
|
||||
for (int i = 0; i < cases.size(); i++) {
|
||||
Node thisCase = (Node)cases.get(i);
|
||||
Node test = thisCase.getFirstChild();
|
||||
// the case expression is the firstmost child
|
||||
// the rest will be generated when the case
|
||||
// statements are encountered as siblings of
|
||||
// the switch statement.
|
||||
Node child = switchNode.getFirstChild();
|
||||
visitExpression(child);
|
||||
for (Node.Jump caseNode = (Node.Jump)child.getNext();
|
||||
caseNode != null;
|
||||
caseNode = (Node.Jump)caseNode.getNext())
|
||||
{
|
||||
if (caseNode.getType() != Token.CASE)
|
||||
throw badTree(caseNode);
|
||||
Node test = caseNode.getFirstChild();
|
||||
addIcode(Icode_DUP);
|
||||
stackChange(1);
|
||||
visitExpression(test);
|
||||
addToken(Token.SHEQ);
|
||||
stackChange(-1);
|
||||
Node.Target target = new Node.Target();
|
||||
thisCase.addChildAfter(target, test);
|
||||
// If true, Icode_IFEQ_POP will jump and remove case value
|
||||
// from stack
|
||||
addGoto(target, Icode_IFEQ_POP);
|
||||
addGoto(caseNode.target, Icode_IFEQ_POP);
|
||||
stackChange(-1);
|
||||
}
|
||||
addIcode(Icode_POP);
|
||||
stackChange(-1);
|
||||
|
||||
Node defaultNode = (Node) switchNode.getProp(Node.DEFAULT_PROP);
|
||||
if (defaultNode != null) {
|
||||
Node.Target defaultTarget = new Node.Target();
|
||||
defaultNode.getFirstChild().addChildToFront(defaultTarget);
|
||||
addGoto(defaultTarget, Token.GOTO);
|
||||
}
|
||||
|
||||
Node.Target breakTarget = switchNode.target;
|
||||
addGoto(breakTarget, Token.GOTO);
|
||||
}
|
||||
|
||||
private void visitCall(Node node)
|
||||
|
|
|
@ -51,10 +51,8 @@ public class Node
|
|||
LOCAL_PROP = 2,
|
||||
LOCAL_BLOCK_PROP = 3,
|
||||
REGEXP_PROP = 4,
|
||||
CASES_PROP = 5,
|
||||
DEFAULT_PROP = 6,
|
||||
CASEARRAY_PROP = 7,
|
||||
SPECIAL_PROP_PROP = 8,
|
||||
CASEARRAY_PROP = 5,
|
||||
SPECIAL_PROP_PROP = 6,
|
||||
/*
|
||||
the following properties are defined and manipulated by the
|
||||
optimizer -
|
||||
|
@ -67,15 +65,15 @@ public class Node
|
|||
matches.
|
||||
*/
|
||||
|
||||
TARGETBLOCK_PROP = 9,
|
||||
VARIABLE_PROP = 10,
|
||||
ISNUMBER_PROP = 11,
|
||||
DIRECTCALL_PROP = 12,
|
||||
SPECIALCALL_PROP = 13,
|
||||
SKIP_INDEXES_PROP = 14, // array of skipped indexes of array literal
|
||||
OBJECT_IDS_PROP = 15, // array of properties for object literal
|
||||
INCRDECR_PROP = 16, // pre or post type of increment/decerement
|
||||
LAST_PROP = 16;
|
||||
TARGETBLOCK_PROP = 7,
|
||||
VARIABLE_PROP = 8,
|
||||
ISNUMBER_PROP = 9,
|
||||
DIRECTCALL_PROP = 10,
|
||||
SPECIALCALL_PROP = 11,
|
||||
SKIP_INDEXES_PROP = 12, // array of skipped indexes of array literal
|
||||
OBJECT_IDS_PROP = 13, // array of properties for object literal
|
||||
INCRDECR_PROP = 14, // pre or post type of increment/decerement
|
||||
LAST_PROP = 14;
|
||||
|
||||
// values of ISNUMBER_PROP to specify
|
||||
// which of the children are Number types
|
||||
|
@ -158,6 +156,20 @@ public class Node
|
|||
this.label = label;
|
||||
}
|
||||
|
||||
public final Target getDefault()
|
||||
{
|
||||
if (!(type == Token.SWITCH)) Kit.codeBug();
|
||||
return target2;
|
||||
}
|
||||
|
||||
public final void setDefault(Target defaultTarget)
|
||||
{
|
||||
if (!(type == Token.SWITCH)) Kit.codeBug();
|
||||
if (defaultTarget == null) Kit.codeBug();
|
||||
if (target2 != null) Kit.codeBug(); //only once
|
||||
target2 = defaultTarget;
|
||||
}
|
||||
|
||||
public final Target getFinally()
|
||||
{
|
||||
if (!(type == Token.TRY)) Kit.codeBug();
|
||||
|
@ -419,8 +431,6 @@ public class Node
|
|||
case LOCAL_PROP: return "local";
|
||||
case LOCAL_BLOCK_PROP: return "local_block";
|
||||
case REGEXP_PROP: return "regexp";
|
||||
case CASES_PROP: return "cases";
|
||||
case DEFAULT_PROP: return "default";
|
||||
case CASEARRAY_PROP: return "casearray";
|
||||
case SPECIAL_PROP_PROP: return "special_prop";
|
||||
|
||||
|
|
|
@ -144,37 +144,10 @@ public class NodeTransformer
|
|||
|
||||
case Token.SWITCH:
|
||||
{
|
||||
Node.Target breakTarget = new Node.Target();
|
||||
parent.addChildAfter(breakTarget, node);
|
||||
|
||||
// make all children siblings except for selector
|
||||
Node sib = node;
|
||||
Node child = node.getFirstChild().next;
|
||||
while (child != null) {
|
||||
Node next = child.next;
|
||||
node.removeChild(child);
|
||||
parent.addChildAfter(child, sib);
|
||||
sib = child;
|
||||
child = next;
|
||||
}
|
||||
|
||||
((Node.Jump)node).target = breakTarget;
|
||||
loops.push(node);
|
||||
Node.Jump switchNode = (Node.Jump)node;
|
||||
Node breakTarget = switchNode.target;
|
||||
loops.push(switchNode);
|
||||
loopEnds.push(breakTarget);
|
||||
node.putProp(Node.CASES_PROP, new ObjArray());
|
||||
break;
|
||||
}
|
||||
|
||||
case Token.DEFAULT:
|
||||
case Token.CASE:
|
||||
{
|
||||
Node sw = (Node) loops.peek();
|
||||
if (type == Token.CASE) {
|
||||
ObjArray cases = (ObjArray) sw.getProp(Node.CASES_PROP);
|
||||
cases.add(node);
|
||||
} else {
|
||||
sw.putProp(Node.DEFAULT_PROP, node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -554,13 +554,11 @@ public class Parser
|
|||
nf.addChildToBack(block, statement());
|
||||
}
|
||||
|
||||
if (caseExpression != null) {
|
||||
nf.addSwitchCase(pn, caseExpression, block);
|
||||
} else {
|
||||
nf.addSwitchDefault(pn, block);
|
||||
}
|
||||
// caseExpression == null => add default lable
|
||||
nf.addSwitchCase(pn, caseExpression, block);
|
||||
}
|
||||
decompiler.addEOL(Token.RC);
|
||||
nf.closeSwitch(pn);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1400,10 +1400,6 @@ class BodyCodegen
|
|||
--withNesting;
|
||||
break;
|
||||
|
||||
case Token.CASE:
|
||||
case Token.DEFAULT:
|
||||
// XXX shouldn't these be StatementNodes?
|
||||
|
||||
case Token.SCRIPT:
|
||||
case Token.BLOCK:
|
||||
case Token.EMPTY:
|
||||
|
@ -2112,38 +2108,37 @@ class BodyCodegen
|
|||
+")V");
|
||||
}
|
||||
|
||||
private void acquireTargetLabel(Node.Target target)
|
||||
private int getTargetLabel(Node.Target target)
|
||||
{
|
||||
if (target.labelId == -1) {
|
||||
target.labelId = cfw.acquireLabel();
|
||||
}
|
||||
return target.labelId;
|
||||
}
|
||||
|
||||
private void visitTarget(Node.Target target)
|
||||
{
|
||||
acquireTargetLabel(target);
|
||||
cfw.markLabel(target.labelId);
|
||||
int label = getTargetLabel(target);
|
||||
cfw.markLabel(label);
|
||||
}
|
||||
|
||||
private void visitGOTO(Node.Jump node, int type, Node child)
|
||||
{
|
||||
Node.Target target = node.target;
|
||||
acquireTargetLabel(target);
|
||||
int targetLabel = target.labelId;
|
||||
if (type == Token.IFEQ || type == Token.IFNE) {
|
||||
if (child == null) throw Codegen.badTree();
|
||||
int targetLabel = getTargetLabel(target);
|
||||
int fallThruLabel = cfw.acquireLabel();
|
||||
if (type == Token.IFEQ)
|
||||
generateIfJump(child, node, targetLabel, fallThruLabel);
|
||||
else
|
||||
generateIfJump(child, node, fallThruLabel, targetLabel);
|
||||
cfw.markLabel(fallThruLabel);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (type == Token.JSR)
|
||||
cfw.add(ByteCode.JSR, targetLabel);
|
||||
addGoto(target, ByteCode.JSR);
|
||||
else
|
||||
cfw.add(ByteCode.GOTO, targetLabel);
|
||||
addGoto(target, ByteCode.GOTO);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2883,41 +2878,32 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
cfw.add(ByteCode.ATHROW);
|
||||
}
|
||||
|
||||
private void visitSwitch(Node.Jump node, Node child)
|
||||
private void visitSwitch(Node.Jump switchNode, Node child)
|
||||
{
|
||||
generateExpression(child, node);
|
||||
// See comments in IRFactory.createSwitch() for description
|
||||
// of SWITCH node
|
||||
|
||||
generateExpression(child, switchNode);
|
||||
// save selector value
|
||||
short selector = getNewWordLocal();
|
||||
cfw.addAStore(selector);
|
||||
|
||||
ObjArray cases = (ObjArray) node.getProp(Node.CASES_PROP);
|
||||
for (int i=0; i < cases.size(); i++) {
|
||||
Node thisCase = (Node) cases.get(i);
|
||||
Node first = thisCase.getFirstChild();
|
||||
generateExpression(first, thisCase);
|
||||
for (Node.Jump caseNode = (Node.Jump)child.getNext();
|
||||
caseNode != null;
|
||||
caseNode = (Node.Jump)caseNode.getNext())
|
||||
{
|
||||
if (caseNode.getType() != Token.CASE)
|
||||
throw Codegen.badTree();
|
||||
Node test = caseNode.getFirstChild();
|
||||
generateExpression(test, caseNode);
|
||||
cfw.addALoad(selector);
|
||||
addScriptRuntimeInvoke("shallowEq",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
+")Z");
|
||||
Node.Target target = new Node.Target();
|
||||
thisCase.replaceChild(first, target);
|
||||
acquireTargetLabel(target);
|
||||
cfw.add(ByteCode.IFNE, target.labelId);
|
||||
addGoto(caseNode.target, ByteCode.IFNE);
|
||||
}
|
||||
releaseWordLocal(selector);
|
||||
|
||||
Node defaultNode = (Node) node.getProp(Node.DEFAULT_PROP);
|
||||
if (defaultNode != null) {
|
||||
Node.Target defaultTarget = new Node.Target();
|
||||
defaultNode.getFirstChild().addChildToFront(defaultTarget);
|
||||
acquireTargetLabel(defaultTarget);
|
||||
cfw.add(ByteCode.GOTO, defaultTarget.labelId);
|
||||
}
|
||||
|
||||
Node.Target breakTarget = node.target;
|
||||
acquireTargetLabel(breakTarget);
|
||||
cfw.add(ByteCode.GOTO, breakTarget.labelId);
|
||||
}
|
||||
|
||||
private void visitTypeofname(Node node)
|
||||
|
@ -3884,6 +3870,12 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
cfw.markLabel(beyond);
|
||||
}
|
||||
|
||||
private void addGoto(Node.Target target, byte jumpcode)
|
||||
{
|
||||
int targetLabel = getTargetLabel(target);
|
||||
cfw.add(jumpcode, targetLabel);
|
||||
}
|
||||
|
||||
private void addObjectToDouble()
|
||||
{
|
||||
addScriptRuntimeInvoke("toNumber", "(Ljava/lang/Object;)D");
|
||||
|
|
Загрузка…
Ссылка в новой задаче