diff --git a/CHANGELOG.md b/CHANGELOG.md index 6010545bb..5bf3eba4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. - Using new FFDec icon on Mac - AS3: get/set slot for global scope - AS3: Incorrect handling of strict equals operator in if vs switch resulting in §§pop +- Better goto detection/for continue +- Support for comma operator in switch case statements ### Changed - AS3 test methods separated to classes diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java index 6e261b927..44c285289 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -28,6 +28,7 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.model.AndItem; import com.jpexs.decompiler.graph.model.BranchStackResistant; import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.CommaExpressionItem; import com.jpexs.decompiler.graph.model.ContinueItem; import com.jpexs.decompiler.graph.model.DefaultItem; import com.jpexs.decompiler.graph.model.DoWhileItem; @@ -100,7 +101,6 @@ public class Graph { return new LinkedHashMap<>(); } - protected List filter(List list) { return new ArrayList<>(list); } @@ -747,7 +747,6 @@ public class Graph { //GraphPartDecision decision = prefix.isEmpty() ? null : prefix.get(prefix.size() - 1); //int removedCount = decisionToRemovedCount.containsKey(decision) ? decisionToRemovedCount.get(decision) : 0; - for (int i = 0; i < comparedPaths.size(); i++) { for (int j = prefix.size(); j < comparedPaths.get(i).size(); j++) { GraphPart partToClose = comparedPaths.get(i).get(j).part; @@ -877,7 +876,6 @@ public class Graph { } return null; }*/ - protected boolean isPartEmpty(GraphPart part) { return false; } @@ -2346,8 +2344,12 @@ public class Graph { if (part.nextParts.size() > 2) { GraphPart next = getMostCommonPart(localData, part.nextParts, loops, new ArrayList<>()); List vis = new ArrayList<>(); - GraphTargetItem switchedItem = stack.pop(); + GraphTargetItem originalSwitchedItem = stack.pop(); makeAllCommands(currentRet, stack); + GraphTargetItem switchedItem = originalSwitchedItem; + if ((switchedItem instanceof PopItem) && !currentRet.isEmpty() && (currentRet.get(currentRet.size() - 1) instanceof IfItem)) { + switchedItem = currentRet.get(currentRet.size() - 1); + } List caseValues = new ArrayList<>(); List> caseCommands = new ArrayList<>(); @@ -2365,26 +2367,68 @@ public class Graph { GraphTargetItem it = switchedItem; int defaultBranch = 0; boolean hasExpr = false; + List commaCommands = new ArrayList<>(); + Map> caseCommaCommands = new HashMap<>(); - while (it instanceof TernarOpItem) { - TernarOpItem to = (TernarOpItem) it; - if (to.expression instanceof EqualsTypeItem) { - if (to.onTrue instanceof IntegerValueTypeItem) { - int cpos = ((IntegerValueTypeItem) to.onTrue).intValue(); - caseExpressionLeftSides.put(cpos, ((EqualsTypeItem) to.expression).getLeftSide()); - caseExpressionRightSides.put(cpos, ((EqualsTypeItem) to.expression).getRightSide()); - it = to.onFalse; + while ((it instanceof TernarOpItem) || (it instanceof IfItem)) { + + if (it instanceof IfItem) { + IfItem ii = (IfItem) it; + if (ii.expression instanceof EqualsTypeItem) { + if (!ii.onFalse.isEmpty() && !ii.onTrue.isEmpty() + && ii.onTrue.get(ii.onTrue.size() - 1) instanceof PushItem + && ii.onTrue.get(ii.onTrue.size() - 1).value instanceof IntegerValueTypeItem) { + int cpos = ((IntegerValueTypeItem) ii.onTrue.get(ii.onTrue.size() - 1).value).intValue(); + caseCommaCommands.put(cpos, commaCommands); + caseExpressionLeftSides.put(cpos, ((EqualsTypeItem) ii.expression).getLeftSide()); + caseExpressionRightSides.put(cpos, ((EqualsTypeItem) ii.expression).getRightSide()); + commaCommands = new ArrayList<>(); + for (int f = 0; f < ii.onFalse.size() - 1; f++) { + commaCommands.add(ii.onFalse.get(f)); + } + it = ii.onFalse.get(ii.onFalse.size() - 1); + if (it instanceof PushItem) { + it = it.value; + } + } else { + break; + } + } else if (ii.expression instanceof FalseItem && !ii.onFalse.isEmpty()) { + it = ii.onFalse.get(ii.onFalse.size() - 1); + } else if (ii.expression instanceof TrueItem && !ii.onTrue.isEmpty()) { + it = ii.onTrue.get(ii.onTrue.size() - 1); + } else { + break; + } + } else if (it instanceof TernarOpItem) { + TernarOpItem to = (TernarOpItem) it; + if (to.expression instanceof EqualsTypeItem) { + if (to.onTrue instanceof IntegerValueTypeItem) { + int cpos = ((IntegerValueTypeItem) to.onTrue).intValue(); + caseExpressionLeftSides.put(cpos, ((EqualsTypeItem) to.expression).getLeftSide()); + caseExpressionRightSides.put(cpos, ((EqualsTypeItem) to.expression).getRightSide()); + caseCommaCommands.put(cpos, commaCommands); + commaCommands = new ArrayList<>(); + it = to.onFalse; + } else { + break; + } + } else if (to.expression instanceof FalseItem) { + it = to.onFalse; + } else if (to.expression instanceof TrueItem) { + it = to.onTrue; } else { break; } - } else if (to.expression instanceof FalseItem) { - it = to.onFalse; - } else if (to.expression instanceof TrueItem) { - it = to.onTrue; - } else { - break; } } + + if (switchedItem != originalSwitchedItem && !caseExpressionRightSides.isEmpty()) { + currentRet.remove(currentRet.size() - 1); + } else { + switchedItem = originalSwitchedItem; + } + //int ignoredBranch = -1; if (it instanceof IntegerValueTypeItem) { defaultBranch = ((IntegerValueTypeItem) it).intValue(); @@ -2432,7 +2476,13 @@ public class Graph { for (int i = 1; i < part.nextParts.size(); i++) { if (caseExpressions.containsKey(pos)) { - caseValues.add(caseExpressions.get(pos)); + GraphTargetItem expr = caseExpressions.get(pos); + if (caseCommaCommands.get(pos).size() > 0) { + List exprCommaCommands = new ArrayList<>(caseCommaCommands.get(pos)); + exprCommaCommands.add(expr); + expr = new CommaExpressionItem(null, expr.lineStartItem, exprCommaCommands); + } + caseValues.add(expr); } else if (part.nextParts.get(i) == defaultPart) { caseValues.add(new DefaultItem()); } else { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java index 6cb3f954b..002bfd1ae 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -1162,6 +1162,22 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } + @Test + public void testSwitchComma() { + decompileMethod("testSwitchComma", "var a:String = \"A\";\r\n" + + "switch(a)\r\n" + + "{\r\n" + + "case \"A\":\r\n" + + "trace(\"is A\");\r\n" + + "break;\r\n" + + "case \"B\":\r\n" + + "trace(\"is B\");\r\n" + + "case TestSwitchComma.X, \"C\":\r\n" + + "trace(\"is C\");\r\n" + + "}\r\n", + false); + } + @Test public void testSwitchDefault() { decompileMethod("testSwitchDefault", "var a:* = 5;\r\n" diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf b/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf index b5034f1bb..0544aa758 100644 Binary files a/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf and b/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf differ diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as index b406df078..582e78714 100644 --- a/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as @@ -69,8 +69,9 @@ package TestRest; TestStrictEquals; TestStringConcat; - TestStrings; + TestStrings; TestSwitch; + TestSwitchComma; TestSwitchDefault; TestTernarOperator; TestTry; diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestSwitchComma.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestSwitchComma.as new file mode 100644 index 000000000..3b12c278e --- /dev/null +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestSwitchComma.as @@ -0,0 +1,27 @@ +package tests +{ + public class TestSwitchComma + { + private static const X:int = 7; + + public function run():* + { + var b:int = 5; + + var a:String = "A"; + switch (a) + { + case "A": + trace("is A"); + break; + case "B": + trace("is B"); + case TestSwitchComma.X,"C": + trace("is C"); + break; + } + } + + } + +} \ No newline at end of file