/* * Copyright (C) 2010-2023 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.graph; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.FinalProcessLocalData; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.model.AndItem; import com.jpexs.decompiler.graph.model.BinaryOpItem; 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; import com.jpexs.decompiler.graph.model.DuplicateItem; import com.jpexs.decompiler.graph.model.ExitItem; import com.jpexs.decompiler.graph.model.FalseItem; 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; import com.jpexs.decompiler.graph.model.LoopItem; import com.jpexs.decompiler.graph.model.NotItem; import com.jpexs.decompiler.graph.model.OrItem; import com.jpexs.decompiler.graph.model.PopItem; import com.jpexs.decompiler.graph.model.PushItem; import com.jpexs.decompiler.graph.model.ScriptEndItem; import com.jpexs.decompiler.graph.model.SwitchItem; import com.jpexs.decompiler.graph.model.TernarOpItem; import com.jpexs.decompiler.graph.model.TrueItem; import com.jpexs.decompiler.graph.model.UniversalLoopItem; import com.jpexs.decompiler.graph.model.WhileItem; import com.jpexs.decompiler.graph.precontinues.GraphPrecontinueDetector; import com.jpexs.helpers.Reference; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.Stack; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author JPEXS */ public class Graph { public List heads; protected GraphSource code; private final List exceptions; public static final int SOP_USE_STATIC = 0; public static final int SOP_SKIP_STATIC = 1; public static final int SOP_REMOVE_STATIC = 2; private boolean debugPrintAllParts = false; private boolean debugPrintLoopList = false; private boolean debugGetLoops = false; private boolean debugPrintGraph = false; protected boolean debugDoNotProcess = false; private static final Logger logger = Logger.getLogger(Graph.class.getName()); public GraphSource getGraphCode() { return code; } public LinkedHashMap getSubGraphs() { return new LinkedHashMap<>(); } public Graph(GraphSource code, List exceptions) { this.code = code; this.exceptions = exceptions; } public void init(BaseLocalData localData) throws InterruptedException { if (heads != null) { return; } heads = makeGraph(code, new ArrayList<>(), exceptions); int time = 1; List ordered = new ArrayList<>(); List visited = new ArrayList<>(); for (GraphPart head : heads) { time = head.setTime(time, ordered, visited); head.setNumblocks(1); } } /** * Calculates time of closing the node. The node is closed when all its * input edges are already visited (not counting back edges), then all its * output edges are processed. * * This time is useful when sorting nodes according their occurence in * getMostCommonPart method - used for switch detection * * @param loops Already calculated loops to get backedges from. */ private void calculateClosedTime(List loops) { ArrayDeque openedNodes = new ArrayDeque<>(); Set closedNodes = new HashSet<>(); Set visitedEdges = new HashSet<>(); for (GraphPart h : heads) { for (GraphPart r : h.refs) { visitedEdges.add(new LevelMapEdge(r, h)); } } for (Loop el : loops) { for (GraphPart be : el.backEdges) { visitedEdges.add(new LevelMapEdge(be, el.loopContinue)); } } int closedTime = 1; for (GraphPart h : heads) { openedNodes.add(h); loopopened: while (!openedNodes.isEmpty()) { GraphPart part = openedNodes.remove(); if (closedNodes.contains(part)) { continue; } for (GraphPart r : part.refs) { if (!visitedEdges.contains(new LevelMapEdge(r, part))) { continue loopopened; } } for (GraphPart n : part.nextParts) { openedNodes.add(n); visitedEdges.add(new LevelMapEdge(part, n)); } closedNodes.add(part); part.closedTime = closedTime++; //System.err.println("part " + part + " closedTime: " + part.closedTime); } } } private class LevelMapEdge { public GraphPart from; public GraphPart to; public LevelMapEdge(GraphPart from, GraphPart to) { this.from = from; this.to = to; } @Override public int hashCode() { int hash = 7; hash = 31 * hash + Objects.hashCode(this.from); hash = 31 * hash + Objects.hashCode(this.to); return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final LevelMapEdge other = (LevelMapEdge) obj; //use == comparison, not equals, as some parts may be equal // (the refs to throw targets have -1,-1 as start/end) if (this.from != other.from) { return false; } return this.to == other.to; } } public List getExceptions() { return exceptions; } protected static void populateParts(GraphPart part, Set allParts) { if (allParts.contains(part)) { return; } allParts.add(part); for (GraphPart p : part.nextParts) { populateParts(p, allParts); } } public GraphPart deepCopy(GraphPart part) { return deepCopy(part, new HashMap<>()); } private GraphPart deepCopy(GraphPart part, Map copies) { GraphPart copy = copies.get(part); if (copy != null) { return copy; } copy = new GraphPart(part.start, part.end); copy.path = part.path; copies.put(part, copy); copy.nextParts = new ArrayList<>(); for (int i = 0; i < part.nextParts.size(); i++) { copy.nextParts.add(deepCopy(part.nextParts.get(i), copies)); } for (int i = 0; i < part.refs.size(); i++) { copy.refs.add(deepCopy(part.refs.get(i), copies)); } return copy; } public void resetGraph(GraphPart part, Set visited) { if (visited.contains(part)) { return; } visited.add(part); int pos = 0; for (GraphPart p : part.nextParts) { if (!visited.contains(p)) { p.path = part.path.sub(pos, p.end); } resetGraph(p, visited); pos++; } } protected void getReachableParts(BaseLocalData localData, GraphPart part, LinkedHashSet ret, List loops, List throwStates) { // use LinkedHashSet to preserve order getReachableParts(localData, part, ret, loops, throwStates, true); } private void getReachableParts(BaseLocalData localData, GraphPart part, LinkedHashSet ret, List loops, List throwStates, boolean first) { // todo: honfika: why call with first = true parameter always? Stack stack = new Stack<>(); GraphPartQueue queue = new GraphPartQueue(); queue.add(part); stack.add(queue); stacknext: while (!stack.isEmpty()) { queue = stack.peek(); if (!queue.isEmpty()) { part = queue.remove(); } else if (queue.currentLoop != null) { Loop cLoop = queue.currentLoop; part = cLoop.loopBreak; queue.currentLoop = null; if (ret.contains(part)) { continue; } ret.add(part); cLoop.reachableMark = 2; } else { stack.pop(); continue; } for (Loop l : loops) { l.reachableMark = 0; } Loop currentLoop = null; for (Loop l : loops) { if ((l.phase == 1) || (l.reachableMark == 1)) { if (l.loopContinue == part) { continue stacknext; } if (l.loopBreak == part) { continue stacknext; } if (l.loopPreContinue == part) { continue stacknext; } } if (l.reachableMark == 0) { if (l.loopContinue == part) { l.reachableMark = 1; currentLoop = l; } } } GraphPartQueue newParts = new GraphPartQueue(); List nextParts = new ArrayList<>(getNextParts(localData, part)); for (ThrowState ts : throwStates) { if (ts.state != 1) { if (ts.throwingParts.contains(part)) { newParts.add(ts.targetPart); } } } loopnext: for (GraphPart nextRaw : nextParts) { GraphPart next = checkPart(null, localData, part, nextRaw, null); if (next == null) { continue; } for (Loop l : loops) { if ((l.phase == 1) || (l.reachableMark == 1)) { if (l.loopContinue == next) { continue loopnext; } if (l.loopBreak == next) { continue loopnext; } if (l.loopPreContinue == next) { continue loopnext; } } } if (!ret.contains(next)) { newParts.add(next); } } ret.addAll(newParts); if (currentLoop != null && currentLoop.loopBreak != null) { newParts.currentLoop = currentLoop; } if (!newParts.isEmpty() || newParts.currentLoop != null) { stack.add(newParts); } } } public GraphPart getNextCommonPart(BaseLocalData localData, GraphPart part, List loops, List throwStates) throws InterruptedException { return getCommonPart(localData, part, getNextParts(localData, part), loops, throwStates); } //TODO: Make this faster! public GraphPart getCommonPart(BaseLocalData localData, GraphPart prev, List parts, List loops, List throwStates) throws InterruptedException { if (parts.isEmpty()) { return null; } List loopContinues = new ArrayList<>(); for (Loop l : loops) { if (l.phase == 1) { loopContinues.add(l.loopContinue); if (l.loopPreContinue != null) { 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(localData, this, code, p, loops, throwStates, false /*!!THROW*/)) { common = false; break; } } if (common) { return p; } } List> reachable = new ArrayList<>(); for (GraphPart p : parts) { LinkedHashSet r1 = new LinkedHashSet<>(); getReachableParts(localData, p, r1, loops, throwStates); r1.add(p); reachable.add(r1); } Set first = reachable.get(0); for (GraphPart p : first) { boolean common = true; for (Set r : reachable) { if (!r.contains(p)) { common = false; break; } } if (common) { if (loopContinues.contains(p)) { return null; } return p; } } return null; } public GraphPart getMostCommonPart(BaseLocalData localData, List parts, List loops, List throwStates, List stopPart) throws InterruptedException { if (parts.isEmpty()) { return null; } Set s = new HashSet<>(parts); //unique parts = new ArrayList<>(s); //make local copy List loopContinues = new ArrayList<>(); for (Loop l : loops) { if (l.phase == 1) { loopContinues.add(l.loopContinue); loopContinues.add(l.loopPreContinue); } } Map> reachable = new HashMap<>(); Set allReachable = new LinkedHashSet<>(); for (GraphPart p : parts) { LinkedHashSet r1 = new LinkedHashSet<>(); getReachableParts(localData, p, r1, loops, throwStates); Set r2 = new LinkedHashSet<>(); r2.add(p); r2.addAll(r1); reachable.put(p, r2); allReachable.add(p); allReachable.addAll(r1); } Comparator comparator = new Comparator() { @Override public int compare(PartCommon o1, PartCommon o2) { int levelCompare = o2.level - o1.level; if (levelCompare == 0) { try { if (o1.part.leadsTo(localData, Graph.this, code, o2.part, loops, throwStates, false)) { return -1; } if (o2.part.leadsTo(localData, Graph.this, code, o1.part, loops, throwStates, false)) { return 1; } return 0; } catch (InterruptedException ex) { //ignore return 0; } //return o1.part.discoveredTime - o2.part.discoveredTime; } else { return levelCompare; } } }; Set commonSet = new TreeSet<>(); for (GraphPart r : allReachable) { if (loopContinues.contains(r)) { continue; } boolean common = true; int commonLevel = 0; for (GraphPart p : parts) { if (p == r) { commonLevel++; continue; } if (!reachable.get(p).contains(r)) { common = false; } else { commonLevel++; } } if (common) { Stack toProcess = new Stack<>(); Set visited = new HashSet<>(); toProcess.addAll(parts); loopprocess: while (!toProcess.isEmpty()) { GraphPart p = toProcess.pop(); if (p == r) { continue; } if (loopContinues.contains(p)) { continue; } if (visited.contains(p)) { continue; } visited.add(p); for (GraphPart n : p.nextParts) { if (n == r) { continue; } if (loopContinues.contains(n)) { continue; } if (visited.contains(n)) { continue; } if (!n.leadsTo(localData, this, code, r, loops, throwStates, false)) { common = false; break loopprocess; } } toProcess.addAll(p.nextParts); } if (common) { if (debugPrintLoopList) { System.err.println("all time common: " + r); } return r; } } /*if (commonLevel > maxCommonLevel) { maxCommonPart = r; maxCommonLevel = commonLevel; commonSet.add(e) }*/ commonSet.add(new PartCommon(r, commonLevel)); } /*Set partsLeadingToStopPart = new LinkedHashSet<>(); if (stopPart != null) { for (GraphPart p : parts) { for (GraphPart sp : stopPart) { if (sp == p || p.leadsTo(localData, this, code, sp, new ArrayList(), throwStates, false)) { partsLeadingToStopPart.add(p); } } } } if (debugPrintLoopList) { System.err.println("commonset:"); for (PartCommon pc : commonSet) { System.err.println("- " + pc); } System.err.println("parts:"); for (GraphPart p : parts) { System.err.println("- " + p); } if (stopPart != null) { System.err.println("stopparts:"); for (GraphPart p : stopPart) { System.err.println("- " + p); } } System.err.println("partsLeadingToStopPart:"); for (GraphPart p : partsLeadingToStopPart) { System.err.println("- " + p); } }*/ loopc: for (PartCommon pc : commonSet) { /*for (GraphPart p : partsLeadingToStopPart) { if (p != pc.part && !p.leadsTo(localData, this, code, pc.part, loops, throwStates, false)) { if (debugPrintLoopList) { System.err.println("ignoring " + pc.part + ", " + p + " does not lead to it"); } continue loopc; } }*/ if (pc.level <= 1) { return null; } if (debugPrintLoopList) { StringBuilder sb = new StringBuilder(); for (GraphPart p : parts) { sb.append(" "); sb.append(p.toString()); } System.err.println("most common part of" + sb.toString() + " is " + pc.part); } return pc.part; } return null; } private class PartCommon implements Comparable { public GraphPart part; public int level; public PartCommon(GraphPart part, int level) { this.part = part; this.level = level; } @Override public int compareTo(PartCommon o) { int ret = o.level - level; if (ret == 0) { ret = part.closedTime - o.part.closedTime; if (ret == 0) { //some nodes may be split in half and thus have same closedTime - like in try..catch return part.start - o.part.start; } } return ret; } @Override public String toString() { return "" + part.toString() + " (level=" + level + ")"; } @Override public int hashCode() { int hash = 5; hash = 71 * hash + Objects.hashCode(this.part); hash = 71 * hash + this.level; return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final PartCommon other = (PartCommon) obj; if (this.level != other.level) { return false; } return Objects.equals(this.part, other.part); } } public GraphPart getNextNoJump(GraphPart part, BaseLocalData localData) { while (code.get(part.start).isJump()) { part = part.getSubParts().get(0).nextParts.get(0); } /*localData = prepareBranchLocalData(localData); TranslateStack st = new TranslateStack(); List output=new ArrayList<>(); GraphPart startPart = part; for (int i = part.start; i <= part.end; i++) { GraphSourceItem src = code.get(i); if (src.isJump()) { part = getNextParts(localData, part).get(0); if(st.isEmpty()){ startPart = part; } i = part.start - 1; continue; } try{ src.translate(localData, st, output, SOP_USE_STATIC, ""); }catch(Exception ex){ return startPart; } if(!output.isEmpty()){ return startPart; } }*/ return part; } public static List translateViaGraph(BaseLocalData localData, String path, GraphSource code, List exceptions, int staticOperation) throws InterruptedException { Graph g = new Graph(code, exceptions); g.init(localData); return g.translate(localData, staticOperation, path); } protected void afterPopupateAllParts(Set allParts) { } protected List getThrowStates(BaseLocalData localData, Set allParts) { return new ArrayList<>(); } public List translate(BaseLocalData localData, int staticOperation, String path) throws InterruptedException { Set allParts = new HashSet<>(); for (GraphPart head : heads) { populateParts(head, allParts); } afterPopupateAllParts(allParts); if (debugPrintAllParts) { System.err.println("parts:"); for (GraphPart p : allParts) { System.err.print(p); if (!getNextParts(localData, p).isEmpty()) { System.err.print(", next: "); } boolean first = true; for (GraphPart n : getNextParts(localData, p)) { if (!first) { System.err.print(","); } System.err.print(n); first = false; } System.err.println(""); } System.err.println("/parts"); } TranslateStack stack = new TranslateStack(path); List throwStates = getThrowStates(localData, allParts); beforeGetLoops(localData, path, allParts, throwStates); List loops = new ArrayList<>(); getLoops(localData, heads.get(0), loops, throwStates, null); afterGetLoops(localData, path, allParts); //TODO: Make getPrecontinues faster getBackEdges(localData, loops, throwStates); calculateClosedTime(loops); new GraphPrecontinueDetector().detectPrecontinues(heads, allParts, loops, throwStates); if (debugPrintLoopList) { System.err.println(""); for (Loop el : loops) { System.err.println(el); } System.err.println(""); } /*System.err.println(""); for (Loop el : loops) { System.err.println(el); } System.err.println("");//*/ List gotos = new ArrayList<>(); List ret = printGraph(gotos, new HashMap<>(), new HashMap<>(), new HashSet<>(), localData, stack, allParts, null, heads.get(0), null, null, loops, throwStates, staticOperation, path); if (localData.secondPassData == null) { SecondPassData secondPassData = prepareSecondPass(ret); if (secondPassData == null) { if (!localData.allSwitchParts.isEmpty()) { secondPassData = new SecondPassData(); secondPassData.allSwitchParts = localData.allSwitchParts; throw new SecondPassException(secondPassData); } } else { if (secondPassData.getClass() == SecondPassData.class && localData.allSwitchParts.isEmpty()) { //nothing } else { secondPassData.allSwitchParts = localData.allSwitchParts; throw new SecondPassException(secondPassData); } } } processIfGotos2(new ArrayList<>(), gotos, ret, ret); processIfGotos(gotos, ret, ret); Map usages = new HashMap<>(); Map lastUsage = new HashMap<>(); for (GotoItem gi : gotos) { if (!usages.containsKey(gi.labelName)) { usages.put(gi.labelName, 0); } usages.put(gi.labelName, usages.get(gi.labelName) + 1); lastUsage.put(gi.labelName, gi); } for (String labelName : usages.keySet()) { logger.log(Level.FINE, "usage - {0}: {1}", new Object[]{labelName, usages.get(labelName)}); if (usages.get(labelName) == 1) { lastUsage.get(labelName).labelName = null; } } expandGotos(ret); processIfs(ret); finalProcessStack(stack, ret, path); makeAllCommands(ret, stack); finalProcessAll(ret, 0, getFinalData(localData, loops, throwStates), path); return ret; } protected SecondPassData prepareSecondPass(List list) { return null; } protected void processSwitches(List list) { processSwitches(list, -1); } /* while(something){ switch(xx){ case 1: trace("1"); continue; case 2: trace("2"); continue; case 3: break; default: continue; } item } => while(something){ switch(xx){ case 1: trace("1"); break; case 2: trace("2"); break; case 3: item break; default: break; } } This will fix precontinue handler which detects multiple continues */ protected void processSwitches(List list, long lastLoopId) { loopi: for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); if (item instanceof SwitchItem) { SwitchItem swi = (SwitchItem) item; Set allItems = swi.getAllSubItemsRecursively(); int breakCount = 0; for (GraphTargetItem it : allItems) { if (it instanceof BreakItem) { BreakItem br = (BreakItem) it; if (br.loopId == swi.loop.id) { breakCount++; if (breakCount > 1) { continue loopi; } } } } if (!swi.caseCommands.isEmpty()) { List lastCommands = swi.caseCommands.get(swi.caseCommands.size() - 1); if (lastCommands.isEmpty() && breakCount == 1) { continue loopi; } if (breakCount == 1 && !(lastCommands.get(lastCommands.size() - 1) instanceof ContinueItem) && !(lastCommands.get(lastCommands.size() - 1) instanceof ExitItem)) { continue loopi; } } int breakCaseIndex = -1; for (int c = 0; c < swi.caseCommands.size(); c++) { List commands = swi.caseCommands.get(c); if (!commands.isEmpty()) { if (commands.get(commands.size() - 1) instanceof BreakItem) { BreakItem br = (BreakItem) commands.get(commands.size() - 1); if (br.loopId == swi.loop.id) { breakCaseIndex = c; break; } } } if (c == swi.caseCommands.size() - 1) { if (commands.isEmpty()) { breakCaseIndex = c; break; } if (!(commands.get(commands.size() - 1) instanceof ContinueItem) && !(commands.get(commands.size() - 1) instanceof ExitItem)) { breakCaseIndex = c; break; } } } boolean hasContinues = false; for (int c = 0; c < swi.caseCommands.size(); c++) { List commands = swi.caseCommands.get(c); if (!commands.isEmpty()) { if (commands.get(commands.size() - 1) instanceof ContinueItem) { ContinueItem cnt = (ContinueItem) commands.get(commands.size() - 1); if (cnt.loopId == lastLoopId) { hasContinues = true; commands.set(commands.size() - 1, new BreakItem(null, null, swi.loop.id)); if (c == swi.caseCommands.size() - 1) { if (commands.size() == 1) { commands.remove(0); } } } } } } if (hasContinues && breakCaseIndex > -1 && i + 1 < list.size()) { List toAdd = new ArrayList<>(); boolean continueOnEnd = list.get(list.size() - 1) instanceof ContinueItem; for (int j = i + 1; j < list.size() - (continueOnEnd ? 1 : 0); j++) { toAdd.add(list.remove(i + 1)); } List targetCommands = swi.caseCommands.get(breakCaseIndex); if (!targetCommands.isEmpty() && (targetCommands.get(targetCommands.size() - 1) instanceof BreakItem)) { targetCommands.remove(targetCommands.size() - 1); } targetCommands.addAll(toAdd); if (toAdd.isEmpty() || (!((toAdd.get(toAdd.size() - 1) instanceof ExitItem) || (toAdd.get(toAdd.size() - 1) instanceof BreakItem)))) { targetCommands.add(new BreakItem(null, null, swi.loop.id)); } } } } } protected FinalProcessLocalData getFinalData(BaseLocalData localData, List loops, List throwStates) { return new FinalProcessLocalData(loops); } protected void beforeGetLoops(BaseLocalData localData, String path, Set allParts, List throwStates) throws InterruptedException { } protected void afterGetLoops(BaseLocalData localData, String path, Set allParts) throws InterruptedException { } protected boolean isPartEmpty(GraphPart part) { return false; } private String pathToString(Collection list) { List strs = new ArrayList<>(); for (Object p : list) { strs.add(p.toString()); } return "[" + String.join(", ", strs) + "]"; } private List getUnicatePartList(List list) { List result = new ArrayList<>(); for (GraphPart p : list) { if (!result.contains(p)) { result.add(p); } } return result; } private List getUnicateRefsNoThrow(GraphPart part, Set throwEdges) { List result = new ArrayList<>(); for (GraphPart r : part.refs) { GraphPartEdge edge = new GraphPartEdge(r, part); if (!throwEdges.contains(edge)) { result.add(r); } } return getUnicatePartList(result); } private List getUsableRefs(GraphPart g, List loops, List throwStates) { List ret = getUnicatePartList(g.refs); for (Loop el : loops) { for (GraphPart be : el.backEdges) { if (ret.contains(be)) { ret.remove(be); } } } return ret; } /**/ //if (getNextParts(localData, ref)) private void getBackEdges(BaseLocalData localData, List loops, List throwStates) throws InterruptedException { clearLoops(loops); for (Loop el : loops) { el.backEdges.clear(); Set uniqueRefs = new HashSet<>(el.loopContinue.refs); for (GraphPart r : uniqueRefs) { if (el.loopContinue.leadsTo(localData, this, code, r, loops, throwStates, true)) { el.backEdges.add(r); } } el.phase = 1; } clearLoops(loops); } public void finalProcessStack(TranslateStack stack, List output, String path) { } private void finalProcessAll(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { if (debugDoNotProcess) { return; } finalProcess(list, level, localData, path); for (GraphTargetItem item : list) { if (item instanceof Block) { List> subs = ((Block) item).getSubs(); for (List sub : subs) { finalProcessAll(sub, level + 1, localData, path); } } } finalProcessAfter(list, level, localData, path); } private boolean processSubBlk(Block b, GraphTargetItem replacement) { boolean allSubPush = true; boolean atleastOne = false; for (List sub : b.getSubs()) { if (!sub.isEmpty()) { int lastPos = sub.size() - 1; GraphTargetItem last = sub.get(sub.size() - 1); GraphTargetItem br = null; if ((last instanceof BreakItem) && (sub.size() >= 2)) { br = last; lastPos--; last = sub.get(lastPos); } if (last instanceof Block) { if (!processSubBlk((Block) last, replacement)) { allSubPush = false; } else { atleastOne = true; } } else if (last instanceof PushItem) { if (replacement != null) { GraphTargetItem e2 = (((GraphTargetItem) replacement).clone()); e2.value = last.value; sub.set(lastPos, e2); if (br != null) { sub.remove(sub.size() - 1); } } atleastOne = true; } else if (!(last instanceof ExitItem)) { allSubPush = false; } } } return allSubPush && atleastOne; } protected void finalProcessAfter(List list, int level, FinalProcessLocalData localData, String path) { if (list.size() >= 2) { if (list.get(list.size() - 1) instanceof ExitItem) { ExitItem e = (ExitItem) list.get(list.size() - 1); if (list.get(list.size() - 1).value instanceof PopItem) { if (list.get(list.size() - 2) instanceof Block) { Block b = (Block) list.get(list.size() - 2); if (processSubBlk(b, (GraphTargetItem) e)) { list.remove(list.size() - 1); } } } } } } protected void finalProcess(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { //For detection based on debug line information boolean[] toDelete = new boolean[list.size()]; for (int i = 0; i < list.size(); i++) { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } GraphTargetItem itemI = list.get(i); if (itemI instanceof ForItem) { ForItem fori = (ForItem) itemI; int exprLine = fori.getLine(); if (exprLine > 0) { List forFirstCommands = new ArrayList<>(); for (int j = i - 1; j >= 0; j--) { if (list.get(j).getLine() == exprLine && !(list.get(j) instanceof LoopItem /*to avoid recursion and StackOverflow*/)) { forFirstCommands.add(0, list.get(j)); toDelete[j] = true; } else { break; } } fori.firstCommands.addAll(0, forFirstCommands); } } if (itemI instanceof WhileItem) { WhileItem whi = (WhileItem) itemI; int whileExprLine = whi.getLine(); if (whileExprLine > 0) { List forFirstCommands = new ArrayList<>(); List forFinalCommands = new ArrayList<>(); for (int j = i - 1; j >= 0; j--) { GraphTargetItem itemJ = list.get(j); if (itemJ.getLine() == whileExprLine && !(itemJ instanceof LoopItem /*to avoid recursion and StackOverflow*/)) { forFirstCommands.add(0, itemJ); toDelete[j] = true; } else { break; } } for (int j = whi.commands.size() - 1; j >= 0; j--) { if (whi.commands.get(j).getLine() == whileExprLine && !(whi.commands.get(j) instanceof LoopItem /*to avoid recursion and StackOverflow*/)) { forFinalCommands.add(0, whi.commands.remove(j)); } else { break; } } if (!forFirstCommands.isEmpty() || !forFinalCommands.isEmpty()) { //Do not allow more than 2 first/final commands, since it can be obfuscated if (forFirstCommands.size() > 2 || forFinalCommands.size() > 2) { //put it back for (int k = 0; k < forFirstCommands.size(); k++) { toDelete[i - 1 - k] = false; } whi.commands.addAll(forFinalCommands); //put it back } else if (whi.commands.isEmpty() && forFirstCommands.isEmpty()) { //it would be for(;expr;commands) {} which looks better as while(expr){commands} whi.commands.addAll(forFinalCommands); //put it back } else { GraphTargetItem lastExpr = whi.expression.remove(whi.expression.size() - 1); forFirstCommands.addAll(whi.expression); list.set(i, new ForItem(whi.getSrc(), whi.getLineStartItem(), whi.loop, forFirstCommands, lastExpr, forFinalCommands, whi.commands)); } } } } } for (int i = toDelete.length - 1; i >= 0; i--) { if (toDelete[i]) { list.remove(i); } } } private void expandGotos(List list) { if (!list.isEmpty() && (list.get(list.size() - 1) instanceof GotoItem)) { GotoItem gi = (GotoItem) list.get(list.size() - 1); if (gi.targetCommands != null) { list.remove(gi); if (gi.labelName != null) { list.add(new LabelItem(null, gi.lineStartItem, gi.labelName)); } list.addAll(gi.targetCommands); } } for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); if (item instanceof Block) { List> subs = ((Block) item).getSubs(); for (List sub : subs) { expandGotos(sub); } } } } private void processIfGotos2(List> alreadyProcessedBlocks, List allGotos, List list, List rootList) { for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); if (item instanceof Block) { List> subs = ((Block) item).getSubs(); for (List sub : subs) { processIfGotos2(alreadyProcessedBlocks, allGotos, sub, rootList); } } if (item instanceof GotoItem) { GotoItem gi = (GotoItem) item; loopblk: for (List blk : alreadyProcessedBlocks) { for (int j = 0; j < blk.size(); j++) { GraphTargetItem ti = blk.get(j); if (ti instanceof LabelItem) { LabelItem label = (LabelItem) ti; if (label.labelName.equals(gi.labelName)) { if (blk.get(blk.size() - 1) instanceof ExitItem) { int siz = blk.size(); for (int k = 0; k < siz - j; k++) { list.add(i + 1 + k, blk.remove(j)); } blk.add(j, list.remove(i)); } break loopblk; } } } } } } alreadyProcessedBlocks.add(list); } /** * if (xxx) { y ; goto a } else { z ; goto a } * * => * * if (xxx) { y } else { z } goto a * */ private void processIfGotos(List allGotos, List list, List rootList) { for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); if (item instanceof Block) { List> subs = ((Block) item).getSubs(); for (List sub : subs) { processIfGotos(allGotos, sub, rootList); } } if (item instanceof IfItem) { IfItem ii = (IfItem) item; if (!ii.onTrue.isEmpty() && !ii.onFalse.isEmpty()) { if (ii.onTrue.get(ii.onTrue.size() - 1) instanceof GotoItem) { if (ii.onFalse.get(ii.onFalse.size() - 1) instanceof GotoItem) { for (int j = i + 1; j < list.size(); j++) { list.remove(i + 1); } } } } if (!ii.onTrue.isEmpty() && !ii.onFalse.isEmpty()) { if (ii.onTrue.get(ii.onTrue.size() - 1) instanceof GotoItem) { if (ii.onFalse.get(ii.onFalse.size() - 1) instanceof GotoItem) { GotoItem gotoOnTrue = (GotoItem) ii.onTrue.get(ii.onTrue.size() - 1); GotoItem gotoOnFalse = (GotoItem) ii.onFalse.get(ii.onFalse.size() - 1); if (gotoOnTrue.labelName.equals(gotoOnFalse.labelName)) { String labelOnTrue = gotoOnTrue.labelName; String labelOnFalse = gotoOnFalse.labelName; if (labelOnTrue != null && labelOnFalse != null) { if (labelOnTrue.equals(labelOnFalse)) { GotoItem gotoMerged; GotoItem gotoRemoved; if (gotoOnTrue.targetCommands != null) { gotoMerged = gotoOnTrue; gotoRemoved = gotoOnFalse; } else { gotoMerged = gotoOnFalse; gotoRemoved = gotoOnTrue; } ii.onTrue.remove(ii.onTrue.size() - 1); ii.onFalse.remove(ii.onFalse.size() - 1); list.add(i + 1, gotoMerged); allGotos.remove(gotoRemoved); } } } } } } if (!ii.onTrue.isEmpty() && ii.onFalse.isEmpty()) { if (ii.onTrue.get(ii.onTrue.size() - 1) instanceof GotoItem) { GotoItem g1 = (GotoItem) ii.onTrue.get(ii.onTrue.size() - 1); if (i + 1 < list.size()) { if (list.get(i + 1) instanceof GotoItem) { GotoItem g2 = (GotoItem) list.get(i + 1); if (g1.labelName.equals(g2.labelName)) { ii.onTrue.remove(ii.onTrue.size() - 1); } } } } } } } } protected void processIfs(List list) { if (debugDoNotProcess) { return; } for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); if ((item instanceof LoopItem) && (item instanceof Block)) { List> subs = ((Block) item).getSubs(); for (List sub : subs) { processIfs(sub); checkContinueAtTheEnd(sub, ((LoopItem) item).loop); } } else if (item instanceof Block) { List> subs = ((Block) item).getSubs(); for (List sub : subs) { processIfs(sub); } } if (item instanceof IfItem) { IfItem ifi = (IfItem) item; List onTrue = ifi.onTrue; List onFalse = ifi.onFalse; if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) { if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) { if (onFalse.get(onFalse.size() - 1) instanceof ContinueItem) { if (((ContinueItem) onTrue.get(onTrue.size() - 1)).loopId == ((ContinueItem) onFalse.get(onFalse.size() - 1)).loopId) { onTrue.remove(onTrue.size() - 1); list.add(i + 1, onFalse.remove(onFalse.size() - 1)); } } } } if (i < list.size() - 1) { if (list.get(i + 1) instanceof ContinueItem) { if ((!onTrue.isEmpty()) && (onFalse.isEmpty())) { if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) { if (((ContinueItem) onTrue.get(onTrue.size() - 1)).loopId == ((ContinueItem) list.get(i + 1)).loopId) { onTrue.remove(onTrue.size() - 1); } } } } } /* if (xx) { A; continue } else { B; break/exit/continue; } => if (xx) { A; } else { B; break/exit/continue; } continue; */ if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) { if ((onFalse.get(onFalse.size() - 1) instanceof ExitItem) || (onFalse.get(onFalse.size() - 1) instanceof BreakItem)) { if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) { list.add(i + 1, onTrue.remove(onTrue.size() - 1)); } } } /* if (xx) { A; break/exit/continue; } else { B; } => if (xx) { A; break/exit/continue; } B; */ if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) { GraphTargetItem last = onTrue.get(onTrue.size() - 1); if ((last instanceof ExitItem) || (last instanceof ContinueItem) || (last instanceof BreakItem)) { list.addAll(i + 1, onFalse); onFalse.clear(); } } //Prefer continue/return/throw/break in onTrue rather than onFalse if (!onFalse.isEmpty() && ((onFalse.get(onFalse.size() - 1) instanceof BreakItem) || (onFalse.get(onFalse.size() - 1) instanceof ExitItem) || (onFalse.get(onFalse.size() - 1) instanceof ContinueItem)) && !(onFalse.get(onFalse.size() - 1) instanceof ScriptEndItem) && (onTrue.isEmpty() || !((onTrue.get(onTrue.size() - 1) instanceof BreakItem) || (onTrue.get(onTrue.size() - 1) instanceof ExitItem) || (onTrue.get(onTrue.size() - 1) instanceof ContinueItem)))) { ifi.expression = ifi.expression.invert(null); ifi.onTrue = onFalse; ifi.onFalse = new ArrayList<>(); list.addAll(i + 1, onTrue); onFalse = ifi.onFalse; onTrue = ifi.onTrue; } if (i < list.size() - 1) { if ((list.get(i + 1) instanceof BreakItem) && onFalse.isEmpty()) { if (!onTrue.isEmpty() && (onTrue.get(onTrue.size() - 1) instanceof ContinueItem)) { ifi.expression = ifi.expression.invert(null); list.addAll(i + 2, ifi.onTrue); ifi.onTrue.clear(); ifi.onTrue.add(list.remove(i + 1)); } } } //Switch break onFalse and continue onTrue if (i < list.size() - 1) { if ((list.get(list.size() - 1) instanceof ExitItem) || (list.get(list.size() - 1) instanceof BreakItem)) { if (onFalse.isEmpty() && !onTrue.isEmpty() && (onTrue.get(onTrue.size() - 1) instanceof ContinueItem)) { ifi.expression = ifi.expression.invert(null); List onTrueItems = new ArrayList<>(); for (int j = i; j < list.size(); j++) { onTrueItems.add(list.remove(i + 1)); } list.addAll(i + 1, ifi.onTrue); ifi.onTrue.clear(); ifi.onTrue.addAll(onTrueItems); } } } } } //Same continues in onTrue and onFalse gets continue on parent level } protected List getLoopsContinuesPreAndBreaks(List loops) { List ret = new ArrayList<>(); for (Loop l : loops) { if (l.loopContinue != null) { ret.add(l.loopContinue); } if (l.loopPreContinue != null) { ret.add(l.loopPreContinue); } if (l.loopBreak != null) { ret.add(l.loopBreak); } } return ret; } protected List getLoopsContinuesAndPre(List loops) { List ret = new ArrayList<>(); for (Loop l : loops) { if (l.loopContinue != null) { ret.add(l.loopContinue); } if (l.loopPreContinue != null) { ret.add(l.loopPreContinue); } } return ret; } protected List getLoopsContinues(List loops) { List ret = new ArrayList<>(); for (Loop l : loops) { if (l.loopContinue != null) { ret.add(l.loopContinue); } /*if (l.loopPreContinue != null) { ret.add(l.loopPreContinue); }*/ } return ret; } private void checkContinueAtTheEnd(List commands, Loop loop) { if (!commands.isEmpty()) { int i = commands.size() - 1; for (; i >= 0; i--) { if (commands.get(i) instanceof ContinueItem) { continue; } if (commands.get(i) instanceof BreakItem) { continue; } break; } if (i < commands.size() - 1) { for (int k = i + 2; k < commands.size(); k++) { commands.remove(k); } } if (commands.get(commands.size() - 1) instanceof ContinueItem) { if (((ContinueItem) commands.get(commands.size() - 1)).loopId == loop.id) { commands.remove(commands.size() - 1); } } } } protected boolean isEmpty(List output) { if (output.isEmpty()) { return true; } if (output.size() == 1) { if (output.get(0) instanceof MarkItem) { return true; } } return false; } protected List check(List currentRet, List foundGotos, Map> partCodes, Map partCodePos, Set visited, GraphSource code, BaseLocalData localData, Set allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List stopPartKind, List loops, List throwStates, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { return null; } protected GraphPart checkPartWithOutput(List output, TranslateStack stack, BaseLocalData localData, GraphPart prev, GraphPart part, Set allParts) { return checkPart(stack, localData, prev, part, allParts); } protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart prev, GraphPart part, Set allParts) { return part; } //@SuppressWarnings("unchecked") protected GraphTargetItem translatePartGetStack(BaseLocalData localData, GraphPart part, TranslateStack stack, int staticOperation) throws InterruptedException, GraphPartChangeException { stack = (TranslateStack) stack.clone(); translatePart(localData, part, stack, staticOperation, null); return stack.pop(); } protected List translatePart(BaseLocalData localData, GraphPart part, TranslateStack stack, int staticOperation, String path) throws InterruptedException, GraphPartChangeException { List sub = part.getSubParts(); List ret = new ArrayList<>(); int end; for (GraphPart p : sub) { if (p.end == -1) { p.end = code.size() - 1; } if (p.start == code.size()) { continue; } else if (p.end == code.size()) { p.end--; } end = p.end; int start = p.start; ret.addAll(code.translatePart(this, part, localData, stack, start, end, staticOperation, path)); } return ret; } private void markBranchEnd(List items) { if (!items.isEmpty()) { if (items.get(items.size() - 1) instanceof BreakItem) { return; } if (items.get(items.size() - 1) instanceof ContinueItem) { return; } if (items.get(items.size() - 1) instanceof ExitItem) { return; } } items.add(new MarkItem("finish")); } private static GraphTargetItem getLastNoEnd(List list) { if (list.isEmpty()) { return null; } if (list.get(list.size() - 1) instanceof ScriptEndItem) { if (list.size() >= 2) { return list.get(list.size() - 2); } return list.get(list.size() - 1); } return list.get(list.size() - 1); } private static void removeLastNoEnd(List list) { if (list.isEmpty()) { return; } if (list.get(list.size() - 1) instanceof ScriptEndItem) { if (list.size() >= 2) { list.remove(list.size() - 2); } return; } list.remove(list.size() - 1); } protected GraphTargetItem checkLoop(List output, GraphPart part, List stopPart, List loops, List throwStates) { if (stopPart.contains(part)) { return null; } GraphSourceItem firstIns = null; if (part != null) { if (part.start >= 0 && part.start < code.size()) { firstIns = code.get(part.start); } } for (Loop l : loops) { if (l.phase == 2) { continue; } if (l.loopContinue == part) { return (new ContinueItem(null, firstIns, l.id)); } if (l.loopBreak == part) { return (new BreakItem(null, firstIns, l.id)); } } return null; } protected GraphTargetItem checkLoop(List output, LoopItem loopItem, BaseLocalData localData, List loops, List throwStates, TranslateStack stack) { return loopItem; } private void clearLoops(List loops) { for (Loop l : loops) { l.phase = 0; } } private void clearThrowStates(List throwStates) { for (ThrowState ts : throwStates) { ts.state = 0; } } private void getLoops(BaseLocalData localData, GraphPart part, List loops, List throwStates, List stopPart) throws InterruptedException { clearLoops(loops); clearThrowStates(throwStates); getLoopsWalk(localData, part, loops, throwStates, stopPart, true, new ArrayList<>(), 0); clearLoops(loops); clearThrowStates(throwStates); } protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part, List throwStates) { return true; } protected void checkGetLoopsPart(GraphPart part) { } private void findPartsOutsideTry(ThrowState ts, GraphPart part, List ret, Set visited) { if (visited.contains(part)) { return; } visited.add(part); if (!ts.throwingParts.contains(part)) { ret.add(part); return; } for (GraphPart n : part.nextParts) { findPartsOutsideTry(ts, n, ret, visited); } } private void getLoopsWalk(BaseLocalData localData, GraphPart part, List loops, List throwStates, List stopPart, boolean first, List visited, int level) throws InterruptedException { loopwalk: while (true) { if (part == null) { return; } part = checkPart(null, localData, null, part, null); if (part == null) { return; } if (!visited.contains(part)) { visited.add(part); } checkGetLoopsPart(part); if (debugGetLoops) { System.err.println("getloops: " + part); } //List loopContinues = getLoopsContinues(loops); Loop lastP1 = null; for (Loop el : loops) { if ((el.phase == 1) && el.loopBreak == null) { //break not found yet if (el.loopContinue != part) { lastP1 = el; } else { lastP1 = null; } } } boolean canBeCandidate = true; if (lastP1 != null) { for (ThrowState ts : throwStates) { if (!ts.catchParts.contains(lastP1.loopContinue) && ts.catchParts.contains(part)) { canBeCandidate = false; break; } } } try { if (lastP1 != null && canBeCandidate && canBeBreakCandidate(localData, part, throwStates)) { if (lastP1.breakCandidates.contains(part)) { lastP1.breakCandidates.add(part); lastP1.breakCandidatesLevels.add(level); return; } else { List loops2 = new ArrayList<>(loops); loops2.remove(lastP1); if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2, throwStates, true)) { if (lastP1.breakCandidatesLocked == 0) { if (debugGetLoops) { System.err.println("added breakCandidate " + part + " to " + lastP1); } lastP1.breakCandidates.add(part); lastP1.breakCandidatesLevels.add(level); return; } } } } } finally { for (ThrowState ts : throwStates) { if (ts.throwingParts.contains(part)) { ts.state = 1; } } } for (Loop el : loops) { if (el.loopContinue == part) { return; } } if (stopPart != null && stopPart.contains(part)) { return; } part.level = level; boolean isLoop = part.leadsTo(localData, this, code, part, loops, throwStates, true); Loop currentLoop = null; if (isLoop) { currentLoop = new Loop(loops.size(), part, null); currentLoop.phase = 1; loops.add(currentLoop); } for (ThrowState ts : throwStates) { if (ts.throwingParts.contains(part)) { GraphPart t = ts.targetPart; if (!visited.contains(t)) { getLoopsWalk(localData, t, loops, throwStates, stopPart, false, visited, level); } } } if (part.nextParts.size() == 2 && !partIsSwitch(part)) { List nps; nps = part.nextParts; GraphPart next = getCommonPart(localData, part, nps, loops, throwStates); List stopPart2 = stopPart == null ? new ArrayList<>() : new ArrayList<>(stopPart); if (next != null) { stopPart2.add(next); } if (next != nps.get(0)) { getLoopsWalk(localData, nps.get(0), loops, throwStates, stopPart2, false, visited, level + 1); } if (next != nps.get(1)) { getLoopsWalk(localData, nps.get(1), loops, throwStates, stopPart2, false, visited, level + 1); } if (next != null) { getLoopsWalk(localData, next, loops, throwStates, stopPart, false, visited, level); } } else if (part.nextParts.size() > 2 || partIsSwitch(part)) { GraphPart next = getMostCommonPart(localData, part.nextParts, loops, throwStates, stopPart); for (GraphPart p : part.nextParts) { List stopPart2 = stopPart == null ? new ArrayList<>() : new ArrayList<>(stopPart); if (next != null) { stopPart2.add(next); } for (GraphPart p2 : part.nextParts) { if (p2 == p) { continue; } if (!stopPart2.contains(p2)) { stopPart2.add(p2); } } if (next != p) { getLoopsWalk(localData, p, loops, throwStates, stopPart2, false, visited, level + 1); } } if (next != null) { getLoopsWalk(localData, next, loops, throwStates, stopPart, false, visited, level); } } else if (part.nextParts.size() == 1) { if (!isLoop || currentLoop == null) { part = part.nextParts.get(0); first = false; continue loopwalk; //to avoid recursion } else { getLoopsWalk(localData, part.nextParts.get(0), loops, throwStates, stopPart, false, visited, level); } } if (isLoop && currentLoop != null) { GraphPart found; for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { GraphPart ch = checkPart(null, localData, null, currentLoop.breakCandidates.get(i), null); if (ch == null) { currentLoop.breakCandidates.remove(i); currentLoop.breakCandidatesLevels.remove(i); i--; } } if (debugGetLoops) { System.err.println("loop " + currentLoop + " break candidates:"); for (GraphPart cand : currentLoop.breakCandidates) { System.err.println("- " + cand); } } List contThrowStates = new ArrayList<>(); for (ThrowState ts : throwStates) { if (ts.throwingParts.contains(currentLoop.loopContinue)) { contThrowStates.add(ts.exceptionId); } } Map removed = new HashMap<>(); loopcand: for (int c = 0; c < currentLoop.breakCandidates.size(); c++) { GraphPart cand = currentLoop.breakCandidates.get(c); List candThrowStates = new ArrayList<>(); for (ThrowState ts : throwStates) { if (ts.throwingParts.contains(cand) && ts.startPart != cand) { if (contThrowStates.equals(candThrowStates)) { //adding new ts //this means breakcandidate is in nested try if (debugGetLoops) { System.err.println("candidate " + cand + " is in inner try, getting outside parts"); } List outsideTry = new ArrayList<>(); findPartsOutsideTry(ts, cand, outsideTry, new HashSet<>()); for (int j = outsideTry.size() - 1; j >= 0; j--) { if (!canBeBreakCandidate(localData, outsideTry.get(j), throwStates)) { outsideTry.remove(j); } } if (debugGetLoops) { for (GraphPart op : outsideTry) { System.err.println("- outsidepart " + op); } } int bcLevel = currentLoop.breakCandidatesLevels.get(c); currentLoop.breakCandidates.remove(c); currentLoop.breakCandidates.addAll(c, outsideTry); currentLoop.breakCandidatesLevels.remove(c); removed.put(cand, bcLevel); for (int j = 0; j < outsideTry.size(); j++) { currentLoop.breakCandidatesLevels.add(c, bcLevel); } c--; continue loopcand; } candThrowStates.add(ts.exceptionId); } } } do { found = null; loopcand: for (int c1 = 0; c1 < currentLoop.breakCandidates.size(); c1++) { GraphPart cand = currentLoop.breakCandidates.get(c1); for (int c2 = 0; c2 < currentLoop.breakCandidates.size(); c2++) { GraphPart cand2 = currentLoop.breakCandidates.get(c2); if (cand == cand2) { continue; } if (cand.leadsTo(localData, this, code, cand2, loops, throwStates, true)) { int curLevl = currentLoop.breakCandidatesLevels.get(c1); int curLev2 = currentLoop.breakCandidatesLevels.get(c2); /* found = cand; int lev2 = currentLoop.breakCandidatesLevels.get(c2); currentLoop.breakCandidates.set(c1, cand2); currentLoop.breakCandidatesLevels.set(c1, lev2);*/ int lev1 = Integer.MAX_VALUE; int lev2 = Integer.MAX_VALUE; for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { if (currentLoop.breakCandidates.get(i) == cand) { if (currentLoop.breakCandidatesLevels.get(i) < lev1) { lev1 = currentLoop.breakCandidatesLevels.get(i); } } if (currentLoop.breakCandidates.get(i) == cand2) { if (currentLoop.breakCandidatesLevels.get(i) < lev2) { lev2 = currentLoop.breakCandidatesLevels.get(i); } } } // GraphPart other; int curLev; if (lev1 <= lev2) { found = cand2; other = cand; curLev = curLevl; } else { found = cand; other = cand2; curLev = curLev2; } currentLoop.breakCandidates.add(other); currentLoop.breakCandidatesLevels.add(curLev); break loopcand; } } } if (found != null) { int maxlevel = 0; while (currentLoop.breakCandidates.contains(found)) { int ind = currentLoop.breakCandidates.indexOf(found); currentLoop.breakCandidates.remove(ind); int lev = currentLoop.breakCandidatesLevels.remove(ind); if (lev > maxlevel) { maxlevel = lev; } } if (removed.containsKey(found)) { if (removed.get(found) > maxlevel) { maxlevel = removed.get(found); } } removed.put(found, maxlevel); } } while ((found != null) && (currentLoop.breakCandidates.size() > 1)); Map count = new HashMap<>(); GraphPart winner = null; int winnerCount = 0; int winnerNumBlock = Integer.MAX_VALUE; Set bannedCandidates = new HashSet<>(); if (localData.secondPassData != null) { bannedCandidates = localData.secondPassData.allSwitchParts; } if (debugPrintLoopList) { System.err.println("bannedCandidates:"); for (GraphPart p : bannedCandidates) { System.err.println("- " + p); } } for (GraphPart cand : currentLoop.breakCandidates) { if (bannedCandidates.contains(cand)) { if (debugPrintLoopList) { System.err.println("cand " + cand + " is banned"); } continue; } if (!count.containsKey(cand)) { count.put(cand, 0); } count.put(cand, count.get(cand) + 1); boolean otherBreakCandidate = false; for (Loop el : loops) { if (el == currentLoop) { continue; } if (el.breakCandidates.contains(cand)) { otherBreakCandidate = true; break; } } if (otherBreakCandidate) { //empty } else if (count.get(cand) > winnerCount) { winnerCount = count.get(cand); winner = cand; } else if (count.get(cand) == winnerCount && winner != null) { if (cand.path.length() < winner.path.length()) { winner = cand; } } } for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { GraphPart cand = currentLoop.breakCandidates.get(i); if (cand != winner) { 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; currentLoop.phase = 2; boolean start = false; for (int l = 0; l < loops.size(); l++) { Loop el = loops.get(l); if (start) { el.phase = 1; } if (el == currentLoop) { start = true; } } List removedVisited = new ArrayList<>(); for (GraphPart r : removed.keySet()) { if (removedVisited.contains(r)) { continue; } getLoopsWalk(localData, r, loops, throwStates, stopPart, false, visited, removed.get(r)); removedVisited.add(r); } start = false; for (int l = 0; l < loops.size(); l++) { Loop el = loops.get(l); if (el == currentLoop) { start = true; } if (start) { el.phase = 2; } } getLoopsWalk(localData, currentLoop.loopBreak, loops, throwStates, stopPart, false, visited, level); } break; } } private void getContinuesCommands(List commands, List> result, long loopId) { for (GraphTargetItem ti : commands) { if (ti instanceof ContinueItem) { ContinueItem ci = (ContinueItem) ti; if (ci.loopId == loopId) { result.add(commands); } } if (ti instanceof Block) { Block bl = (Block) ti; for (List subCommands : bl.getSubs()) { getContinuesCommands(subCommands, result, loopId); } } } } protected List getNextParts(BaseLocalData localData, GraphPart part) { return part.nextParts; } protected boolean checkPartOutput(List currentRet, List foundGotos, Map> partCodes, Map partCodePos, Set visited, GraphSource code, BaseLocalData localData, Set allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List stopPartKind, List loops, List throwStates, Loop currentLoop, int staticOperation, String path, int recursionLevel) throws InterruptedException { return false; } protected boolean partIsLoopContBrePre(GraphPart part, List loops, List throwStates) { for (Loop el : loops) { if (el.phase == 1) { if (el.loopBreak == part) { return true; } if (el.loopContinue == part) { return true; } if (el.loopPreContinue == part) { return true; } } } return false; } protected boolean canHandleLoop(BaseLocalData localData, GraphPart part, List loops, List throwStates) { return true; } protected boolean canHandleVisited(BaseLocalData localData, GraphPart part) { return true; } protected boolean canBeCommaised(List list) { for (GraphTargetItem item : list) { if (item instanceof Block) { return false; } } return true; } protected List printGraph(List foundGotos, Map> partCodes, Map partCodePos, Set visited, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List stopPartKind, List loops, List throwStates, int staticOperation, String path) throws InterruptedException { return printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, stopPartKind, loops, throwStates, null, staticOperation, path, 0); } protected List printGraph(List foundGotos, Map> partCodes, Map partCodePos, Set visited, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List stopPartKind, List loops, List throwStates, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { loopPrintGraph: while (true) { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } if (stopPart == null) { stopPart = new ArrayList<>(); } if (stopPartKind == null) { stopPartKind = new ArrayList<>(); } if (recursionLevel > allParts.size() + 1) { throw new TranslateException("printGraph max recursion level reached."); } if (ret == null) { ret = new GraphPartMarkedArrayList<>(); } //try { if (debugPrintGraph) { System.err.println("PART " + part + " nextsize:" + getNextParts(localData, part).size()); } /*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; } GraphTargetItem lop = checkLoop(getNextParts(localData, part).get(0), stopPart, loops); if (lop == null) { part = getNextParts(localData, part).get(0); } else { break; } }*/ if (part == null) { return ret; } part = checkPartWithOutput(ret, stack, localData, parent, part, allParts); if (part == null) { return ret; } if (part.ignored) { return ret; } //List loopContinues = getLoopsContinues(loops); boolean isLoop = false; Loop currentLoop = null; boolean vCanHandleLoop = canHandleLoop(localData, part, loops, throwStates); Loop ignoredLoop = null; for (Loop el : loops) { if ((el.loopContinue == part) && (el.phase == 0)) { if (vCanHandleLoop) { currentLoop = el; currentLoop.phase = 1; isLoop = true; } else { ignoredLoop = el; } break; } } if (isLoop) { makeAllCommands(ret, stack); } if (debugPrintGraph) { System.err.println("loopsize:" + loops.size()); } for (int l = loops.size() - 1; l >= 0; l--) { Loop el = loops.get(l); if (el == ignoredLoop) { if (debugPrintGraph) { System.err.println("ignoring to be loop " + el); } continue; } if (el == currentLoop) { if (debugPrintGraph) { System.err.println("ignoring current loop " + el); } continue; } if (el.phase != 1) { if (debugPrintGraph) { System.err.println("ignoring loop " + el); } continue; } if (el.loopBreak == part) { if (currentLoop != null) { currentLoop.phase = 0; } if (debugPrintGraph) { System.err.println("Adding break"); } ret.add(new BreakItem(null, localData.lineStartInstruction, el.id)); return ret; } if (el.loopPreContinue == part) { if (currentLoop != null) { currentLoop.phase = 0; } if (debugPrintGraph) { System.err.println("Adding precontinue"); } ret.add(new ContinueItem(null, localData.lineStartInstruction, el.id)); return ret; } if (el.loopContinue == part) { if (currentLoop != null) { currentLoop.phase = 0; } if (debugPrintGraph) { System.err.println("Adding continue"); } ret.add(new ContinueItem(null, localData.lineStartInstruction, el.id)); return ret; } } if (debugPrintGraph) { System.err.println("stopParts: " + pathToString(stopPart)); } if (stopPart.contains(part)) { boolean hasBlockClosesAfter = false; //this weird stuff handles some goto problems: loopi: for (int i = 0; i < stopPartKind.size(); i++) { if (stopPart.get(i) == part) { for (int j = i + 1; j < stopPartKind.size(); j++) { if (stopPart.get(j) != part) { if (stopPartKind.get(j) == StopPartKind.BLOCK_CLOSE) { hasBlockClosesAfter = true; break loopi; } } } } } if (!hasBlockClosesAfter) { if (currentLoop != null) { currentLoop.phase = 0; } if (debugPrintGraph) { System.err.println("Stopped on part " + part); } return ret; } } if (code.size() <= part.start) { ret.add(new ScriptEndItem()); return ret; } boolean vCanHandleVisited = canHandleVisited(localData, part); if (vCanHandleVisited) { if (visited.contains(part)) { String labelName = "addr" + part.start; List firstCode = partCodes.get(part); int firstCodePos = partCodePos.get(part); if (firstCodePos > firstCode.size()) { firstCodePos = firstCode.size(); } if (firstCode instanceof GraphPartMarkedArrayList) { GraphPartMarkedArrayList markedFirstCode = (GraphPartMarkedArrayList) firstCode; firstCodePos = markedFirstCode.indexOfPart(part); if (firstCodePos == -1) { firstCodePos = firstCode.size(); } ((GraphPartMarkedArrayList) firstCode).startPart(part); } if (firstCode.size() > firstCodePos && (firstCode.get(firstCodePos) instanceof LabelItem)) { labelName = ((LabelItem) firstCode.get(firstCodePos)).labelName; } else { firstCode.add(firstCodePos, new LabelItem(null, localData.lineStartInstruction, labelName)); } ret.add(new GotoItem(null, localData.lineStartInstruction, labelName)); return ret; } else { visited.add(part); partCodes.put(part, ret); partCodePos.put(part, ret.size()); } } List currentRet = ret; UniversalLoopItem loopItem = null; TranslateStack sPreLoop = stack; if (isLoop) { //makeAllCommands(currentRet, stack); stack = (TranslateStack) stack.clone(); //hack for as1/2 for..in to get enumeration through GraphTargetItem topBsr = !stack.isEmpty() && (stack.peek() instanceof BranchStackResistant) ? stack.peek() : null; stack.clear(); if (topBsr != null) { stack.push(topBsr); } loopItem = new UniversalLoopItem(null, localData.lineStartInstruction, currentLoop, new ArrayList<>()); //loopItem.commands=printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops); currentRet.add(loopItem); currentRet = loopItem.commands; //return ret; } boolean parseNext = true; //****************************DECOMPILING PART************* if (stack.isEmpty() && (currentRet instanceof GraphPartMarkedArrayList)) { ((GraphPartMarkedArrayList) currentRet).clearCurrentParts(); } GraphPartMarkedArrayList output = new GraphPartMarkedArrayList<>(); output.startPart(part); if (currentRet instanceof GraphPartMarkedArrayList) { ((GraphPartMarkedArrayList) currentRet).startPart(part); } if (checkPartOutput(currentRet, foundGotos, partCodes, partCodePos, visited, code, localData, allParts, stack, parent, part, stopPart, stopPartKind, loops, throwStates, currentLoop, staticOperation, path, recursionLevel)) { parseNext = false; } else { boolean exHappened = false; int ipStart = part.start; do { exHappened = false; try { output.addAll(code.translatePart(this, part, localData, stack, ipStart, part.end, staticOperation, path)); } catch (GraphPartChangeException ex) { //Special case for ifFrameLoaded when it's over multiple parts output.addAll(ex.getOutput()); for (GraphPart p : allParts) { if (p.containsIP(ex.getIp())) { if (ex.getIp() == p.start) { currentRet.addAll(output); //to check for stopparts,etc. we need to call printGraph again part = p; //return printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel); continue loopPrintGraph; } exHappened = true; ipStart = ex.getIp(); part = p; break; } } } } while (exHappened); if ((part.end >= code.size() - 1) && getNextParts(localData, part).isEmpty()) { output.add(new ScriptEndItem()); } } if (parseNext) { List retCheck = check(currentRet, foundGotos, partCodes, partCodePos, visited, code, localData, allParts, stack, parent, part, stopPart, stopPartKind, loops, throwStates, output, currentLoop, staticOperation, path); if (retCheck != null) { if (!retCheck.isEmpty()) { currentRet.addAll(retCheck); } parseNext = false; //return ret; } else { currentRet.addAll(output); } } //********************************END PART DECOMPILING if (parseNext) { if (getNextParts(localData, part).size() > 2 || partIsSwitch(part)) { 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<>(); List valueMappings = new ArrayList<>(); boolean first = false; int pos; Map caseExpressions = new HashMap<>(); Map caseExpressionLeftSides = new HashMap<>(); Map caseExpressionRightSides = new HashMap<>(); GraphTargetItem it = switchedItem; int defaultBranch = 0; boolean hasExpr = false; List commaCommands = new ArrayList<>(); Map> caseCommaCommands = new HashMap<>(); while ((it instanceof TernarOpItem) || (it instanceof IfItem)) { if (it instanceof IfItem) { IfItem ii = (IfItem) it; List iiOnTrue = ii.onTrue; List iiOnFalse = ii.onFalse; if ((ii.expression instanceof EqualsTypeItem) || (ii.expression instanceof NotEqualsTypeItem)) { if (ii.expression instanceof NotEqualsTypeItem) { iiOnTrue = ii.onFalse; iiOnFalse = ii.onTrue; } if (!iiOnFalse.isEmpty() && !iiOnTrue.isEmpty() && iiOnTrue.get(iiOnTrue.size() - 1) instanceof PushItem && iiOnTrue.get(iiOnTrue.size() - 1).value instanceof IntegerValueTypeItem) { int cpos = ((IntegerValueTypeItem) iiOnTrue.get(iiOnTrue.size() - 1).value).intValue(); caseCommaCommands.put(cpos, commaCommands); caseExpressionLeftSides.put(cpos, ((BinaryOpItem) ii.expression).getLeftSide()); caseExpressionRightSides.put(cpos, ((BinaryOpItem) ii.expression).getRightSide()); commaCommands = new ArrayList<>(); for (int f = 0; f < iiOnFalse.size() - 1; f++) { commaCommands.add(iiOnFalse.get(f)); } it = iiOnFalse.get(iiOnFalse.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; GraphTargetItem toOnTrue = to.onTrue; GraphTargetItem toOnFalse = to.onFalse; if ((to.expression instanceof EqualsTypeItem) || (to.expression instanceof NotEqualsTypeItem)) { if (to.expression instanceof NotEqualsTypeItem) { toOnTrue = to.onFalse; toOnFalse = to.onTrue; } if (toOnTrue instanceof IntegerValueTypeItem) { int cpos = ((IntegerValueTypeItem) toOnTrue).intValue(); caseExpressionLeftSides.put(cpos, ((BinaryOpItem) to.expression).getLeftSide()); caseExpressionRightSides.put(cpos, ((BinaryOpItem) to.expression).getRightSide()); caseCommaCommands.put(cpos, commaCommands); commaCommands = new ArrayList<>(); it = toOnFalse; if (it instanceof CommaExpressionItem) { commaCommands = new ArrayList<>(); CommaExpressionItem ce = (CommaExpressionItem) it; for (int f = 0; f < ce.commands.size() - 1; f++) { commaCommands.add(ce.commands.get(f)); } it = ce.commands.get(ce.commands.size() - 1); } } 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) && !(switchedItem instanceof IntegerValueTypeItem)) { defaultBranch = ((IntegerValueTypeItem) it).intValue(); } Map caseExpressionOtherSides = caseExpressions; 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; caseExpressionOtherSides = caseExpressionRightSides; switchedItem = firstItem; hasExpr = true; } 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; caseExpressionOtherSides = caseExpressionLeftSides; switchedItem = firstItem; hasExpr = true; } } } first = true; pos = 0; //This is tied to AS3 switch implementation which has nextparts switched from index 1. TODO: Make more universal GraphPart defaultPart = hasExpr ? getNextParts(localData, part).get(1 + defaultBranch) : getNextParts(localData, part).get(0); List caseBodyParts = new ArrayList<>(); for (int i = 1; i < getNextParts(localData, part).size(); i++) { if (!hasExpr) { if (getNextParts(localData, part).get(i) == defaultPart) { pos++; continue; } caseValues.add(new IntegerValueItem(null, localData.lineStartInstruction, pos)); } else if (caseExpressions.containsKey(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 { pos++; continue; } caseBodyParts.add(getNextParts(localData, part).get(i)); pos++; } Reference nextRef = new Reference<>(null); Reference tiRef = new Reference<>(null); SwitchItem sw = handleSwitch(switchedItem, originalSwitchedItem.getSrc(), foundGotos, partCodes, partCodePos, visited, allParts, stack, stopPart, stopPartKind, loops, throwStates, localData, staticOperation, path, caseValues, defaultPart, caseBodyParts, nextRef, tiRef); GraphPart next = nextRef.getVal(); checkSwitch(localData, sw, caseExpressionOtherSides.values(), currentRet); currentRet.add(sw); if (next != null) { if (tiRef.getVal() != null) { ret.add(tiRef.getVal()); } else { printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1); } } pos++; } //else GraphPart nextOnePart = null; if (getNextParts(localData, part).size() == 2 && !partIsSwitch(part)) { GraphTargetItem expr = stack.pop(); /*if (expr instanceof LogicalOpItem) { expr = ((LogicalOpItem) expr).invert(); } else { expr = new NotItem(null, expr); }*/ if (nextOnePart == null) { List nps; nps = getNextParts(localData, part); boolean isEmpty = nps.get(0) == nps.get(1); GraphPart next = getCommonPart(localData, part, nps, loops, throwStates); //System.err.println("on part " + part + ", next: " + next); TranslateStack trueStack = (TranslateStack) stack.clone(); TranslateStack falseStack = (TranslateStack) stack.clone(); //hack for as1/2 for..in to get enumeration through GraphTargetItem topBsr = !stack.isEmpty() && (stack.peek() instanceof BranchStackResistant) ? stack.peek() : null; trueStack.clear(); falseStack.clear(); if (topBsr != null) { trueStack.add(topBsr); falseStack.add(topBsr); } if (isEmpty) { next = nps.get(0); } boolean hasOntrue = nps.get(1) != next; boolean hasOnFalse = nps.get(0) != next; List stopPart2 = new ArrayList<>(stopPart); List stopPartKind2 = new ArrayList<>(stopPartKind); if ((!isEmpty) && (next != null)) { if (!stopPart2.contains(next)) { //?? might be a break or something stopPart2.add(next); stopPartKind2.add(StopPartKind.BLOCK_CLOSE); } } List onTrue = new ArrayList<>(); if (!isEmpty && hasOntrue) { onTrue = printGraph(foundGotos, partCodes, partCodePos, visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, stopPartKind2, loops, throwStates, null, staticOperation, path, recursionLevel + 1); } List onFalse = new ArrayList<>(); if (!isEmpty && hasOnFalse) { onFalse = printGraph(foundGotos, partCodes, partCodePos, visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, stopPartKind2, loops, throwStates, null, staticOperation, path, recursionLevel + 1); } //List out2 = new ArrayList<>(); //makeAllCommands(out2, stack); makeAllCommands(onTrue, trueStack); makeAllCommands(onFalse, falseStack); GraphTargetItem addAfterIf = null; if (!onTrue.isEmpty() && !onFalse.isEmpty() && (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) && (onFalse.get(onFalse.size() - 1) instanceof ContinueItem)) { ContinueItem contTrue = (ContinueItem) onTrue.get(onTrue.size() - 1); ContinueItem contFalse = (ContinueItem) onFalse.get(onFalse.size() - 1); if (contTrue.loopId == contFalse.loopId) { onTrue.remove(onTrue.size() - 1); onFalse.remove(onFalse.size() - 1); addAfterIf = contTrue; } } List filteredOnTrue = onTrue; List filteredOnFalse = onFalse; if (!isEmpty(filteredOnTrue) && !isEmpty(filteredOnFalse) && (filteredOnTrue.get(filteredOnTrue.size() - 1) instanceof PushItem) && (filteredOnFalse.get(filteredOnFalse.size() - 1) instanceof PushItem) && canBeCommaised(filteredOnTrue) && canBeCommaised(filteredOnFalse)) { GraphTargetItem ternarOnTrue; if (filteredOnTrue.size() > 1) { filteredOnTrue.set(filteredOnTrue.size() - 1, filteredOnTrue.get(filteredOnTrue.size() - 1).value); // replace Pushitem with its value ternarOnTrue = new CommaExpressionItem(null, null, filteredOnTrue); } else { ternarOnTrue = filteredOnTrue.get(0).value; } GraphTargetItem ternarOnFalse; if (filteredOnFalse.size() > 1) { filteredOnFalse.set(filteredOnFalse.size() - 1, filteredOnFalse.get(filteredOnFalse.size() - 1).value); // replace Pushitem with its value ternarOnFalse = new CommaExpressionItem(null, null, filteredOnFalse); } else { ternarOnFalse = filteredOnFalse.get(0).value; } stack.push(new TernarOpItem(null, localData.lineStartInstruction, expr.invert(null), ternarOnTrue, ternarOnFalse)); } else { boolean isIf = true; //If the ontrue is empty, switch ontrue and onfalse if (filteredOnTrue.isEmpty() && !filteredOnFalse.isEmpty()) { expr = expr.invert(null); List tmp = onTrue; onTrue = onFalse; onFalse = tmp; //tmp = filteredOnTrue; filteredOnTrue = filteredOnFalse; //filteredOnFalse = tmp; } if (!stack.isEmpty() && ((filteredOnTrue.size() == 1 && (filteredOnTrue.get(0) instanceof PopItem)) || ((filteredOnTrue.size() >= 2) && (filteredOnTrue.get(0) instanceof PopItem) && (filteredOnTrue.get(filteredOnTrue.size() - 1) instanceof PushItem)))) { if (filteredOnTrue.size() > 1) { GraphTargetItem rightSide = ((PushItem) filteredOnTrue.get(filteredOnTrue.size() - 1)).value; GraphTargetItem prevExpr = stack.pop(); GraphTargetItem leftSide = expr.getNotCoercedNoDup(); boolean hideEmptyTrueFalse = true; if (leftSide instanceof DuplicateItem) { isIf = false; if (hideEmptyTrueFalse && prevExpr.getNotCoercedNoDup() instanceof FalseItem) { stack.push(rightSide); } else if (hideEmptyTrueFalse && rightSide.getNotCoercedNoDup() instanceof FalseItem) { stack.push(prevExpr); } else { stack.push(new OrItem(null, localData.lineStartInstruction, prevExpr, rightSide)); } } else if (leftSide.invert(null).getNotCoercedNoDup() instanceof DuplicateItem) { isIf = false; if (hideEmptyTrueFalse && prevExpr.getNotCoercedNoDup() instanceof TrueItem) { stack.push(rightSide); } else if (hideEmptyTrueFalse && rightSide.getNotCoercedNoDup() instanceof TrueItem) { stack.push(prevExpr); } else { stack.push(new AndItem(null, localData.lineStartInstruction, prevExpr, rightSide)); } } else if (prevExpr instanceof FalseItem) { isIf = false; leftSide = leftSide.invert(null); if (hideEmptyTrueFalse && leftSide.getNotCoercedNoDup() instanceof TrueItem) { stack.push(rightSide); } else if (hideEmptyTrueFalse && rightSide.getNotCoercedNoDup() instanceof TrueItem) { stack.push(leftSide); } else { stack.push(new AndItem(null, localData.lineStartInstruction, leftSide, rightSide)); } } else if (prevExpr instanceof TrueItem) { isIf = false; if (hideEmptyTrueFalse && leftSide.getNotCoercedNoDup() instanceof FalseItem) { stack.push(rightSide); } else if (hideEmptyTrueFalse && rightSide.getNotCoercedNoDup() instanceof FalseItem) { stack.push(leftSide); } else { stack.push(new OrItem(null, localData.lineStartInstruction, leftSide, rightSide)); } } else { stack.push(prevExpr); //push it back //Still unstructured } } else { isIf = false; } } if (isIf) { makeAllCommands(currentRet, stack); IfItem b = new IfItem(null, localData.lineStartInstruction, expr.invert(null), onTrue, onFalse); b.decisionPart = part; b.onTruePart = nps.get(0); b.onFalsePart = nps.get(1); currentRet.add(b); if (processSubBlk(b, null)) { stack.push(new PopItem(null, localData.lineStartInstruction)); } } } if (addAfterIf != null) { currentRet.add(addAfterIf); } //currentRet.addAll(out2); if (next != null) { printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1); //currentRet.addAll(); } } } //else if (getNextParts(localData, part).size() == 1) { nextOnePart = getNextParts(localData, part).get(0); } if (getNextParts(localData, part).isEmpty()) { makeAllCommands(currentRet, stack); } if (nextOnePart != null) { printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, getNextParts(localData, part).get(0), stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1); } } if (isLoop && loopItem != null && currentLoop != null) { LoopItem li = loopItem; boolean loopTypeFound = false; boolean hasContinue = false; processIfs(loopItem.commands); processSwitches(loopItem.commands, currentLoop.id); 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); List stopContPartKind = new ArrayList<>(); stopContPartKind.add(StopPartKind.OTHER); GraphPart precoBackup = currentLoop.loopPreContinue; currentLoop.loopPreContinue = null; printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, null, precoBackup, stopContPart, stopContPartKind, loops, throwStates, loopItem.commands, staticOperation, path, recursionLevel + 1); checkContinueAtTheEnd(loopItem.commands, currentLoop); } } //Loop with condition at the beginning (While) if (!loopTypeFound && (!loopItem.commands.isEmpty())) { if (loopItem.commands.get(0) instanceof IfItem) { IfItem ifi = (IfItem) loopItem.commands.get(0); List bodyBranch = null; boolean inverted = false; boolean breakpos2 = false; BreakItem addBreakItem = null; if ((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem)) { BreakItem bi = (BreakItem) ifi.onTrue.get(0); if (bi.loopId == currentLoop.id) { bodyBranch = ifi.onFalse; inverted = true; } } else if ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof BreakItem)) { BreakItem bi = (BreakItem) ifi.onFalse.get(0); 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 (ifi.onTrue.isEmpty()) { inverted = true; } bodyBranch = inverted ? ifi.onFalse : ifi.onTrue; breakpos2 = true; if (bi.loopId != currentLoop.id) { //it's break of another parent loop addBreakItem = bi; //we must add it after the loop } } if (bodyBranch != null) { int index = ret.indexOf(loopItem); ret.remove(index); List exprList = new ArrayList<>(); GraphTargetItem expr = ifi.expression; if (inverted) { if (expr instanceof LogicalOpItem) { expr = ((LogicalOpItem) expr).invert(null); } else { expr = new NotItem(null, expr.getLineStartItem(), expr); } } exprList.add(expr); 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<>(); //findGotoTargets - comment this out: if (currentLoop.loopPreContinue != null) { GraphPart backup = currentLoop.loopPreContinue; currentLoop.loopPreContinue = null; List stopPart2 = new ArrayList<>(stopPart); stopPart2.add(currentLoop.loopContinue); List stopPartKind2 = new ArrayList<>(stopPartKind); stopPartKind2.add(StopPartKind.OTHER); List precoCommands = printGraph(foundGotos, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, stopPartKind2, loops, throwStates, null, staticOperation, path, recursionLevel + 1); currentLoop.loopPreContinue = backup; checkContinueAtTheEnd(precoCommands, currentLoop); List> continueCommands = new ArrayList<>(); getContinuesCommands(commands, continueCommands, currentLoop.id); if (continueCommands.isEmpty()) { commands.addAll(precoCommands); precoCommands = new ArrayList<>(); //Single continue and there is break/continue/return/throw at end of the commands } else if (!commands.isEmpty() && continueCommands.size() == 1) { GraphTargetItem lastItem = commands.get(commands.size() - 1); if ((lastItem instanceof BreakItem) || (lastItem instanceof ContinueItem) || (lastItem instanceof ExitItem)) { continueCommands.get(0).addAll(continueCommands.get(0).size() - 1, precoCommands); precoCommands = new ArrayList<>(); } } boolean isAllNotBlock = true; for (GraphTargetItem ti : precoCommands) { if (ti instanceof Block) { isAllNotBlock = false; break; } } if (isAllNotBlock) { finalComm.addAll(precoCommands); } else { commands.addAll(precoCommands); } } if (currentLoop.precontinueCommands != null) { finalComm.addAll(currentLoop.precontinueCommands); } if (!finalComm.isEmpty()) { ret.add(index, li = new ForItem(expr.getSrc(), expr.getLineStartItem(), currentLoop, new ArrayList<>(), exprList.get(exprList.size() - 1), finalComm, commands)); } else { ret.add(index, li = new WhileItem(expr.getSrc(), expr.getLineStartItem(), currentLoop, exprList, commands)); } if (addBreakItem != null) { ret.add(index + 1, addBreakItem); } loopTypeFound = true; } } } //Loop with condition at the end (Do..While) if (!loopTypeFound && (!loopItem.commands.isEmpty())) { if (loopItem.commands.get(loopItem.commands.size() - 1) instanceof IfItem) { IfItem ifi = (IfItem) loopItem.commands.get(loopItem.commands.size() - 1); List bodyBranch = null; boolean inverted = false; if ((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem)) { BreakItem bi = (BreakItem) ifi.onTrue.get(0); if (bi.loopId == currentLoop.id) { bodyBranch = ifi.onFalse; inverted = true; } } else if ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof BreakItem)) { BreakItem bi = (BreakItem) ifi.onFalse.get(0); if (bi.loopId == currentLoop.id) { bodyBranch = ifi.onTrue; } } if (bodyBranch != null) { //Condition at the beginning int index = ret.indexOf(loopItem); ret.remove(index); List exprList = new ArrayList<>(); GraphTargetItem expr = ifi.expression; if (inverted) { expr = expr.invert(null); } checkContinueAtTheEnd(bodyBranch, currentLoop); List commands = new ArrayList<>(); if (!bodyBranch.isEmpty()) { ret.add(index, loopItem); } else { loopItem.commands.remove(loopItem.commands.size() - 1); commands.addAll(loopItem.commands); commands.addAll(bodyBranch); exprList.add(expr); checkContinueAtTheEnd(commands, currentLoop); ret.add(index, li = new DoWhileItem(null, exprList.get(0).getLineStartItem(), currentLoop, commands, exprList)); } loopTypeFound = true; } } } if (!loopTypeFound) { if (currentLoop.loopPreContinue != null) { loopTypeFound = true; GraphPart backup = currentLoop.loopPreContinue; currentLoop.loopPreContinue = null; List stopPart2 = new ArrayList<>(stopPart); stopPart2.add(currentLoop.loopContinue); List stopPartKind2 = new ArrayList<>(stopPartKind); stopPartKind2.add(StopPartKind.OTHER); List finalComm = printGraph(foundGotos, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, stopPartKind2, loops, throwStates, null, staticOperation, path, recursionLevel + 1); currentLoop.loopPreContinue = backup; checkContinueAtTheEnd(finalComm, currentLoop); if (!finalComm.isEmpty()) { if (finalComm.get(finalComm.size() - 1) instanceof IfItem) { IfItem ifi = (IfItem) finalComm.get(finalComm.size() - 1); boolean ok = false; boolean invert = false; if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem) && (((BreakItem) ifi.onTrue.get(0)).loopId == currentLoop.id)) && ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) { ok = true; invert = true; } if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onTrue.get(0)).loopId == currentLoop.id)) && ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof BreakItem) && (((BreakItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) { ok = true; } if (ok) { finalComm.remove(finalComm.size() - 1); int index = ret.indexOf(loopItem); ret.remove(index); List exprList = new ArrayList<>(finalComm); GraphTargetItem expr = ifi.expression; if (invert) { expr = expr.invert(null); } exprList.add(expr); ret.add(index, li = new DoWhileItem(null, expr.getLineStartItem(), currentLoop, loopItem.commands, exprList)); } } } } } if (!loopTypeFound) { checkContinueAtTheEnd(loopItem.commands, currentLoop); } currentLoop.phase = 2; GraphTargetItem replaced = checkLoop(ret, li, localData, loops, throwStates, sPreLoop); if (replaced != li) { int index = ret.indexOf(li); ret.remove(index); if (replaced != null) { ret.add(index, replaced); } } if (currentLoop.loopBreak != null) { printGraph(foundGotos, partCodes, partCodePos, visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel + 1); } } break; } return ret; } protected void checkSwitch(BaseLocalData localData, SwitchItem switchItem, Collection otherSides, List output) { } protected void checkGraph(List allBlocks) { } protected int checkIp(int ip) { return ip; } public GraphPart searchPart(int ip, Collection allParts) { if (ip < 0) { return null; } for (GraphPart p : allParts) { if (ip >= p.start && ip <= p.end) { return p; } } return null; } public List makeGraph(GraphSource code, List allBlocks, List exceptions) throws InterruptedException { List alternateEntries = new ArrayList<>(); for (GraphException ex : exceptions) { alternateEntries.add(ex.start); //alternateEntries.add(ex.end); alternateEntries.add(ex.target); } HashMap> refs = code.visitCode(alternateEntries); List ret = new ArrayList<>(); boolean[] visited = new boolean[code.size()]; ret.add(makeGraph(null, new GraphPath(), code, 0, 0, allBlocks, refs, visited)); for (int pos : alternateEntries) { GraphPart e1 = new GraphPart(-1, -1); e1.path = new GraphPath("e"); ret.add(makeGraph(e1, new GraphPath("e"), code, pos, pos, allBlocks, refs, visited)); } checkGraph(allBlocks); return ret; } private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startip, int lastIp, List allBlocks, HashMap> refs, boolean[] visited) throws InterruptedException { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } int ip = startip; GraphPart existingPart = searchPart(ip, allBlocks); if (existingPart != null) { if (parent != null) { existingPart.refs.add(parent); parent.nextParts.add(existingPart); } return existingPart; } GraphPart ret = new GraphPart(ip, -1); ret.path = path; GraphPart part = ret; if (parent != null) { ret.refs.add(parent); parent.nextParts.add(ret); } while (ip < code.size()) { ip = checkIp(ip); if (ip >= code.size()) { break; } if (visited[ip] || ((ip != startip) && (refs.get(ip).size() > 1))) { part.end = lastIp; GraphPart found = searchPart(ip, allBlocks); allBlocks.add(part); if (found != null) { part.nextParts.add(found); found.refs.add(part); break; } else { GraphPart nextPart = new GraphPart(ip, -1); nextPart.path = path; part.nextParts.add(nextPart); nextPart.refs.add(part); part = nextPart; } } visited[ip] = true; lastIp = ip; GraphSourceItem ins = code.get(ip); if (ins.isIgnored()) { ip++; continue; } if (ins instanceof GraphSourceItemContainer) { GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; if (ins instanceof Action) { //TODO: Remove dependency of AVM1 long endAddr = ((Action) ins).getAddress() + cnt.getHeaderSize(); for (long size : cnt.getContainerSizes()) { endAddr += size; } ip = code.adr2pos(endAddr); if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) { part.end = lastIp; allBlocks.add(part); GraphPart nextGraphPart = new GraphPart(ip, -1); nextGraphPart.path = path; part.nextParts.add(nextGraphPart); nextGraphPart.refs.add(part); part = nextGraphPart; } } continue; } else if (ins.isExit()) { part.end = ip; allBlocks.add(part); break; } else if (ins.isJump()) { part.end = ip; allBlocks.add(part); ip = ins.getBranches(code).get(0); makeGraph(part, path, code, ip, lastIp, allBlocks, refs, visited); break; } else if (ins.isBranch()) { part.end = ip; allBlocks.add(part); List branches = ins.getBranches(code); for (int i = 0; i < branches.size(); i++) { makeGraph(part, path.sub(i, ip), code, branches.get(i), ip, allBlocks, refs, visited); } break; } ip++; } if ((part.end == -1) && (ip >= code.size())) { if (part.start == code.size()) { part.end = code.size(); allBlocks.add(part); } else { part.end = ip - 1; for (GraphPart p : allBlocks) { if (p.start == ip) { p.refs.add(part); part.nextParts.add(p); allBlocks.add(part); return ret; } } GraphPart gp = new GraphPart(ip, ip); allBlocks.add(gp); gp.refs.add(part); part.nextParts.add(gp); allBlocks.add(part); } } return ret; } /** * String used to indent line when converting to string */ public static final String INDENTOPEN = "INDENTOPEN"; /** * String used to unindent line when converting to string */ public static final String INDENTCLOSE = "INDENTCLOSE"; /** * Converts list of TreeItems to string * * @param tree List of TreeItem * @param writer * @param localData * @return String * @throws java.lang.InterruptedException */ public static GraphTextWriter graphToString(List tree, GraphTextWriter writer, LocalData localData) throws InterruptedException { for (GraphTargetItem ti : tree) { if (!ti.isEmpty()) { ti.toStringSemicoloned(writer, localData); if (!ti.handlesNewLine()) { writer.newLine(); } } } return writer; } public BaseLocalData prepareBranchLocalData(BaseLocalData localData) { return localData; } 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; } protected static void makeAllStack(List commands, TranslateStack stack) { int pcnt = 0; for (int i = commands.size() - 1; i >= 0; i--) { if (commands.get(i) instanceof PushItem) { pcnt++; } else { break; } } for (int i = commands.size() - pcnt; i < commands.size(); i++) { stack.push(commands.remove(i).value); i--; } } public void makeAllCommands(List commands, TranslateStack stack) { int clen = commands.size(); boolean isExit = false; if (clen > 0) { if (commands.get(clen - 1) instanceof ScriptEndItem) { clen--; isExit = true; } } if (clen > 0) { if (commands.get(clen - 1) instanceof ExitItem) { isExit = true; clen--; } } if (clen > 0) { if (commands.get(clen - 1) instanceof BreakItem) { clen--; } } if (clen > 0) { if (commands.get(clen - 1) instanceof ContinueItem) { clen--; } } for (int i = stack.size() - 1; i >= 0; i--) { GraphTargetItem p = stack.get(i); if (p instanceof BranchStackResistant) { continue; } stack.remove(i); if (!(p instanceof PopItem)) { if (isExit) { //ASC2 leaves some function calls unpopped on stack before returning from a method commands.add(clen, p); } else { commands.add(clen, new PushItem(p)); } } } } protected SwitchItem handleSwitch(GraphTargetItem switchedObject, GraphSourceItem switchStartItem, List foundGotos, Map> partCodes, Map partCodePos, Set visited, Set allParts, TranslateStack stack, List stopPart, List stopPartKind, List loops, List throwStates, BaseLocalData localData, int staticOperation, String path, List caseValuesMap, GraphPart defaultPart, List caseBodyParts, Reference nextRef, Reference tiRef) throws InterruptedException { boolean hasDefault = false; /* case 4: case 5: default: trace("5 & def"); ... case 6: */ //must go backwards to hit case 5, not case 4 for (int i = caseBodyParts.size() - 1; i >= 0; i--) { if (caseBodyParts.get(i) == defaultPart) { DefaultItem di = new DefaultItem(); caseValuesMap.add(i + 1, di); caseBodyParts.add(i + 1, defaultPart); hasDefault = true; break; } } if (!hasDefault) { /* case 1: trace("1"); case 2: trace("2"); //no break default: trace("def"); ... case 3: */ //must go backwards to hit case 2, not case 1 for (int i = caseBodyParts.size() - 1; i >= 0; i--) { if (caseBodyParts.get(i).leadsTo(localData, this, code, defaultPart, loops, throwStates, false)) { DefaultItem di = new DefaultItem(); caseValuesMap.add(i + 1, di); caseBodyParts.add(i + 1, defaultPart); hasDefault = true; break; } } } if (!hasDefault) { /* case 1: trace("1"); break; default: trace("def"); //no break case 2: trace("2"); */ for (int i = 0; i < caseBodyParts.size(); i++) { if (defaultPart.leadsTo(localData, this, code, caseBodyParts.get(i), loops, throwStates, false)) { DefaultItem di = new DefaultItem(); caseValuesMap.add(i, di); caseBodyParts.add(i, defaultPart); hasDefault = true; break; } } } if (!hasDefault) { /* case 1: ... case 2: ... default: trace("def"); */ caseValuesMap.add(new DefaultItem()); caseBodyParts.add(defaultPart); } //gotoTargets GraphPart breakPart = getMostCommonPart(localData, caseBodyParts, loops, throwStates, stopPart); //removeEdgeToFromList(gotoTargets, breakPart); List> caseCommands = new ArrayList<>(); GraphPart next = breakPart; //create switch as new loop break command detection to work Loop currentLoop = new Loop(loops.size(), null, next); currentLoop.phase = 1; loops.add(currentLoop); List valuesMapping = new ArrayList<>(); List caseBodies = new ArrayList<>(); for (int i = 0; i < caseValuesMap.size(); i++) { GraphPart cur = caseBodyParts.get(i); if (!caseBodies.contains(cur)) { caseBodies.add(cur); } } //Sort bodies by leadsto to proper handle clauses without a break statement loopi: for (int i = 0; i < caseBodies.size(); i++) { GraphPart b = caseBodies.get(i); for (int j = i + 1; j < caseBodies.size(); j++) { GraphPart b2 = caseBodies.get(j); if (b2.leadsTo(localData, this, code, b, loops, throwStates, false)) { if (b.leadsTo(localData, this, code, b2, loops, throwStates, false)) { //unstructured code continue; } caseBodies.remove(j); caseBodies.add(i, b2); i--; continue loopi; } else if (j > i + 1) { if (b.leadsTo(localData, this, code, b2, loops, throwStates, false)) { caseBodies.remove(j); caseBodies.add(i + 1, b2); continue loopi; } } } } for (int i = 0; i < caseValuesMap.size(); i++) { GraphPart cur = caseBodyParts.get(i); valuesMapping.add(caseBodies.indexOf(cur)); } for (int i = 0; i < caseBodies.size(); i++) { List currentCaseCommands = new ArrayList<>(); boolean willHaveBreak = false; if (i < caseBodies.size() - 1) { if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops, throwStates, false)) { willHaveBreak = true; } } localData.allSwitchParts.add(caseBodies.get(i)); List stopPart2x = new ArrayList<>(stopPart); List stopPartKind2x = new ArrayList<>(stopPartKind); for (GraphPart b : caseBodies) { if (b != caseBodies.get(i)) { stopPart2x.add(b); stopPartKind2x.add(StopPartKind.OTHER); } } if (breakPart != null) { stopPart2x.add(breakPart); stopPartKind2x.add(StopPartKind.OTHER); } currentCaseCommands = printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, null, caseBodies.get(i), stopPart2x, stopPartKind2x, loops, throwStates, staticOperation, path); if (willHaveBreak) { if (!currentCaseCommands.isEmpty()) { GraphTargetItem last = currentCaseCommands.get(currentCaseCommands.size() - 1); if (!(last instanceof ContinueItem) && !(last instanceof BreakItem) && !(last instanceof GotoItem) && !(last instanceof ExitItem) && !(last instanceof ScriptEndItem)) { currentCaseCommands.add(new BreakItem(null, localData.lineStartInstruction, currentLoop.id)); } } } caseCommands.add(currentCaseCommands); makeAllCommands(currentCaseCommands, stack); } /* switch(a) { case 0: case 1: break; case 2: break; } => switch(a) { case 0: case 1: case 2: break; } */ for (int i = 0; i < caseCommands.size(); i++) { if (caseCommands.get(i).size() == 1 && (caseCommands.get(i).get(0) instanceof BreakItem) && (((BreakItem) caseCommands.get(i).get(0)).loopId == currentLoop.id)) { for (int j = i + 1; j < caseCommands.size(); j++) { if (caseCommands.get(j).size() == 1 && (caseCommands.get(j).get(0) instanceof BreakItem) && (((BreakItem) caseCommands.get(j).get(0)).loopId == currentLoop.id)) { caseCommands.get(j - 1).remove(0); } else { break; } } } } //If the lastone is default empty and alone, remove it if (!caseCommands.isEmpty()) { List lastc = caseCommands.get(caseCommands.size() - 1); if (!lastc.isEmpty() && (lastc.get(lastc.size() - 1) instanceof BreakItem)) { BreakItem bi = (BreakItem) lastc.get(lastc.size() - 1); lastc.remove(lastc.size() - 1); } if (lastc.isEmpty()) { int cnt2 = 0; if (caseValuesMap.get(caseValuesMap.size() - 1) instanceof DefaultItem) { for (int i = valuesMapping.size() - 1; i >= 0; i--) { if (valuesMapping.get(i) == caseCommands.size() - 1) { cnt2++; } } caseValuesMap.remove(caseValuesMap.size() - 1); valuesMapping.remove(valuesMapping.size() - 1); if (cnt2 == 1) { caseCommands.remove(caseCommands.size() - 1); } } } } //remove last break from last section if (!caseCommands.isEmpty()) { List lastc = caseCommands.get(caseCommands.size() - 1); if (!lastc.isEmpty() && (lastc.get(lastc.size() - 1) instanceof BreakItem)) { BreakItem bi = (BreakItem) lastc.get(lastc.size() - 1); lastc.remove(lastc.size() - 1); } } nextRef.setVal(next); currentLoop.phase = 2; GraphTargetItem ti = checkLoop(new ArrayList<>() /*??*/, next, stopPart, loops, throwStates); tiRef.setVal(ti); return new SwitchItem(null, switchStartItem, currentLoop, switchedObject, caseValuesMap, caseCommands, valuesMapping); } protected boolean partIsSwitch(GraphPart part) { return false; } }