diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java index bfa9ea48a..d691e4cf4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java @@ -48,7 +48,7 @@ import java.util.Set; * * @author JPEXS */ -public class AbcIndexing { +public final class AbcIndexing { private AbcIndexing parent = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfoSpecial.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfoSpecial.java index c81568811..79bbcf18a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfoSpecial.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfoSpecial.java @@ -36,4 +36,9 @@ public class DumpInfoSpecial extends DumpInfo { this.specialType = specialType; this.specialValue = specialValue; } + + @Override + public String toString() { + return super.toString() + " (special: " + specialValue + ")"; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java index 631a75f84..4c1c5c780 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java @@ -1,160 +1,160 @@ -/* - * Copyright (C) 2010-2016 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.flash.importers; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.helpers.ImageHelper; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.enums.ImageFormat; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.Helper; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/** - * - * @author JPEXS - */ -public class ImageImporter extends TagImporter { - - public Tag importImage(ImageTag it, byte[] newData) throws IOException { - return importImage(it, newData, 0); - } - - public Tag importImage(ImageTag it, byte[] newData, int tagType) throws IOException { - if (newData[0] == 'B' && newData[1] == 'M') { - BufferedImage b = ImageHelper.read(newData); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageHelper.write(b, ImageFormat.PNG, baos); - newData = baos.toByteArray(); - } - - if (tagType == 0) { - if (it instanceof DefineBitsTag) { - tagType = DefineBitsJPEG2Tag.ID; - } else { - tagType = it.getId(); - } - } - - if (it.getId() == tagType) { - it.setImage(newData); - } else { - SWF swf = it.getSwf(); - ImageTag imageTag; - ByteArrayRange range = it.getOriginalRange(); - int characterId = it.getCharacterId(); - switch (tagType) { - case DefineBitsJPEG2Tag.ID: { - imageTag = new DefineBitsJPEG2Tag(swf, range, characterId, newData); - break; - } - case DefineBitsJPEG3Tag.ID: { - imageTag = new DefineBitsJPEG3Tag(swf, range, characterId, newData); - break; - } - case DefineBitsJPEG4Tag.ID: { - imageTag = new DefineBitsJPEG4Tag(swf, range, characterId, newData); - break; - } - case DefineBitsLosslessTag.ID: { - DefineBitsLosslessTag losslessTag = new DefineBitsLosslessTag(swf, range, characterId); - losslessTag.setImage(newData); - imageTag = losslessTag; - break; - } - case DefineBitsLossless2Tag.ID: { - DefineBitsLossless2Tag lossless2Tag = new DefineBitsLossless2Tag(swf, range, characterId); - lossless2Tag.setImage(newData); - imageTag = lossless2Tag; - break; - } - default: - throw new Error("Unsupported image type tag."); - } - - imageTag.setModified(true); - swf.replaceTag(it, imageTag); - swf.updateCharacters(); - return imageTag; - } - - return null; - } - - public Tag importImageAlpha(ImageTag it, byte[] newData) throws IOException { - - try { - BufferedImage img = ImageHelper.read(newData); - int width = img.getWidth(); - int height = img.getHeight(); - byte[] data = new byte[width * height]; - int[] imgData = img.getRGB(0, 0, width, height, null, 0, width); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int alpha = (imgData[y * width + x] >> 24) & 0xff; - data[y * width + x] = (byte) alpha; - } - } - - newData = data; - } catch (IOException ex) { - } - - if (it instanceof DefineBitsJPEG3Tag) { - ((DefineBitsJPEG3Tag) it).setImageAlpha(newData); - } else if (it instanceof DefineBitsJPEG4Tag) { - ((DefineBitsJPEG4Tag) it).setImageAlpha(newData); - } - return null; - } - - public void convertImage(ImageTag it, int tagType) throws IOException { - importImage(it, Helper.readStream(it.getImageData()), tagType); - } - - public static int getImageTagType(String format) { - int res = 0; - switch (format) { - case "lossless": - res = DefineBitsLosslessTag.ID; - break; - case "lossless2": - res = DefineBitsLossless2Tag.ID; - break; - case "jpeg2": - res = DefineBitsJPEG2Tag.ID; - break; - case "jpeg3": - res = DefineBitsJPEG3Tag.ID; - break; - case "jpeg4": - res = DefineBitsJPEG4Tag.ID; - break; - } - - return res; - } -} +/* + * Copyright (C) 2010-2016 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.flash.importers; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.helpers.ImageHelper; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.enums.ImageFormat; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * + * @author JPEXS + */ +public class ImageImporter extends TagImporter { + + public Tag importImage(ImageTag it, byte[] newData) throws IOException { + return importImage(it, newData, 0); + } + + public Tag importImage(ImageTag it, byte[] newData, int tagType) throws IOException { + if (newData[0] == 'B' && newData[1] == 'M') { + BufferedImage b = ImageHelper.read(newData); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageHelper.write(b, ImageFormat.PNG, baos); + newData = baos.toByteArray(); + } + + if (tagType == 0) { + if (it instanceof DefineBitsTag) { + tagType = DefineBitsJPEG2Tag.ID; + } else { + tagType = it.getId(); + } + } + + if (it.getId() == tagType) { + it.setImage(newData); + } else { + SWF swf = it.getSwf(); + ImageTag imageTag; + ByteArrayRange range = it.getOriginalRange(); + int characterId = it.getCharacterId(); + switch (tagType) { + case DefineBitsJPEG2Tag.ID: { + imageTag = new DefineBitsJPEG2Tag(swf, range, characterId, newData); + break; + } + case DefineBitsJPEG3Tag.ID: { + imageTag = new DefineBitsJPEG3Tag(swf, range, characterId, newData); + break; + } + case DefineBitsJPEG4Tag.ID: { + imageTag = new DefineBitsJPEG4Tag(swf, range, characterId, newData); + break; + } + case DefineBitsLosslessTag.ID: { + DefineBitsLosslessTag losslessTag = new DefineBitsLosslessTag(swf, range, characterId); + losslessTag.setImage(newData); + imageTag = losslessTag; + break; + } + case DefineBitsLossless2Tag.ID: { + DefineBitsLossless2Tag lossless2Tag = new DefineBitsLossless2Tag(swf, range, characterId); + lossless2Tag.setImage(newData); + imageTag = lossless2Tag; + break; + } + default: + throw new Error("Unsupported image type tag."); + } + + imageTag.setModified(true); + swf.replaceTag(it, imageTag); + swf.updateCharacters(); + return imageTag; + } + + return null; + } + + public Tag importImageAlpha(ImageTag it, byte[] newData) throws IOException { + + try { + BufferedImage img = ImageHelper.read(newData); + int width = img.getWidth(); + int height = img.getHeight(); + byte[] data = new byte[width * height]; + int[] imgData = img.getRGB(0, 0, width, height, null, 0, width); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int alpha = (imgData[y * width + x] >> 24) & 0xff; + data[y * width + x] = (byte) alpha; + } + } + + newData = data; + } catch (IOException ex) { + } + + if (it instanceof DefineBitsJPEG3Tag) { + ((DefineBitsJPEG3Tag) it).setImageAlpha(newData); + } else if (it instanceof DefineBitsJPEG4Tag) { + ((DefineBitsJPEG4Tag) it).setImageAlpha(newData); + } + return null; + } + + public void convertImage(ImageTag it, int tagType) throws IOException { + importImage(it, Helper.readStream(it.getImageData()), tagType); + } + + public static int getImageTagType(String format) { + int res = 0; + switch (format) { + case "lossless": + res = DefineBitsLosslessTag.ID; + break; + case "lossless2": + res = DefineBitsLossless2Tag.ID; + break; + case "jpeg2": + res = DefineBitsJPEG2Tag.ID; + break; + case "jpeg3": + res = DefineBitsJPEG3Tag.ID; + break; + case "jpeg4": + res = DefineBitsJPEG4Tag.ID; + break; + } + + return res; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java index ccb379054..99fa23bc1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -1,2480 +1,2480 @@ -/* - * Copyright (C) 2010-2016 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.model.FunctionActionItem; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.model.AndItem; -import com.jpexs.decompiler.graph.model.BreakItem; -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 java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -/** - * - * @author JPEXS - */ -public class Graph { - - public List heads; - - protected GraphSource code; - - private final List alternateEntries; - - public static final int SOP_USE_STATIC = 0; - - public static final int SOP_SKIP_STATIC = 1; - - public static final int SOP_REMOVE_STATIC = 2; - - /** - * Identify loop exits - * - * @param localData - * @param allParts All nodes - * @return - */ - public Map> identifyLoopBreaks(BaseLocalData localData, Set allParts) { - Map> lb = new HashMap<>(); - - for (GraphPart b0 : allParts) { - List np = new ArrayList<>(b0.nextParts); - np.addAll(b0.throwParts); - for (GraphPart b : np) { - GraphPart hdr = (b0.type == GraphPart.TYPE_LOOP_HEADER || b0.type == GraphPart.TYPE_REENTRY) ? b0 : b0.iloop_header; - - if (hdr != null && b.iloop_header != hdr && b.iloop_header == hdr.iloop_header && b != hdr) { - if (!lb.containsKey(hdr)) { - lb.put(hdr, new ArrayList<>()); - } - if (!lb.get(hdr).contains(b)) { - lb.get(hdr).add(b); - } - } - } - } - - return lb; - } - - /** - * Identifying loops. Based on http://lenx.100871.net/papers/loop-SAS.pdf - * - * @param localData - * @param loopContinues Result - list of loop headers - * @param heads Entries - * @param appParts All Nodes - */ - public void identifyLoops(BaseLocalData localData, List loopContinues, List heads, Set appParts) { - for (GraphPart b : appParts) { - b.traversed = false; - b.DFSP_pos = 0; - b.irreducible = false; - b.type = GraphPart.TYPE_NONE; - //initialize b - } - for (GraphPart h0 : heads) { - trav_loops_DFS(localData, loopContinues, h0, 1); - } - } - - /** - * Tag h as loop headr of b. - * - * @param b Block - * @param h Loop header - */ - protected void tag_lhead(GraphPart b, GraphPart h) { - if (b == h || h == null) { - return; - } - GraphPart cur1 = b; - GraphPart cur2 = h; - while (cur1.iloop_header != null) { - GraphPart ih = cur1.iloop_header; - if (ih == cur2) { - return; - } - if (ih.DFSP_pos < cur2.DFSP_pos) { - cur1.iloop_header = cur2; - cur1 = cur2; - cur2 = ih; - } else { - cur1 = h; - } - } - if (cur1 == cur2) { - return; - } - - cur1.iloop_header = cur2; - } - - protected List filter(List list) { - return new ArrayList<>(list); - } - - /** - * Traverse loops deep first search - * - * @param localData - * @param loopHeaders Resulting loop headers - * @param b0 Current node - * @param DFSP_pos Position in DFSP - * @return innermost loop header of b0 - */ - protected GraphPart trav_loops_DFS(BaseLocalData localData, List loopHeaders, GraphPart b0, int DFSP_pos) { - - List folParts = new ArrayList<>(b0.nextParts); - folParts.addAll(b0.throwParts); - - b0.traversed = true; - b0.DFSP_pos = DFSP_pos; //Mark b0’s position in DFSP - for (GraphPart b : folParts) { - b = checkPart(null, localData, b, null); - if (b == null) { - continue; - } - if (!b.traversed) { - //case (A), new - GraphPart nh = trav_loops_DFS(localData, loopHeaders, b, DFSP_pos + 1); - tag_lhead(b0, nh); - } else if (b.DFSP_pos > 0) { // b in DFSP(b0) - //case (B) - if (b.type != GraphPart.TYPE_LOOP_HEADER) { - b.type = GraphPart.TYPE_LOOP_HEADER; - loopHeaders.add(b); - } - tag_lhead(b0, b); - } else if (b.iloop_header == null) { - //case (C), do nothing - } else { - GraphPart h = b.iloop_header; - if (h.DFSP_pos > 0) { // h in DFSP(b0) - //case (D) - tag_lhead(b0, h); - } else { // h not in DFSP(b0) - //case (E), reentry - b.type = GraphPart.TYPE_REENTRY; //TODO:and b0,b ? - h.irreducible = true; - while (h.iloop_header != null) { - h = h.iloop_header; - if (h.DFSP_pos > 0) { //h in DFSP(b0) - tag_lhead(b0, h); - break; - } - h.irreducible = true; - } - } - } - } - b0.DFSP_pos = 0; // clear b0’s DFSP position - return b0.iloop_header; - } - - public Graph(GraphSource code, List alternateEntries) { - this.code = code; - this.alternateEntries = alternateEntries; - - } - - public void init(BaseLocalData localData) throws InterruptedException { - if (heads != null) { - return; - } - heads = makeGraph(code, new ArrayList<>(), alternateEntries); - int time = 1; - List ordered = new ArrayList<>(); - List visited = new ArrayList<>(); - for (GraphPart head : heads) { - time = head.setTime(time, ordered, visited); - } - } - - 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++; - } - } - - private void getReachableParts(GraphPart part, LinkedHashSet ret, List loops) { - // use LinkedHashSet to preserve order - getReachableParts(part, ret, loops, true); - } - - private void getReachableParts(GraphPart part, LinkedHashSet ret, List loops, 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(); - loopnext: - for (GraphPart next : part.nextParts) { - 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) throws InterruptedException { - return getCommonPart(localData, part.nextParts, loops); - } - - //TODO: Make this faster! - public GraphPart getCommonPart(BaseLocalData localData, List parts, List loops) throws InterruptedException { - if (parts.isEmpty()) { - return null; - } - - List loopContinues = new ArrayList<>();//getLoopsContinues(loops); - for (Loop l : loops) { - if (l.phase == 1) { - loopContinues.add(l.loopContinue); - } - } - - 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)) { - common = false; - break; - } - } - if (common) { - return p; - } - } - List> reachable = new ArrayList<>(); - for (GraphPart p : parts) { - LinkedHashSet r1 = new LinkedHashSet<>(); - getReachableParts(p, r1, loops); - r1.add(p); - reachable.add(r1); - } - Set first = reachable.get(0); - for (GraphPart p : first) { - /*if (ignored.contains(p)) { - continue; - }*/ - p = checkPart(null, localData, p, null); - if (p == null) { - continue; - } - boolean common = true; - for (Set r : reachable) { - if (!r.contains(p)) { - common = false; - break; - } - } - if (common) { - return p; - } - } - return null; - } - - public GraphPart getMostCommonPart(BaseLocalData localData, List parts, List loops) throws InterruptedException { - if (parts.isEmpty()) { - return null; - } - - Set s = new HashSet<>(parts); //unique - parts = new ArrayList<>(s); //make local copy - - List loopContinues = new ArrayList<>();//getLoopsContinues(loops); - for (Loop l : loops) { - if (l.phase == 1) { - loopContinues.add(l.loopContinue); - loopContinues.add(l.loopPreContinue); - } - } - - for (GraphPart p : parts) { - if (loopContinues.contains(p)) { - break; - } - boolean common = true; - for (GraphPart q : parts) { - if (q == p) { - continue; - } - if (!q.leadsTo(localData, this, code, p, loops)) { - common = false; - break; - } - } - if (common) { - return p; - } - } - - loopi: - for (int i = 0; i < parts.size(); i++) { - for (int j = 0; j < parts.size(); j++) { - if (j == i) { - continue; - } - if (parts.get(i).leadsTo(localData, this, code, parts.get(j), loops)) { - parts.remove(i); - i--; - continue loopi; - } - } - } - List> reachable = new ArrayList<>(); - for (GraphPart p : parts) { - LinkedHashSet r1 = new LinkedHashSet<>(); - getReachableParts(p, r1, loops); - Set r2 = new LinkedHashSet<>(); - r2.add(p); - r2.addAll(r1); - reachable.add(r2); - } - ///List first = reachable.get(0); - int commonLevel; - Map levelMap = new HashMap<>(); - for (Set first : reachable) { - int maxclevel = 0; - Set visited = new HashSet<>(); - for (GraphPart p : first) { - if (loopContinues.contains(p)) { - break; - } - if (visited.contains(p)) { - continue; - } - visited.add(p); - boolean common = true; - commonLevel = 1; - for (Set r : reachable) { - if (r == first) { - continue; - } - if (r.contains(p)) { - commonLevel++; - } - } - if (commonLevel <= maxclevel) { - continue; - } - maxclevel = commonLevel; - if (levelMap.containsKey(p)) { - if (levelMap.get(p) > commonLevel) { - commonLevel = levelMap.get(p); - } - } - levelMap.put(p, commonLevel); - if (common) { - //return p; - } - } - } - for (int i = reachable.size() - 1; i >= 2; i--) { - for (GraphPart p : levelMap.keySet()) { - if (levelMap.get(p) == i) { - return p; - } - } - } - for (GraphPart p : levelMap.keySet()) { - if (levelMap.get(p) == parts.size()) { - return p; - } - } - return null; - } - - public GraphPart getNextNoJump(GraphPart part, 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 = part.nextParts.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 alternateEntries, int staticOperation) throws InterruptedException { - Graph g = new Graph(code, alternateEntries); - g.init(localData); - return g.translate(localData, staticOperation, path); - } - - public List translate(BaseLocalData localData, int staticOperation, String path) throws InterruptedException { - Set allParts = new HashSet<>(); - for (GraphPart head : heads) { - populateParts(head, allParts); - } - TranslateStack stack = new TranslateStack(path); - List loops = new ArrayList<>(); - - //TODO: Make this working. :-( - final boolean newLoopDetection = false; - - if (!newLoopDetection) { - getLoops(localData, heads.get(0), loops, null); - } else { - List loopHeads = new ArrayList<>(); - identifyLoops(localData, loopHeads, heads, allParts); - Map> loopBreaks = identifyLoopBreaks(localData, allParts); - - List loops2 = new ArrayList<>(); - for (int i = 0; i < loopHeads.size(); i++) { - loops2.add(new Loop(loops2.size(), loopHeads.get(i), null)); - } - for (int i = 0; i < loopHeads.size(); i++) { - if (loopBreaks.containsKey(loopHeads.get(i))) { - loops2.get(i).loopBreak = loopBreaks.get(loopHeads.get(i)).get(0);//getMostCommonPart(localData, loopBreaks.get(loopHeads.get(i)), loops2); - } else { - loops2.get(i).loopBreak = null; - } - } - - loops = loops2; - } - - /* - System.err.println(""); - for (Loop el : loops) { - System.err.println(el); - } - System.err.println(""); - */ - //TODO: Make getPrecontinues faster - getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); - - /*System.err.println(""); - for (Loop el : loops) { - System.err.println(el); - } - System.err.println("");//*/ - List ret = printGraph(new HashMap<>(), new HashMap<>(), localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path); - processIfs(ret); - finalProcessStack(stack, ret); - finalProcessAll(ret, 0, new FinalProcessLocalData()); - return ret; - } - - public void finalProcessStack(TranslateStack stack, List output) { - } - - private void finalProcessAll(List list, int level, FinalProcessLocalData localData) throws InterruptedException { - finalProcess(list, level, localData); - for (GraphTargetItem item : list) { - if (item instanceof Block) { - List> subs = ((Block) item).getSubs(); - for (List sub : subs) { - finalProcessAll(sub, level + 1, localData); - } - } - } - finalProcessAfter(list, level, localData); - } - - 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) { - 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) 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 processIfs(List list) { - - 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 ((!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(); - } - } - - if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) { - if (onFalse.get(onFalse.size() - 1) instanceof ExitItem) { - if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) { - list.add(i + 1, onTrue.remove(onTrue.size() - 1)); - } - } - } - } - } - - //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; - } - - protected GraphTargetItem checkLoop(GraphPart part, List stopPart, List loops) { - 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.loopContinue == part) { - return (new ContinueItem(null, firstIns, l.id)); - } - if (l.loopBreak == part) { - return (new BreakItem(null, firstIns, l.id)); - } - } - return null; - } - - 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(Map> partCodes, Map partCodePos, GraphSource code, BaseLocalData localData, Set allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { - return null; - } - - protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart part, Set allParts) { - return part; - } - - //@SuppressWarnings("unchecked") - protected GraphTargetItem translatePartGetStack(BaseLocalData localData, GraphPart part, TranslateStack stack, int staticOperation) throws InterruptedException { - 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 { - 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(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 List printGraph(Map> partCodes, Map partCodePos, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List loops, int staticOperation, String path) throws InterruptedException { - return printGraph(partCodes, partCodePos, new HashSet<>(), localData, stack, allParts, parent, part, stopPart, loops, null, staticOperation, path, 0); - } - - protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List loops) { - return loopItem; - } - - //TODO: Make this faster!!! - private void getPrecontinues(String path, BaseLocalData localData, GraphPart parent, GraphPart part, Set allParts, List loops, List stopPart) throws InterruptedException { - try { - markLevels(path, localData, part, allParts, loops); - } catch (ThreadDeath | InterruptedException iex) { - throw iex; - } catch (Throwable ex) { - //It is unusual code so markLevels failed, nevermind, it can still work - } - //Note: this also marks part as precontinue when there is if - /* - while(k<10){ - if(k==7){ - trace(a); - }else{ - trace(b); - } - //precontinue - k++; - } - - */ - looploops: - for (Loop l : loops) { - if (l.loopContinue != null) { - Set uniqueRefs = new HashSet<>(); - uniqueRefs.addAll(l.loopContinue.refs); - if (uniqueRefs.size() == 2) { //only one path - from precontinue - List uniqueRefsList = new ArrayList<>(uniqueRefs); - if (uniqueRefsList.get(0).discoveredTime > uniqueRefsList.get(1).discoveredTime) { //latch node is discovered later - part = uniqueRefsList.get(0); - } else { - part = uniqueRefsList.get(1); - } - if (part == l.loopContinue) { - continue looploops; - } - - while (part.refs.size() == 1) { - if (part.refs.get(0).nextParts.size() != 1) { - continue looploops; - } - - part = part.refs.get(0); - if (part == l.loopContinue) { - break; - } - } - if (part.level == 0 && part != l.loopContinue) { - l.loopPreContinue = part; - } - } - } - } - /*clearLoops(loops); - getPrecontinues(parent, part, loops, stopPart, 0, new ArrayList()); - clearLoops(loops);*/ - } - - private void markLevels(String path, BaseLocalData localData, GraphPart part, Set allParts, List loops) throws InterruptedException { - clearLoops(loops); - markLevels(path, localData, part, allParts, loops, new ArrayList<>(), 1, new HashSet<>(), 0); - clearLoops(loops); - } - - private void markLevels(String path, BaseLocalData localData, GraphPart part, Set allParts, List loops, List stopPart, int level, Set visited, int recursionLevel) throws InterruptedException { - boolean debugMode = false; - if (stopPart == null) { - stopPart = new ArrayList<>(); - } - if (recursionLevel > allParts.size() + 1) { - throw new RuntimeException(path + ": markLevels max recursion level reached"); - } - - if (debugMode) { - System.err.println("markLevels " + part); - } - if (stopPart.contains(part)) { - return; - } - for (Loop el : loops) { - if ((el.phase == 2) && (el.loopContinue == part)) { - return; - } - if (el.phase != 1) { - if (debugMode) { - //System.err.println("ignoring "+el); - } - continue; - } - if (el.loopContinue == part) { - return; - } - if (el.loopPreContinue == part) { - return; - } - if (el.loopBreak == part) { - return; - } - } - - if (visited.contains(part)) { - part.level = 0; - } else { - visited.add(part); - part.level = level; - } - - boolean isLoop = false; - Loop currentLoop = null; - for (Loop el : loops) { - if ((el.phase == 0) && (el.loopContinue == part)) { - isLoop = true; - currentLoop = el; - el.phase = 1; - break; - } - } - - List nextParts = checkPrecoNextParts(part); - if (nextParts == null) { - nextParts = part.nextParts; - } - - if (nextParts.size() == 2) { - GraphPart next = getCommonPart(localData, nextParts, loops);//part.getNextPartPath(new ArrayList()); - List stopParts2 = new ArrayList<>(); //stopPart); - if (next != null) { - stopParts2.add(next); - } else if (!stopPart.isEmpty()) { - stopParts2.add(stopPart.get(stopPart.size() - 1)); - } - if (next != nextParts.get(0)) { - markLevels(path, localData, nextParts.get(0), allParts, loops, next == null ? stopPart : stopParts2, level + 1, visited, recursionLevel + 1); - } - if (next != nextParts.get(1)) { - markLevels(path, localData, nextParts.get(1), allParts, loops, next == null ? stopPart : stopParts2, level + 1, visited, recursionLevel + 1); - } - if (next != null) { - markLevels(path, localData, next, allParts, loops, stopPart, level, visited, recursionLevel + 1); - } - } - - if (nextParts.size() > 2) { - GraphPart next = getMostCommonPart(localData, nextParts, loops); - List vis = new ArrayList<>(); - for (GraphPart p : nextParts) { - if (vis.contains(p)) { - continue; - } - List stopPart2 = new ArrayList<>(); //(stopPart); - if (next != null) { - stopPart2.add(next); - } else if (!stopPart.isEmpty()) { - stopPart2.add(stopPart.get(stopPart.size() - 1)); - } - for (GraphPart p2 : nextParts) { - if (p2 == p) { - continue; - } - if (!stopPart2.contains(p2)) { - stopPart2.add(p2); - } - } - if (next != p) { - markLevels(path, localData, p, allParts, loops, stopPart2, level + 1, visited, recursionLevel + 1); - vis.add(p); - } - } - if (next != null) { - markLevels(path, localData, next, allParts, loops, stopPart, level, visited, recursionLevel + 1); - } - } - - if (nextParts.size() == 1) { - markLevels(path, localData, nextParts.get(0), allParts, loops, stopPart, level, visited, recursionLevel + 1); - } - - for (GraphPart t : part.throwParts) { - if (!visited.contains(t)) { - List stopPart2 = new ArrayList<>(); - List cmn = new ArrayList<>(); - cmn.add(part); - cmn.add(t); - GraphPart next = getCommonPart(localData, cmn, loops); - if (next != null) { - stopPart2.add(next); - } else { - stopPart2 = stopPart; - } - - markLevels(path, localData, t, allParts, loops, stopPart2, level, visited, recursionLevel + 1); - } - } - - if (isLoop) { - if (currentLoop != null && currentLoop.loopBreak != null) { - currentLoop.phase = 2; - markLevels(path, localData, currentLoop.loopBreak, allParts, loops, stopPart, level, visited, recursionLevel + 1); - } - } - } - - private void clearLoops(List loops) { - for (Loop l : loops) { - l.phase = 0; - } - } - - private void getLoops(BaseLocalData localData, GraphPart part, List loops, List stopPart) throws InterruptedException { - clearLoops(loops); - getLoops(localData, part, loops, stopPart, true, 1, new ArrayList<>()); - clearLoops(loops); - } - - private void getLoops(BaseLocalData localData, GraphPart part, List loops, List stopPart, boolean first, int level, List visited) throws InterruptedException { - boolean debugMode = false; - - if (part == null) { - return; - } - - part = checkPart(null, localData, part, null); - if (part == null) { - return; - } - if (!visited.contains(part)) { - visited.add(part); - } - - if (debugMode) { - 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; - } - - } - } - if (lastP1 != null) { - if (lastP1.breakCandidates.contains(part)) { - lastP1.breakCandidates.add(part); - lastP1.breakCandidatesLevels.add(level); - return; - } else { - //List loopContinues2 = new ArrayList<>(loopContinues); - //loopContinues2.remove(lastP1.loopContinue); - List loops2 = new ArrayList<>(loops); - loops2.remove(lastP1); - if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2)) { - if (lastP1.breakCandidatesLocked == 0) { - if (debugMode) { - System.err.println("added breakCandidate " + part + " to " + lastP1); - } - - lastP1.breakCandidates.add(part); - lastP1.breakCandidatesLevels.add(level); - return; - } - } - } - } - - 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); - Loop currentLoop = null; - if (isLoop) { - currentLoop = new Loop(loops.size(), part, null); - currentLoop.phase = 1; - loops.add(currentLoop); - //loopContinues.add(part); - } - - if (part.nextParts.size() == 2) { - - List nps;/* = new ArrayList<>(part.nextParts); - for(int i=0;i stopPart2 = stopPart == null ? new ArrayList<>() : new ArrayList<>(stopPart); - if (next != null) { - stopPart2.add(next); - } - if (next != nps.get(0)) { - getLoops(localData, nps.get(0), loops, stopPart2, false, level + 1, visited); - } - if (next != nps.get(1)) { - getLoops(localData, nps.get(1), loops, stopPart2, false, level + 1, visited); - } - if (next != null) { - getLoops(localData, next, loops, stopPart, false, level, visited); - } - } else if (part.nextParts.size() > 2) { - GraphPart next = getNextCommonPart(localData, part, loops); - - 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) { - getLoops(localData, p, loops, stopPart2, false, level + 1, visited); - } - } - if (next != null) { - getLoops(localData, next, loops, stopPart, false, level, visited); - } - } else if (part.nextParts.size() == 1) { - getLoops(localData, part.nextParts.get(0), loops, stopPart, false, level, visited); - } - - List loops2 = new ArrayList<>(loops); - for (Loop l : loops2) { - l.breakCandidatesLocked++; - } - for (GraphPart t : part.throwParts) { - if (!visited.contains(t)) { - getLoops(localData, t, loops, stopPart, false, level, visited); - } - } - for (Loop l : loops2) { - l.breakCandidatesLocked--; - } - - if (isLoop && currentLoop != null) { - GraphPart found; - Map removed = new HashMap<>(); - do { - found = null; - for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { - GraphPart ch = checkPart(null, localData, currentLoop.breakCandidates.get(i), null); - if (ch == null) { - currentLoop.breakCandidates.remove(i); - i--; - } - } - loopcand: - for (GraphPart cand : currentLoop.breakCandidates) { - for (GraphPart cand2 : currentLoop.breakCandidates) { - if (cand == cand2) { - continue; - } - if (cand.leadsTo(localData, this, code, cand2, loops)) { - 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); - } - } - } - if (lev1 <= lev2) { - found = cand2; - } else { - found = cand; - } - 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; - for (GraphPart cand : currentLoop.breakCandidates) { - - 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) { - } 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; - } - getLoops(localData, r, loops, stopPart, false, removed.get(r), visited); - 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; - } - } - getLoops(localData, currentLoop.loopBreak, loops, stopPart, false, level, visited); - } - } - - protected List printGraph(Map> partCodes, Map partCodePos, Set visited, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List loops, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - if (stopPart == null) { - stopPart = new ArrayList<>(); - } - if (recursionLevel > allParts.size() + 1) { - throw new TranslateException("printGraph max recursion level reached."); - } - - if (ret == null) { - ret = new ArrayList<>(); - } - //try { - boolean debugMode = false; - - if (debugMode) { - System.err.println("PART " + part + " nextsize:" + part.nextParts.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(part.nextParts.get(0), stopPart, loops); - if (lop == null) { - part = part.nextParts.get(0); - } else { - break; - } - }*/ - if (part == null) { - return ret; - } - part = checkPart(stack, localData, part, allParts); - if (part == null) { - return ret; - } - - if (part.ignored) { - return ret; - } - - //List loopContinues = getLoopsContinues(loops); - boolean isLoop = false; - Loop currentLoop = null; - for (Loop el : loops) { - if ((el.loopContinue == part) && (el.phase == 0)) { - currentLoop = el; - currentLoop.phase = 1; - isLoop = true; - break; - } - } - - if (debugMode) { - System.err.println("loopsize:" + loops.size()); - } - for (int l = loops.size() - 1; l >= 0; l--) { - Loop el = loops.get(l); - if (el == currentLoop) { - if (debugMode) { - System.err.println("ignoring current loop " + el); - } - continue; - } - if (el.phase != 1) { - if (debugMode) { - System.err.println("ignoring loop " + el); - } - continue; - } - if (el.loopBreak == part) { - if (currentLoop != null) { - currentLoop.phase = 0; - } - if (debugMode) { - 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 (debugMode) { - 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 (debugMode) { - System.err.println("Adding continue"); - } - ret.add(new ContinueItem(null, localData.lineStartInstruction, el.id)); - return ret; - } - } - - if (stopPart.contains(part)) { - if (currentLoop != null) { - currentLoop.phase = 0; - } - if (debugMode) { - System.err.println("Stopped on part " + part); - } - return ret; - } - - if (code.size() <= part.start) { - ret.add(new ScriptEndItem()); - return ret; - } - - 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.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(); - stack.clear(); - loopItem = new UniversalLoopItem(null, localData.lineStartInstruction, currentLoop); - //loopItem.commands=printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops); - currentRet.add(loopItem); - loopItem.commands = new ArrayList<>(); - currentRet = loopItem.commands; - //return ret; - } - - boolean parseNext = true; - - //****************************DECOMPILING PART************* - List output = new ArrayList<>(); - - List parts = new ArrayList<>(); - if (part instanceof GraphPartMulti) { - parts = ((GraphPartMulti) part).parts; - } else { - parts.add(part); - } - for (GraphPart p : parts) { - int end = p.end; - int start = p.start; - - output.addAll(code.translatePart(p, localData, stack, start, end, staticOperation, path)); - if ((end >= code.size() - 1) && p.nextParts.isEmpty()) { - output.add(new ScriptEndItem()); - } - } - - if (parseNext) { - List retCheck = check(partCodes, partCodePos, code, localData, allParts, stack, parent, part, stopPart, loops, 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 (part.nextParts.size() > 2) { - GraphPart next = getMostCommonPart(localData, part.nextParts, loops); - List vis = new ArrayList<>(); - GraphTargetItem switchedItem = stack.pop(); - makeAllCommands(currentRet, stack); - - List caseValues = new ArrayList<>(); - List> caseCommands = new ArrayList<>(); - List valueMappings = new ArrayList<>(); - Loop swLoop = new Loop(loops.size(), null, next); - swLoop.phase = 1; - loops.add(swLoop); - 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; - - while (it instanceof TernarOpItem) { - TernarOpItem to = (TernarOpItem) it; - if (to.expression instanceof EqualsTypeItem) { - if (to.onTrue instanceof IntegerValueTypeItem) { - int cpos = ((IntegerValueTypeItem) to.onTrue).intValue(); - caseExpressionLeftSides.put(cpos, ((EqualsTypeItem) to.expression).getLeftSide()); - caseExpressionRightSides.put(cpos, ((EqualsTypeItem) to.expression).getRightSide()); - it = to.onFalse; - } else { - break; - } - } else if (to.expression instanceof FalseItem) { - it = to.onFalse; - } else if (to.expression instanceof TrueItem) { - it = to.onTrue; - } else { - break; - } - } - //int ignoredBranch = -1; - if (it instanceof IntegerValueTypeItem) { - defaultBranch = ((IntegerValueTypeItem) it).intValue(); - } - - if (!caseExpressionRightSides.isEmpty()) { - GraphTargetItem firstItem; - firstItem = (GraphTargetItem) caseExpressionRightSides.values().toArray()[0]; - boolean sameRight = true; - for (GraphTargetItem cit : caseExpressionRightSides.values()) { - if (!cit.equals(firstItem)) { - sameRight = false; - break; - } - } - - if (sameRight) { - caseExpressions = caseExpressionLeftSides; - switchedItem = firstItem; - 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; - 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 ? part.nextParts.get(1 + defaultBranch) : part.nextParts.get(0); - //int defaultNum = hasExpr ? 1 + defaultBranch : 0; - - for (int i = 1; i < part.nextParts.size(); i++) { - if (caseExpressions.containsKey(pos)) { - caseValues.add(caseExpressions.get(pos)); - } else if (part.nextParts.get(i) == defaultPart) { - caseValues.add(new DefaultItem()); - } else { - caseValues.add(new IntegerValueItem(null, localData.lineStartInstruction, pos)); - } - pos++; - } - - first = true; - pos = 0; - List nextCommands = new ArrayList<>(); - for (int i = 1; i < part.nextParts.size(); i++) { - GraphPart p = part.nextParts.get(i); - /*if (pos == ignoredBranch) { - pos++; - continue; - }*/ - //if (p != defaultPart) - { - if (vis.contains(p)) { - valueMappings.add(caseCommands.size() - 1); - continue; - } - valueMappings.add(caseCommands.size()); - } - List stopPart2 = new ArrayList<>(); - if (next != null) { - stopPart2.add(next); - } else if (!stopPart.isEmpty()) { - stopPart2.add(stopPart.get(stopPart.size() - 1)); - } - for (GraphPart p2 : part.nextParts) { - if (p2 == p) { - continue; - } - if (!stopPart2.contains(p2)) { - stopPart2.add(p2); - } - } - if (next != p) { - //if (p == defaultPart && !defaultCommands.isEmpty()) { - //ignore - //} else - { - TranslateStack s2 = (TranslateStack) stack.clone(); - s2.clear(); - nextCommands = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), s2, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - makeAllCommands(nextCommands, s2); - caseCommands.add(nextCommands); - vis.add(p); - } - } else { - caseCommands.add(nextCommands); - } - first = false; - pos++; - } - - //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); - if (bi.loopId == swLoop.id) { - lastc.remove(lastc.size() - 1); - } - } - if (lastc.isEmpty()) { - int cnt = 0; - if (caseValues.get(caseValues.size() - 1) instanceof DefaultItem) { - for (int i = valueMappings.size() - 1; i >= 0; i--) { - if (valueMappings.get(i) == caseCommands.size() - 1) { - cnt++; - } - } - if (cnt == 1) { - caseValues.remove(caseValues.size() - 1); - valueMappings.remove(valueMappings.size() - 1); - caseCommands.remove(lastc); - } - } - } - } - //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); - if (bi.loopId == swLoop.id) { - lastc.remove(lastc.size() - 1); - } - } - } - SwitchItem sw = new SwitchItem(null, localData.lineStartInstruction, swLoop, switchedItem, caseValues, caseCommands, valueMappings); - currentRet.add(sw); - swLoop.phase = 2; - if (next != null) { - currentRet.addAll(printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); - } - pos++; - } //else - GraphPart nextOnePart = null; - if (part.nextParts.size() == 2) { - GraphTargetItem expr = stack.pop(); - /*if (expr instanceof LogicalOpItem) { - expr = ((LogicalOpItem) expr).invert(); - } else { - expr = new NotItem(null, expr); - }*/ - if (nextOnePart == null) { - - List nps; - nps = part.nextParts; - boolean isEmpty = nps.get(0) == nps.get(1); - - GraphPart next = getCommonPart(localData, nps, loops); - TranslateStack trueStack = (TranslateStack) stack.clone(); - TranslateStack falseStack = (TranslateStack) stack.clone(); - trueStack.clear(); - falseStack.clear(); - - if (isEmpty) { - next = nps.get(0); - } - boolean hasOntrue = nps.get(1) != next; - boolean hasOnFalse = nps.get(0) != next; - - List stopPart2 = new ArrayList<>(stopPart); - - if ((!isEmpty) && (next != null)) { - stopPart2.add(next); - } - - List onTrue = new ArrayList<>(); - if (!isEmpty && hasOntrue) { - onTrue = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - } - List onFalse = new ArrayList<>(); - - if (!isEmpty && hasOnFalse) { - onFalse = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - } - //List out2 = new ArrayList<>(); - //makeAllCommands(out2, stack); - makeAllCommands(onTrue, trueStack); - makeAllCommands(onFalse, falseStack); - - List filteredOnTrue = filter(onTrue); - List filteredOnFalse = filter(onFalse); - - if (!isEmpty(filteredOnTrue) && !isEmpty(filteredOnFalse) && filteredOnTrue.size() == 1 && filteredOnFalse.size() == 1 && (filteredOnTrue.get(0) instanceof PushItem) && (filteredOnFalse.get(0) instanceof PushItem)) { - stack.push(new TernarOpItem(null, localData.lineStartInstruction, expr.invert(null), ((PushItem) filteredOnTrue.get(0)).value, ((PushItem) filteredOnFalse.get(0)).value)); - } 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(); - - if (leftSide instanceof DuplicateItem) { - isIf = false; - stack.push(new OrItem(null, localData.lineStartInstruction, prevExpr, rightSide)); - } else if (leftSide.invert(null).getNotCoercedNoDup() instanceof DuplicateItem) { - isIf = false; - stack.push(new AndItem(null, localData.lineStartInstruction, prevExpr, rightSide)); - } else if (prevExpr instanceof FalseItem) { - isIf = false; - leftSide = leftSide.invert(null); - stack.push(new AndItem(null, localData.lineStartInstruction, leftSide, rightSide)); - } else if (prevExpr instanceof TrueItem) { - isIf = false; - stack.push(new OrItem(null, localData.lineStartInstruction, leftSide, rightSide)); - } else { - //:-( - } - } else { - isIf = false; - } - } - - if (isIf) { - makeAllCommands(currentRet, stack); - IfItem b = new IfItem(null, localData.lineStartInstruction, expr.invert(null), onTrue, onFalse); - currentRet.add(b); - if (processSubBlk(b, null)) { - stack.push(new PopItem(null, localData.lineStartInstruction)); - } - } - } - //currentRet.addAll(out2); - if (next != null) { - printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); - //currentRet.addAll(); - } - } - } //else - if (part.nextParts.size() == 1) { - nextOnePart = part.nextParts.get(0); - } - if (nextOnePart != null) { - printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); - } - - } - if (isLoop && loopItem != null && currentLoop != null) { - - LoopItem li = loopItem; - boolean loopTypeFound = false; - - boolean hasContinue = false; - processIfs(loopItem.commands); - checkContinueAtTheEnd(loopItem.commands, currentLoop); - List continues = loopItem.getContinues(); - for (ContinueItem c : continues) { - if (c.loopId == currentLoop.id) { - hasContinue = true; - break; - } - } - if (!hasContinue) { - if (currentLoop.loopPreContinue != null) { - List stopContPart = new ArrayList<>(); - stopContPart.add(currentLoop.loopContinue); - GraphPart precoBackup = currentLoop.loopPreContinue; - currentLoop.loopPreContinue = null; - loopItem.commands.addAll(printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, precoBackup, stopContPart, loops, null, staticOperation, path, recursionLevel + 1)); - } - } - - //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; - 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 (bi.loopId == currentLoop.id) { - if (ifi.onTrue.isEmpty()) { - inverted = true; - } - bodyBranch = inverted ? ifi.onFalse : ifi.onTrue; - breakpos2 = true; - } - } - 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<>(); - if (currentLoop.loopPreContinue != null) { - GraphPart backup = currentLoop.loopPreContinue; - currentLoop.loopPreContinue = null; - List stopPart2 = new ArrayList<>(stopPart); - stopPart2.add(currentLoop.loopContinue); - finalComm = printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - currentLoop.loopPreContinue = backup; - checkContinueAtTheEnd(finalComm, currentLoop); - } - 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)); - } - - 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); - /* - loopItem.commands.remove(loopItem.commands.size() - 1); - exprList.addAll(loopItem.commands); - commands.addAll(bodyBranch); - exprList.add(expr); - checkContinueAtTheEnd(commands, currentLoop); - ret.add(index, li = new WhileItem(null, currentLoop, exprList, commands));*/ - } 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 finalComm = printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, 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(li, localData, loops); - if (replaced != li) { - int index = ret.indexOf(li); - ret.remove(index); - if (replaced != null) { - ret.add(index, replaced); - } - } - - if (currentLoop.loopBreak != null) { - ret.addAll(printGraph(partCodes, partCodePos, visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); - } - } - - return ret; - } - - protected void checkGraph(List allBlocks) { - } - - private List makeGraph(GraphSource code, List allBlocks, List alternateEntries) throws InterruptedException { - 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; - } - - protected int checkIp(int ip) { - return ip; - } - - private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startip, int lastIp, List allBlocks, HashMap> refs, boolean[] visited2) throws InterruptedException { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - - int ip = startip; - for (GraphPart p : allBlocks) { - if (p.start == ip) { - p.refs.add(parent); - return p; - } - } - GraphPart g; - GraphPart ret = new GraphPart(ip, -1); - ret.path = path; - GraphPart part = ret; - while (ip < code.size()) { - if (visited2[ip] || ((ip != startip) && (refs.get(ip).size() > 1))) { - part.end = lastIp; - GraphPart found = null; - for (GraphPart p : allBlocks) { - if (p.start == ip) { - found = p; - break; - } - } - - allBlocks.add(part); - - if (found != null) { - part.nextParts.add(found); - found.refs.add(part); - break; - } else { - GraphPart gp = new GraphPart(ip, -1); - gp.path = path; - part.nextParts.add(gp); - gp.refs.add(part); - part = gp; - } - } - - ip = checkIp(ip); - 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); - } - 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); - part.nextParts.add(g = makeGraph(part, path, code, ip, lastIp, allBlocks, refs, visited2)); - g.refs.add(part); - break; - } else if (ins.isBranch()) { - part.end = ip; - - allBlocks.add(part); - List branches = ins.getBranches(code); - for (int i = 0; i < branches.size(); i++) { - part.nextParts.add(g = makeGraph(part, path.sub(i, ip), code, branches.get(i), ip, allBlocks, refs, visited2)); - g.refs.add(part); - } - 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).newLine(); - } - } - return writer; - } - - public BaseLocalData prepareBranchLocalData(BaseLocalData localData) { - return localData; - } - - protected List checkPrecoNextParts(GraphPart part) { - return null; - } - - protected GraphPart makeMultiPart(GraphPart part) { - List parts = new ArrayList<>(); - do { - parts.add(part); - if (part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) { - part = part.nextParts.get(0); - } else { - part = null; - } - } while (part != null); - if (parts.size() > 1) { - GraphPartMulti ret = new GraphPartMulti(parts); - ret.refs.addAll(parts.get(0).refs); - ret.nextParts.addAll(parts.get(parts.size() - 1).nextParts); - return ret; - } else { - return parts.get(0); - } - } - - protected List getPartItems(GraphPart part) { - List ret = new ArrayList<>(); - do { - for (int i = 0; i < part.getHeight(); i++) { - if (part.getPosAt(i) < code.size()) { - if (part.getPosAt(i) < 0) { - continue; - } - GraphSourceItem s = code.get(part.getPosAt(i)); - if (!s.isJump()) { - ret.add(s); - } - } - } - if (part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) { - part = part.nextParts.get(0); - } else { - part = null; - } - } while (part != null); - return ret; - } - - 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--; - } - } - - protected static void makeAllCommands(List commands, TranslateStack stack) { - int clen = commands.size(); - if (!commands.isEmpty()) { - if (commands.get(commands.size() - 1) instanceof BreakItem) { - clen--; - } - } - while (stack.size() > 0) { - GraphTargetItem p = stack.pop(); - if (!(p instanceof PopItem)) { - if (p instanceof FunctionActionItem) { - commands.add(clen, p); - } else { - commands.add(clen, new PushItem(p)); - } - } - } - } -} +/* + * Copyright (C) 2010-2016 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.model.FunctionActionItem; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.model.AndItem; +import com.jpexs.decompiler.graph.model.BreakItem; +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 java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +/** + * + * @author JPEXS + */ +public class Graph { + + public List heads; + + protected GraphSource code; + + private final List alternateEntries; + + public static final int SOP_USE_STATIC = 0; + + public static final int SOP_SKIP_STATIC = 1; + + public static final int SOP_REMOVE_STATIC = 2; + + /** + * Identify loop exits + * + * @param localData + * @param allParts All nodes + * @return + */ + public Map> identifyLoopBreaks(BaseLocalData localData, Set allParts) { + Map> lb = new HashMap<>(); + + for (GraphPart b0 : allParts) { + List np = new ArrayList<>(b0.nextParts); + np.addAll(b0.throwParts); + for (GraphPart b : np) { + GraphPart hdr = (b0.type == GraphPart.TYPE_LOOP_HEADER || b0.type == GraphPart.TYPE_REENTRY) ? b0 : b0.iloop_header; + + if (hdr != null && b.iloop_header != hdr && b.iloop_header == hdr.iloop_header && b != hdr) { + if (!lb.containsKey(hdr)) { + lb.put(hdr, new ArrayList<>()); + } + if (!lb.get(hdr).contains(b)) { + lb.get(hdr).add(b); + } + } + } + } + + return lb; + } + + /** + * Identifying loops. Based on http://lenx.100871.net/papers/loop-SAS.pdf + * + * @param localData + * @param loopContinues Result - list of loop headers + * @param heads Entries + * @param appParts All Nodes + */ + public void identifyLoops(BaseLocalData localData, List loopContinues, List heads, Set appParts) { + for (GraphPart b : appParts) { + b.traversed = false; + b.DFSP_pos = 0; + b.irreducible = false; + b.type = GraphPart.TYPE_NONE; + //initialize b + } + for (GraphPart h0 : heads) { + trav_loops_DFS(localData, loopContinues, h0, 1); + } + } + + /** + * Tag h as loop headr of b. + * + * @param b Block + * @param h Loop header + */ + protected void tag_lhead(GraphPart b, GraphPart h) { + if (b == h || h == null) { + return; + } + GraphPart cur1 = b; + GraphPart cur2 = h; + while (cur1.iloop_header != null) { + GraphPart ih = cur1.iloop_header; + if (ih == cur2) { + return; + } + if (ih.DFSP_pos < cur2.DFSP_pos) { + cur1.iloop_header = cur2; + cur1 = cur2; + cur2 = ih; + } else { + cur1 = h; + } + } + if (cur1 == cur2) { + return; + } + + cur1.iloop_header = cur2; + } + + protected List filter(List list) { + return new ArrayList<>(list); + } + + /** + * Traverse loops deep first search + * + * @param localData + * @param loopHeaders Resulting loop headers + * @param b0 Current node + * @param DFSP_pos Position in DFSP + * @return innermost loop header of b0 + */ + protected GraphPart trav_loops_DFS(BaseLocalData localData, List loopHeaders, GraphPart b0, int DFSP_pos) { + + List folParts = new ArrayList<>(b0.nextParts); + folParts.addAll(b0.throwParts); + + b0.traversed = true; + b0.DFSP_pos = DFSP_pos; //Mark b0’s position in DFSP + for (GraphPart b : folParts) { + b = checkPart(null, localData, b, null); + if (b == null) { + continue; + } + if (!b.traversed) { + //case (A), new + GraphPart nh = trav_loops_DFS(localData, loopHeaders, b, DFSP_pos + 1); + tag_lhead(b0, nh); + } else if (b.DFSP_pos > 0) { // b in DFSP(b0) + //case (B) + if (b.type != GraphPart.TYPE_LOOP_HEADER) { + b.type = GraphPart.TYPE_LOOP_HEADER; + loopHeaders.add(b); + } + tag_lhead(b0, b); + } else if (b.iloop_header == null) { + //case (C), do nothing + } else { + GraphPart h = b.iloop_header; + if (h.DFSP_pos > 0) { // h in DFSP(b0) + //case (D) + tag_lhead(b0, h); + } else { // h not in DFSP(b0) + //case (E), reentry + b.type = GraphPart.TYPE_REENTRY; //TODO:and b0,b ? + h.irreducible = true; + while (h.iloop_header != null) { + h = h.iloop_header; + if (h.DFSP_pos > 0) { //h in DFSP(b0) + tag_lhead(b0, h); + break; + } + h.irreducible = true; + } + } + } + } + b0.DFSP_pos = 0; // clear b0’s DFSP position + return b0.iloop_header; + } + + public Graph(GraphSource code, List alternateEntries) { + this.code = code; + this.alternateEntries = alternateEntries; + + } + + public void init(BaseLocalData localData) throws InterruptedException { + if (heads != null) { + return; + } + heads = makeGraph(code, new ArrayList<>(), alternateEntries); + int time = 1; + List ordered = new ArrayList<>(); + List visited = new ArrayList<>(); + for (GraphPart head : heads) { + time = head.setTime(time, ordered, visited); + } + } + + 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++; + } + } + + private void getReachableParts(GraphPart part, LinkedHashSet ret, List loops) { + // use LinkedHashSet to preserve order + getReachableParts(part, ret, loops, true); + } + + private void getReachableParts(GraphPart part, LinkedHashSet ret, List loops, 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(); + loopnext: + for (GraphPart next : part.nextParts) { + 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) throws InterruptedException { + return getCommonPart(localData, part.nextParts, loops); + } + + //TODO: Make this faster! + public GraphPart getCommonPart(BaseLocalData localData, List parts, List loops) throws InterruptedException { + if (parts.isEmpty()) { + return null; + } + + List loopContinues = new ArrayList<>();//getLoopsContinues(loops); + for (Loop l : loops) { + if (l.phase == 1) { + loopContinues.add(l.loopContinue); + } + } + + 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)) { + common = false; + break; + } + } + if (common) { + return p; + } + } + List> reachable = new ArrayList<>(); + for (GraphPart p : parts) { + LinkedHashSet r1 = new LinkedHashSet<>(); + getReachableParts(p, r1, loops); + r1.add(p); + reachable.add(r1); + } + Set first = reachable.get(0); + for (GraphPart p : first) { + /*if (ignored.contains(p)) { + continue; + }*/ + p = checkPart(null, localData, p, null); + if (p == null) { + continue; + } + boolean common = true; + for (Set r : reachable) { + if (!r.contains(p)) { + common = false; + break; + } + } + if (common) { + return p; + } + } + return null; + } + + public GraphPart getMostCommonPart(BaseLocalData localData, List parts, List loops) throws InterruptedException { + if (parts.isEmpty()) { + return null; + } + + Set s = new HashSet<>(parts); //unique + parts = new ArrayList<>(s); //make local copy + + List loopContinues = new ArrayList<>();//getLoopsContinues(loops); + for (Loop l : loops) { + if (l.phase == 1) { + loopContinues.add(l.loopContinue); + loopContinues.add(l.loopPreContinue); + } + } + + for (GraphPart p : parts) { + if (loopContinues.contains(p)) { + break; + } + boolean common = true; + for (GraphPart q : parts) { + if (q == p) { + continue; + } + if (!q.leadsTo(localData, this, code, p, loops)) { + common = false; + break; + } + } + if (common) { + return p; + } + } + + loopi: + for (int i = 0; i < parts.size(); i++) { + for (int j = 0; j < parts.size(); j++) { + if (j == i) { + continue; + } + if (parts.get(i).leadsTo(localData, this, code, parts.get(j), loops)) { + parts.remove(i); + i--; + continue loopi; + } + } + } + List> reachable = new ArrayList<>(); + for (GraphPart p : parts) { + LinkedHashSet r1 = new LinkedHashSet<>(); + getReachableParts(p, r1, loops); + Set r2 = new LinkedHashSet<>(); + r2.add(p); + r2.addAll(r1); + reachable.add(r2); + } + ///List first = reachable.get(0); + int commonLevel; + Map levelMap = new HashMap<>(); + for (Set first : reachable) { + int maxclevel = 0; + Set visited = new HashSet<>(); + for (GraphPart p : first) { + if (loopContinues.contains(p)) { + break; + } + if (visited.contains(p)) { + continue; + } + visited.add(p); + boolean common = true; + commonLevel = 1; + for (Set r : reachable) { + if (r == first) { + continue; + } + if (r.contains(p)) { + commonLevel++; + } + } + if (commonLevel <= maxclevel) { + continue; + } + maxclevel = commonLevel; + if (levelMap.containsKey(p)) { + if (levelMap.get(p) > commonLevel) { + commonLevel = levelMap.get(p); + } + } + levelMap.put(p, commonLevel); + if (common) { + //return p; + } + } + } + for (int i = reachable.size() - 1; i >= 2; i--) { + for (GraphPart p : levelMap.keySet()) { + if (levelMap.get(p) == i) { + return p; + } + } + } + for (GraphPart p : levelMap.keySet()) { + if (levelMap.get(p) == parts.size()) { + return p; + } + } + return null; + } + + public GraphPart getNextNoJump(GraphPart part, 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 = part.nextParts.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 alternateEntries, int staticOperation) throws InterruptedException { + Graph g = new Graph(code, alternateEntries); + g.init(localData); + return g.translate(localData, staticOperation, path); + } + + public List translate(BaseLocalData localData, int staticOperation, String path) throws InterruptedException { + Set allParts = new HashSet<>(); + for (GraphPart head : heads) { + populateParts(head, allParts); + } + TranslateStack stack = new TranslateStack(path); + List loops = new ArrayList<>(); + + //TODO: Make this working. :-( + final boolean newLoopDetection = false; + + if (!newLoopDetection) { + getLoops(localData, heads.get(0), loops, null); + } else { + List loopHeads = new ArrayList<>(); + identifyLoops(localData, loopHeads, heads, allParts); + Map> loopBreaks = identifyLoopBreaks(localData, allParts); + + List loops2 = new ArrayList<>(); + for (int i = 0; i < loopHeads.size(); i++) { + loops2.add(new Loop(loops2.size(), loopHeads.get(i), null)); + } + for (int i = 0; i < loopHeads.size(); i++) { + if (loopBreaks.containsKey(loopHeads.get(i))) { + loops2.get(i).loopBreak = loopBreaks.get(loopHeads.get(i)).get(0);//getMostCommonPart(localData, loopBreaks.get(loopHeads.get(i)), loops2); + } else { + loops2.get(i).loopBreak = null; + } + } + + loops = loops2; + } + + /* + System.err.println(""); + for (Loop el : loops) { + System.err.println(el); + } + System.err.println(""); + */ + //TODO: Make getPrecontinues faster + getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); + + /*System.err.println(""); + for (Loop el : loops) { + System.err.println(el); + } + System.err.println("");//*/ + List ret = printGraph(new HashMap<>(), new HashMap<>(), localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path); + processIfs(ret); + finalProcessStack(stack, ret); + finalProcessAll(ret, 0, new FinalProcessLocalData()); + return ret; + } + + public void finalProcessStack(TranslateStack stack, List output) { + } + + private void finalProcessAll(List list, int level, FinalProcessLocalData localData) throws InterruptedException { + finalProcess(list, level, localData); + for (GraphTargetItem item : list) { + if (item instanceof Block) { + List> subs = ((Block) item).getSubs(); + for (List sub : subs) { + finalProcessAll(sub, level + 1, localData); + } + } + } + finalProcessAfter(list, level, localData); + } + + 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) { + 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) 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 processIfs(List list) { + + 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 ((!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(); + } + } + + if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) { + if (onFalse.get(onFalse.size() - 1) instanceof ExitItem) { + if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) { + list.add(i + 1, onTrue.remove(onTrue.size() - 1)); + } + } + } + } + } + + //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; + } + + protected GraphTargetItem checkLoop(GraphPart part, List stopPart, List loops) { + 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.loopContinue == part) { + return (new ContinueItem(null, firstIns, l.id)); + } + if (l.loopBreak == part) { + return (new BreakItem(null, firstIns, l.id)); + } + } + return null; + } + + 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(Map> partCodes, Map partCodePos, GraphSource code, BaseLocalData localData, Set allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { + return null; + } + + protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart part, Set allParts) { + return part; + } + + //@SuppressWarnings("unchecked") + protected GraphTargetItem translatePartGetStack(BaseLocalData localData, GraphPart part, TranslateStack stack, int staticOperation) throws InterruptedException { + 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 { + 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(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 List printGraph(Map> partCodes, Map partCodePos, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List loops, int staticOperation, String path) throws InterruptedException { + return printGraph(partCodes, partCodePos, new HashSet<>(), localData, stack, allParts, parent, part, stopPart, loops, null, staticOperation, path, 0); + } + + protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List loops) { + return loopItem; + } + + //TODO: Make this faster!!! + private void getPrecontinues(String path, BaseLocalData localData, GraphPart parent, GraphPart part, Set allParts, List loops, List stopPart) throws InterruptedException { + try { + markLevels(path, localData, part, allParts, loops); + } catch (ThreadDeath | InterruptedException iex) { + throw iex; + } catch (Throwable ex) { + //It is unusual code so markLevels failed, nevermind, it can still work + } + //Note: this also marks part as precontinue when there is if + /* + while(k<10){ + if(k==7){ + trace(a); + }else{ + trace(b); + } + //precontinue + k++; + } + + */ + looploops: + for (Loop l : loops) { + if (l.loopContinue != null) { + Set uniqueRefs = new HashSet<>(); + uniqueRefs.addAll(l.loopContinue.refs); + if (uniqueRefs.size() == 2) { //only one path - from precontinue + List uniqueRefsList = new ArrayList<>(uniqueRefs); + if (uniqueRefsList.get(0).discoveredTime > uniqueRefsList.get(1).discoveredTime) { //latch node is discovered later + part = uniqueRefsList.get(0); + } else { + part = uniqueRefsList.get(1); + } + if (part == l.loopContinue) { + continue looploops; + } + + while (part.refs.size() == 1) { + if (part.refs.get(0).nextParts.size() != 1) { + continue looploops; + } + + part = part.refs.get(0); + if (part == l.loopContinue) { + break; + } + } + if (part.level == 0 && part != l.loopContinue) { + l.loopPreContinue = part; + } + } + } + } + /*clearLoops(loops); + getPrecontinues(parent, part, loops, stopPart, 0, new ArrayList()); + clearLoops(loops);*/ + } + + private void markLevels(String path, BaseLocalData localData, GraphPart part, Set allParts, List loops) throws InterruptedException { + clearLoops(loops); + markLevels(path, localData, part, allParts, loops, new ArrayList<>(), 1, new HashSet<>(), 0); + clearLoops(loops); + } + + private void markLevels(String path, BaseLocalData localData, GraphPart part, Set allParts, List loops, List stopPart, int level, Set visited, int recursionLevel) throws InterruptedException { + boolean debugMode = false; + if (stopPart == null) { + stopPart = new ArrayList<>(); + } + if (recursionLevel > allParts.size() + 1) { + throw new RuntimeException(path + ": markLevels max recursion level reached"); + } + + if (debugMode) { + System.err.println("markLevels " + part); + } + if (stopPart.contains(part)) { + return; + } + for (Loop el : loops) { + if ((el.phase == 2) && (el.loopContinue == part)) { + return; + } + if (el.phase != 1) { + if (debugMode) { + //System.err.println("ignoring "+el); + } + continue; + } + if (el.loopContinue == part) { + return; + } + if (el.loopPreContinue == part) { + return; + } + if (el.loopBreak == part) { + return; + } + } + + if (visited.contains(part)) { + part.level = 0; + } else { + visited.add(part); + part.level = level; + } + + boolean isLoop = false; + Loop currentLoop = null; + for (Loop el : loops) { + if ((el.phase == 0) && (el.loopContinue == part)) { + isLoop = true; + currentLoop = el; + el.phase = 1; + break; + } + } + + List nextParts = checkPrecoNextParts(part); + if (nextParts == null) { + nextParts = part.nextParts; + } + + if (nextParts.size() == 2) { + GraphPart next = getCommonPart(localData, nextParts, loops);//part.getNextPartPath(new ArrayList()); + List stopParts2 = new ArrayList<>(); //stopPart); + if (next != null) { + stopParts2.add(next); + } else if (!stopPart.isEmpty()) { + stopParts2.add(stopPart.get(stopPart.size() - 1)); + } + if (next != nextParts.get(0)) { + markLevels(path, localData, nextParts.get(0), allParts, loops, next == null ? stopPart : stopParts2, level + 1, visited, recursionLevel + 1); + } + if (next != nextParts.get(1)) { + markLevels(path, localData, nextParts.get(1), allParts, loops, next == null ? stopPart : stopParts2, level + 1, visited, recursionLevel + 1); + } + if (next != null) { + markLevels(path, localData, next, allParts, loops, stopPart, level, visited, recursionLevel + 1); + } + } + + if (nextParts.size() > 2) { + GraphPart next = getMostCommonPart(localData, nextParts, loops); + List vis = new ArrayList<>(); + for (GraphPart p : nextParts) { + if (vis.contains(p)) { + continue; + } + List stopPart2 = new ArrayList<>(); //(stopPart); + if (next != null) { + stopPart2.add(next); + } else if (!stopPart.isEmpty()) { + stopPart2.add(stopPart.get(stopPart.size() - 1)); + } + for (GraphPart p2 : nextParts) { + if (p2 == p) { + continue; + } + if (!stopPart2.contains(p2)) { + stopPart2.add(p2); + } + } + if (next != p) { + markLevels(path, localData, p, allParts, loops, stopPart2, level + 1, visited, recursionLevel + 1); + vis.add(p); + } + } + if (next != null) { + markLevels(path, localData, next, allParts, loops, stopPart, level, visited, recursionLevel + 1); + } + } + + if (nextParts.size() == 1) { + markLevels(path, localData, nextParts.get(0), allParts, loops, stopPart, level, visited, recursionLevel + 1); + } + + for (GraphPart t : part.throwParts) { + if (!visited.contains(t)) { + List stopPart2 = new ArrayList<>(); + List cmn = new ArrayList<>(); + cmn.add(part); + cmn.add(t); + GraphPart next = getCommonPart(localData, cmn, loops); + if (next != null) { + stopPart2.add(next); + } else { + stopPart2 = stopPart; + } + + markLevels(path, localData, t, allParts, loops, stopPart2, level, visited, recursionLevel + 1); + } + } + + if (isLoop) { + if (currentLoop != null && currentLoop.loopBreak != null) { + currentLoop.phase = 2; + markLevels(path, localData, currentLoop.loopBreak, allParts, loops, stopPart, level, visited, recursionLevel + 1); + } + } + } + + private void clearLoops(List loops) { + for (Loop l : loops) { + l.phase = 0; + } + } + + private void getLoops(BaseLocalData localData, GraphPart part, List loops, List stopPart) throws InterruptedException { + clearLoops(loops); + getLoops(localData, part, loops, stopPart, true, 1, new ArrayList<>()); + clearLoops(loops); + } + + private void getLoops(BaseLocalData localData, GraphPart part, List loops, List stopPart, boolean first, int level, List visited) throws InterruptedException { + boolean debugMode = false; + + if (part == null) { + return; + } + + part = checkPart(null, localData, part, null); + if (part == null) { + return; + } + if (!visited.contains(part)) { + visited.add(part); + } + + if (debugMode) { + 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; + } + + } + } + if (lastP1 != null) { + if (lastP1.breakCandidates.contains(part)) { + lastP1.breakCandidates.add(part); + lastP1.breakCandidatesLevels.add(level); + return; + } else { + //List loopContinues2 = new ArrayList<>(loopContinues); + //loopContinues2.remove(lastP1.loopContinue); + List loops2 = new ArrayList<>(loops); + loops2.remove(lastP1); + if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2)) { + if (lastP1.breakCandidatesLocked == 0) { + if (debugMode) { + System.err.println("added breakCandidate " + part + " to " + lastP1); + } + + lastP1.breakCandidates.add(part); + lastP1.breakCandidatesLevels.add(level); + return; + } + } + } + } + + 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); + Loop currentLoop = null; + if (isLoop) { + currentLoop = new Loop(loops.size(), part, null); + currentLoop.phase = 1; + loops.add(currentLoop); + //loopContinues.add(part); + } + + if (part.nextParts.size() == 2) { + + List nps;/* = new ArrayList<>(part.nextParts); + for(int i=0;i stopPart2 = stopPart == null ? new ArrayList<>() : new ArrayList<>(stopPart); + if (next != null) { + stopPart2.add(next); + } + if (next != nps.get(0)) { + getLoops(localData, nps.get(0), loops, stopPart2, false, level + 1, visited); + } + if (next != nps.get(1)) { + getLoops(localData, nps.get(1), loops, stopPart2, false, level + 1, visited); + } + if (next != null) { + getLoops(localData, next, loops, stopPart, false, level, visited); + } + } else if (part.nextParts.size() > 2) { + GraphPart next = getNextCommonPart(localData, part, loops); + + 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) { + getLoops(localData, p, loops, stopPart2, false, level + 1, visited); + } + } + if (next != null) { + getLoops(localData, next, loops, stopPart, false, level, visited); + } + } else if (part.nextParts.size() == 1) { + getLoops(localData, part.nextParts.get(0), loops, stopPart, false, level, visited); + } + + List loops2 = new ArrayList<>(loops); + for (Loop l : loops2) { + l.breakCandidatesLocked++; + } + for (GraphPart t : part.throwParts) { + if (!visited.contains(t)) { + getLoops(localData, t, loops, stopPart, false, level, visited); + } + } + for (Loop l : loops2) { + l.breakCandidatesLocked--; + } + + if (isLoop && currentLoop != null) { + GraphPart found; + Map removed = new HashMap<>(); + do { + found = null; + for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { + GraphPart ch = checkPart(null, localData, currentLoop.breakCandidates.get(i), null); + if (ch == null) { + currentLoop.breakCandidates.remove(i); + i--; + } + } + loopcand: + for (GraphPart cand : currentLoop.breakCandidates) { + for (GraphPart cand2 : currentLoop.breakCandidates) { + if (cand == cand2) { + continue; + } + if (cand.leadsTo(localData, this, code, cand2, loops)) { + 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); + } + } + } + if (lev1 <= lev2) { + found = cand2; + } else { + found = cand; + } + 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; + for (GraphPart cand : currentLoop.breakCandidates) { + + 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) { + } 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; + } + getLoops(localData, r, loops, stopPart, false, removed.get(r), visited); + 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; + } + } + getLoops(localData, currentLoop.loopBreak, loops, stopPart, false, level, visited); + } + } + + protected List printGraph(Map> partCodes, Map partCodePos, Set visited, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List loops, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + if (stopPart == null) { + stopPart = new ArrayList<>(); + } + if (recursionLevel > allParts.size() + 1) { + throw new TranslateException("printGraph max recursion level reached."); + } + + if (ret == null) { + ret = new ArrayList<>(); + } + //try { + boolean debugMode = false; + + if (debugMode) { + System.err.println("PART " + part + " nextsize:" + part.nextParts.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(part.nextParts.get(0), stopPart, loops); + if (lop == null) { + part = part.nextParts.get(0); + } else { + break; + } + }*/ + if (part == null) { + return ret; + } + part = checkPart(stack, localData, part, allParts); + if (part == null) { + return ret; + } + + if (part.ignored) { + return ret; + } + + //List loopContinues = getLoopsContinues(loops); + boolean isLoop = false; + Loop currentLoop = null; + for (Loop el : loops) { + if ((el.loopContinue == part) && (el.phase == 0)) { + currentLoop = el; + currentLoop.phase = 1; + isLoop = true; + break; + } + } + + if (debugMode) { + System.err.println("loopsize:" + loops.size()); + } + for (int l = loops.size() - 1; l >= 0; l--) { + Loop el = loops.get(l); + if (el == currentLoop) { + if (debugMode) { + System.err.println("ignoring current loop " + el); + } + continue; + } + if (el.phase != 1) { + if (debugMode) { + System.err.println("ignoring loop " + el); + } + continue; + } + if (el.loopBreak == part) { + if (currentLoop != null) { + currentLoop.phase = 0; + } + if (debugMode) { + 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 (debugMode) { + 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 (debugMode) { + System.err.println("Adding continue"); + } + ret.add(new ContinueItem(null, localData.lineStartInstruction, el.id)); + return ret; + } + } + + if (stopPart.contains(part)) { + if (currentLoop != null) { + currentLoop.phase = 0; + } + if (debugMode) { + System.err.println("Stopped on part " + part); + } + return ret; + } + + if (code.size() <= part.start) { + ret.add(new ScriptEndItem()); + return ret; + } + + 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.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(); + stack.clear(); + loopItem = new UniversalLoopItem(null, localData.lineStartInstruction, currentLoop); + //loopItem.commands=printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops); + currentRet.add(loopItem); + loopItem.commands = new ArrayList<>(); + currentRet = loopItem.commands; + //return ret; + } + + boolean parseNext = true; + + //****************************DECOMPILING PART************* + List output = new ArrayList<>(); + + List parts = new ArrayList<>(); + if (part instanceof GraphPartMulti) { + parts = ((GraphPartMulti) part).parts; + } else { + parts.add(part); + } + for (GraphPart p : parts) { + int end = p.end; + int start = p.start; + + output.addAll(code.translatePart(p, localData, stack, start, end, staticOperation, path)); + if ((end >= code.size() - 1) && p.nextParts.isEmpty()) { + output.add(new ScriptEndItem()); + } + } + + if (parseNext) { + List retCheck = check(partCodes, partCodePos, code, localData, allParts, stack, parent, part, stopPart, loops, 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 (part.nextParts.size() > 2) { + GraphPart next = getMostCommonPart(localData, part.nextParts, loops); + List vis = new ArrayList<>(); + GraphTargetItem switchedItem = stack.pop(); + makeAllCommands(currentRet, stack); + + List caseValues = new ArrayList<>(); + List> caseCommands = new ArrayList<>(); + List valueMappings = new ArrayList<>(); + Loop swLoop = new Loop(loops.size(), null, next); + swLoop.phase = 1; + loops.add(swLoop); + 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; + + while (it instanceof TernarOpItem) { + TernarOpItem to = (TernarOpItem) it; + if (to.expression instanceof EqualsTypeItem) { + if (to.onTrue instanceof IntegerValueTypeItem) { + int cpos = ((IntegerValueTypeItem) to.onTrue).intValue(); + caseExpressionLeftSides.put(cpos, ((EqualsTypeItem) to.expression).getLeftSide()); + caseExpressionRightSides.put(cpos, ((EqualsTypeItem) to.expression).getRightSide()); + it = to.onFalse; + } else { + break; + } + } else if (to.expression instanceof FalseItem) { + it = to.onFalse; + } else if (to.expression instanceof TrueItem) { + it = to.onTrue; + } else { + break; + } + } + //int ignoredBranch = -1; + if (it instanceof IntegerValueTypeItem) { + defaultBranch = ((IntegerValueTypeItem) it).intValue(); + } + + if (!caseExpressionRightSides.isEmpty()) { + GraphTargetItem firstItem; + firstItem = (GraphTargetItem) caseExpressionRightSides.values().toArray()[0]; + boolean sameRight = true; + for (GraphTargetItem cit : caseExpressionRightSides.values()) { + if (!cit.equals(firstItem)) { + sameRight = false; + break; + } + } + + if (sameRight) { + caseExpressions = caseExpressionLeftSides; + switchedItem = firstItem; + 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; + 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 ? part.nextParts.get(1 + defaultBranch) : part.nextParts.get(0); + //int defaultNum = hasExpr ? 1 + defaultBranch : 0; + + for (int i = 1; i < part.nextParts.size(); i++) { + if (caseExpressions.containsKey(pos)) { + caseValues.add(caseExpressions.get(pos)); + } else if (part.nextParts.get(i) == defaultPart) { + caseValues.add(new DefaultItem()); + } else { + caseValues.add(new IntegerValueItem(null, localData.lineStartInstruction, pos)); + } + pos++; + } + + first = true; + pos = 0; + List nextCommands = new ArrayList<>(); + for (int i = 1; i < part.nextParts.size(); i++) { + GraphPart p = part.nextParts.get(i); + /*if (pos == ignoredBranch) { + pos++; + continue; + }*/ + //if (p != defaultPart) + { + if (vis.contains(p)) { + valueMappings.add(caseCommands.size() - 1); + continue; + } + valueMappings.add(caseCommands.size()); + } + List stopPart2 = new ArrayList<>(); + if (next != null) { + stopPart2.add(next); + } else if (!stopPart.isEmpty()) { + stopPart2.add(stopPart.get(stopPart.size() - 1)); + } + for (GraphPart p2 : part.nextParts) { + if (p2 == p) { + continue; + } + if (!stopPart2.contains(p2)) { + stopPart2.add(p2); + } + } + if (next != p) { + //if (p == defaultPart && !defaultCommands.isEmpty()) { + //ignore + //} else + { + TranslateStack s2 = (TranslateStack) stack.clone(); + s2.clear(); + nextCommands = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), s2, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + makeAllCommands(nextCommands, s2); + caseCommands.add(nextCommands); + vis.add(p); + } + } else { + caseCommands.add(nextCommands); + } + first = false; + pos++; + } + + //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); + if (bi.loopId == swLoop.id) { + lastc.remove(lastc.size() - 1); + } + } + if (lastc.isEmpty()) { + int cnt = 0; + if (caseValues.get(caseValues.size() - 1) instanceof DefaultItem) { + for (int i = valueMappings.size() - 1; i >= 0; i--) { + if (valueMappings.get(i) == caseCommands.size() - 1) { + cnt++; + } + } + if (cnt == 1) { + caseValues.remove(caseValues.size() - 1); + valueMappings.remove(valueMappings.size() - 1); + caseCommands.remove(lastc); + } + } + } + } + //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); + if (bi.loopId == swLoop.id) { + lastc.remove(lastc.size() - 1); + } + } + } + SwitchItem sw = new SwitchItem(null, localData.lineStartInstruction, swLoop, switchedItem, caseValues, caseCommands, valueMappings); + currentRet.add(sw); + swLoop.phase = 2; + if (next != null) { + currentRet.addAll(printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); + } + pos++; + } //else + GraphPart nextOnePart = null; + if (part.nextParts.size() == 2) { + GraphTargetItem expr = stack.pop(); + /*if (expr instanceof LogicalOpItem) { + expr = ((LogicalOpItem) expr).invert(); + } else { + expr = new NotItem(null, expr); + }*/ + if (nextOnePart == null) { + + List nps; + nps = part.nextParts; + boolean isEmpty = nps.get(0) == nps.get(1); + + GraphPart next = getCommonPart(localData, nps, loops); + TranslateStack trueStack = (TranslateStack) stack.clone(); + TranslateStack falseStack = (TranslateStack) stack.clone(); + trueStack.clear(); + falseStack.clear(); + + if (isEmpty) { + next = nps.get(0); + } + boolean hasOntrue = nps.get(1) != next; + boolean hasOnFalse = nps.get(0) != next; + + List stopPart2 = new ArrayList<>(stopPart); + + if ((!isEmpty) && (next != null)) { + stopPart2.add(next); + } + + List onTrue = new ArrayList<>(); + if (!isEmpty && hasOntrue) { + onTrue = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + } + List onFalse = new ArrayList<>(); + + if (!isEmpty && hasOnFalse) { + onFalse = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + } + //List out2 = new ArrayList<>(); + //makeAllCommands(out2, stack); + makeAllCommands(onTrue, trueStack); + makeAllCommands(onFalse, falseStack); + + List filteredOnTrue = filter(onTrue); + List filteredOnFalse = filter(onFalse); + + if (!isEmpty(filteredOnTrue) && !isEmpty(filteredOnFalse) && filteredOnTrue.size() == 1 && filteredOnFalse.size() == 1 && (filteredOnTrue.get(0) instanceof PushItem) && (filteredOnFalse.get(0) instanceof PushItem)) { + stack.push(new TernarOpItem(null, localData.lineStartInstruction, expr.invert(null), ((PushItem) filteredOnTrue.get(0)).value, ((PushItem) filteredOnFalse.get(0)).value)); + } 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(); + + if (leftSide instanceof DuplicateItem) { + isIf = false; + stack.push(new OrItem(null, localData.lineStartInstruction, prevExpr, rightSide)); + } else if (leftSide.invert(null).getNotCoercedNoDup() instanceof DuplicateItem) { + isIf = false; + stack.push(new AndItem(null, localData.lineStartInstruction, prevExpr, rightSide)); + } else if (prevExpr instanceof FalseItem) { + isIf = false; + leftSide = leftSide.invert(null); + stack.push(new AndItem(null, localData.lineStartInstruction, leftSide, rightSide)); + } else if (prevExpr instanceof TrueItem) { + isIf = false; + stack.push(new OrItem(null, localData.lineStartInstruction, leftSide, rightSide)); + } else { + //:-( + } + } else { + isIf = false; + } + } + + if (isIf) { + makeAllCommands(currentRet, stack); + IfItem b = new IfItem(null, localData.lineStartInstruction, expr.invert(null), onTrue, onFalse); + currentRet.add(b); + if (processSubBlk(b, null)) { + stack.push(new PopItem(null, localData.lineStartInstruction)); + } + } + } + //currentRet.addAll(out2); + if (next != null) { + printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); + //currentRet.addAll(); + } + } + } //else + if (part.nextParts.size() == 1) { + nextOnePart = part.nextParts.get(0); + } + if (nextOnePart != null) { + printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); + } + + } + if (isLoop && loopItem != null && currentLoop != null) { + + LoopItem li = loopItem; + boolean loopTypeFound = false; + + boolean hasContinue = false; + processIfs(loopItem.commands); + checkContinueAtTheEnd(loopItem.commands, currentLoop); + List continues = loopItem.getContinues(); + for (ContinueItem c : continues) { + if (c.loopId == currentLoop.id) { + hasContinue = true; + break; + } + } + if (!hasContinue) { + if (currentLoop.loopPreContinue != null) { + List stopContPart = new ArrayList<>(); + stopContPart.add(currentLoop.loopContinue); + GraphPart precoBackup = currentLoop.loopPreContinue; + currentLoop.loopPreContinue = null; + loopItem.commands.addAll(printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, precoBackup, stopContPart, loops, null, staticOperation, path, recursionLevel + 1)); + } + } + + //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; + 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 (bi.loopId == currentLoop.id) { + if (ifi.onTrue.isEmpty()) { + inverted = true; + } + bodyBranch = inverted ? ifi.onFalse : ifi.onTrue; + breakpos2 = true; + } + } + 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<>(); + if (currentLoop.loopPreContinue != null) { + GraphPart backup = currentLoop.loopPreContinue; + currentLoop.loopPreContinue = null; + List stopPart2 = new ArrayList<>(stopPart); + stopPart2.add(currentLoop.loopContinue); + finalComm = printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + currentLoop.loopPreContinue = backup; + checkContinueAtTheEnd(finalComm, currentLoop); + } + 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)); + } + + 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); + /* + loopItem.commands.remove(loopItem.commands.size() - 1); + exprList.addAll(loopItem.commands); + commands.addAll(bodyBranch); + exprList.add(expr); + checkContinueAtTheEnd(commands, currentLoop); + ret.add(index, li = new WhileItem(null, currentLoop, exprList, commands));*/ + } 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 finalComm = printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, 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(li, localData, loops); + if (replaced != li) { + int index = ret.indexOf(li); + ret.remove(index); + if (replaced != null) { + ret.add(index, replaced); + } + } + + if (currentLoop.loopBreak != null) { + ret.addAll(printGraph(partCodes, partCodePos, visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); + } + } + + return ret; + } + + protected void checkGraph(List allBlocks) { + } + + private List makeGraph(GraphSource code, List allBlocks, List alternateEntries) throws InterruptedException { + 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; + } + + protected int checkIp(int ip) { + return ip; + } + + private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startip, int lastIp, List allBlocks, HashMap> refs, boolean[] visited2) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + + int ip = startip; + for (GraphPart p : allBlocks) { + if (p.start == ip) { + p.refs.add(parent); + return p; + } + } + GraphPart g; + GraphPart ret = new GraphPart(ip, -1); + ret.path = path; + GraphPart part = ret; + while (ip < code.size()) { + if (visited2[ip] || ((ip != startip) && (refs.get(ip).size() > 1))) { + part.end = lastIp; + GraphPart found = null; + for (GraphPart p : allBlocks) { + if (p.start == ip) { + found = p; + break; + } + } + + allBlocks.add(part); + + if (found != null) { + part.nextParts.add(found); + found.refs.add(part); + break; + } else { + GraphPart gp = new GraphPart(ip, -1); + gp.path = path; + part.nextParts.add(gp); + gp.refs.add(part); + part = gp; + } + } + + ip = checkIp(ip); + 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); + } + 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); + part.nextParts.add(g = makeGraph(part, path, code, ip, lastIp, allBlocks, refs, visited2)); + g.refs.add(part); + break; + } else if (ins.isBranch()) { + part.end = ip; + + allBlocks.add(part); + List branches = ins.getBranches(code); + for (int i = 0; i < branches.size(); i++) { + part.nextParts.add(g = makeGraph(part, path.sub(i, ip), code, branches.get(i), ip, allBlocks, refs, visited2)); + g.refs.add(part); + } + 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).newLine(); + } + } + return writer; + } + + public BaseLocalData prepareBranchLocalData(BaseLocalData localData) { + return localData; + } + + protected List checkPrecoNextParts(GraphPart part) { + return null; + } + + protected GraphPart makeMultiPart(GraphPart part) { + List parts = new ArrayList<>(); + do { + parts.add(part); + if (part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) { + part = part.nextParts.get(0); + } else { + part = null; + } + } while (part != null); + if (parts.size() > 1) { + GraphPartMulti ret = new GraphPartMulti(parts); + ret.refs.addAll(parts.get(0).refs); + ret.nextParts.addAll(parts.get(parts.size() - 1).nextParts); + return ret; + } else { + return parts.get(0); + } + } + + protected List getPartItems(GraphPart part) { + List ret = new ArrayList<>(); + do { + for (int i = 0; i < part.getHeight(); i++) { + if (part.getPosAt(i) < code.size()) { + if (part.getPosAt(i) < 0) { + continue; + } + GraphSourceItem s = code.get(part.getPosAt(i)); + if (!s.isJump()) { + ret.add(s); + } + } + } + if (part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) { + part = part.nextParts.get(0); + } else { + part = null; + } + } while (part != null); + return ret; + } + + 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--; + } + } + + protected static void makeAllCommands(List commands, TranslateStack stack) { + int clen = commands.size(); + if (!commands.isEmpty()) { + if (commands.get(commands.size() - 1) instanceof BreakItem) { + clen--; + } + } + while (stack.size() > 0) { + GraphTargetItem p = stack.pop(); + if (!(p instanceof PopItem)) { + if (p instanceof FunctionActionItem) { + commands.add(clen, p); + } else { + commands.add(clen, new PushItem(p)); + } + } + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index c554c43b1..925ce731c 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -1862,7 +1862,61 @@ public class Main { */ public static void main(String[] args) throws IOException { setSessionLoaded(false); + + //args = new String[]{"--resourcedates"}; + //args = new String[]{"-cli", "C:\\124\\123flashchat.swf"}; + //args = new String[]{"-compress", "C:\\FFDec\\5.1.0_nightly900\\as3.swf", "C:\\FFDec\\5.1.0_nightly543\\as3_4.swf", "lzma"}; + //String a = new String(Helper.readStream(ApplicationInfo.class.getResourceAsStream("/project.properties"))); + //ResourceBundle a = ResourceBundle.getBundle("jsyntaxpane/Bundle"); + //CheckResources.compareResources(System.out, "2635a809463a450d2630f6cd488d679d4a5c3e13", null); + String nightlyVersion = "1317"; + /*args = new String[]{ + "-stat", + "-config", "parallelSpeedUp=1,cacheOnDisk=0,autoDeobfuscate=1", + "-format", "script:as,singlescript:0,singletext:1", + "-exportTimeout", "3600", + "-timeout", "60", + "-exportFileTimeout", "1800", + "-selectclass", "com.ludia.gameengine.F__ZN3AS32ui5flash3net12NetGroupInfo8internal41iprop_postingReceiveControlBytesPerSecondcvdEv", + "-stdout", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txtnb/out/{swfFile}.txt", + "-stderr", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txtnb/err/{swfFile}_err.txt", + "-export", "script_as2", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txtnb/exp", "C:\\FFDec/swf/" + };*/ + + /*args = new String[]{ + "-config", "parallelSpeedUp=0,cacheOnDisk=0,autoDeobfuscate=1", + "-format", "script:as,singlescript:0,singletext:1", + "-exportTimeout", "3600", + "-timeout", "60", + "-stdout", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txt/out/{swfFile}.txt", + "-stderr", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txt/err/{swfFile}_err.txt", + "-selectclass", "�^q�.�!l�", + "-export", "script", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txt/out", "C:\\FFDec/swf/06AC0000-10.swf" + };*/ + /*args = new String[]{ + "-config", "parallelSpeedUp=0,cacheOnDisk=0,autoDeobfuscate=1", + "-format", "script:as,singlescript:0,singletext:1", + "-exportTimeout", "3600", + "-timeout", "60", + "-stdout", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txt/out/{swfFile}.txt", + "-stderr", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txt/err/{swfFile}_err.txt", + //"-selectclass", "preloader.Preloader", + "-export", "script", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/txt/out", "C:\\FFDec/swf/Enter2.swf" + };*/ + /*args = new String[]{ + "-remove", "C:\\FFDec/swf/06AC0000-10.swf", "C:\\FFDec/swf/06AC0000-10_2.swf", "2", "1",}; + args = new String[0]; + args = new String[]{ + "-config", "parallelSpeedUp=1", + "-dumpSWF", "C:\\FFDec/swf/06AC0000-10.swf"};*/ + //args = new String[]{"-replace", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/as2.swf", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/as2_zg.swf", "34", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/a.txt",}; + //args = new String[]{"-replaceCharacterId", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/as2.swf", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/as2_zg.swf", "1,101,2,102,3,103",}; + //args = new String[]{"-replace", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/as2.swf", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/as2_2.swf", "1", "C:\\FFDec/8.0.1_nightly" + nightlyVersion + "/1.png", "nofill",}; + //args = new String[]{"-renameInvalidIdentifiers", "typeNumber", "C:\\111\\original.swf", "C:\\111\\renamed.swf",}; clearTemp(); +// String pluginPath = Configuration.pluginPath.get(); +// if (pluginPath != null && !pluginPath.isEmpty()) { +// } try { SWFDecompilerPlugin.loadPlugins(); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 1cdc81599..137a504fd 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -3298,10 +3298,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se setSourceWorker = null; } if (!Main.isInited() || !Main.isWorking() || Main.isDebugging()) { + ABCPanel abcPanel = getABCPanel(); CancellableWorker worker = new CancellableWorker() { @Override protected Void doInBackground() throws Exception { - ABCPanel abcPanel = getABCPanel(); abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); abcPanel.setAbc(scriptLeaf.abc); abcPanel.decompiledTextArea.setScript(scriptLeaf, true); @@ -3321,7 +3321,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se try { get(); } catch (CancellationException ex) { - getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("work.canceled")); + abcPanel.decompiledTextArea.setText("// " + AppStrings.translate("work.canceled")); } catch (Exception ex) { logger.log(Level.SEVERE, "Error", ex); getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex);