From 681127a92ae9fcee2e8b668bff8d5ec57038751f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Sat, 29 Jun 2013 17:29:38 +0200 Subject: [PATCH] improved loop detection fixed AS1/2 switch --- .../flash/abc/avm2/graph/AVM2Graph.java | 55 ++- .../decompiler/flash/action/ActionGraph.java | 121 ++++- .../action/treemodel/DirectValueTreeItem.java | 3 + .../jpexs/decompiler/flash/graph/Graph.java | 451 +++++++++++++++--- .../decompiler/flash/graph/GraphPart.java | 13 + .../jpexs/decompiler/flash/graph/Loop.java | 3 +- 6 files changed, 555 insertions(+), 91 deletions(-) diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java index 0a9fc7f75..0b52d873b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java @@ -112,6 +112,7 @@ public class AVM2Graph extends Graph { public static List translateViaGraph(String path, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, Stack scopeStack, HashMap localRegNames, List fullyQualifiedNames) { AVM2Graph g = new AVM2Graph(code, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames); + g.init(); List allParts = new ArrayList<>(); for (GraphPart head : g.heads) { populateParts(head, allParts); @@ -135,7 +136,49 @@ public class AVM2Graph extends Graph { } @Override - protected List check(GraphSource srcCode, List localData, List allParts, Stack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output) { + protected void checkGraph(List allBlocks) { + for (ABCException ex : body.exceptions) { + int startIp = code.adr2pos(ex.start); + int endIp = code.adr2pos(ex.end); + int targetIp = code.adr2pos(ex.target); + GraphPart target = null; + for (GraphPart p : allBlocks) { + if (p.start == targetIp) { + target = p; + break; + } + } + for (GraphPart p : allBlocks) { + if (p.start >= startIp && p.end <= endIp) { + p.throwParts.add(target); + target.refs.add(p); + } + } + } + + /*for(ABCException ex:body.exceptions){ + for(GraphPart p:allBlocks){ + boolean next_is_ex_start=false; + for(GraphPart n:p.nextParts){ + if(n.start==code.adr2pos(ex.start)){ + next_is_ex_start = true; + break; + } + } + if(next_is_ex_start){ + for(GraphPart q:allBlocks){ //find target part + if(q.start==code.adr2pos(ex.target)){ + p.nextParts.add(q); + break; + } + } + } + } + }*/ + } + + @Override + protected List check(GraphSource srcCode, List localData, List allParts, Stack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop) { List ret = null; @@ -164,6 +207,9 @@ public class AVM2Graph extends Graph { } } if (catchedExceptions.size() > 0) { + if (currentLoop != null) { + //currentLoop.phase=0; + } parsedExceptions.addAll(catchedExceptions); int endpos = code.adr2pos(code.fixAddrAfterDebugLine(catchedExceptions.get(0).end)); int endposStartBlock = code.adr2pos(catchedExceptions.get(0).end); @@ -235,7 +281,7 @@ public class AVM2Graph extends Graph { } } } - + List catchParts = new ArrayList<>(); for (int e = 0; e < catchedExceptions.size(); e++) { int eendpos; if (e < catchedExceptions.size() - 1) { @@ -249,6 +295,7 @@ public class AVM2Graph extends Graph { for (GraphPart p : allParts) { if (p.start == findpos) { npart = p; + catchParts.add(p); break; } } @@ -279,6 +326,7 @@ public class AVM2Graph extends Graph { } List stopPart2 = new ArrayList<>(stopPart); stopPart2.add(nepart); + stopPart2.addAll(catchParts); List tryCommands = printGraph(new ArrayList(), localData, stack, allParts, parent, part, stopPart2, loops); output.clear(); @@ -432,9 +480,8 @@ public class AVM2Graph extends Graph { } GraphTargetItem ti = checkLoop(next, stopPart, loops); - Loop currentLoop = new Loop(loops.size(), null, next); + currentLoop = new Loop(loops.size(), null, next); currentLoop.phase = 1; - currentLoop.used = true; loops.add(currentLoop); //switchLoc.getNextPartPath(new ArrayList()); List valuesMapping = new ArrayList<>(); diff --git a/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java b/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java index a38af617c..cf079ed2f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java @@ -21,7 +21,10 @@ import com.jpexs.decompiler.flash.action.swf4.ActionIf; import com.jpexs.decompiler.flash.action.swf4.ActionNot; import com.jpexs.decompiler.flash.action.swf4.ActionPush; import com.jpexs.decompiler.flash.action.swf4.Null; +import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; +import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; import com.jpexs.decompiler.flash.action.treemodel.DirectValueTreeItem; import com.jpexs.decompiler.flash.action.treemodel.EnumerateTreeItem; import com.jpexs.decompiler.flash.action.treemodel.FunctionTreeItem; @@ -41,8 +44,6 @@ import com.jpexs.decompiler.flash.graph.Loop; import com.jpexs.decompiler.flash.graph.SwitchItem; import com.jpexs.decompiler.flash.graph.WhileItem; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Stack; @@ -68,6 +69,7 @@ public class ActionGraph extends Graph { ActionGraph g = new ActionGraph(code, registerNames, variables, functions, version); List localData = new ArrayList<>(); localData.add(registerNames); + g.init(); return g.translate(localData); } @@ -124,7 +126,64 @@ public class ActionGraph extends Graph { } @Override - protected List check(GraphSource code, List localData, List allParts, Stack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output) { + protected List checkPrecoNextParts(GraphPart part) { + List items = getPartItems(part); + part = makeMultiPart(part); + if (!items.isEmpty()) { + if (items.get(items.size() - 1) instanceof ActionIf) { + if (items.get(items.size() - 2) instanceof ActionStrictEquals) { + List storeRegisters = new ArrayList<>(); + for (GraphSourceItem s : items) { + if (s instanceof ActionStoreRegister) { + ActionStoreRegister sr = (ActionStoreRegister) s; + storeRegisters.add(sr.registerNumber); + } + } + if (!storeRegisters.isEmpty()) { + List caseBodies = new ArrayList<>(); + boolean proceed = false; + do { + proceed = false; + caseBodies.add(part.nextParts.get(0)); //jump + part = part.nextParts.get(1); //nojump + items = getPartItems(part); + part = makeMultiPart(part); + if (!items.isEmpty()) { + if (items.get(0) instanceof ActionPush) { + ActionPush pu = (ActionPush) items.get(0); + if (!pu.values.isEmpty()) { + if (pu.values.get(0) instanceof RegisterNumber) { + RegisterNumber rn = (RegisterNumber) pu.values.get(0); + if (storeRegisters.contains(rn.number)) { + storeRegisters.clear(); + storeRegisters.add(rn.number); + if (items.get(items.size() - 1) instanceof ActionIf) { + if (items.size() > 1) { + if (items.get(items.size() - 2) instanceof ActionStrictEquals) { + proceed = true; + } + } + } + } + } + } + } + } + } while (proceed); + + if (caseBodies.size() > 1) { + caseBodies.add(part); //TODO: properly detect default clause (?) + return caseBodies; + } + } + } + } + } + return null; + } + + @Override + protected List check(GraphSource code, List localData, List allParts, Stack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop) { if (!output.isEmpty()) { if (output.get(output.size() - 1) instanceof StoreRegisterTreeItem) { StoreRegisterTreeItem str = (StoreRegisterTreeItem) output.get(output.size() - 1); @@ -175,7 +234,7 @@ public class ActionGraph extends Graph { } else { part = part.nextParts.get(1); - GraphPart defaultPart = part; + GraphPart defaultPart = part; //21-21 //caseBodyParts.add(defaultPart); @@ -184,7 +243,7 @@ public class ActionGraph extends Graph { defaultAndLastPart.add(defaultPart); defaultAndLastPart.add(caseBodyParts.get(caseBodyParts.size() - 1)); - GraphPart defaultPart2 = getCommonPart(defaultAndLastPart, new ArrayList()); + GraphPart defaultPart2 = getCommonPart(defaultAndLastPart, loops);//34-37 List defaultCommands = new ArrayList<>(); List stopPart2 = new ArrayList<>(stopPart); @@ -200,25 +259,26 @@ public class ActionGraph extends Graph { } List breakParts = new ArrayList<>(); - for (int g = 0; g < caseBodyParts.size(); g++) { - if (g < caseBodyParts.size() - 1) { - if (caseBodyParts.get(g).leadsTo(code, caseBodyParts.get(g + 1), loops)) { - continue; - } - } - GraphPart nsp = caseBodyParts.get(g).getNextSuperPartPath(loopContinues); - if (nsp != null) { - breakParts.add(nsp); - } - } - Collections.sort(breakParts, new Comparator() { - @Override - public int compare(GraphPart o1, GraphPart o2) { - return o2.path.length() - o1.path.length(); - } - }); + /*for (int g = 0; g < caseBodyParts.size(); g++) { + if (g < caseBodyParts.size() - 1) { + if (caseBodyParts.get(g).leadsTo(code, caseBodyParts.get(g + 1), loops)) { + continue; + } + } + GraphPart nsp = caseBodyParts.get(g).getNextSuperPartPath(loopContinues); + if (nsp != null) { + breakParts.add(nsp); + } + } + Collections.sort(breakParts, new Comparator() { + @Override + public int compare(GraphPart o1, GraphPart o2) { + return o2.path.length() - o1.path.length(); + } + });*/ - GraphPart breakPart = breakParts.isEmpty() ? null : breakParts.get(0); + //GraphPart breakPart = breakParts.isEmpty() ? null : breakParts.get(0); + GraphPart breakPart = getMostCommonPart(caseBodyParts, loops); if ((defaultPart2 != breakPart) && (defaultCommands.isEmpty())) { defaultPart = defaultPart2; } @@ -241,7 +301,7 @@ public class ActionGraph extends Graph { next = breakPart; GraphTargetItem ti = checkLoop(next, stopPart, loops); - Loop currentLoop = new Loop(loops.size(), null, next); + currentLoop = new Loop(loops.size(), null, next); loops.add(currentLoop); //switchLoc.getNextPartPath(new ArrayList()); List valuesMapping = new ArrayList<>(); @@ -291,7 +351,18 @@ public class ActionGraph extends Graph { } } List stopPart2x = new ArrayList<>(stopPart); - stopPart2.add(nextCase); + //stopPart2.add(nextCase); + for (GraphPart b : caseBodies) { + if (b != caseBodies.get(i)) { + stopPart2x.add(b); + } + } + if (defaultPart != null) { + stopPart2x.add(defaultPart); + } + if (breakPart != null) { + stopPart2x.add(breakPart); + } cc.addAll(0, printGraph(new ArrayList(), localData, stack, allParts, null, caseBodies.get(i), stopPart2x, loops)); if (cc.size() >= 2) { if (cc.get(cc.size() - 1) instanceof BreakItem) { diff --git a/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java b/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java index b5a5848ef..6eedfb060 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/treemodel/DirectValueTreeItem.java @@ -206,6 +206,9 @@ public class DirectValueTreeItem extends TreeItem { if (obj == null) { return false; } + if (!(obj instanceof DirectValueTreeItem)) { + return false; + } final DirectValueTreeItem other = (DirectValueTreeItem) obj; if (!Objects.equals(this.value, other.value)) { return false; diff --git a/trunk/src/com/jpexs/decompiler/flash/graph/Graph.java b/trunk/src/com/jpexs/decompiler/flash/graph/Graph.java index 7fc6b1c0e..34fd4deb2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/graph/Graph.java +++ b/trunk/src/com/jpexs/decompiler/flash/graph/Graph.java @@ -36,15 +36,24 @@ public class Graph { public List heads; protected GraphSource code; + private List alternateEntries; public Graph(GraphSource code, List alternateEntries) { this.code = code; + this.alternateEntries = alternateEntries; + + } + + public void init() { heads = makeGraph(code, new ArrayList(), alternateEntries); + int time = 1; + List ordered = new ArrayList<>(); + List visited = new ArrayList<>(); for (GraphPart head : heads) { + time = head.setTime(time, ordered, visited); fixGraph(head); makeMulti(head, new ArrayList()); } - } protected static void populateParts(GraphPart part, List allParts) { @@ -394,6 +403,117 @@ public class Graph { return null; } + public GraphPart getMostCommonPart(List parts, List loops) { + if (parts.isEmpty()) { + return null; + } + + + + Set s = new HashSet<>(parts); //unique + parts = new ArrayList<>(s); //make local copy + + List loopContinues = new ArrayList<>();//getLoopsContinues(loops); + for (Loop l : loops) { + if (l.phase == 1) { + loopContinues.add(l.loopContinue); + loopContinues.add(l.loopPreContinue); + } + } + + for (GraphPart p : parts) { + if (loopContinues.contains(p)) { + break; + } + boolean common = true; + for (GraphPart q : parts) { + if (q == p) { + continue; + } + if (!q.leadsTo(code, p, loops)) { + common = false; + break; + } + } + if (common) { + return p; + } + } + + loopi: + for (int i = 0; i < parts.size(); i++) { + for (int j = 0; j < parts.size(); j++) { + if (j == i) { + continue; + } + if (parts.get(i).leadsTo(code, parts.get(j), loops)) { + parts.remove(i); + i--; + continue loopi; + } + } + } + List> reachable = new ArrayList<>(); + for (GraphPart p : parts) { + List r1 = new ArrayList<>(); + getReachableParts(p, r1, loops); + r1.add(0, p); + reachable.add(r1); + } + ///List first = reachable.get(0); + int commonLevel = 0; + Map levelMap = new HashMap<>(); + for (List first : reachable) { + int maxclevel = 0; + Set visited = new HashSet<>(); + for (GraphPart p : first) { + if (loopContinues.contains(p)) { + break; + } + if (visited.contains(p)) { + continue; + } + visited.add(p); + boolean common = true; + commonLevel = 1; + for (List r : reachable) { + if (r == first) { + continue; + } + if (r.contains(p)) { + commonLevel++; + } + } + if (commonLevel <= maxclevel) { + continue; + } + maxclevel = commonLevel; + if (levelMap.containsKey(p)) { + if (levelMap.get(p) > commonLevel) { + commonLevel = levelMap.get(p); + } + } + levelMap.put(p, commonLevel); + if (common) { + //return p; + } + } + } + for (int i = reachable.size() - 1; i >= 2; i--) { + for (GraphPart p : levelMap.keySet()) { + if (levelMap.get(p) == i) { + return p; + } + } + } + for (GraphPart p : levelMap.keySet()) { + if (levelMap.get(p) == parts.size()) { + return p; + } + } + return null; + } + public GraphPart getNextNoJump(GraphPart part) { while (code.get(part.start).isJump()) { part = part.getSubParts().get(0).nextParts.get(0); @@ -403,6 +523,7 @@ public class Graph { public static List translateViaGraph(List localData, String path, GraphSource code, List alternateEntries) { Graph g = new Graph(code, alternateEntries); + g.init(); return g.translate(localData); } @@ -422,10 +543,10 @@ public class Graph { System.out.println("");*/ getPrecontinues(null, heads.get(0), loops, null); /*System.out.println(""); - for (Loop el : loops) { - System.out.println(el); - } - System.out.println("");*/ + for (Loop el : loops) { + System.out.println(el); + } + System.out.println("");//*/ List ret = printGraph(new ArrayList(), localData, stack, allParts, null, heads.get(0), null, loops); processIfs(ret); @@ -608,7 +729,7 @@ public class Graph { return false; } - protected List check(GraphSource code, List localData, List allParts, Stack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output) { + protected List check(GraphSource code, List localData, List allParts, Stack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop) { return null; } @@ -693,18 +814,61 @@ public class Graph { } private void getPrecontinues(GraphPart parent, GraphPart part, List loops, List stopPart) { - clearLoops(loops); - getPrecontinues(parent, part, loops, stopPart, 0, new ArrayList()); - clearLoops(loops); + //Note: this also marks part as precontinue when there is if + /* + while(k<10){ + if(k==7){ + trace(a); + }else{ + trace(b); + } + //precontinue + k++; + } + + */ + looploops: + for (Loop l : loops) { + if (l.loopContinue != null) { + Set uniqueRefs = new HashSet<>(); + uniqueRefs.addAll(l.loopContinue.refs); + if (uniqueRefs.size() == 2) { //only one path - from precontinue + List uniqueRefsList = new ArrayList<>(uniqueRefs); + if (uniqueRefsList.get(0).discoveredTime > uniqueRefsList.get(1).discoveredTime) { //latch node is discovered later + part = uniqueRefsList.get(0); + } else { + part = uniqueRefsList.get(1); + } + if (part == l.loopContinue) { + continue looploops; + } + + while (part.refs.size() == 1) { + if (part.refs.get(0).nextParts.size() != 1) { + continue looploops; + } + + part = part.refs.get(0); + if (part == l.loopContinue) { + break; + } + } + if (part != l.loopContinue) { + l.loopPreContinue = part; + } + } + } + } + /*clearLoops(loops); + getPrecontinues(parent, part, loops, stopPart, 0, new ArrayList()); + clearLoops(loops);*/ } private void getPrecontinues(GraphPart parent, GraphPart part, List loops, List stopPart, int level, List visited) { - boolean debugMode = false; + boolean debugMode = true; if (stopPart == null) { stopPart = new ArrayList<>(); } - - if (debugMode) { System.err.println("preco " + part); } @@ -773,35 +937,46 @@ public class Graph { break; } } - if (part.nextParts.size() == 2) { - GraphPart next = getNextCommonPart(part, loops);//part.getNextPartPath(new ArrayList()); - List stopParts2 = new ArrayList<>(stopPart); + + + List nextParts = checkPrecoNextParts(part); + if (nextParts == null) { + nextParts = part.nextParts; + } + + if (nextParts.size() == 2) { + GraphPart next = getCommonPart(nextParts, loops);//part.getNextPartPath(new ArrayList()); + List stopParts2 = new ArrayList<>(); //stopPart); if (next != null) { stopParts2.add(next); + } else if (!stopPart.isEmpty()) { + stopParts2.add(stopPart.get(stopPart.size() - 1)); } - if (next != part.nextParts.get(0)) { - getPrecontinues(part, part.nextParts.get(0), loops, next == null ? stopPart : stopParts2, level + 1, visited); + if (next != nextParts.get(0)) { + getPrecontinues(part, nextParts.get(0), loops, next == null ? stopPart : stopParts2, level + 1, visited); } - if (next != part.nextParts.get(1)) { - getPrecontinues(part, part.nextParts.get(1), loops, next == null ? stopPart : stopParts2, level + 1, visited); + if (next != nextParts.get(1)) { + getPrecontinues(part, nextParts.get(1), loops, next == null ? stopPart : stopParts2, level + 1, visited); } if (next != null) { getPrecontinues(part, next, loops, stopPart, level, visited); } } - if (part.nextParts.size() > 2) { - GraphPart next = getNextCommonPart(part, loops); + if (nextParts.size() > 2) { + GraphPart next = getCommonPart(nextParts, loops); List vis = new ArrayList<>(); - for (GraphPart p : part.nextParts) { + for (GraphPart p : nextParts) { if (vis.contains(p)) { continue; } - List stopPart2 = new ArrayList<>(stopPart); + List stopPart2 = new ArrayList<>(); //(stopPart); if (next != null) { stopPart2.add(next); + } else if (!stopPart.isEmpty()) { + stopPart2.add(stopPart.get(stopPart.size() - 1)); } - for (GraphPart p2 : part.nextParts) { + for (GraphPart p2 : nextParts) { if (p2 == p) { continue; } @@ -826,9 +1001,16 @@ public class Graph { } } - if (part.nextParts.size() == 1) { - getPrecontinues(part, part.nextParts.get(0), loops, stopPart, level, visited); + if (nextParts.size() == 1) { + getPrecontinues(part, nextParts.get(0), loops, stopPart, level, visited); } + + for (GraphPart t : part.throwParts) { + if (!visited.contains(t)) { + getPrecontinues(part, t, loops, stopPart, level, visited); + } + } + if (isLoop) { if (currentLoop.loopBreak != null) { currentLoop.phase = 2; @@ -845,11 +1027,11 @@ public class Graph { private void getLoops(GraphPart part, List loops, List stopPart) { clearLoops(loops); - getLoops(part, loops, stopPart, true, 1); + getLoops(part, loops, stopPart, true, 1, new ArrayList()); clearLoops(loops); } - private void getLoops(GraphPart part, List loops, List stopPart, boolean first, int level) { + private void getLoops(GraphPart part, List loops, List stopPart, boolean first, int level, List visited) { boolean debugMode = false; if (stopPart == null) { @@ -858,6 +1040,9 @@ public class Graph { if (part == null) { return; } + if (!visited.contains(part)) { + visited.add(part); + } if (debugMode) { System.err.println("getloops: " + part); @@ -865,7 +1050,7 @@ public class Graph { List loopContinues = getLoopsContinues(loops); Loop lastP1 = null; for (Loop el : loops) { - if ((el.phase == 1) && el.loopBreak == null) { //break not found yet + if ((el.phase == 1) && el.loopBreak == null) { //break not found yet if (el.loopContinue != part) { lastP1 = el; @@ -884,9 +1069,37 @@ public class Graph { List loops2 = new ArrayList<>(loops); loops2.remove(lastP1); if (!part.leadsTo(code, lastP1.loopContinue, loops2)) { - lastP1.breakCandidates.add(part); - lastP1.breakCandidatesLevels.add(level); - return; + /*boolean notlead=true; + for(GraphPart p:part.throwParts){ + if(p.leadsTo(code, lastP1.loopContinue, loops2)){ + notlead=false; + break; + } + }*/ + //if(notlead){ + /*boolean isthrow = false; + loopthrow: + for (GraphPart p : part.refs) { + if (p.throwParts.contains(part)) { + isthrow = true; + break; + } + for (GraphPart t : p.throwParts) { + if (t.leadsTo(code, part, loops)) { + isthrow = true; + break loopthrow; + } + } + } + if (!isthrow) {*/ + + if (lastP1.breakCandidatesLocked == 0) { + lastP1.breakCandidates.add(part); + lastP1.breakCandidatesLevels.add(level); + return; + } + //} + //} } } } @@ -918,13 +1131,13 @@ public class Graph { stopPart2.add(next); } if (next != part.nextParts.get(0)) { - getLoops(part.nextParts.get(0), loops, stopPart2, false, level + 1); + getLoops(part.nextParts.get(0), loops, stopPart2, false, level + 1, visited); } if (next != part.nextParts.get(1)) { - getLoops(part.nextParts.get(1), loops, stopPart2, false, level + 1); + getLoops(part.nextParts.get(1), loops, stopPart2, false, level + 1, visited); } if (next != null) { - getLoops(next, loops, stopPart, false, level); + getLoops(next, loops, stopPart, false, level, visited); } } if (part.nextParts.size() > 2) { @@ -945,17 +1158,29 @@ public class Graph { } } if (next != p) { - getLoops(p, loops, stopPart2, false, level + 1); + getLoops(p, loops, stopPart2, false, level + 1, visited); } } if (next != null) { - getLoops(next, loops, stopPart, false, level); + getLoops(next, loops, stopPart, false, level, visited); } } if (part.nextParts.size() == 1) { - getLoops(part.nextParts.get(0), loops, stopPart, false, level); + getLoops(part.nextParts.get(0), loops, stopPart, false, level, visited); } + List loops2 = new ArrayList<>(loops); + for (Loop l : loops2) { + l.breakCandidatesLocked++; + } + for (GraphPart t : part.throwParts) { + if (!visited.contains(t)) { + getLoops(t, loops, stopPart, false, level, visited); + } + } + for (Loop l : loops2) { + l.breakCandidatesLocked--; + } if (isLoop) { @@ -974,7 +1199,7 @@ public class Graph { backupCandidates.add(currentLoop.breakCandidates.remove(i)); } } - Set removed = new HashSet<>(); + Map removed = new HashMap<>(); /* Set newcommon=new HashSet<>(); for (GraphPart cand : currentLoop.breakCandidates) { @@ -985,7 +1210,7 @@ public class Graph { List c=new ArrayList<>(); c.add(cand); c.add(cand2); - + GraphPart common=getCommonPart(c, loops); if(common!=null){ if(!currentLoop.breakCandidates.contains(common)){ @@ -1018,7 +1243,7 @@ public class Graph { } } } - if (lev1 < lev2) { + if (lev1 <= lev2) { found = cand2; } else { found = cand; @@ -1029,12 +1254,21 @@ public class Graph { } } if (found != null) { + int maxlevel = 0; while (currentLoop.breakCandidates.contains(found)) { int ind = currentLoop.breakCandidates.indexOf(found); currentLoop.breakCandidates.remove(ind); - currentLoop.breakCandidatesLevels.remove(ind); + int lev = currentLoop.breakCandidatesLevels.remove(ind); + if (lev > maxlevel) { + maxlevel = lev; + } } - removed.add(found); + if (removed.containsKey(found)) { + if (removed.get(found) > maxlevel) { + maxlevel = removed.get(found); + } + } + removed.put(found, maxlevel); } } while ((found != null) && (currentLoop.breakCandidates.size() > 1)); @@ -1079,9 +1313,16 @@ public class Graph { winner = backupCandidates.get(backupCandidates.size() - 1); } } - for (GraphPart cand : currentLoop.breakCandidates) { + for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { + GraphPart cand = currentLoop.breakCandidates.get(i); if (cand != winner) { - removed.add(cand); + int lev = currentLoop.breakCandidatesLevels.get(i); + if (removed.containsKey(cand)) { + if (removed.get(cand) > lev) { + lev = removed.get(cand); + } + } + removed.put(cand, lev); } } currentLoop.loopBreak = winner; @@ -1096,8 +1337,8 @@ public class Graph { start = true; } } - for (GraphPart r : removed) { - getLoops(r, loops, stopPart, false, 1/*FIXME?*/); + for (GraphPart r : removed.keySet()) { + getLoops(r, loops, stopPart, false, removed.get(r), visited); } start = false; for (int l = 0; l < loops.size(); l++) { @@ -1110,7 +1351,7 @@ public class Graph { } } //currentLoop.phase = 2; - getLoops(currentLoop.loopBreak, loops, stopPart, false, level); + getLoops(currentLoop.loopBreak, loops, stopPart, false, level, visited); } } @@ -1134,7 +1375,7 @@ public class Graph { System.err.println("PART " + part); } - /*while (((part != null) && (part.getHeight() == 1)) && (code.size() > part.start) && (code.get(part.start).isJump())) { //Parts with only jump in it gets ignored + /*while (((part != null) && (part.getHeight() == 1)) && (code.size() > part.start) && (code.get(part.start).isJump())) { //Parts with only jump in it gets ignored if (part == stopPart) { return ret; @@ -1163,7 +1404,6 @@ public class Graph { return ret; } - /* if ((parent != null) && (part.path.length() < parent.path.length())) { boolean can = true; for (Loop el : loops) { @@ -1280,12 +1520,6 @@ public class Graph { ret.add(new ScriptEndItem()); return ret; } - - if (currentLoop != null) { - currentLoop.used = true; - } - - List currentRet = ret; UniversalLoopItem loopItem = null; if (isLoop) { @@ -1456,15 +1690,19 @@ public class Graph { //********************************END PART DECOMPILING if (parseNext) { - List retCheck = check(code, localData, allParts, stack, parent, part, stopPart, loops, output); + List retCheck = check(code, localData, allParts, stack, parent, part, stopPart, loops, output, currentLoop); if (retCheck != null) { if (!retCheck.isEmpty()) { currentRet.addAll(retCheck); } - return ret; + parseNext = false; + //return ret; } else { currentRet.addAll(output); } + } + if (parseNext) { + if (part.nextParts.size() == 2) { //List ignore = new ArrayList<>(); @@ -1525,17 +1763,51 @@ public class Graph { } } else if (part.nextParts.size() == 1) { - printGraph(visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet); + boolean nextloop = false; + for (Loop l : loops) { + if (part.nextParts.get(0) == l.loopContinue) { + nextloop = true; + break; + } + if (part.nextParts.get(0) == l.loopPreContinue) { + nextloop = true; + break; + } + } + if (true)//if (nextloop || (part.nextParts.get(0).refs.size() == 1)) //not join node + { + printGraph(visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet); + } } } if (isLoop) { - currentLoop.phase = 2; + LoopItem li = loopItem; boolean loopTypeFound = false; + + + boolean hasContinue = false; + processIfs(loopItem.commands); checkContinueAtTheEnd(loopItem.commands, currentLoop); + List continues = loopItem.getContinues(); + for (ContinueItem c : continues) { + if (c.loopId == currentLoop.id) { + hasContinue = true; + break; + } + } + if (!hasContinue) { + if (currentLoop.loopPreContinue != null) { + List stopContPart = new ArrayList<>(); + stopContPart.add(currentLoop.loopContinue); + GraphPart precoBackup = currentLoop.loopPreContinue; + currentLoop.loopPreContinue = null; + loopItem.commands.addAll(printGraph(visited, localData, new Stack(), allParts, null, precoBackup, stopContPart, loops)); + } + } //Loop with condition at the beginning (While) if (!loopTypeFound && (!loopItem.commands.isEmpty())) { @@ -1545,6 +1817,7 @@ public class Graph { List bodyBranch = null; boolean inverted = false; + boolean breakpos2 = false; if ((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem)) { BreakItem bi = (BreakItem) ifi.onTrue.get(0); if (bi.loopId == currentLoop.id) { @@ -1556,6 +1829,12 @@ public class Graph { if (bi.loopId == currentLoop.id) { bodyBranch = ifi.onTrue; } + } else if (loopItem.commands.size() == 2 && (loopItem.commands.get(1) instanceof BreakItem)) { + BreakItem bi = (BreakItem) loopItem.commands.get(1); + if (bi.loopId == currentLoop.id) { + bodyBranch = ifi.onTrue; + breakpos2 = true; + } } if (bodyBranch != null) { int index = ret.indexOf(loopItem); @@ -1573,6 +1852,9 @@ public class Graph { List commands = new ArrayList<>(); commands.addAll(bodyBranch); loopItem.commands.remove(0); + if (breakpos2) { + loopItem.commands.remove(0); //remove that break too + } commands.addAll(loopItem.commands); checkContinueAtTheEnd(commands, currentLoop); List finalComm = new ArrayList<>(); @@ -1706,6 +1988,7 @@ public class Graph { if (!loopTypeFound) { checkContinueAtTheEnd(loopItem.commands, currentLoop); } + currentLoop.phase = 2; GraphTargetItem replaced = checkLoop(li, localData, loops); if (replaced != li) { @@ -1726,6 +2009,9 @@ public class Graph { } + protected void checkGraph(List allBlocks) { + } + private List makeGraph(GraphSource code, List allBlocks, List alternateEntries) { HashMap> refs = code.visitCode(alternateEntries); List ret = new ArrayList<>(); @@ -1736,6 +2022,7 @@ public class Graph { e1.path = new GraphPath("e"); ret.add(makeGraph(e1, new GraphPath("e"), code, pos, pos, allBlocks, refs, visited)); } + checkGraph(allBlocks); return ret; } @@ -1972,4 +2259,48 @@ public class Graph { public List prepareBranchLocalData(List localData) { return localData; } + + protected List checkPrecoNextParts(GraphPart part) { + return null; + } + + protected GraphPart makeMultiPart(GraphPart part) { + List parts = new ArrayList<>(); + do { + parts.add(part); + if (part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) { + part = part.nextParts.get(0); + } else { + part = null; + } + } while (part != null); + if (parts.size() > 1) { + return new GraphPartMulti(parts); + } else { + return parts.get(0); + } + } + + protected List getPartItems(GraphPart part) { + List ret = new ArrayList<>(); + do { + for (int i = 0; i < part.getHeight(); i++) { + if (part.getPosAt(i) < code.size()) { + if (part.getPosAt(i) < 0) { + continue; + } + GraphSourceItem s = code.get(part.getPosAt(i)); + if (!s.isJump()) { + ret.add(s); + } + } + } + if (part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) { + part = part.nextParts.get(0); + } else { + part = null; + } + } while (part != null); + return ret; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/graph/GraphPart.java b/trunk/src/com/jpexs/decompiler/flash/graph/GraphPart.java index 53e3547cc..280b111e9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/graph/GraphPart.java +++ b/trunk/src/com/jpexs/decompiler/flash/graph/GraphPart.java @@ -39,8 +39,12 @@ public class GraphPart { public int discoveredTime; public int finishedTime; public int order; + public List throwParts = new ArrayList<>(); public int setTime(int time, List ordered, List visited) { + if (visited.contains(this)) { + return time; + } discoveredTime = time; visited.add(this); for (GraphPart next : nextParts) { @@ -97,6 +101,15 @@ public class GraphPart { } } } + for (GraphPart p : throwParts) { + if (p == part) { + return true; + } else { + if (p.leadsTo(code, part, visited, loops)) { + return true; + } + } + } return false; } diff --git a/trunk/src/com/jpexs/decompiler/flash/graph/Loop.java b/trunk/src/com/jpexs/decompiler/flash/graph/Loop.java index 91069ff12..5ef78bc3a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/graph/Loop.java +++ b/trunk/src/com/jpexs/decompiler/flash/graph/Loop.java @@ -31,11 +31,10 @@ public class Loop { public List breakCandidates = new ArrayList<>(); public List breakCandidatesLevels = new ArrayList<>(); public long id; - public boolean used = false; - public boolean finished = false; public int leadsToMark; public int reachableMark; public int phase; + public int breakCandidatesLocked = 0; public Loop(long id, GraphPart loopContinue, GraphPart loopBreak) { this.loopContinue = loopContinue;