final InstructionCollection instructions = methodBody.getInstructions();
agenda.push(entryNode);
while (!agenda.isEmpty()) {
final ControlFlowNode node = agenda.pop();
if (node == null) {
continue;
}
//
// Find a block that represents a simple condition.
//
if (scope.contains(node)) {
final BasicBlock block = (BasicBlock) node.getUserData();
final List<Node> blockBody = block.getBody();
final StrongBox<Label[]> caseLabels = new StrongBox<>();
final StrongBox<Expression> switchArgument = new StrongBox<>();
final StrongBox<Label> fallLabel = new StrongBox<>();
final StrongBox<Label> tempTarget = new StrongBox<>();
AstCode switchCode;
if (matchLastAndBreak(block, switchCode = AstCode.TableSwitch, caseLabels, switchArgument, fallLabel) ||
matchLastAndBreak(block, switchCode = AstCode.LookupSwitch, caseLabels, switchArgument, fallLabel)) {
final Expression switchExpression = (Expression) blockBody.get(blockBody.size() - 2);
final Collection<Range> switchRanges = switchExpression.getArguments().get(0).getRanges();
final Instruction switchInstruction = instructions.atOffset(switchRanges.get(switchRanges.size() - 1).getStart());
//
// Replace the switch code with a Switch node.
//
final Switch switchNode = new Switch();
switchNode.setCondition(switchArgument.get());
removeTail(blockBody, switchCode, AstCode.Goto);
blockBody.add(switchNode);
result.add(block);
//
// Replace the item so it isn't picked up as content.
//
removeOrThrow(scope, node);
//
// Pull in code of cases.
//
final Label[] labels = caseLabels.get();
final SwitchInfo switchInfo = switchInstruction.getOperand(0);
final int lowValue = switchInfo.getLowValue();
final int[] keys = switchInfo.getKeys();
final Label defaultLabel = labels[0];
final ControlFlowNode defaultTarget = labelsToNodes.get(defaultLabel);
boolean defaultFollowsSwitch = false;
for (int i = 1; i < labels.length; i++) {
final Label caseLabel = labels[i];
//
// Find or create a new case block.
//
CaseBlock caseBlock = null;
for (final CaseBlock cb : switchNode.getCaseBlocks()) {
if (cb.getEntryGoto().getOperand() == caseLabel) {
caseBlock = cb;
break;
}
}
if (caseBlock == null) {
caseBlock = new CaseBlock();
caseBlock.setEntryGoto(new Expression(AstCode.Goto, caseLabel));
final ControlFlowNode caseTarget = labelsToNodes.get(caseLabel);
final List<Node> caseBody = caseBlock.getBody();
if (caseLabel == defaultLabel) {
continue;
}
else {
switchNode.getCaseBlocks().add(caseBlock);
if (caseTarget != null) {
if (caseTarget.getDominanceFrontier().contains(defaultTarget)) {
defaultFollowsSwitch = true;
}
final Set<ControlFlowNode> content = findDominatedNodes(scope, caseTarget);
scope.removeAll(content);
caseBody.addAll(findConditions(content, caseTarget));
}
else {
final BasicBlock explicitGoto = new BasicBlock();
explicitGoto.getBody().add(new Label("SwitchGoto_" + _nextLabelIndex++));
explicitGoto.getBody().add(new Expression(AstCode.Goto, caseLabel));
caseBody.add(explicitGoto);
}
}
if (caseBody.isEmpty() ||
!matchLast((BasicBlock) caseBody.get(caseBody.size() - 1), AstCode.Goto, tempTarget) ||
!ArrayUtilities.contains(labels, tempTarget.get())) {
//
// Add explicit break that should not be used by default, but which might be used
// by goto removal.
//
final BasicBlock explicitBreak = new BasicBlock();
explicitBreak.getBody().add(new Label("SwitchBreak_" + _nextLabelIndex++));
explicitBreak.getBody().add(new Expression(AstCode.LoopOrSwitchBreak, null));
caseBody.add(explicitBreak);
}
}
if (i != 0) {
if (switchCode == AstCode.TableSwitch) {
caseBlock.getValues().add(lowValue + i - 1);
}
else {
caseBlock.getValues().add(keys[i - 1]);
}
}
}
if (!defaultFollowsSwitch) {
final CaseBlock defaultBlock = new CaseBlock();
switchNode.getCaseBlocks().add(defaultBlock);
// defaultBlock.setEntryGoto(new Expression(AstCode.Goto, labels[0]));
final Set<ControlFlowNode> content = findDominatedNodes(scope, defaultTarget);
scope.removeAll(content);
defaultBlock.getBody().addAll(findConditions(content, defaultTarget));
//
// Add explicit break that should not be used by default, but which might be used
// by goto removal.
//
final BasicBlock explicitBreak = new BasicBlock();
explicitBreak.getBody().add(new Label("SwitchBreak_" + _nextLabelIndex++));
explicitBreak.getBody().add(new Expression(AstCode.LoopOrSwitchBreak, null));
defaultBlock.getBody().add(explicitBreak);
}
//
// TODO: Arrange the case blocks such that fall-throughs go to the right block.
//
}
//
// Two-way branch...
//
final StrongBox<Expression> condition = new StrongBox<>();
final StrongBox<Label> trueLabel = new StrongBox<>();
final StrongBox<Label> falseLabel = new StrongBox<>();
if (matchLastAndBreak(block, AstCode.IfTrue, trueLabel, condition, falseLabel)) {
//
// Flip bodies since that seems to be the Java compiler tradition.
//
final Label temp = trueLabel.get();
trueLabel.set(falseLabel.get());
falseLabel.set(temp);
condition.set(AstOptimizer.simplifyLogicalNot(new Expression(AstCode.LogicalNot, null, condition.get())));
//
// Convert IfTrue expression to Condition.
//
final Condition conditionNode = new Condition();
final Block trueBlock = new Block();
final Block falseBlock = new Block();
trueBlock.setEntryGoto(new Expression(AstCode.Goto, trueLabel.get()));
falseBlock.setEntryGoto(new Expression(AstCode.Goto, falseLabel.get()));
conditionNode.setCondition(condition.get());
conditionNode.setTrueBlock(trueBlock);
conditionNode.setFalseBlock(falseBlock);
removeTail(blockBody, AstCode.IfTrue, AstCode.Goto);
blockBody.add(conditionNode);
result.add(block);
//
// Remove the item immediately so it isn't picked up as content.
//
removeOrThrow(scope, node);
final ControlFlowNode trueTarget = labelsToNodes.get(trueLabel.get());
final ControlFlowNode falseTarget = labelsToNodes.get(falseLabel.get());
//
// Pull in the conditional code.
//