From 3a22038f035f0c03ac7baf7724ec20d2d85f3f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 4 Jul 2015 15:21:47 +0200 Subject: [PATCH] AS3 - improved switch handling --- .../flash/abc/avm2/graph/AVM2Graph.java | 14 +- .../abc/avm2/model/IntegerValueAVM2Item.java | 8 +- .../abc/avm2/model/LocalRegAVM2Item.java | 23 ++++ .../abc/avm2/model/operations/EqAVM2Item.java | 3 +- .../model/operations/StrictEqAVM2Item.java | 3 +- .../action/model/operations/EqActionItem.java | 3 +- .../model/operations/StrictEqActionItem.java | 6 +- .../decompiler/graph/EqualsTypeItem.java | 11 ++ .../src/com/jpexs/decompiler/graph/Graph.java | 121 ++++++++++++++++-- .../graph/model/IntegerValueItem.java | 12 +- .../graph/model/IntegerValueTypeItem.java | 10 ++ 11 files changed, 192 insertions(+), 22 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/EqualsTypeItem.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueTypeItem.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java index 2d9a0c447..84be277f3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java @@ -444,7 +444,7 @@ public class AVM2Graph extends Graph { ret.addAll(output); return ret; } - if (((part.nextParts.size() == 2) + if (false && (((part.nextParts.size() == 2) && (!stack.isEmpty()) && (stack.peek() instanceof StrictEqAVM2Item) && (part.nextParts.get(0).getHeight() >= 2) @@ -457,7 +457,7 @@ public class AVM2Graph extends Graph { && (part.nextParts.get(1).getHeight() >= 2) && (this.avm2code.code.get(this.avm2code.fixIPAfterDebugLine(part.nextParts.get(1).start)).definition instanceof PushIntegerTypeIns) && (!part.nextParts.get(1).nextParts.isEmpty()) - && (this.avm2code.code.get(part.nextParts.get(1).nextParts.get(0).end).definition instanceof LookupSwitchIns))) { + && (this.avm2code.code.get(part.nextParts.get(1).nextParts.get(0).end).definition instanceof LookupSwitchIns)))) { if (stack.peek() instanceof StrictEqAVM2Item) { ignoredSwitches2.add(part.nextParts.get(0).nextParts.get(0).end); @@ -815,6 +815,16 @@ public class AVM2Graph extends Graph { } } if (avm2code.isKilled(ri.regIndex, 0, Integer.MAX_VALUE)) { + if (i + 1 < list.size()) { + if (list.get(i + 1) instanceof SwitchItem) { + SwitchItem si = (SwitchItem) list.get(i + 1); + if (si.switchedObject instanceof LocalRegAVM2Item) { + if (((LocalRegAVM2Item) si.switchedObject).regIndex == ri.regIndex) { + si.switchedObject = ri.value; + } + } + } + } if (i + 2 < list.size()) { if ((list.get(i + 1) instanceof IntegerValueAVM2Item) && (list.get(i + 2) instanceof ReturnValueAVM2Item)) { ReturnValueAVM2Item r = (ReturnValueAVM2Item) list.get(i + 2); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java index 8c75a60ad..55d8e485d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java @@ -29,11 +29,12 @@ import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SimpleValue; import com.jpexs.decompiler.graph.SourceGenerator; import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.IntegerValueTypeItem; import com.jpexs.decompiler.graph.model.LocalData; import java.util.List; import java.util.Set; -public class IntegerValueAVM2Item extends NumberValueAVM2Item { +public class IntegerValueAVM2Item extends NumberValueAVM2Item implements IntegerValueTypeItem { public Long value; @@ -81,4 +82,9 @@ public class IntegerValueAVM2Item extends NumberValueAVM2Item { return true; } + @Override + public int intValue() { + return (int) (long) value; + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java index 9dc5ef03d..582a5a45e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/LocalRegAVM2Item.java @@ -125,4 +125,27 @@ public class LocalRegAVM2Item extends AVM2Item { public boolean hasReturnValue() { return true; } + + @Override + public int hashCode() { + int hash = 3; + hash = 67 * hash + this.regIndex; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final LocalRegAVM2Item other = (LocalRegAVM2Item) obj; + if (this.regIndex != other.regIndex) { + return false; + } + return true; + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/EqAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/EqAVM2Item.java index fe2f677bd..b018c2a9e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/EqAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/EqAVM2Item.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfEqIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNeIns; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.EqualsTypeItem; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; @@ -32,7 +33,7 @@ import com.jpexs.decompiler.graph.model.BinaryOpItem; import com.jpexs.decompiler.graph.model.LogicalOpItem; import java.util.List; -public class EqAVM2Item extends BinaryOpItem implements LogicalOpItem, IfCondition { +public class EqAVM2Item extends BinaryOpItem implements LogicalOpItem, IfCondition, EqualsTypeItem { public EqAVM2Item(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { super(instruction, PRECEDENCE_EQUALITY, leftSide, rightSide, "=="); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/StrictEqAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/StrictEqAVM2Item.java index e13bce5a9..188f53fb8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/StrictEqAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/StrictEqAVM2Item.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictEqIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.EqualsTypeItem; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; @@ -32,7 +33,7 @@ import com.jpexs.decompiler.graph.model.BinaryOpItem; import com.jpexs.decompiler.graph.model.LogicalOpItem; import java.util.List; -public class StrictEqAVM2Item extends BinaryOpItem implements LogicalOpItem, IfCondition { +public class StrictEqAVM2Item extends BinaryOpItem implements LogicalOpItem, IfCondition, EqualsTypeItem { public StrictEqAVM2Item(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { super(instruction, PRECEDENCE_EQUALITY, leftSide, rightSide, "==="); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/EqActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/EqActionItem.java index 4408950b3..4d9ae30b8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/EqActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/EqActionItem.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.EqualsTypeItem; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; @@ -29,7 +30,7 @@ import com.jpexs.decompiler.graph.model.BinaryOpItem; import com.jpexs.decompiler.graph.model.LogicalOpItem; import java.util.List; -public class EqActionItem extends BinaryOpItem implements LogicalOpItem { +public class EqActionItem extends BinaryOpItem implements LogicalOpItem, EqualsTypeItem { boolean version2; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StrictEqActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StrictEqActionItem.java index c25133620..1716bf418 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StrictEqActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StrictEqActionItem.java @@ -12,13 +12,15 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.action.model.operations; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.EqualsTypeItem; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; @@ -27,7 +29,7 @@ import com.jpexs.decompiler.graph.model.BinaryOpItem; import com.jpexs.decompiler.graph.model.LogicalOpItem; import java.util.List; -public class StrictEqActionItem extends BinaryOpItem implements LogicalOpItem, Inverted { +public class StrictEqActionItem extends BinaryOpItem implements LogicalOpItem, Inverted, EqualsTypeItem { public StrictEqActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { super(instruction, PRECEDENCE_EQUALITY, leftSide, rightSide, "==="); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/EqualsTypeItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/EqualsTypeItem.java new file mode 100644 index 000000000..bf5f38056 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/EqualsTypeItem.java @@ -0,0 +1,11 @@ +package com.jpexs.decompiler.graph; + +import com.jpexs.decompiler.graph.model.BinaryOp; + +/** + * + * @author JPEXS + */ +public interface EqualsTypeItem extends BinaryOp { + +} 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 2c613e434..2091c4c6b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -34,6 +34,7 @@ import com.jpexs.decompiler.graph.model.ForItem; import com.jpexs.decompiler.graph.model.GotoItem; import com.jpexs.decompiler.graph.model.IfItem; import com.jpexs.decompiler.graph.model.IntegerValueItem; +import com.jpexs.decompiler.graph.model.IntegerValueTypeItem; import com.jpexs.decompiler.graph.model.LabelItem; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.decompiler.graph.model.LogicalOpItem; @@ -1498,7 +1499,7 @@ public class Graph { //********************************END PART DECOMPILING if (parseNext) { - if (part.nextParts.size() > 2) {//direct switch, seen in the wild... + if (part.nextParts.size() > 2) { GraphPart next = getMostCommonPart(localData, part.nextParts, loops); List vis = new ArrayList<>(); GraphTargetItem switchedItem = stack.pop(); @@ -1513,15 +1514,94 @@ public class Graph { loops.add(swLoop); boolean first = false; int pos = 0; + + Map caseExpressions = new HashMap<>(); + Map caseExpressionLeftSides = new HashMap<>(); + Map caseExpressionRightSides = new HashMap<>(); + GraphTargetItem it = switchedItem; + int defaultBranch = 0; + + 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; + } else { + break; + } + } else { + break; + } + } + //int ignoredBranch = -1; + if (it instanceof IntegerValueTypeItem) { + defaultBranch = ((IntegerValueTypeItem) it).intValue(); + } + + if (!caseExpressionRightSides.isEmpty()) { + GraphTargetItem firstItem; + firstItem = (GraphTargetItem) caseExpressionRightSides.values().toArray()[0]; + boolean sameRight = true; + for (GraphTargetItem cit : caseExpressionRightSides.values()) { + if (!cit.equals(firstItem)) { + sameRight = false; + break; + } + } + + if (sameRight) { + caseExpressions = caseExpressionLeftSides; + switchedItem = firstItem; + } else { + firstItem = (GraphTargetItem) caseExpressionLeftSides.values().toArray()[0]; + + boolean sameLeft = true; + for (GraphTargetItem cit : caseExpressionLeftSides.values()) { + if (!cit.equals(firstItem)) { + sameLeft = false; + break; + } + } + if (sameLeft) { + caseExpressions = caseExpressionRightSides; + switchedItem = firstItem; + } + } + + } + first = true; + pos = 0; + //This is tied to AS3 switch implementation which has nextparts switched from index 1. TODO: Make more universal + GraphPart defaultPart = part.nextParts.get(1 + defaultBranch); + + for (GraphPart p : part.nextParts) { + if (p != defaultPart) { + if (caseExpressions.containsKey(pos)) { + caseValues.add(caseExpressions.get(pos)); + } else { + caseValues.add(new IntegerValueItem(null, pos)); + } + pos++; + } + } + + first = true; + pos = 0; + List nextCommands = null; for (GraphPart p : part.nextParts) { - if (!first) { - caseValues.add(new IntegerValueItem(null, pos++)); + /*if (pos == ignoredBranch) { + pos++; + continue; + }*/ + if (p != defaultPart) { if (vis.contains(p)) { valueMappings.add(caseCommands.size() - 1); continue; } - valueMappings.add(caseCommands.size()); } List stopPart2 = new ArrayList<>(); @@ -1539,19 +1619,37 @@ public class Graph { } } if (next != p) { - //int stackLenBefore = stack.size(); - TranslateStack s2 = (TranslateStack) stack.clone(); - s2.clear(); - List nextCommands = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), s2, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - makeAllCommands(nextCommands, s2); - if (first) { + + if (p == defaultPart && !defaultCommands.isEmpty()) { + //ignore + } else { + TranslateStack s2 = (TranslateStack) stack.clone(); + s2.clear(); + nextCommands = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), s2, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + makeAllCommands(nextCommands, s2); + if (p == defaultPart) { + defaultCommands = nextCommands; + } else { + caseCommands.add(nextCommands); + } + vis.add(p); + } + } else { + if (p == defaultPart) { defaultCommands = nextCommands; } else { caseCommands.add(nextCommands); } - vis.add(p); } first = false; + pos++; + } + //remove last break from default clause + if (!defaultCommands.isEmpty() && (defaultCommands.get(defaultCommands.size() - 1) instanceof BreakItem)) { + BreakItem bi = (BreakItem) defaultCommands.get(defaultCommands.size() - 1); + if (bi.loopId == swLoop.id) { + defaultCommands.remove(defaultCommands.size() - 1); + } } SwitchItem sw = new SwitchItem(null, swLoop, switchedItem, caseValues, caseCommands, defaultCommands, valueMappings); currentRet.add(sw); @@ -1559,6 +1657,7 @@ public class Graph { if (next != null) { currentRet.addAll(printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); } + pos++; } //else GraphPart nextOnePart = null; if (part.nextParts.size() == 2) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueItem.java index 56142028d..0a684b339 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueItem.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.graph.model; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; @@ -25,7 +26,7 @@ import java.util.Set; * * @author JPEXS */ -public class IntegerValueItem extends GraphTargetItem { +public class IntegerValueItem extends GraphTargetItem implements IntegerValueTypeItem { private final int intValue; @@ -46,7 +47,7 @@ public class IntegerValueItem extends GraphTargetItem { @Override public Object getResult() { - return (double) intValue; + return (long) intValue; } @Override @@ -58,4 +59,9 @@ public class IntegerValueItem extends GraphTargetItem { public GraphTargetItem returnType() { return TypeItem.UNBOUNDED; } + + @Override + public int intValue() { + return intValue; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueTypeItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueTypeItem.java new file mode 100644 index 000000000..2fc6e2d64 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/IntegerValueTypeItem.java @@ -0,0 +1,10 @@ +package com.jpexs.decompiler.graph.model; + +/** + * + * @author JPEXS + */ +public interface IntegerValueTypeItem { + + public int intValue(); +}