package org.benf.cfr.reader.bytecode.analysis.opgraph.op4rewriters; import org.benf.cfr.reader.bytecode.analysis.opgraph.Op04StructuredStatement; import org.benf.cfr.reader.bytecode.analysis.opgraph.op4rewriters.transformers.ExpressionRewriterTransformer; import org.benf.cfr.reader.bytecode.analysis.opgraph.op4rewriters.transformers.StructuredStatementTransformer; import org.benf.cfr.reader.bytecode.analysis.opgraph.op4rewriters.util.MiscStatementTools; import org.benf.cfr.reader.bytecode.analysis.parse.Expression; import org.benf.cfr.reader.bytecode.analysis.parse.LValue; import org.benf.cfr.reader.bytecode.analysis.parse.StatementContainer; import org.benf.cfr.reader.bytecode.analysis.parse.expression.LValueExpression; import org.benf.cfr.reader.bytecode.analysis.parse.expression.Literal; import org.benf.cfr.reader.bytecode.analysis.parse.expression.MemberFunctionInvokation; import org.benf.cfr.reader.bytecode.analysis.parse.expression.SuperFunctionInvokation; import org.benf.cfr.reader.bytecode.analysis.parse.expression.SwitchExpression; import org.benf.cfr.reader.bytecode.analysis.parse.lvalue.FieldVariable; import org.benf.cfr.reader.bytecode.analysis.parse.lvalue.LocalVariable; import org.benf.cfr.reader.bytecode.analysis.parse.rewriters.AbstractExpressionRewriter; import org.benf.cfr.reader.bytecode.analysis.parse.rewriters.ExpressionRewriterFlags; import org.benf.cfr.reader.bytecode.analysis.parse.statement.CommentStatement; import org.benf.cfr.reader.bytecode.analysis.parse.statement.Nop; import org.benf.cfr.reader.bytecode.analysis.parse.utils.BlockIdentifier; import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; import org.benf.cfr.reader.bytecode.analysis.parse.utils.SSAIdentifiers; import org.benf.cfr.reader.bytecode.analysis.structured.StructuredScope; import org.benf.cfr.reader.bytecode.analysis.structured.StructuredStatement; import org.benf.cfr.reader.bytecode.analysis.structured.expression.StructuredStatementExpression; import org.benf.cfr.reader.bytecode.analysis.structured.statement.Block; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredAssignment; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredBreak; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredCase; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredComment; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredDefinition; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredExpressionStatement; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredExpressionYield; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredReturn; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredSwitch; import org.benf.cfr.reader.bytecode.analysis.structured.statement.StructuredThrow; import org.benf.cfr.reader.entities.Method; import org.benf.cfr.reader.util.DecompilerComment; import org.benf.cfr.reader.util.DecompilerComments; import org.benf.cfr.reader.util.collections.ListFactory; import org.benf.cfr.reader.util.collections.MapFactory; import org.benf.cfr.reader.util.collections.SetFactory; import org.benf.cfr.reader.util.functors.Predicate; import org.benf.cfr.reader.util.functors.UnaryFunction; import org.benf.cfr.reader.util.getopt.OptionsImpl; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; public class SwitchExpressionRewriter extends AbstractExpressionRewriter implements StructuredStatementTransformer { private final boolean experimental; private final Method method; private DecompilerComments comments; private final Set<StructuredStatement> classifiedEmpty = SetFactory.newIdentitySet(); public SwitchExpressionRewriter(DecompilerComments comments, Method method) { this.comments = comments; this.experimental = OptionsImpl.switchExpressionVersion.isExperimentalIn(method.getClassFile().getClassFileVersion()); this.method = method; } public void transform(Op04StructuredStatement root) { doTransform(root); doAggressiveTransforms(root); rewriteBlockSwitches(root); } private void doTransform(Op04StructuredStatement root) { root.transform(this, new StructuredScope()); } private static class LValueSingleUsageCheckingRewriter extends AbstractExpressionRewriter { Map<LValue, Boolean> usages = MapFactory.newMap(); Map<LValue, Op04StructuredStatement> usageSites = MapFactory.newMap(); private Set<StatementContainer> creators; LValueSingleUsageCheckingRewriter(Set<StatementContainer> creators) { this.creators = creators; } @Override public LValue rewriteExpression(LValue lValue, SSAIdentifiers ssaIdentifiers, StatementContainer statementContainer, ExpressionRewriterFlags flags) { Boolean prev = usages.get(lValue); if (prev == Boolean.FALSE) { return lValue; } else if (prev == null) { if (creators.contains(statementContainer)) return lValue; usages.put(lValue, Boolean.TRUE); usageSites.put(lValue, (Op04StructuredStatement)statementContainer); } else { usages.put(lValue, Boolean.FALSE); usageSites.remove(lValue); } return lValue; } } private static class BlockSwitchDiscoverer implements StructuredStatementTransformer { Map<StructuredStatement, List<Op04StructuredStatement>> blockSwitches = MapFactory.newOrderedMap(); @Override public StructuredStatement transform(StructuredStatement in, StructuredScope scope) { if (in instanceof StructuredAssignment && ((StructuredAssignment) in).getRvalue() instanceof SwitchExpression) { Op04StructuredStatement switchStatementContainer = in.getContainer(); StructuredStatement parent = scope.get(0); if (parent != null) { List<Op04StructuredStatement> targetPairs = blockSwitches.get(parent); if (targetPairs == null) { targetPairs = ListFactory.newList(); blockSwitches.put(parent, targetPairs); } targetPairs.add(switchStatementContainer); } } in.transformStructuredChildren(this, scope); return in; } } private void rewriteBlockSwitches(Op04StructuredStatement root) { BlockSwitchDiscoverer bsd = new BlockSwitchDiscoverer(); root.transform(bsd, new StructuredScope()); if (bsd.blockSwitches.isEmpty()) return; Set<StatementContainer> creators = SetFactory.newSet(); for (List<Op04StructuredStatement> list : bsd.blockSwitches.values()) { creators.addAll(list); } LValueSingleUsageCheckingRewriter scr = new LValueSingleUsageCheckingRewriter(creators); root.transform(new ExpressionRewriterTransformer(scr), new StructuredScope()); for (Map.Entry<StructuredStatement, List<Op04StructuredStatement>> entry : bsd.blockSwitches.entrySet()) { List<Op04StructuredStatement> switches = entry.getValue(); StructuredStatement stm = entry.getKey(); if (!(stm instanceof Block)) continue; List<Op04StructuredStatement> statements = ((Block) stm).getBlockStatements(); Set<Op04StructuredStatement> usages = SetFactory.newOrderedSet(); Set<Op04StructuredStatement> swtchSet = SetFactory.newSet(); for (Op04StructuredStatement swtch : switches) { if (!(swtch.getStatement() instanceof StructuredAssignment)) continue; StructuredAssignment sa = (StructuredAssignment)swtch.getStatement(); if (!sa.isCreator(sa.getLvalue())) continue; swtchSet.add(swtch); Op04StructuredStatement usage = scr.usageSites.get(sa.getLvalue()); if (usage == null) continue; usages.add(usage); } for (Op04StructuredStatement usage : usages) { int usageIdx = statements.indexOf(usage); for (int x = usageIdx-1;x>=0;--x) { Op04StructuredStatement backstm = statements.get(x); if (backstm.getStatement().isEffectivelyNOP()) continue; if (swtchSet.contains(backstm)) { StructuredStatement stss = backstm.getStatement(); if (!(stss instanceof StructuredAssignment)) { // This should not happen, but if we have a multiple rewrite? break; } StructuredAssignment sa = (StructuredAssignment)stss; ExpressionReplacingRewriter err = new ExpressionReplacingRewriter(new LValueExpression(sa.getLvalue()), sa.getRvalue()); usage.getStatement().rewriteExpressions(err); backstm.nopOut(); continue; } break; } } } } @Override public StructuredStatement transform(StructuredStatement in, StructuredScope scope) { in.transformStructuredChildren(this, scope); if (in instanceof StructuredSwitch) { Op04StructuredStatement container = in.getContainer(); rewrite(container, scope); return container.getStatement(); } return in; } // TODO : This is a very common pattern - linearize is treated as a util - we should just walk. public void rewrite(Op04StructuredStatement root, StructuredScope scope) { // While we're linearising inside a recursion here, this isn't as bad as it looks, because we only // do it for switch statements. List<StructuredStatement> structuredStatements = MiscStatementTools.linearise(root); if (structuredStatements == null) return; if (replaceSwitch(root, structuredStatements, scope) && experimental) { comments.addComment(DecompilerComment.EXPERIMENTAL_FEATURE); } } private boolean replaceSwitch(Op04StructuredStatement container, List<StructuredStatement> structuredStatements, StructuredScope scope) { StructuredStatement swat = structuredStatements.get(0); if (!(swat instanceof StructuredSwitch)) { return false; } StructuredSwitch swatch = (StructuredSwitch)swat; // At this point, the switch needs total coverage, and every item needs to assign // a single thing to target, or throw an exception; Op04StructuredStatement swBody = swatch.getBody(); if (!(swBody.getStatement() instanceof Block)) { return false; } Block b = (Block)swBody.getStatement(); List<Op04StructuredStatement> content = b.getBlockStatements(); int size = content.size(); List<Pair<StructuredCase, Expression>> extracted = ListFactory.newList(); List<Pair<Op04StructuredStatement, StructuredStatement>> replacements = ListFactory.newList(); LValue target = null; for (int itm = 0; itm < size && target == null; ++itm) { target = extractSwitchLValue(swatch.getBlockIdentifier(), content.get(itm), itm == size - 1); } if (target == null) { return false; } for (int itm = 0; itm < size; ++itm) { Pair<StructuredCase, Expression> e = extractSwitchEntryPair(target, swatch.getBlockIdentifier(), content.get(itm), replacements,itm == size -1); if (e == null) { return false; } extracted.add(e); } /* * We have to find definition of target in our scope. */ StructuredStatement declarationContainer = scope.get(1); if (!(declarationContainer instanceof Block)) return false; // Find the definition of the var, and ensure it's not used between there and statement. // TODO : This is expensive, and we could improve this by ensuring variable is declared // closer to usage. List<Op04StructuredStatement> blockContent = ((Block) declarationContainer).getBlockStatements(); Op04StructuredStatement definition = null; UsageCheck usageCheck = new UsageCheck(target); for (Op04StructuredStatement blockItem : blockContent) { if (definition == null) { StructuredStatement stm = blockItem.getStatement(); if (stm instanceof StructuredDefinition) { if (target.equals(((StructuredDefinition) stm).getLvalue())) { definition = blockItem; } } continue; } if (blockItem == container) break; blockItem.getStatement().rewriteExpressions(usageCheck); if (usageCheck.failed) { return false; } } if (definition == null) { return false; } // Now we're sure we're doing the transformation.... for (Pair<Op04StructuredStatement, StructuredStatement> replacement : replacements) { replacement.getFirst().replaceStatement(replacement.getSecond()); } List<SwitchExpression.Branch> items = ListFactory.newList(); for (Pair<StructuredCase, Expression> e : extracted) { items.add(new SwitchExpression.Branch(e.getFirst().getValues(), e.getSecond())); } definition.nopOut(); StructuredAssignment switchStatement = new StructuredAssignment(target, new SwitchExpression(target.getInferredJavaType(), swatch.getSwitchOn(), items)); swat.getContainer().replaceStatement(switchStatement); Op04StructuredStatement switchStatementContainer = switchStatement.getContainer(); switchStatement.markCreator(target, switchStatementContainer); return true; } private static class SwitchExpressionSearcher implements StructuredStatementTransformer { StructuredStatement last = null; LValue found = null; private BlockIdentifier blockIdentifier; SwitchExpressionSearcher(BlockIdentifier blockIdentifier) { this.blockIdentifier = blockIdentifier; } @Override public StructuredStatement transform(StructuredStatement in, StructuredScope scope) { if (found != null) { return in; } if (in instanceof Block) { in.transformStructuredChildren(this, scope); return in; } if (in instanceof StructuredBreak) { if (blockIdentifier.equals(((StructuredBreak) in).getBreakBlock())) { checkLast(); return in; } } if (!in.isEffectivelyNOP()) { last = in; } in.transformStructuredChildren(this, scope); return in; } private void checkLast() { if (last instanceof StructuredAssignment) { found = ((StructuredAssignment) last).getLvalue(); } } } private LValue extractSwitchLValue(BlockIdentifier blockIdentifier, Op04StructuredStatement item, boolean last) { SwitchExpressionSearcher ses = new SwitchExpressionSearcher(blockIdentifier); item.transform(ses, new StructuredScope()); if (ses.found != null) { return ses.found; } if (last) ses.checkLast(); return ses.found; } private final static Predicate<Op04StructuredStatement> notEmpty = new Predicate<Op04StructuredStatement>() { @Override public boolean test(Op04StructuredStatement in) { return !(in.getStatement() instanceof Nop || in.getStatement() instanceof CommentStatement); } }; private Pair<StructuredCase, Expression> extractSwitchEntryPair(LValue target, BlockIdentifier blockIdentifier, Op04StructuredStatement item, List<Pair<Op04StructuredStatement, StructuredStatement>> replacements, boolean last) { StructuredStatement stm = item.getStatement(); if (!(stm instanceof StructuredCase)) { return null; } StructuredCase sc = (StructuredCase)stm; Expression res = extractSwitchEntry(target, blockIdentifier, sc.getBody(), replacements, last); if (res == null) { return null; } return Pair.make(sc, res); } /* * The body of a switch expression is a legitimate result if it assigns to the target or throws before every * exit point. * * All exit points must target the eventual target of the switch. (otherwise we could assign, then break * an outer block). * (fortunately by this time we're structured, so all exit points must be a structured break, or roll off the end. * a break inside an inner breakable construct is therefore not adequate). * * No assignment to the target can happen other than just prior to an exit point. */ private Expression extractSwitchEntry(LValue target, BlockIdentifier blockIdentifier, Op04StructuredStatement body, List<Pair<Op04StructuredStatement, StructuredStatement>> replacements, boolean last) { SwitchExpressionTransformer transformer = new SwitchExpressionTransformer(target, blockIdentifier, replacements, last); body.transform(transformer, new StructuredScope()); if (transformer.failed) return null; if (!transformer.lastMarked) return null; if (transformer.lastAssign && !last) return null; if (transformer.totalStatements == 1 && transformer.singleValue != null) { return transformer.singleValue; } return new StructuredStatementExpression(target.getInferredJavaType(), body.getStatement()); } /* * Other than in the prescribed place, our lvalue can't be touched. */ static class UsageCheck extends AbstractExpressionRewriter { private final LValue target; private boolean failed; UsageCheck(LValue target) { this.target = target; } @Override public LValue rewriteExpression(LValue lValue, SSAIdentifiers ssaIdentifiers, StatementContainer statementContainer, ExpressionRewriterFlags flags) { if (target.equals(lValue)) { failed = true; } return super.rewriteExpression(lValue, ssaIdentifiers, statementContainer, flags); } } static class SwitchExpressionTransformer implements StructuredStatementTransformer { private UsageCheck rewriter; private BlockIdentifier blockIdentifier; private List<Pair<Op04StructuredStatement, StructuredStatement>> replacements; private boolean last; private final LValue target; private boolean failed; private boolean lastAssign = true; private boolean lastMarked = false; private Expression singleValue = null; private int totalStatements; private SwitchExpressionTransformer(LValue target, BlockIdentifier blockIdentifier, List<Pair<Op04StructuredStatement, StructuredStatement>> replacements, boolean last) { this.target = target; this.rewriter = new UsageCheck(target); this.blockIdentifier = blockIdentifier; this.replacements = replacements; this.last = last; } @Override public StructuredStatement transform(StructuredStatement in, StructuredScope scope) { if (failed) return in; lastMarked = false; lastAssign = true; if (in.isEffectivelyNOP()) return in; if (!(in instanceof Block)) { totalStatements++; } if (in instanceof StructuredBreak) { BreakClassification bk = classifyBreak((StructuredBreak)in, scope); switch (bk) { case CORRECT: lastMarked = true; lastAssign = false; totalStatements--; replacements.add(Pair.make(in.getContainer(),(StructuredStatement)StructuredComment.EMPTY_COMMENT)); return in; case INNER: break; case TOO_FAR: failed = true; return in; } } if (in instanceof StructuredReturn) { failed = true; return in; } if (in instanceof StructuredAssignment) { if (((StructuredAssignment) in).getLvalue().equals(target)) { Set<Op04StructuredStatement> nextFallThrough = scope.getNextFallThrough(in); lastMarked = true; replacements.add(Pair.make(in.getContainer(), (StructuredStatement)new StructuredExpressionYield(((StructuredAssignment) in).getRvalue()))); singleValue = ((StructuredAssignment) in).getRvalue(); boolean foundBreak = false; for (Op04StructuredStatement fall : nextFallThrough) { StructuredStatement fallStatement = fall.getStatement(); if (fallStatement.isEffectivelyNOP()) continue; if (fallStatement instanceof StructuredBreak) { BreakClassification bk = classifyBreak((StructuredBreak)fallStatement, scope); if (bk != BreakClassification.CORRECT) { failed = true; return in; } else { foundBreak = true; } } else { failed = true; return in; } } if (!last && !foundBreak) { failed = true; return in; } return in; } } if (in instanceof StructuredThrow) { replacements.add(Pair.make(in.getContainer(), in)); singleValue = new StructuredStatementExpression(target.getInferredJavaType(), in); lastAssign = false; lastMarked = true; } in.rewriteExpressions(rewriter); if (rewriter.failed) { failed = true; return in; } in.transformStructuredChildren(this, scope); return in; } BreakClassification classifyBreak(StructuredBreak in, StructuredScope scope) { BlockIdentifier breakBlock = in.getBreakBlock(); if (breakBlock == blockIdentifier) return BreakClassification.CORRECT; for (StructuredStatement stm : scope.getAll()) { BlockIdentifier block = stm.getBreakableBlockOrNull(); if (block == breakBlock) return BreakClassification.INNER; } return BreakClassification.TOO_FAR; } enum BreakClassification { CORRECT, TOO_FAR, INNER } } /* * There are times when we want to eliminate intermediate steps - i.e. when constructing an anonymous * array, or when passing arguments to a chained constructor. * * We can (if we have to!) perform the following somewhat crazy code 'reductions' * (currently only considered before constructor chains). * * 1) ////////////// * switch (A) { * default: * } * X : NON SWITCH EXPRESSION * * --> * * switch (A) { * default: * Y * X * } * * This can be rolled uptil the first initialisation, at which point it becomes a case of 2. * * 2) /////////////// * * switch (A) { * default: * } * r = switch (B) { * } * * ---> * * r = switch (A) { * default -> switch (B) { * } * } * * 3) //////////////// (by far the ugliest). Unless * * r = switch (A) { * XXXXXX * } * switch (B) { * default: * } * * --> * * r = switch (0) { * default -> { * var tmp = switch (A) { * xxx * }; * var ignore0 = B; * yield tmp; * } * } */ private RollState getRollState(Op04StructuredStatement body) { StructuredStatement s = body.getStatement(); if (!(s instanceof Block)) return new RollState(); Block b = (Block)s; List<Op04StructuredStatement> prequel = ListFactory.newList(); LinkedList<ClassifiedStm> tt = ListFactory.newLinkedList(); List<Op04StructuredStatement> others = ListFactory.newList(); Iterator<Op04StructuredStatement> it = b.getBlockStatements().iterator(); Set<Expression> directs = SetFactory.newSet(); boolean found = false; boolean inPrequel = method.getClassFile().isInnerClass(); while (it.hasNext()) { Op04StructuredStatement item = it.next(); if (item.getStatement().isEffectivelyNOP()) continue; if (inPrequel) { if (prequelAssign(item, directs)) { prequel.add(item); continue; } else { inPrequel = false; } } ClassifiedStm type = classify(item); if (type.type == ClassifyType.CHAINED_CONSTRUCTOR) { others.add(item); while (it.hasNext()) { others.add(it.next()); } found = true; } else { tt.add(type); } } if (!found) return new RollState(); return new RollState(prequel, tt, others, b, directs); } private boolean prequelAssign(Op04StructuredStatement item, Set<Expression> directs) { StructuredStatement s = item.getStatement(); if (!(s instanceof StructuredAssignment)) return false; LValue lv = ((StructuredAssignment) s).getLvalue(); if (!(lv instanceof FieldVariable)) return false; FieldVariable fv = (FieldVariable)lv; if (!fv.objectIsThis()) return false; directs.add(new LValueExpression(lv)); directs.add(((StructuredAssignment) s).getRvalue()); return true; } class RollState { boolean valid; List<Op04StructuredStatement> prequel; LinkedList<ClassifiedStm> switchdata; List<Op04StructuredStatement> remainder; Block block; private Set<Expression> directs; RollState() { this.valid = false; } RollState(List<Op04StructuredStatement> prequel, LinkedList<ClassifiedStm> switchdata, List<Op04StructuredStatement> remainder, Block block, Set<Expression> directs) { this.directs = directs; this.valid = true; this.prequel = prequel; this.switchdata = switchdata; this.remainder = remainder; this.block = block; } } private boolean rollOne(Op04StructuredStatement root, UnaryFunction<RollState, Boolean> apply) { RollState rollState = getRollState(root); if (!rollState.valid) return false; if (apply.invoke(rollState)) { List<Op04StructuredStatement> mutableBlockStatements = rollState.block.getBlockStatements(); mutableBlockStatements.clear(); mutableBlockStatements.addAll(rollState.prequel); for (ClassifiedStm t : rollState.switchdata) { mutableBlockStatements.add(t.stm); } mutableBlockStatements.addAll(rollState.remainder); doTransform(root); } return true; } private void doAggressiveTransforms(Op04StructuredStatement root) { if (!method.isConstructor()) return; if (!rollOne(root, new UnaryFunction<RollState, Boolean>() { @Override public Boolean invoke(RollState arg) { return rollUpEmptySwitches(arg); } })) return; rollOne(root, new UnaryFunction<RollState, Boolean>() { @Override public Boolean invoke(RollState arg) { return rollUpEmptySwitchCreation(arg); } }); rollOne(root, new UnaryFunction<RollState, Boolean>() { @Override public Boolean invoke(RollState arg) { return rollUpEmptySwitchAggregation(arg); } }); // As a final pass - if there's a single 'default switch' remaining (SwitchConstructorTest1,6) // capture the first argument to the chain. rollOne(root, new UnaryFunction<RollState, Boolean>() { @Override public Boolean invoke(RollState arg) { return rollSingleDefault(arg); } }); } /* switch (b) { default: { System.out.println("Hello world"); if (b < 3) { System.out.println("one"); } else { System.out.println("two"); } } } this(2); ->> int footmp; switch (b) { default: { System.out.println("Hello world"); if (b < 3) { System.out.println("one"); } else { System.out.println("two"); } } footmp = 2; } this(footmp); */ private boolean rollSingleDefault(RollState rollState) { if (rollState.switchdata.size() != 1) return false; ClassifiedStm t = rollState.switchdata.get(0); if (t.type != ClassifyType.EMPTY_SWITCH) return false; // Try to extract the first expression from the call. if (rollState.remainder.isEmpty()) return false; Op04StructuredStatement call = rollState.remainder.get(0); StructuredStatement s = call.getStatement(); if (!(s instanceof StructuredExpressionStatement)) return false; Expression e = ((StructuredExpressionStatement) s).getExpression(); List<Expression> args = null; if (e instanceof MemberFunctionInvokation && ((MemberFunctionInvokation) e).isInitMethod()) { args = ((MemberFunctionInvokation) e).getArgs(); } else if (e instanceof SuperFunctionInvokation) { args = ((SuperFunctionInvokation) e).getArgs(); } if (args == null) return false; // We have to use the first NON hidden arg - however (of course) we don't necessarily know that here! int idx; for (idx=0;idx<args.size();++idx) { if (!rollState.directs.contains(args.get(idx))) break; } if (idx >= args.size()) return false; Expression tmpValue = args.get(idx); LValue tmp = new LocalVariable("cfr_switch_hack2", tmpValue.getInferredJavaType()); rollState.switchdata.add(0, new ClassifiedStm(ClassifyType.DEFINITION, new Op04StructuredStatement(new StructuredDefinition(tmp)))); addToSwitch(t.stm, new Op04StructuredStatement(new StructuredAssignment(tmp, tmpValue))); args.set(idx, new LValueExpression(tmp)); return true; } private boolean rollUpEmptySwitchAggregation(RollState rollState) { LinkedList<ClassifiedStm> tt = rollState.switchdata; Iterator<ClassifiedStm> di = tt.descendingIterator(); ClassifiedStm last = null; boolean doneWork = false; while (di.hasNext()) { ClassifiedStm curr = di.next(); if (last != null) { if (curr.type == ClassifyType.SWITCH_EXPRESSION) { if (last.type == ClassifyType.EMPTY_SWITCH) { // this could work for 'other' too.... combineSwitchExpressionWithOther(curr, last); di.remove(); doneWork = true; continue; } } } last = curr; } return doneWork; } /* This is by far the ugliest - we introduce a switch statement :( byte by = switch (0) { default -> b; 1 -> 12; }; switch (0) { default: { System.out.println("HERE"); } } --> byte by = switch (0) { default -> { byte tmp = switch (0) { default -> b; 1 -> 12; }; switch (0) { default: { System.out.println("HERE"); } } yield tmp; } }; */ private void combineSwitchExpressionWithOther(ClassifiedStm switchExpression, ClassifiedStm other) { StructuredAssignment assignment = (StructuredAssignment)switchExpression.stm.getStatement(); LValue lv = assignment.getLvalue(); Expression se = assignment.getRvalue(); LinkedList<Op04StructuredStatement> newBlockContent = ListFactory.newLinkedList(); LValue tmp = new LocalVariable("cfr_switch_hack", lv.getInferredJavaType()); newBlockContent.add(new Op04StructuredStatement(new StructuredAssignment(tmp, se, true))); newBlockContent.add(other.stm); newBlockContent.add(new Op04StructuredStatement(new StructuredExpressionYield(new LValueExpression(tmp)))); Block newBlock = new Block(newBlockContent, true); SwitchExpression nse = new SwitchExpression(lv.getInferredJavaType(), Literal.INT_ZERO, Collections.singletonList(new SwitchExpression.Branch(Collections.<Expression>emptyList(), new StructuredStatementExpression(lv.getInferredJavaType(), newBlock)))); StructuredAssignment nsa = new StructuredAssignment(lv, nse, true); other.type = ClassifyType.SWITCH_EXPRESSION; other.stm = new Op04StructuredStatement(nsa); } private boolean rollUpEmptySwitchCreation(RollState rollState) { LinkedList<ClassifiedStm> tt = rollState.switchdata; Iterator<ClassifiedStm> di = tt.descendingIterator(); ClassifiedStm last = null; boolean doneWork = false; while (di.hasNext()) { ClassifiedStm curr = di.next(); if (curr.type == ClassifyType.EMPTY_SWITCH) { if (last != null) { if (last.type == ClassifyType.OTHER_CREATION || last.type == ClassifyType.SWITCH_EXPRESSION) { combineEmptySwitchWithCreation(curr, last); doneWork = true; continue; } } } last = curr; } return doneWork; } /* switch (0) { default: } byte by = b; -> byte by switch(0) { default: by = b; } */ private void combineEmptySwitchWithCreation(ClassifiedStm switchStm, ClassifiedStm assignStm) { StructuredAssignment stm = (StructuredAssignment)assignStm.stm.getStatement(); Expression rhs = stm.getRvalue(); LValue lhs = stm.getLvalue(); addToSwitch(switchStm.stm, new Op04StructuredStatement(new StructuredAssignment(lhs, rhs))); StructuredStatement swtch = switchStm.stm.getStatement(); assignStm.stm.replaceStatement(swtch); assignStm.type = ClassifyType.EMPTY_SWITCH; switchStm.stm.replaceStatement(new StructuredDefinition(lhs)); switchStm.type = ClassifyType.DEFINITION; } private boolean rollUpEmptySwitches(RollState rollState) { LinkedList<ClassifiedStm> tt = rollState.switchdata; List<ClassifiedStm> lt = ListFactory.newList(tt); boolean doneWork = false; for (int x=lt.size()-2;x>=0;--x) { ClassifiedStm curr = lt.get(x); ClassifiedStm last = x+1 < lt.size() ? lt.get(x+1) : null; if (curr.type == ClassifyType.EMPTY_SWITCH) { if (last != null) { if (last.type == ClassifyType.OTHER || last.type == ClassifyType.EMPTY_SWITCH) { addToSwitch(curr.stm, last.stm); last.stm = curr.stm; last.type = ClassifyType.EMPTY_SWITCH; lt.remove(x); ++x; doneWork = true; } } } } if (doneWork) { rollState.switchdata.clear(); rollState.switchdata.addAll(lt); } return doneWork; } private void addToSwitch(Op04StructuredStatement swtch, Op04StructuredStatement add) { StructuredSwitch ss = (StructuredSwitch)swtch.getStatement(); Block block = (Block)ss.getBody().getStatement(); StructuredCase caseStm = (StructuredCase)(block.getOneStatementIfPresent().getSecond().getStatement()); Block block1 = (Block) caseStm.getBody().getStatement(); block1.setIndenting(true); block1.addStatement(add); } private class ClassifiedStm { ClassifyType type; Op04StructuredStatement stm; ClassifiedStm(ClassifyType type, Op04StructuredStatement stm) { this.type = type; this.stm = stm; } @Override public String toString() { return "{" + "" + type + ", " + stm + '}'; } } private ClassifiedStm classify(Op04StructuredStatement item) { StructuredStatement stm = item.getStatement(); if (stm instanceof StructuredDefinition) { return new ClassifiedStm(ClassifyType.DEFINITION, item); } if (stm instanceof StructuredAssignment) { StructuredAssignment as = (StructuredAssignment)stm; LValue lv = as.getLvalue(); if (!as.isCreator(lv)) { return new ClassifiedStm(ClassifyType.OTHER, item); } Expression rv = as.getRvalue(); if (rv instanceof SwitchExpression) { return new ClassifiedStm(ClassifyType.SWITCH_EXPRESSION, item); } else { return new ClassifiedStm(ClassifyType.OTHER_CREATION, item); } } if (stm instanceof StructuredSwitch) { StructuredSwitch ss = (StructuredSwitch)stm; if (ss.isOnlyEmptyDefault() || classifiedEmpty.contains(ss)) { classifiedEmpty.add(ss); return new ClassifiedStm(ClassifyType.EMPTY_SWITCH, item); } else { return new ClassifiedStm(ClassifyType.OTHER, item); } } if (isConstructorChain(item)) { return new ClassifiedStm(ClassifyType.CHAINED_CONSTRUCTOR, item); } return new ClassifiedStm(ClassifyType.OTHER, item); } private enum ClassifyType { NONE, EMPTY_SWITCH, SWITCH_EXPRESSION, OTHER_CREATION, CHAINED_CONSTRUCTOR, DEFINITION, OTHER } // TODO : Or super!!! private boolean isConstructorChain(Op04StructuredStatement item) { StructuredStatement s = item.getStatement(); if (!(s instanceof StructuredExpressionStatement)) return false; Expression e = ((StructuredExpressionStatement) s).getExpression(); if (e instanceof SuperFunctionInvokation) return true; if (!(e instanceof MemberFunctionInvokation)) return false; return ((MemberFunctionInvokation) e).isInitMethod(); } }