From f8f80c3b8a97c15fae00a82d84952e4a28dc356c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 1 Jun 2015 07:05:16 +0200 Subject: [PATCH] various AS3 decompilation improvements --- .../decompiler/flash/abc/avm2/AVM2Code.java | 54 +- .../flash/abc/avm2/graph/AVM2Graph.java | 1720 +++---- .../instructions/other/DeletePropertyIns.java | 148 +- .../avm2/instructions/other/HasNext2Ins.java | 104 +- .../avm2/instructions/other/SetSlotIns.java | 368 +- .../abc/avm2/instructions/stack/PopIns.java | 122 +- .../flash/abc/avm2/model/ConvertAVM2Item.java | 3 +- .../flash/abc/avm2/model/ThrowAVM2Item.java | 3 +- .../avm2/model/clauses/ExceptionAVM2Item.java | 3 +- .../abc/avm2/model/clauses/TryAVM2Item.java | 8 +- .../parser/script/AVM2SourceGenerator.java | 8 +- .../parser/script/ActionScriptParser.java | 7 +- .../flash/abc/types/MethodBody.java | 655 +-- .../parser/script/ActionScriptParser.java | 5 +- .../src/com/jpexs/decompiler/graph/Graph.java | 4317 +++++++++-------- .../com/jpexs/decompiler/graph/GraphPart.java | 631 ++- .../decompiler/graph/model/BinaryOpItem.java | 4 +- .../decompiler/flash/gui/abc/ABCPanel.java | 4 +- 18 files changed, 4149 insertions(+), 4015 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 6945ea0f5..e9c33f7c3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -1284,7 +1284,7 @@ public class AVM2Code implements Cloneable { calcKilledStats(body); boolean debugMode = DEBUG_MODE; if (debugMode) { - System.out.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); + System.err.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); } if (visited == null) { visited = new boolean[code.size()]; @@ -1442,18 +1442,24 @@ public class AVM2Code implements Cloneable { } } if (!isKilled(reg, 0, end)) { - for (int i = ip; i >= start; i--) { + GraphTargetItem vx = stack.pop(); + int dupCnt = 1; + for (int i = ip - 1; i >= start; i--) { if (code.get(i).definition instanceof DupIns) { if (stack.isEmpty()) { break; // FIXME?o } - GraphTargetItem v = stack.pop(); - stack.push(new LocalRegAVM2Item(ins, reg, v)); - stack.push(v); + stack.pop(); + dupCnt++; + //stack.push(v); } else { break; } } + for (int i = 0; i < dupCnt; i++) { + stack.push(new LocalRegAVM2Item(ins, reg, vx)); + } + stack.push(vx); } else { ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); } @@ -1530,7 +1536,7 @@ public class AVM2Code implements Cloneable { } if (debugMode) { - System.out.println("CLOSE SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); + System.err.println("CLOSE SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); } /*if (hideTemporaryRegisters) { clearTemporaryRegisters(output); @@ -2499,6 +2505,40 @@ public class AVM2Code implements Cloneable { return cnt; } + public void inlineJumpExit() { + int csize = code.size(); + for (int i = 0; i < csize; i++) { + AVM2Instruction ins = code.get(i); + int insLen = code.get(i).getBytes().length; + int ofs = pos2adr(i); + if (ins.definition instanceof JumpIns) { + int targetOfs = ofs + insLen + ins.operands[0]; + try { + int ni = adr2pos(targetOfs); + if (ni < code.size() && ni > -1) { + AVM2Instruction ins2 = code.get(ni); + if (ins2.isExit()) { + code.set(i, new AVM2Instruction(ofs, ins2.definition, ins2.operands)); + AVM2Instruction nopIns; + nopIns = new AVM2Instruction(ofs + 1, new NopIns(), new int[]{}); + code.add(i + 1, nopIns); + nopIns = new AVM2Instruction(ofs + 2, new NopIns(), new int[]{}); + code.add(i + 2, nopIns); + nopIns = new AVM2Instruction(ofs + 3, new NopIns(), new int[]{}); + code.add(i + 3, nopIns); + i += 3; + csize = code.size(); + buildCache(); + } + } + } catch (ConvertException ex) { + //ignore + } + } + //ofs += insLen; + } + } + public void markMappedOffsets() { int ofs = 0; for (int i = 0; i < code.size(); i++) { @@ -2910,7 +2950,7 @@ public class AVM2Code implements Cloneable { } ip++; } - if (ip < 0) { + if (ip < 0 && debugMode) { System.out.println("Visited Negative: " + ip); } return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java index 38713f132..75beee2bf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java @@ -1,826 +1,894 @@ -/* - * Copyright (C) 2010-2015 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.abc.avm2.graph; - -import com.jpexs.decompiler.flash.BaseLocalData; -import com.jpexs.decompiler.flash.FinalProcessLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.AVM2LocalData; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictEqIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntegerTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.model.FilteredCheckAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NextNameAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NextValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.FilterAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictEqAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictNeqAVM2Item; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphPart; -import com.jpexs.decompiler.graph.GraphPartMulti; -import com.jpexs.decompiler.graph.GraphSource; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.Loop; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.TranslateStack; -import com.jpexs.decompiler.graph.model.BreakItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.LoopItem; -import com.jpexs.decompiler.graph.model.NotItem; -import com.jpexs.decompiler.graph.model.SwitchItem; -import com.jpexs.decompiler.graph.model.WhileItem; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class AVM2Graph extends Graph { - - private final AVM2Code avm2code; - - private final ABC abc; - - private final MethodBody body; - - public AVM2Code getCode() { - return avm2code; - } - - public AVM2Graph(AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, ScopeStack scopeStack, HashMap localRegNames, List fullyQualifiedNames, HashMap localRegAssigmentIps, HashMap> refs) { - super(new AVM2GraphSource(code, isStatic, scriptIndex, classIndex, localRegs, scopeStack, abc, body, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs), body.getExceptionEntries()); - this.avm2code = code; - this.abc = abc; - this.body = body; - /*heads = makeGraph(code, new ArrayList(), body); - this.code = code; - this.abc = abc; - this.body = body; - for (GraphPart head : heads) { - fixGraph(head); - makeMulti(head, new ArrayList()); - }*/ - - } - - public static List translateViaGraph(String path, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, ScopeStack scopeStack, HashMap localRegNames, List fullyQualifiedNames, int staticOperation, HashMap localRegAssigmentIps, HashMap> refs) throws InterruptedException { - AVM2Graph g = new AVM2Graph(code, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs); - - AVM2LocalData localData = new AVM2LocalData(); - localData.isStatic = isStatic; - localData.classIndex = classIndex; - localData.localRegs = localRegs; - localData.scopeStack = scopeStack; - localData.constants = abc.constants; - localData.methodInfo = abc.method_info; - localData.methodBody = body; - localData.abc = abc; - localData.localRegNames = localRegNames; - localData.fullyQualifiedNames = fullyQualifiedNames; - localData.parsedExceptions = new ArrayList<>(); - localData.finallyJumps = new ArrayList<>(); - localData.ignoredSwitches = new ArrayList<>(); - localData.scriptIndex = scriptIndex; - localData.localRegAssignmentIps = new HashMap<>(); - localData.ip = 0; - localData.refs = refs; - localData.code = code; - g.init(localData); - List allParts = new ArrayList<>(); - for (GraphPart head : g.heads) { - populateParts(head, allParts); - } - return g.translate(localData, staticOperation, path); - } - - @Override - protected void checkGraph(List allBlocks) { - for (ABCException ex : body.exceptions) { - int startIp = avm2code.adr2pos(ex.start); - int endIp = avm2code.adr2pos(ex.end); - int targetIp = avm2code.adr2pos(ex.target); - GraphPart target = null; - for (GraphPart p : allBlocks) { - if (p.start == targetIp) { - target = p; - break; - } - } - for (GraphPart p : allBlocks) { - if (p.start >= startIp && p.end <= endIp) { - p.throwParts.add(target); - target.refs.add(p); - } - } - } - - /*for(ABCException ex:body.exceptions){ - for(GraphPart p:allBlocks){ - boolean next_is_ex_start=false; - for(GraphPart n:p.nextParts){ - if(n.start==code.adr2pos(ex.start)){ - next_is_ex_start = true; - break; - } - } - if(next_is_ex_start){ - for(GraphPart q:allBlocks){ //find target part - if(q.start==code.adr2pos(ex.target)){ - p.nextParts.add(q); - break; - } - } - } - } - }*/ - } - - @Override - protected List check(GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { - List ret = null; - - AVM2LocalData aLocalData = (AVM2LocalData) localData; - List parsedExceptions = aLocalData.parsedExceptions; - List finallyJumps = aLocalData.finallyJumps; - List ignoredSwitches = aLocalData.ignoredSwitches; - int ip = part.start; - int addr = this.avm2code.fixAddrAfterDebugLine(this.avm2code.pos2adr(part.start)); - int maxend = -1; - List catchedExceptions = new ArrayList<>(); - for (int e = 0; e < body.exceptions.length; e++) { - if (addr == this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) { - //Add finally only when the list is empty - if (!body.exceptions[e].isFinally() || catchedExceptions.isEmpty()) { - if (!parsedExceptions.contains(body.exceptions[e])) { - if (((body.exceptions[e].end) > maxend)) { - catchedExceptions.clear(); - maxend = this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end); - catchedExceptions.add(body.exceptions[e]); - } else if (this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) == maxend) { - catchedExceptions.add(body.exceptions[e]); - } - - } - } else if (body.exceptions[e].isFinally()) { - parsedExceptions.add(body.exceptions[e]); - } - } - } - if (catchedExceptions.size() > 0) { - parsedExceptions.addAll(catchedExceptions); - int endpos = code.adr2pos(this.avm2code.fixAddrAfterDebugLine(catchedExceptions.get(0).end)); - int endposStartBlock = code.adr2pos(catchedExceptions.get(0).end); - - List> catchedCommands = new ArrayList<>(); - if (this.avm2code.code.get(endpos).definition instanceof JumpIns) { - int afterCatchAddr = this.avm2code.pos2adr(endpos + 1) + this.avm2code.code.get(endpos).operands[0]; - int afterCatchPos = this.avm2code.adr2pos(afterCatchAddr); - final AVM2Graph t = this; - Collections.sort(catchedExceptions, new Comparator() { - @Override - public int compare(ABCException o1, ABCException o2) { - return t.avm2code.fixAddrAfterDebugLine(o1.target) - t.avm2code.fixAddrAfterDebugLine(o2.target); - } - }); - - List finallyCommands = new ArrayList<>(); - int returnPos = afterCatchPos; - for (int e = 0; e < body.exceptions.length; e++) { - if (body.exceptions[e].isFinally()) { - if (addr == this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) { - if (afterCatchPos + 1 == code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))) { - AVM2Instruction jmpIns = this.avm2code.code.get(code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))); - - if (jmpIns.definition instanceof JumpIns) { - int finStart = code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytes().length + jmpIns.operands[0]); - - GraphPart fpart = null; - for (GraphPart p : allParts) { - if (p.start == finStart) { - fpart = p; - break; - } - } - int swPos = -1; - for (int f = finStart; f < this.avm2code.code.size(); f++) { - if (this.avm2code.code.get(f).definition instanceof LookupSwitchIns) { - AVM2Instruction swins = this.avm2code.code.get(f); - if (swins.operands.length >= 3) { - if (swins.operands[0] == swins.getBytes().length) { - if (code.adr2pos(code.pos2adr(f) + swins.operands[2]) < finStart) { - stack.push(new ExceptionAVM2Item(body.exceptions[e])); - GraphPart fepart = null; - for (GraphPart p : allParts) { - if (p.start == f + 1) { - fepart = p; - break; - } - } - //this.code.code.get(f).ignored = true; - //ignoredSwitches.add(f); - swPos = f; - - List stopPart2 = new ArrayList<>(stopPart); - stopPart2.add(fepart); - //finallyCommands = printGraph(new ArrayList(), localData, stack, allParts, parent, fpart, stopPart2, loops, staticOperation, path); - returnPos = f + 1; - break; - } - } - } - } - } - //ignoredSwitches.add(-1); - //int igs_size=ignoredSwitches.size(); - List oldFinallyJumps = new ArrayList<>(finallyJumps); - finallyJumps.clear(); - ignoredSwitches.add(swPos); - finallyCommands = printGraph(localData, stack, allParts, parent, fpart, null, loops, staticOperation, path); - //ignoredSwitches.remove(igs_size-1); - finallyJumps.addAll(oldFinallyJumps); - finallyJumps.add(finStart); - break; - } - } - } - } - } - - GraphPart retPart = null; - for (GraphPart p : allParts) { - if (p.start == returnPos) { - retPart = p; - break; - } - } - List catchParts = new ArrayList<>(); - for (int e = 0; e < catchedExceptions.size(); e++) { - int eendpos; - if (e < catchedExceptions.size() - 1) { - eendpos = code.adr2pos(this.avm2code.fixAddrAfterDebugLine(catchedExceptions.get(e + 1).target)) - 2; - } else { - eendpos = afterCatchPos - 1; - } - - GraphPart npart = null; - int findpos = code.adr2pos(catchedExceptions.get(e).target); - for (GraphPart p : allParts) { - if (p.start == findpos) { - npart = p; - catchParts.add(p); - break; - } - } - - GraphPart nepart = null; - for (GraphPart p : allParts) { - if (p.start == eendpos + 1) { - nepart = p; - break; - } - } - stack.add(new ExceptionAVM2Item(catchedExceptions.get(e))); - AVM2LocalData localData2 = new AVM2LocalData(aLocalData); - localData2.scopeStack = new ScopeStack(); - List stopPart2 = new ArrayList<>(stopPart); - stopPart2.add(nepart); - if (retPart != null) { - stopPart2.add(retPart); - } - catchedCommands.add(printGraph(localData2, stack, allParts, parent, npart, stopPart2, loops, staticOperation, path)); - if (catchedExceptions.get(e).isFinally()) { - catchedCommands.remove(catchedCommands.size() - 1); - catchedExceptions.remove(e); - e--; - } - } - - GraphPart nepart = null; - - for (GraphPart p : allParts) { - if (p.start == endposStartBlock) { - nepart = p; - break; - } - } - List stopPart2 = new ArrayList<>(stopPart); - stopPart2.add(nepart); - stopPart2.addAll(catchParts); - - if (retPart != null) { - stopPart2.add(retPart); - } - List tryCommands = printGraph(localData, stack, allParts, parent, part, stopPart2, loops, staticOperation, path); - - output.clear(); - output.add(new TryAVM2Item(tryCommands, catchedExceptions, catchedCommands, finallyCommands)); - ip = returnPos; - } - - } - - if (ip != part.start) { - part = null; - for (GraphPart p : allParts) { - List ps = p.getSubParts(); - for (GraphPart p2 : ps) { - if (p2.start == ip) { - part = p2; - break; - } - } - } - ret = new ArrayList<>(); - ret.addAll(output); - GraphTargetItem lop = checkLoop(part, stopPart, loops); - if (lop == null) { - ret.addAll(printGraph(localData, stack, allParts, null, part, stopPart, loops, staticOperation, path)); - } else { - ret.add(lop); - } - return ret; - } - - if (part.nextParts.isEmpty()) { - if (this.avm2code.code.get(part.end).definition instanceof ReturnValueIns) { //returns in finally clause - if (part.getHeight() >= 3) { - if (this.avm2code.code.get(part.getPosAt(part.getHeight() - 2)).definition instanceof KillIns) { - if (this.avm2code.code.get(part.getPosAt(part.getHeight() - 3)).definition instanceof GetLocalTypeIns) { - if (output.size() >= 2) { - if (output.get(output.size() - 2) instanceof SetLocalAVM2Item) { - ret = new ArrayList<>(); - ret.addAll(output); - ret.remove(ret.size() - 1); - ret.add(new ReturnValueAVM2Item(this.avm2code.code.get(part.end), ((SetLocalAVM2Item) output.get(output.size() - 2)).value)); - return ret; - } - } - } - } - } - } - } - if ((this.avm2code.code.get(part.end).definition instanceof LookupSwitchIns) && ignoredSwitches.contains(part.end)) { - ret = new ArrayList<>(); - ret.addAll(output); - return ret; - } - if (((part.nextParts.size() == 2) - && (!stack.isEmpty()) - && (stack.peek() instanceof StrictEqAVM2Item) - && (part.nextParts.get(0).getHeight() >= 2) - && (this.avm2code.code.get(this.avm2code.fixIPAfterDebugLine(part.nextParts.get(0).start)).definition instanceof PushIntegerTypeIns) - && (!part.nextParts.get(0).nextParts.isEmpty()) - && (this.avm2code.code.get(part.nextParts.get(0).nextParts.get(0).end).definition instanceof LookupSwitchIns)) - || ((part.nextParts.size() == 2) - && (!stack.isEmpty()) - && (stack.peek() instanceof StrictNeqAVM2Item) - && (part.nextParts.get(1).getHeight() >= 2) - && (this.avm2code.code.get(this.avm2code.fixIPAfterDebugLine(part.nextParts.get(1).start)).definition instanceof PushIntegerTypeIns) - && (!part.nextParts.get(1).nextParts.isEmpty()) - && (this.avm2code.code.get(part.nextParts.get(1).nextParts.get(0).end).definition instanceof LookupSwitchIns))) { - - if (stack.peek() instanceof StrictEqAVM2Item) { - ignoredSwitches.add(part.nextParts.get(0).nextParts.get(0).end); - } else { - ignoredSwitches.add(part.nextParts.get(1).nextParts.get(0).end); - } - ret = new ArrayList<>(); - ret.addAll(output); - boolean reversed = false; - if (stack.peek() instanceof StrictEqAVM2Item) { - reversed = true; - } - GraphTargetItem switchedObject = null; - if (!output.isEmpty()) { - if (output.get(output.size() - 1) instanceof SetLocalAVM2Item) { - switchedObject = ((SetLocalAVM2Item) output.get(output.size() - 1)).value; - } - } - if (switchedObject == null) { - switchedObject = new NullAVM2Item(null); - } - HashMap caseValuesMap = new HashMap<>(); - - GraphTargetItem tar = stack.pop(); - if (tar instanceof StrictEqAVM2Item) { - tar = ((StrictEqAVM2Item) tar).leftSide; - } - if (tar instanceof StrictNeqAVM2Item) { - tar = ((StrictNeqAVM2Item) tar).leftSide; - } - caseValuesMap.put(this.avm2code.code.get(part.nextParts.get(reversed ? 0 : 1).start).operands[0], tar); - - GraphPart switchLoc = part.nextParts.get(reversed ? 0 : 1).nextParts.get(0); - - while ((this.avm2code.code.get(part.nextParts.get(reversed ? 1 : 0).end).definition instanceof IfStrictNeIns) - || (this.avm2code.code.get(part.nextParts.get(reversed ? 1 : 0).end).definition instanceof IfStrictEqIns)) { - part = part.nextParts.get(reversed ? 1 : 0); - translatePart(localData, part, stack, staticOperation, null); - tar = stack.pop(); - if (tar instanceof StrictEqAVM2Item) { - tar = ((StrictEqAVM2Item) tar).leftSide; - } - if (tar instanceof StrictNeqAVM2Item) { - tar = ((StrictNeqAVM2Item) tar).leftSide; - } - if (this.avm2code.code.get(part.end).definition instanceof IfStrictNeIns) { - reversed = false; - } else { - reversed = true; - } - GraphPart numPart = part.nextParts.get(reversed ? 0 : 1); - AVM2Instruction ins = null; - TranslateStack sstack = new TranslateStack(); - do { - for (int n = 0; n < numPart.getHeight(); n++) { - ins = this.avm2code.code.get(numPart.getPosAt(n)); - if (ins.definition instanceof LookupSwitchIns) { - break; - } - ins.translate(localData, sstack, new ArrayList(), staticOperation, path); - } - if (numPart.nextParts.size() > 1) { - break; - } else { - numPart = numPart.nextParts.get(0); - } - } while (!(this.avm2code.code.get(numPart.end).definition instanceof LookupSwitchIns)); - GraphTargetItem nt = sstack.peek(); - - if (!(nt instanceof IntegerValueAVM2Item)) { - throw new RuntimeException("Invalid integer value in Switch"); - } - IntegerValueAVM2Item iv = (IntegerValueAVM2Item) nt; - caseValuesMap.put((int) (long) iv.value, tar); - while (this.avm2code.code.get(part.nextParts.get(reversed ? 1 : 0).start).definition instanceof JumpIns) { - reversed = false; - part = part.nextParts.get(reversed ? 1 : 0); - if (part instanceof GraphPartMulti) { - part = ((GraphPartMulti) part).parts.get(0); - } - } - } - boolean hasDefault = false; - GraphPart dp = part.nextParts.get(reversed ? 1 : 0); - while (this.avm2code.code.get(dp.start).definition instanceof JumpIns) { - if (dp instanceof GraphPartMulti) { - dp = ((GraphPartMulti) dp).parts.get(0); - } - dp = dp.nextParts.get(0); - } - - GraphPart numPart = dp; - AVM2Instruction ins = null; - TranslateStack sstack = new TranslateStack(); - do { - for (int n = 0; n < numPart.getHeight(); n++) { - ins = this.avm2code.code.get(numPart.getPosAt(n)); - if (ins.definition instanceof LookupSwitchIns) { - break; - } - ins.translate(localData, sstack, new ArrayList(), staticOperation, path); - } - if (numPart.nextParts.size() > 1) { - break; - } else { - numPart = numPart.nextParts.get(0); - } - } while (!(this.avm2code.code.get(numPart.end).definition instanceof LookupSwitchIns)); - GraphTargetItem nt = sstack.peek(); - if (nt instanceof IntegerValueAVM2Item) { - hasDefault = true; - } - List caseValues = new ArrayList<>(); - for (int i = 0; i < switchLoc.nextParts.size() - 1; i++) { - if (caseValuesMap.containsKey(i)) { - caseValues.add(caseValuesMap.get(i)); - } else { - continue; - } - } - - List> caseCommands = new ArrayList<>(); - GraphPart next = null; - - next = getMostCommonPart(localData, switchLoc.nextParts, loops);//getNextPartPath(loopContinues); - currentLoop = new Loop(loops.size(), null, next); - currentLoop.phase = 1; - loops.add(currentLoop); - //switchLoc.getNextPartPath(new ArrayList()); - List valuesMapping = new ArrayList<>(); - List caseBodies = new ArrayList<>(); - for (int i = 0; i < caseValues.size(); i++) { - GraphPart cur = switchLoc.nextParts.get(1 + i); - if (!caseBodies.contains(cur)) { - caseBodies.add(cur); - } - valuesMapping.add(caseBodies.indexOf(cur)); - } - - List defaultCommands = new ArrayList<>(); - GraphPart defaultPart = null; - if (hasDefault) { - defaultPart = switchLoc.nextParts.get(switchLoc.nextParts.size() - 1); - List stopPart2 = new ArrayList<>(stopPart); - stopPart2.add(next); - defaultCommands = printGraph(localData, stack, allParts, switchLoc, defaultPart, stopPart2, loops, staticOperation, path); - if (!defaultCommands.isEmpty()) { - if (defaultCommands.get(defaultCommands.size() - 1) instanceof BreakItem) { - if (((BreakItem) defaultCommands.get(defaultCommands.size() - 1)).loopId == currentLoop.id) { - defaultCommands.remove(defaultCommands.size() - 1); - } - } - } - } - - List ignored = new ArrayList<>(); - for (Loop l : loops) { - ignored.add(l.loopContinue); - } - - for (int i = 0; i < caseBodies.size(); i++) { - List cc = new ArrayList<>(); - List stopPart2 = new ArrayList<>(stopPart); - for (int j = 0; j < caseBodies.size(); j++) { - if (caseBodies.get(j) != caseBodies.get(i)) { - stopPart2.add(caseBodies.get(j)); - } - } - if (hasDefault) { - stopPart2.add(defaultPart); - } - - cc.addAll(0, printGraph(localData, stack, allParts, switchLoc, caseBodies.get(i), stopPart2, loops, staticOperation, path)); - caseCommands.add(cc); - } - - SwitchItem sti = new SwitchItem(null, currentLoop, switchedObject, caseValues, caseCommands, defaultCommands, valuesMapping); - ret.add(sti); - //loops.remove(currentLoop); - if (next != null) { - /*if (ti != null) { - ret.add(ti); - } else {*/ - currentLoop.phase = 2; - ret.addAll(printGraph(localData, stack, allParts, null, next, stopPart, loops, staticOperation, path)); - //} - } - } - return ret; - } - - @Override - protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart next, List allParts) { - AVM2LocalData aLocalData = (AVM2LocalData) localData; - List finallyJumps = aLocalData.finallyJumps; - List ignoredSwitches = aLocalData.ignoredSwitches; - GraphPart ret = next; - for (int f = 0; f < finallyJumps.size(); f++) { - int fip = finallyJumps.get(f); - int swip = ignoredSwitches.get(f); - if (next.start == fip) { - if (stack != null && swip != -1) { - AVM2Instruction swIns = avm2code.code.get(swip); - GraphTargetItem t = stack.pop(); - Double dval = EcmaScript.toNumber(t.getResult()); - int val = (int) (double) dval; - if (swIns.definition instanceof LookupSwitchIns) { - List branches = swIns.getBranches(code); - int nip = branches.get(0); - if (val >= 0 && val < branches.size() - 1) { - nip = branches.get(1 + val); - } - for (GraphPart p : allParts) { - if (p.start == nip) { - return p; - } - } - ret = null; - } - } - ret = null; - } - } - if (ret != next) { - return ret; - } - - int pos = next.start; - int addr = this.avm2code.fixAddrAfterDebugLine(avm2code.pos2adr(pos)); - for (int e = 0; e < body.exceptions.length; e++) { - if (body.exceptions[e].isFinally()) { - if (addr == this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) { - if (true) { //afterCatchPos + 1 == code.adr2pos(this.code.fixAddrAfterDebugLine(body.exceptions[e].end))) { - AVM2Instruction jmpIns = this.avm2code.code.get(avm2code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))); - if (jmpIns.definition instanceof JumpIns) { - int finStart = avm2code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytes().length + jmpIns.operands[0]); - finallyJumps.add(finStart); - ignoredSwitches.add(-1); - break; - } - } - } - } - } - - return next; - } - - @Override - protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List loops) { - AVM2LocalData aLocalData = (AVM2LocalData) localData; - if (loopItem instanceof WhileItem) { - WhileItem w = (WhileItem) loopItem; - - if ((!w.expression.isEmpty()) && (w.expression.get(w.expression.size() - 1) instanceof HasNextAVM2Item)) { - HasNextAVM2Item hn = (HasNextAVM2Item) w.expression.get(w.expression.size() - 1); - if (((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection != null) { - if (((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection.getNotCoerced().getThroughRegister() instanceof FilteredCheckAVM2Item) { - //GraphTargetItem gti = ((HasNextAVM2Item) ((HasNextAVM2Item) w.expression.get(w.expression.size() - 1))).collection.getNotCoerced().getThroughRegister(); - if (w.commands.size() >= 3) { //((w.commands.size() == 3) || (w.commands.size() == 4)) { - int pos = 0; - while (w.commands.get(pos) instanceof SetLocalAVM2Item) { - pos++; - } - GraphTargetItem ft = w.commands.get(pos); - if (ft instanceof WithAVM2Item) { - ft = w.commands.get(pos + 1); - if (ft instanceof IfItem) { - IfItem ift = (IfItem) ft; - if (ift.onTrue.size() > 0) { - ft = ift.onTrue.get(0); - if (ft instanceof SetPropertyAVM2Item) { - SetPropertyAVM2Item spt = (SetPropertyAVM2Item) ft; - if (spt.object instanceof LocalRegAVM2Item) { - int regIndex = ((LocalRegAVM2Item) spt.object).regIndex; - HasNextAVM2Item iti = (HasNextAVM2Item) w.expression.get(w.expression.size() - 1); - HashMap localRegs = aLocalData.localRegs; - localRegs.put(regIndex, new FilterAVM2Item(null, iti.collection.getThroughRegister(), ift.expression)); - return null; - } - } - } - } - } - } - } else if (!w.commands.isEmpty()) { - if (w.commands.get(0) instanceof SetTypeAVM2Item) { - SetTypeAVM2Item sti = (SetTypeAVM2Item) w.commands.remove(0); - GraphTargetItem gti = sti.getValue().getNotCoerced(); - if (gti instanceof NextValueAVM2Item) { - return new ForEachInAVM2Item(w.src, w.loop, new InAVM2Item(hn.instruction, sti.getObject(), ((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection), w.commands); - } else if (gti instanceof NextNameAVM2Item) { - return new ForInAVM2Item(w.src, w.loop, new InAVM2Item(hn.instruction, sti.getObject(), ((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection), w.commands); - } - } - } - } - } - } - return loopItem; - } - - @Override - protected void finalProcess(List list, int level, FinalProcessLocalData localData) { - super.finalProcess(list, level, localData); - if (level == 0) { - if (!list.isEmpty()) { - if (list.get(list.size() - 1) instanceof ReturnVoidAVM2Item) { - list.remove(list.size() - 1); - } - } - } - - /*for (int i = 0; i < list.size(); i++) { - - if (list.get(i) instanceof WhileItem) { - WhileItem w = (WhileItem) list.get(i); - - } - }*/ - List ret = avm2code.clearTemporaryRegisters(list); - if (ret != list) { - list.clear(); - list.addAll(ret); - } - for (int i = 0; i < list.size(); i++) { - if (list.get(i) instanceof SetTypeAVM2Item) { - if (((SetTypeAVM2Item) list.get(i)).getValue() instanceof ExceptionAVM2Item) { - list.remove(i); - i--; - continue; - } - } - if (list.get(i) instanceof IfItem) { - IfItem ifi = (IfItem) list.get(i); - if (((ifi.expression instanceof HasNextAVM2Item) - || ((ifi.expression instanceof NotItem) - && (((NotItem) ifi.expression).getOriginal() instanceof HasNextAVM2Item)))) { - HasNextAVM2Item hnt = null; - List body = new ArrayList<>(); - List nextbody = new ArrayList<>(); - if (ifi.expression instanceof NotItem) { - hnt = (HasNextAVM2Item) ((NotItem) ifi.expression).getOriginal(); - body.addAll(ifi.onFalse); - for (int j = i + 1; j < list.size();) { - body.add(list.remove(i + 1)); - } - nextbody = ifi.onTrue; - } else { - hnt = (HasNextAVM2Item) ifi.expression; - body = ifi.onTrue; - nextbody = ifi.onFalse; - } - if (!body.isEmpty()) { - if (body.get(0) instanceof SetTypeAVM2Item) { - SetTypeAVM2Item sti = (SetTypeAVM2Item) body.remove(0); - GraphTargetItem gti = sti.getValue().getNotCoerced(); - GraphTargetItem repl = null; - - if (gti instanceof NextValueAVM2Item) { - repl = new ForEachInAVM2Item(ifi.src, new Loop(0, null, null), new InAVM2Item(null, sti.getObject(), hnt.collection), body); - } else if (gti instanceof NextNameAVM2Item) { - repl = new ForInAVM2Item(ifi.src, new Loop(0, null, null), new InAVM2Item(null, sti.getObject(), hnt.collection), body); - } - if (repl != null) { - list.remove(i); - list.add(i, repl); - list.addAll(i + 1, nextbody); - } - } - } - } - } - } - } - - @Override - protected boolean isEmpty(List output) { - if (super.isEmpty(output)) { - return true; - } - for (GraphTargetItem i : output) { - if (i instanceof SetLocalAVM2Item) { - if (avm2code.isKilled(((SetLocalAVM2Item) i).regIndex, 0, avm2code.code.size() - 1)) { - continue; - } - } - return false; - } - return true; - } - - @Override - public AVM2LocalData prepareBranchLocalData(BaseLocalData localData) { - AVM2LocalData aLocalData = (AVM2LocalData) localData; - AVM2LocalData ret = new AVM2LocalData(aLocalData); - ScopeStack copyScopeStack = new ScopeStack(); - copyScopeStack.addAll(ret.scopeStack); - ret.scopeStack = copyScopeStack; - return ret; - } -} +/* + * Copyright (C) 2010-2015 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.abc.avm2.graph; + +import com.jpexs.decompiler.flash.BaseLocalData; +import com.jpexs.decompiler.flash.FinalProcessLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.AVM2LocalData; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictEqIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntegerTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FilteredCheckAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NextNameAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NextValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.FilterAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictEqAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictNeqAVM2Item; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphPart; +import com.jpexs.decompiler.graph.GraphPartMulti; +import com.jpexs.decompiler.graph.GraphSource; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.Loop; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.TranslateStack; +import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.ExitItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.LoopItem; +import com.jpexs.decompiler.graph.model.NotItem; +import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.WhileItem; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class AVM2Graph extends Graph { + + private final AVM2Code avm2code; + + private final ABC abc; + + private final MethodBody body; + + public AVM2Code getCode() { + return avm2code; + } + + public AVM2Graph(AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, ScopeStack scopeStack, HashMap localRegNames, List fullyQualifiedNames, HashMap localRegAssigmentIps, HashMap> refs) { + super(new AVM2GraphSource(code, isStatic, scriptIndex, classIndex, localRegs, scopeStack, abc, body, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs), body.getExceptionEntries()); + this.avm2code = code; + this.abc = abc; + this.body = body; + /*heads = makeGraph(code, new ArrayList(), body); + this.code = code; + this.abc = abc; + this.body = body; + for (GraphPart head : heads) { + fixGraph(head); + makeMulti(head, new ArrayList()); + }*/ + + } + + public static List translateViaGraph(String path, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, ScopeStack scopeStack, HashMap localRegNames, List fullyQualifiedNames, int staticOperation, HashMap localRegAssigmentIps, HashMap> refs) throws InterruptedException { + AVM2Graph g = new AVM2Graph(code, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs); + + AVM2LocalData localData = new AVM2LocalData(); + localData.isStatic = isStatic; + localData.classIndex = classIndex; + localData.localRegs = localRegs; + localData.scopeStack = scopeStack; + localData.constants = abc.constants; + localData.methodInfo = abc.method_info; + localData.methodBody = body; + localData.abc = abc; + localData.localRegNames = localRegNames; + localData.fullyQualifiedNames = fullyQualifiedNames; + localData.parsedExceptions = new ArrayList<>(); + localData.finallyJumps = new ArrayList<>(); + localData.ignoredSwitches = new ArrayList<>(); + localData.scriptIndex = scriptIndex; + localData.localRegAssignmentIps = new HashMap<>(); + localData.ip = 0; + localData.refs = refs; + localData.code = code; + g.init(localData); + List allParts = new ArrayList<>(); + for (GraphPart head : g.heads) { + populateParts(head, allParts); + } + return g.translate(localData, staticOperation, path); + } + + @Override + protected void checkGraph(List allBlocks) { + for (ABCException ex : body.exceptions) { + int startIp = avm2code.adr2pos(ex.start); + int endIp = avm2code.adr2pos(ex.end); + int targetIp = avm2code.adr2pos(ex.target); + GraphPart target = null; + for (GraphPart p : allBlocks) { + if (p.start == targetIp) { + target = p; + break; + } + } + for (GraphPart p : allBlocks) { + if (p.start >= startIp && p.end <= endIp) { + p.throwParts.add(target); + target.refs.add(p); + } + } + } + + /*for(ABCException ex:body.exceptions){ + for(GraphPart p:allBlocks){ + boolean next_is_ex_start=false; + for(GraphPart n:p.nextParts){ + if(n.start==code.adr2pos(ex.start)){ + next_is_ex_start = true; + break; + } + } + if(next_is_ex_start){ + for(GraphPart q:allBlocks){ //find target part + if(q.start==code.adr2pos(ex.target)){ + p.nextParts.add(q); + break; + } + } + } + } + }*/ + } + + @Override + protected List check(GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { + List ret = null; + + AVM2LocalData aLocalData = (AVM2LocalData) localData; + List parsedExceptions = aLocalData.parsedExceptions; + List finallyJumps = aLocalData.finallyJumps; + List ignoredSwitches = aLocalData.ignoredSwitches; + int ip = part.start; + int addr = this.avm2code.fixAddrAfterDebugLine(this.avm2code.pos2adr(part.start)); + int maxend = -1; + List catchedExceptions = new ArrayList<>(); + for (int e = 0; e < body.exceptions.length; e++) { + if (addr == this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) { + //Add finally only when the list is empty + if (!body.exceptions[e].isFinally() || catchedExceptions.isEmpty()) { + if (!parsedExceptions.contains(body.exceptions[e])) { + if (((body.exceptions[e].end) > maxend)) { + catchedExceptions.clear(); + maxend = this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end); + catchedExceptions.add(body.exceptions[e]); + } else if (this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) == maxend) { + catchedExceptions.add(body.exceptions[e]); + } + + } + } else if (body.exceptions[e].isFinally()) { + parsedExceptions.add(body.exceptions[e]); + } + } + } + if (catchedExceptions.size() > 0) { + parsedExceptions.addAll(catchedExceptions); + int endpos = code.adr2pos(this.avm2code.fixAddrAfterDebugLine(catchedExceptions.get(0).end)); + int endposStartBlock = code.adr2pos(catchedExceptions.get(0).end); + + String finCatchName = ""; + List> catchedCommands = new ArrayList<>(); + if (this.avm2code.code.get(endpos).definition instanceof JumpIns) { + int afterCatchAddr = this.avm2code.pos2adr(endpos + 1) + this.avm2code.code.get(endpos).operands[0]; + int afterCatchPos = this.avm2code.adr2pos(afterCatchAddr); + final AVM2Graph t = this; + Collections.sort(catchedExceptions, new Comparator() { + @Override + public int compare(ABCException o1, ABCException o2) { + return t.avm2code.fixAddrAfterDebugLine(o1.target) - t.avm2code.fixAddrAfterDebugLine(o2.target); + } + }); + + List finallyCommands = new ArrayList<>(); + boolean hasFinally = false; + int returnPos = afterCatchPos; + for (int e = 0; e < body.exceptions.length; e++) { + if (body.exceptions[e].isFinally()) { + if (addr == this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) { + if (afterCatchPos + 1 == code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))) { + AVM2Instruction jmpIns = this.avm2code.code.get(code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))); + + if (jmpIns.definition instanceof JumpIns) { + int finStart = code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytes().length + jmpIns.operands[0]); + + GraphPart fpart = null; + for (GraphPart p : allParts) { + if (p.start == finStart) { + fpart = p; + break; + } + } + int swPos = -1; + for (int f = finStart; f < this.avm2code.code.size(); f++) { + if (this.avm2code.code.get(f).definition instanceof LookupSwitchIns) { + AVM2Instruction swins = this.avm2code.code.get(f); + if (swins.operands.length >= 3) { + if (swins.operands[0] == swins.getBytes().length) { + if (code.adr2pos(code.pos2adr(f) + swins.operands[2]) < finStart) { + stack.push(new ExceptionAVM2Item(body.exceptions[e])); + GraphPart fepart = null; + for (GraphPart p : allParts) { + if (p.start == f + 1) { + fepart = p; + break; + } + } + //this.code.code.get(f).ignored = true; + //ignoredSwitches.add(f); + swPos = f; + + List stopPart2 = new ArrayList<>(stopPart); + stopPart2.add(fepart); + //finallyCommands = printGraph(new ArrayList(), localData, stack, allParts, parent, fpart, stopPart2, loops, staticOperation, path); + returnPos = f + 1; + break; + } + } + } + } + } + //ignoredSwitches.add(-1); + //int igs_size=ignoredSwitches.size(); + List oldFinallyJumps = new ArrayList<>(finallyJumps); + finallyJumps.clear(); + ignoredSwitches.add(swPos); + finallyCommands = printGraph(localData, stack, allParts, parent, fpart, null, loops, staticOperation, path); + //ignoredSwitches.remove(igs_size-1); + finallyJumps.addAll(oldFinallyJumps); + finallyJumps.add(finStart); + hasFinally = true; + break; + } + } + } + } + } + + GraphPart retPart = null; + for (GraphPart p : allParts) { + if (p.start == returnPos) { + retPart = p; + break; + } + } + List catchParts = new ArrayList<>(); + for (int e = 0; e < catchedExceptions.size(); e++) { + int eendpos; + if (e < catchedExceptions.size() - 1) { + eendpos = code.adr2pos(this.avm2code.fixAddrAfterDebugLine(catchedExceptions.get(e + 1).target)) - 2; + } else { + eendpos = afterCatchPos - 1; + } + + GraphPart npart = null; + int findpos = code.adr2pos(catchedExceptions.get(e).target); + for (GraphPart p : allParts) { + if (p.start == findpos) { + npart = p; + catchParts.add(p); + break; + } + } + + GraphPart nepart = null; + for (GraphPart p : allParts) { + if (p.start == eendpos + 1) { + nepart = p; + break; + } + } + stack.add(new ExceptionAVM2Item(catchedExceptions.get(e))); + AVM2LocalData localData2 = new AVM2LocalData(aLocalData); + localData2.scopeStack = new ScopeStack(); + List stopPart2 = new ArrayList<>(stopPart); + stopPart2.add(nepart); + if (retPart != null) { + stopPart2.add(retPart); + } + List ncatchedCommands = printGraph(localData2, stack, allParts, parent, npart, stopPart2, loops, staticOperation, path); + if (catchedExceptions.get(e).isFinally() && (catchedExceptions.size() > 1 || hasFinally)) { + catchedExceptions.remove(e); + e--; + } else { + catchedCommands.add(ncatchedCommands); + if (retPart != null && avm2code.code.get(retPart.start).isExit() && !(!ncatchedCommands.isEmpty() && (ncatchedCommands.get(ncatchedCommands.size() - 1) instanceof ExitItem))) { + avm2code.code.get(retPart.start).translate(localData, stack, ncatchedCommands, staticOperation, path); + } + if (catchedExceptions.get(e).isFinally()) { + if (!ncatchedCommands.isEmpty() && (ncatchedCommands.get(0) instanceof SetLocalAVM2Item)) { + SetLocalAVM2Item sl = (SetLocalAVM2Item) ncatchedCommands.get(0); + if (sl.value.getNotCoerced() instanceof ExceptionAVM2Item) { + finCatchName = AVM2Item.localRegName(new HashMap(), sl.regIndex); + } + } + } + } + } + + GraphPart nepart = null; + + for (GraphPart p : allParts) { + if (p.start == endposStartBlock) { + nepart = p; + break; + } + } + List stopPart2 = new ArrayList<>(stopPart); + stopPart2.add(nepart); + stopPart2.addAll(catchParts); + + if (retPart != null) { + stopPart2.add(retPart); + } + List tryCommands = printGraph(localData, stack, allParts, parent, part, stopPart2, loops, staticOperation, path); + if (retPart != null && avm2code.code.get(retPart.start).isExit() && !(!tryCommands.isEmpty() && (tryCommands.get(tryCommands.size() - 1) instanceof ExitItem))) { + avm2code.code.get(retPart.start).translate(localData, stack, tryCommands, staticOperation, path); + } + output.clear(); + output.add(new TryAVM2Item(tryCommands, catchedExceptions, catchedCommands, finallyCommands, finCatchName)); + ip = returnPos; + } + + } + + if (ip != part.start) { + part = null; + for (GraphPart p : allParts) { + List ps = p.getSubParts(); + for (GraphPart p2 : ps) { + if (p2.start == ip) { + part = p2; + break; + } + } + } + ret = new ArrayList<>(); + ret.addAll(output); + GraphTargetItem lop = checkLoop(part, stopPart, loops); + if (lop == null) { + ret.addAll(printGraph(localData, stack, allParts, null, part, stopPart, loops, staticOperation, path)); + } else { + ret.add(lop); + } + return ret; + } + + if (part.nextParts.isEmpty()) { + if (this.avm2code.code.get(part.end).definition instanceof ReturnValueIns) { //returns in finally clause + if (part.getHeight() >= 3) { + if (this.avm2code.code.get(part.getPosAt(part.getHeight() - 2)).definition instanceof KillIns) { + if (this.avm2code.code.get(part.getPosAt(part.getHeight() - 3)).definition instanceof GetLocalTypeIns) { + if (output.size() >= 2) { + if (output.get(output.size() - 2) instanceof SetLocalAVM2Item) { + ret = new ArrayList<>(); + ret.addAll(output); + ret.remove(ret.size() - 1); + ret.add(new ReturnValueAVM2Item(this.avm2code.code.get(part.end), ((SetLocalAVM2Item) output.get(output.size() - 2)).value)); + return ret; + } + } + } + } + } + } + } + if ((this.avm2code.code.get(part.end).definition instanceof LookupSwitchIns) && ignoredSwitches.contains(part.end)) { + ret = new ArrayList<>(); + ret.addAll(output); + return ret; + } + if (((part.nextParts.size() == 2) + && (!stack.isEmpty()) + && (stack.peek() instanceof StrictEqAVM2Item) + && (part.nextParts.get(0).getHeight() >= 2) + && (this.avm2code.code.get(this.avm2code.fixIPAfterDebugLine(part.nextParts.get(0).start)).definition instanceof PushIntegerTypeIns) + && (!part.nextParts.get(0).nextParts.isEmpty()) + && (this.avm2code.code.get(part.nextParts.get(0).nextParts.get(0).end).definition instanceof LookupSwitchIns)) + || ((part.nextParts.size() == 2) + && (!stack.isEmpty()) + && (stack.peek() instanceof StrictNeqAVM2Item) + && (part.nextParts.get(1).getHeight() >= 2) + && (this.avm2code.code.get(this.avm2code.fixIPAfterDebugLine(part.nextParts.get(1).start)).definition instanceof PushIntegerTypeIns) + && (!part.nextParts.get(1).nextParts.isEmpty()) + && (this.avm2code.code.get(part.nextParts.get(1).nextParts.get(0).end).definition instanceof LookupSwitchIns))) { + + if (stack.peek() instanceof StrictEqAVM2Item) { + ignoredSwitches.add(part.nextParts.get(0).nextParts.get(0).end); + } else { + ignoredSwitches.add(part.nextParts.get(1).nextParts.get(0).end); + } + ret = new ArrayList<>(); + ret.addAll(output); + boolean reversed = false; + if (stack.peek() instanceof StrictEqAVM2Item) { + reversed = true; + } + GraphTargetItem switchedObject = null; + if (!output.isEmpty()) { + if (output.get(output.size() - 1) instanceof SetLocalAVM2Item) { + switchedObject = ((SetLocalAVM2Item) output.get(output.size() - 1)).value; + } + } + if (switchedObject == null) { + switchedObject = new NullAVM2Item(null); + } + HashMap caseValuesMap = new HashMap<>(); + + GraphTargetItem tar = stack.pop(); + if (tar instanceof StrictEqAVM2Item) { + tar = ((StrictEqAVM2Item) tar).leftSide; + } + if (tar instanceof StrictNeqAVM2Item) { + tar = ((StrictNeqAVM2Item) tar).leftSide; + } + caseValuesMap.put(this.avm2code.code.get(part.nextParts.get(reversed ? 0 : 1).start).operands[0], tar); + + GraphPart switchLoc = part.nextParts.get(reversed ? 0 : 1).nextParts.get(0); + + while ((this.avm2code.code.get(part.nextParts.get(reversed ? 1 : 0).end).definition instanceof IfStrictNeIns) + || (this.avm2code.code.get(part.nextParts.get(reversed ? 1 : 0).end).definition instanceof IfStrictEqIns)) { + part = part.nextParts.get(reversed ? 1 : 0); + translatePart(localData, part, stack, staticOperation, null); + tar = stack.pop(); + if (tar instanceof StrictEqAVM2Item) { + tar = ((StrictEqAVM2Item) tar).leftSide; + } + if (tar instanceof StrictNeqAVM2Item) { + tar = ((StrictNeqAVM2Item) tar).leftSide; + } + if (this.avm2code.code.get(part.end).definition instanceof IfStrictNeIns) { + reversed = false; + } else { + reversed = true; + } + GraphPart numPart = part.nextParts.get(reversed ? 0 : 1); + AVM2Instruction ins = null; + TranslateStack sstack = new TranslateStack(); + do { + for (int n = 0; n < numPart.getHeight(); n++) { + ins = this.avm2code.code.get(numPart.getPosAt(n)); + if (ins.definition instanceof LookupSwitchIns) { + break; + } + ins.translate(localData, sstack, new ArrayList(), staticOperation, path); + } + if (numPart.nextParts.size() > 1) { + break; + } else { + numPart = numPart.nextParts.get(0); + } + } while (!(this.avm2code.code.get(numPart.end).definition instanceof LookupSwitchIns)); + GraphTargetItem nt = sstack.peek(); + + if (!(nt instanceof IntegerValueAVM2Item)) { + throw new RuntimeException("Invalid integer value in Switch"); + } + IntegerValueAVM2Item iv = (IntegerValueAVM2Item) nt; + caseValuesMap.put((int) (long) iv.value, tar); + while (this.avm2code.code.get(part.nextParts.get(reversed ? 1 : 0).start).definition instanceof JumpIns) { + reversed = false; + part = part.nextParts.get(reversed ? 1 : 0); + if (part instanceof GraphPartMulti) { + part = ((GraphPartMulti) part).parts.get(0); + } + } + } + boolean hasDefault = false; + GraphPart dp = part.nextParts.get(reversed ? 1 : 0); + while (this.avm2code.code.get(dp.start).definition instanceof JumpIns) { + if (dp instanceof GraphPartMulti) { + dp = ((GraphPartMulti) dp).parts.get(0); + } + dp = dp.nextParts.get(0); + } + + GraphPart numPart = dp; + AVM2Instruction ins = null; + TranslateStack sstack = new TranslateStack(); + do { + for (int n = 0; n < numPart.getHeight(); n++) { + ins = this.avm2code.code.get(numPart.getPosAt(n)); + if (ins.definition instanceof LookupSwitchIns) { + break; + } + ins.translate(localData, sstack, new ArrayList(), staticOperation, path); + } + if (numPart.nextParts.size() > 1) { + break; + } else { + numPart = numPart.nextParts.get(0); + } + } while (!(this.avm2code.code.get(numPart.end).definition instanceof LookupSwitchIns)); + GraphTargetItem nt = sstack.peek(); + if (nt instanceof IntegerValueAVM2Item) { + hasDefault = true; + } + List caseValues = new ArrayList<>(); + for (int i = 0; i < switchLoc.nextParts.size() - 1; i++) { + if (caseValuesMap.containsKey(i)) { + caseValues.add(caseValuesMap.get(i)); + } else { + continue; + } + } + + List> caseCommands = new ArrayList<>(); + GraphPart next = null; + + next = getMostCommonPart(localData, switchLoc.nextParts, loops);//getNextPartPath(loopContinues); + currentLoop = new Loop(loops.size(), null, next); + currentLoop.phase = 1; + loops.add(currentLoop); + //switchLoc.getNextPartPath(new ArrayList()); + List valuesMapping = new ArrayList<>(); + List caseBodies = new ArrayList<>(); + for (int i = 0; i < caseValues.size(); i++) { + GraphPart cur = switchLoc.nextParts.get(1 + i); + if (!caseBodies.contains(cur)) { + caseBodies.add(cur); + } + valuesMapping.add(caseBodies.indexOf(cur)); + } + + List defaultCommands = new ArrayList<>(); + GraphPart defaultPart = null; + if (hasDefault) { + defaultPart = switchLoc.nextParts.get(switchLoc.nextParts.size() - 1); + List stopPart2 = new ArrayList<>(stopPart); + stopPart2.add(next); + defaultCommands = printGraph(localData, stack, allParts, switchLoc, defaultPart, stopPart2, loops, staticOperation, path); + if (!defaultCommands.isEmpty()) { + if (defaultCommands.get(defaultCommands.size() - 1) instanceof BreakItem) { + if (((BreakItem) defaultCommands.get(defaultCommands.size() - 1)).loopId == currentLoop.id) { + defaultCommands.remove(defaultCommands.size() - 1); + } + } + } + } + + List ignored = new ArrayList<>(); + for (Loop l : loops) { + ignored.add(l.loopContinue); + } + + for (int i = 0; i < caseBodies.size(); i++) { + List cc = new ArrayList<>(); + List stopPart2 = new ArrayList<>(stopPart); + for (int j = 0; j < caseBodies.size(); j++) { + if (caseBodies.get(j) != caseBodies.get(i)) { + stopPart2.add(caseBodies.get(j)); + } + } + if (hasDefault) { + stopPart2.add(defaultPart); + } + + cc.addAll(0, printGraph(localData, stack, allParts, switchLoc, caseBodies.get(i), stopPart2, loops, staticOperation, path)); + caseCommands.add(cc); + } + + SwitchItem sti = new SwitchItem(null, currentLoop, switchedObject, caseValues, caseCommands, defaultCommands, valuesMapping); + ret.add(sti); + //loops.remove(currentLoop); + if (next != null) { + /*if (ti != null) { + ret.add(ti); + } else {*/ + currentLoop.phase = 2; + ret.addAll(printGraph(localData, stack, allParts, null, next, stopPart, loops, staticOperation, path)); + //} + } + } + return ret; + } + + @Override + protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart next, List allParts) { + AVM2LocalData aLocalData = (AVM2LocalData) localData; + List finallyJumps = aLocalData.finallyJumps; + List ignoredSwitches = aLocalData.ignoredSwitches; + GraphPart ret = next; + for (int f = 0; f < finallyJumps.size(); f++) { + int fip = finallyJumps.get(f); + int swip = ignoredSwitches.get(f); + if (next.start == fip) { + if (stack != null && swip != -1) { + AVM2Instruction swIns = avm2code.code.get(swip); + GraphTargetItem t = stack.pop(); + Double dval = EcmaScript.toNumber(t.getResult()); + int val = (int) (double) dval; + if (swIns.definition instanceof LookupSwitchIns) { + List branches = swIns.getBranches(code); + int nip = branches.get(0); + if (val >= 0 && val < branches.size() - 1) { + nip = branches.get(1 + val); + } + for (GraphPart p : allParts) { + if (avm2code.fixIPAfterDebugLine(p.start) == avm2code.fixIPAfterDebugLine(nip)) { + return p; + } + } + ret = null; + } + } + ret = null; + } + } + if (ret != next) { + return ret; + } + + int pos = next.start; + int addr = this.avm2code.fixAddrAfterDebugLine(avm2code.pos2adr(pos)); + for (int e = 0; e < body.exceptions.length; e++) { + if (body.exceptions[e].isFinally()) { + if (addr == this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) { + if (true) { //afterCatchPos + 1 == code.adr2pos(this.code.fixAddrAfterDebugLine(body.exceptions[e].end))) { + AVM2Instruction jmpIns = this.avm2code.code.get(avm2code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))); + if (jmpIns.definition instanceof JumpIns) { + int finStart = avm2code.adr2pos(this.avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytes().length + jmpIns.operands[0]); + finallyJumps.add(finStart); + ignoredSwitches.add(-1); + break; + } + } + } + } + } + + return next; + } + + @Override + protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List loops) { + AVM2LocalData aLocalData = (AVM2LocalData) localData; + if (loopItem instanceof WhileItem) { + WhileItem w = (WhileItem) loopItem; + + if ((!w.expression.isEmpty()) && (w.expression.get(w.expression.size() - 1) instanceof HasNextAVM2Item)) { + HasNextAVM2Item hn = (HasNextAVM2Item) w.expression.get(w.expression.size() - 1); + if (((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection != null) { + if (((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection.getNotCoerced().getThroughRegister() instanceof FilteredCheckAVM2Item) { + if (w.commands.size() >= 3) { + int pos = 0; + while (w.commands.get(pos) instanceof SetLocalAVM2Item) { + pos++; + } + GraphTargetItem ft = w.commands.get(pos); + if (ft instanceof WithAVM2Item) { + ft = w.commands.get(pos + 1); + if (ft instanceof IfItem) { + IfItem ift = (IfItem) ft; + if (ift.onTrue.size() > 0) { + ft = ift.onTrue.get(0); + if (ft instanceof SetPropertyAVM2Item) { + SetPropertyAVM2Item spt = (SetPropertyAVM2Item) ft; + if (spt.object instanceof LocalRegAVM2Item) { + int regIndex = ((LocalRegAVM2Item) spt.object).regIndex; + HasNextAVM2Item iti = (HasNextAVM2Item) w.expression.get(w.expression.size() - 1); + HashMap localRegs = aLocalData.localRegs; + localRegs.put(regIndex, new FilterAVM2Item(null, iti.collection.getThroughRegister(), ift.expression)); + return null; + } + } + } + } + } + } + } else if (!w.commands.isEmpty()) { + if (w.commands.get(0) instanceof SetTypeAVM2Item) { + SetTypeAVM2Item sti = (SetTypeAVM2Item) w.commands.remove(0); + GraphTargetItem gti = sti.getValue().getNotCoerced(); + if (gti instanceof NextValueAVM2Item) { + return new ForEachInAVM2Item(w.src, w.loop, new InAVM2Item(hn.instruction, sti.getObject(), ((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection), w.commands); + } else if (gti instanceof NextNameAVM2Item) { + return new ForInAVM2Item(w.src, w.loop, new InAVM2Item(hn.instruction, sti.getObject(), ((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).collection), w.commands); + } + } + } + } + } + } + return loopItem; + } + + @Override + protected void finalProcessAfter(List list, int level, FinalProcessLocalData localData) { + super.finalProcessAfter(list, level, localData); + for (int i = 0; i < list.size(); i++) { + if (list.get(i) instanceof SetLocalAVM2Item) { + SetLocalAVM2Item ri = (SetLocalAVM2Item) list.get(i); + if (localData.temporaryRegisters.contains(ri.regIndex)) { + list.remove(i); + i--; + } + } + } + } + + @Override + protected void finalProcess(List list, int level, FinalProcessLocalData localData) { + super.finalProcess(list, level, localData); + if (level == 0) { + if (!list.isEmpty()) { + if (list.get(list.size() - 1) instanceof ReturnVoidAVM2Item) { + list.remove(list.size() - 1); + } + } + } + + /*for (int i = 0; i < list.size(); i++) { + + if (list.get(i) instanceof WhileItem) { + WhileItem w = (WhileItem) list.get(i); + + } + }*/ + for (int i = 0; i < list.size(); i++) { + if (list.get(i) instanceof SetLocalAVM2Item) { + SetLocalAVM2Item ri = (SetLocalAVM2Item) list.get(i); + if (ri.value.getNotCoerced() instanceof ExceptionAVM2Item) { + ExceptionAVM2Item ea = (ExceptionAVM2Item) ri.value.getNotCoerced(); + if (ea.exception.isFinally()) { + list.remove(i); + localData.temporaryRegisters.add(ri.regIndex); + i--; + continue; + } + } + if (avm2code.isKilled(ri.regIndex, 0, Integer.MAX_VALUE)) { + if (i + 2 < list.size()) { + if ((list.get(i + 1) instanceof IntegerValueAVM2Item) && (list.get(i + 2) instanceof ReturnValueAVM2Item)) { + ReturnValueAVM2Item r = (ReturnValueAVM2Item) list.get(i + 2); + r.value = ri.value; + list.remove(i + 1); + continue; + } + if ((list.get(i + 1) instanceof IntegerValueAVM2Item) && (list.get(i + 2) instanceof ThrowAVM2Item)) { + ThrowAVM2Item t = (ThrowAVM2Item) list.get(i + 2); + t.value = ri.value; + list.remove(i + 1); + continue; + } + } else if (i + 1 < list.size()) { + if (list.get(i + 1) instanceof IntegerValueAVM2Item) { + list.remove(i + 1); + } + } + } + } + } + + List ret = avm2code.clearTemporaryRegisters(list); + if (ret != list) { + list.clear(); + list.addAll(ret); + } + for (int i = 0; i < list.size(); i++) { + if (list.get(i) instanceof SetTypeAVM2Item) { + if (((SetTypeAVM2Item) list.get(i)).getValue() instanceof ExceptionAVM2Item) { + list.remove(i); + i--; + continue; + } + } + if (list.get(i) instanceof IfItem) { + IfItem ifi = (IfItem) list.get(i); + if (((ifi.expression instanceof HasNextAVM2Item) + || ((ifi.expression instanceof NotItem) + && (((NotItem) ifi.expression).getOriginal() instanceof HasNextAVM2Item)))) { + HasNextAVM2Item hnt = null; + List body = new ArrayList<>(); + List nextbody = new ArrayList<>(); + if (ifi.expression instanceof NotItem) { + hnt = (HasNextAVM2Item) ((NotItem) ifi.expression).getOriginal(); + body.addAll(ifi.onFalse); + for (int j = i + 1; j < list.size();) { + body.add(list.remove(i + 1)); + } + nextbody = ifi.onTrue; + } else { + hnt = (HasNextAVM2Item) ifi.expression; + body = ifi.onTrue; + nextbody = ifi.onFalse; + } + if (!body.isEmpty()) { + if (body.get(0) instanceof SetTypeAVM2Item) { + SetTypeAVM2Item sti = (SetTypeAVM2Item) body.remove(0); + GraphTargetItem gti = sti.getValue().getNotCoerced(); + GraphTargetItem repl = null; + + if (gti instanceof NextValueAVM2Item) { + repl = new ForEachInAVM2Item(ifi.src, new Loop(0, null, null), new InAVM2Item(null, sti.getObject(), hnt.collection), body); + } else if (gti instanceof NextNameAVM2Item) { + repl = new ForInAVM2Item(ifi.src, new Loop(0, null, null), new InAVM2Item(null, sti.getObject(), hnt.collection), body); + } + if (repl != null) { + list.remove(i); + list.add(i, repl); + list.addAll(i + 1, nextbody); + } + } + } + } + } + } + } + + @Override + protected boolean isEmpty(List output) { + if (super.isEmpty(output)) { + return true; + } + for (GraphTargetItem i : output) { + if (i instanceof SetLocalAVM2Item) { + if (avm2code.isKilled(((SetLocalAVM2Item) i).regIndex, 0, avm2code.code.size() - 1)) { + continue; + } + } + return false; + } + return true; + } + + @Override + public AVM2LocalData prepareBranchLocalData(BaseLocalData localData) { + AVM2LocalData aLocalData = (AVM2LocalData) localData; + AVM2LocalData ret = new AVM2LocalData(aLocalData); + ScopeStack copyScopeStack = new ScopeStack(); + copyScopeStack.addAll(ret.scopeStack); + ret.scopeStack = copyScopeStack; + return ret; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/DeletePropertyIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/DeletePropertyIns.java index aa4cf8829..8a053eba0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/DeletePropertyIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/DeletePropertyIns.java @@ -1,74 +1,74 @@ -/* - * Copyright (C) 2010-2015 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.abc.avm2.instructions.other; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.DeletePropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.TranslateStack; -import java.util.HashMap; -import java.util.List; - -public class DeletePropertyIns extends InstructionDefinition { - - public DeletePropertyIns() { - super(0x6a, "deleteproperty", new int[]{AVM2Code.DAT_MULTINAME_INDEX}, true); - } - - @Override - public void execute(LocalDataArea lda, AVM2ConstantPool constants, List arguments) { - /*int multiIndex = (int) ((Long) arguments.get(0)).longValue(); - //if multiname[multinameIndex] is runtime - //pop(name) pop(ns) - Object obj = lda.operandStack.pop(); - //push true if removed*/ - throw new RuntimeException("Cannot remove property"); - - } - - @Override - public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { - int multinameIndex = ins.operands[0]; - FullMultinameAVM2Item multiname = resolveMultiname(stack, constants, multinameIndex, ins); - GraphTargetItem obj = stack.pop(); - stack.add(new BooleanAVM2Item(ins, Boolean.TRUE));//property successfully deleted - output.add(new DeletePropertyAVM2Item(ins, obj, multiname)); - } - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - int ret = -1 + 1; - int multinameIndex = ins.operands[0]; - if (abc.constants.getMultiname(multinameIndex).needsName()) { - ret--; - } - if (abc.constants.getMultiname(multinameIndex).needsNs()) { - ret--; - } - return ret; - } -} +/* + * Copyright (C) 2010-2015 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.abc.avm2.instructions.other; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.DeletePropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.TranslateStack; +import java.util.HashMap; +import java.util.List; + +public class DeletePropertyIns extends InstructionDefinition { + + public DeletePropertyIns() { + super(0x6a, "deleteproperty", new int[]{AVM2Code.DAT_MULTINAME_INDEX}, true); + } + + @Override + public void execute(LocalDataArea lda, AVM2ConstantPool constants, List arguments) { + /*int multiIndex = (int) ((Long) arguments.get(0)).longValue(); + //if multiname[multinameIndex] is runtime + //pop(name) pop(ns) + Object obj = lda.operandStack.pop(); + //push true if removed*/ + throw new RuntimeException("Cannot remove property"); + + } + + @Override + public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { + int multinameIndex = ins.operands[0]; + FullMultinameAVM2Item multiname = resolveMultiname(stack, constants, multinameIndex, ins); + GraphTargetItem obj = stack.pop(); + //stack.add(new BooleanAVM2Item(ins, Boolean.TRUE));//property successfully deleted + stack.add(new DeletePropertyAVM2Item(ins, obj, multiname)); + } + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + int ret = -1 + 1; + int multinameIndex = ins.operands[0]; + if (abc.constants.getMultiname(multinameIndex).needsName()) { + ret--; + } + if (abc.constants.getMultiname(multinameIndex).needsNs()) { + ret--; + } + return ret; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/HasNext2Ins.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/HasNext2Ins.java index 0a5d6cba7..9dcd1aa17 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/HasNext2Ins.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/HasNext2Ins.java @@ -1,52 +1,52 @@ -/* - * Copyright (C) 2010-2015 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.abc.avm2.instructions.other; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.TranslateStack; -import java.util.HashMap; -import java.util.List; - -public class HasNext2Ins extends InstructionDefinition { - - public HasNext2Ins() { - super(0x32, "hasnext2", new int[]{AVM2Code.DAT_LOCAL_REG_INDEX, AVM2Code.DAT_LOCAL_REG_INDEX}, true); - } - - @Override - public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { - int objectReg = ins.operands[0]; - int indexReg = ins.operands[1]; - //stack.push("_loc_" + objectReg + ".hasNext(cnt=_loc_" + indexReg + ")"); - stack.push(new HasNextAVM2Item(ins, new LocalRegAVM2Item(ins, indexReg, localRegs.get(indexReg)), localRegs.get(objectReg))); - } - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return 1; - } -} +/* + * Copyright (C) 2010-2015 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.abc.avm2.instructions.other; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.TranslateStack; +import java.util.HashMap; +import java.util.List; + +public class HasNext2Ins extends InstructionDefinition { + + public HasNext2Ins() { + super(0x32, "hasnext2", new int[]{AVM2Code.DAT_LOCAL_REG_INDEX, AVM2Code.DAT_LOCAL_REG_INDEX}, true); + } + + @Override + public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { + int objectReg = ins.operands[0]; + int indexReg = ins.operands[1]; + //stack.push("_loc_" + objectReg + ".hasNext(cnt=_loc_" + indexReg + ")"); + stack.push(new HasNextAVM2Item(ins, new LocalRegAVM2Item(ins, indexReg, localRegs.get(indexReg)), localRegNames.containsKey(objectReg) ? new LocalRegAVM2Item(ins, objectReg, localRegs.get(objectReg)) : localRegs.get(objectReg))); + } + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return 1; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java index d1bbc2655..aceb9e46e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java @@ -1,183 +1,185 @@ -/* - * Copyright (C) 2010-2015 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.abc.avm2.instructions.other; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.instructions.SetTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ClassAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.DecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.GetSlotAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.IncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NewActivationAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ScriptAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetSlotAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ThisAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.TraitWithSlot; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.TranslateStack; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; - -public class SetSlotIns extends InstructionDefinition implements SetTypeIns { - - public SetSlotIns() { - super(0x6d, "setslot", new int[]{AVM2Code.DAT_SLOT_INDEX}, true); - } - - @Override - public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { - int slotIndex = ins.operands[0]; - GraphTargetItem value = stack.pop(); - GraphTargetItem obj = stack.pop(); //scopeId - GraphTargetItem objnoreg = obj; - obj = obj.getThroughRegister(); - Multiname slotname = null; - if (obj instanceof NewActivationAVM2Item) { - ((NewActivationAVM2Item) obj).slots.put(slotIndex, value); - } - - if (obj instanceof ExceptionAVM2Item) { - slotname = constants.getMultiname(((ExceptionAVM2Item) obj).exception.name_index); - } else if (obj instanceof ClassAVM2Item) { - slotname = ((ClassAVM2Item) obj).className; - } else if (obj instanceof ThisAVM2Item) { - slotname = ((ThisAVM2Item) obj).className; - } else if (obj instanceof ScriptAVM2Item) { - for (int t = 0; t < abc.script_info.get(((ScriptAVM2Item) obj).scriptIndex).traits.traits.size(); t++) { - Trait tr = abc.script_info.get(((ScriptAVM2Item) obj).scriptIndex).traits.traits.get(t); - if (tr instanceof TraitWithSlot) { - if (((TraitWithSlot) tr).getSlotIndex() == slotIndex) { - slotname = tr.getName(abc); - } - } - } - } else if (obj instanceof NewActivationAVM2Item) { - - for (int t = 0; t < body.traits.traits.size(); t++) { - if (body.traits.traits.get(t) instanceof TraitWithSlot) { - if (((TraitWithSlot) body.traits.traits.get(t)).getSlotIndex() == slotIndex) { - slotname = body.traits.traits.get(t).getName(abc); - } - } - - } - } - - if (slotname != null) { - if (value instanceof LocalRegAVM2Item) { - LocalRegAVM2Item lr = (LocalRegAVM2Item) value; - String slotNameStr = slotname.getName(constants, fullyQualifiedNames, true); - if (localRegNames.containsKey(lr.regIndex)) { - if (localRegNames.get(lr.regIndex).equals(slotNameStr)) { - return; //Register with same name to slot - } - } - } - } - - if (value.getNotCoerced().getThroughDuplicate() instanceof IncrementAVM2Item) { - GraphTargetItem inside = ((IncrementAVM2Item) value.getNotCoerced()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); - if (inside instanceof GetSlotAVM2Item) { - GetSlotAVM2Item slotItem = (GetSlotAVM2Item) inside; - if ((slotItem.scope.getThroughRegister() == obj.getThroughRegister()) - && (slotItem.slotName == slotname)) { - if (stack.size() > 0) { - GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); - if (top == inside) { - stack.pop(); - stack.push(new PostIncrementAVM2Item(ins, inside)); - } else if ((top instanceof IncrementAVM2Item) && (((IncrementAVM2Item) top).value == inside)) { - stack.pop(); - stack.push(new PreIncrementAVM2Item(ins, inside)); - } else { - output.add(new PostIncrementAVM2Item(ins, inside)); - } - } else { - output.add(new PostIncrementAVM2Item(ins, inside)); - } - return; - } - } - } - - if (value.getNotCoerced().getThroughDuplicate() instanceof DecrementAVM2Item) { - GraphTargetItem inside = ((DecrementAVM2Item) value.getNotCoerced()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); - if (inside instanceof GetSlotAVM2Item) { - GetSlotAVM2Item slotItem = (GetSlotAVM2Item) inside; - if ((slotItem.scope.getThroughRegister() == obj.getThroughRegister()) - && (slotItem.slotName == slotname)) { - if (stack.size() > 0) { - GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); - if (top == inside) { - stack.pop(); - stack.push(new PostDecrementAVM2Item(ins, inside)); - } else if ((top instanceof DecrementAVM2Item) && (((DecrementAVM2Item) top).value == inside)) { - stack.pop(); - stack.push(new PreDecrementAVM2Item(ins, inside)); - } else { - output.add(new PostDecrementAVM2Item(ins, inside)); - } - } else { - output.add(new PostDecrementAVM2Item(ins, inside)); - } - return; - } - } - } - - output.add(new SetSlotAVM2Item(ins, obj, slotname, value)); - } - - @Override - public String getObject(Stack stack, ABC abc, AVM2Instruction ins, List output, MethodBody body, HashMap localRegNames, List fullyQualifiedNames) { - int slotIndex = ins.operands[0]; - ////String obj = stack.get(1); - String slotname = ""; - for (int t = 0; t < body.traits.traits.size(); t++) { - if (body.traits.traits.get(t) instanceof TraitSlotConst) { - if (((TraitSlotConst) body.traits.traits.get(t)).slot_id == slotIndex) { - slotname = body.traits.traits.get(t).getName(abc).getName(abc.constants, fullyQualifiedNames, true); - } - } - - } - return slotname; - } - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -2; - } -} +/* + * Copyright (C) 2010-2015 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.abc.avm2.instructions.other; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.SetTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ClassAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.DecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetSlotAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.IncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewActivationAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ScriptAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetSlotAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ThisAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.TraitWithSlot; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.TranslateStack; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class SetSlotIns extends InstructionDefinition implements SetTypeIns { + + public SetSlotIns() { + super(0x6d, "setslot", new int[]{AVM2Code.DAT_SLOT_INDEX}, true); + } + + @Override + public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { + int slotIndex = ins.operands[0]; + GraphTargetItem value = stack.pop(); + GraphTargetItem obj = stack.pop(); //scopeId + GraphTargetItem objnoreg = obj; + obj = obj.getThroughRegister(); + Multiname slotname = null; + if (obj instanceof NewActivationAVM2Item) { + ((NewActivationAVM2Item) obj).slots.put(slotIndex, value); + } + + if (obj instanceof ExceptionAVM2Item) { + slotname = constants.getMultiname(((ExceptionAVM2Item) obj).exception.name_index); + } else if (obj instanceof ClassAVM2Item) { + slotname = ((ClassAVM2Item) obj).className; + } else if (obj instanceof ThisAVM2Item) { + slotname = ((ThisAVM2Item) obj).className; + } else if (obj instanceof ScriptAVM2Item) { + for (int t = 0; t < abc.script_info.get(((ScriptAVM2Item) obj).scriptIndex).traits.traits.size(); t++) { + Trait tr = abc.script_info.get(((ScriptAVM2Item) obj).scriptIndex).traits.traits.get(t); + if (tr instanceof TraitWithSlot) { + if (((TraitWithSlot) tr).getSlotIndex() == slotIndex) { + slotname = tr.getName(abc); + } + } + } + } else if (obj instanceof NewActivationAVM2Item) { + + for (int t = 0; t < body.traits.traits.size(); t++) { + if (body.traits.traits.get(t) instanceof TraitWithSlot) { + if (((TraitWithSlot) body.traits.traits.get(t)).getSlotIndex() == slotIndex) { + slotname = body.traits.traits.get(t).getName(abc); + } + } + + } + } + + if (slotname != null) { + if (value instanceof LocalRegAVM2Item) { + LocalRegAVM2Item lr = (LocalRegAVM2Item) value; + String slotNameStr = slotname.getName(constants, fullyQualifiedNames, true); + if (localRegNames.containsKey(lr.regIndex)) { + if (localRegNames.get(lr.regIndex).equals(slotNameStr)) { + return; //Register with same name to slot + } + } + } + } + + if (value.getNotCoerced().getThroughDuplicate() instanceof IncrementAVM2Item) { + GraphTargetItem inside = ((IncrementAVM2Item) value.getNotCoerced()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); + if (inside instanceof GetSlotAVM2Item) { + GetSlotAVM2Item slotItem = (GetSlotAVM2Item) inside; + if ((slotItem.scope.getThroughRegister() == obj.getThroughRegister()) + && (slotItem.slotName == slotname)) { + if (stack.size() > 0) { + GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); + if (top == inside) { + stack.pop(); + stack.push(new PostIncrementAVM2Item(ins, inside)); + } else if ((top instanceof IncrementAVM2Item) && (((IncrementAVM2Item) top).value == inside)) { + stack.pop(); + stack.push(new PreIncrementAVM2Item(ins, inside)); + } else { + output.add(new PostIncrementAVM2Item(ins, inside)); + } + } else { + output.add(new PostIncrementAVM2Item(ins, inside)); + } + return; + } + } + } + + if (value.getNotCoerced().getThroughDuplicate() instanceof DecrementAVM2Item) { + GraphTargetItem inside = ((DecrementAVM2Item) value.getNotCoerced()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); + if (inside instanceof GetSlotAVM2Item) { + GetSlotAVM2Item slotItem = (GetSlotAVM2Item) inside; + if ((slotItem.scope.getThroughRegister() == obj.getThroughRegister()) + && (slotItem.slotName == slotname)) { + if (stack.size() > 0) { + GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); + if (top == inside) { + stack.pop(); + stack.push(new PostDecrementAVM2Item(ins, inside)); + } else if ((top instanceof DecrementAVM2Item) && (((DecrementAVM2Item) top).value == inside)) { + stack.pop(); + stack.push(new PreDecrementAVM2Item(ins, inside)); + } else { + output.add(new PostDecrementAVM2Item(ins, inside)); + } + } else { + output.add(new PostDecrementAVM2Item(ins, inside)); + } + return; + } + } + } + if (slotname == null) { + System.err.println("SLOT NOT FOUND"); + } + output.add(new SetSlotAVM2Item(ins, obj, slotname, value)); + } + + @Override + public String getObject(Stack stack, ABC abc, AVM2Instruction ins, List output, MethodBody body, HashMap localRegNames, List fullyQualifiedNames) { + int slotIndex = ins.operands[0]; + ////String obj = stack.get(1); + String slotname = ""; + for (int t = 0; t < body.traits.traits.size(); t++) { + if (body.traits.traits.get(t) instanceof TraitSlotConst) { + if (((TraitSlotConst) body.traits.traits.get(t)).slot_id == slotIndex) { + slotname = body.traits.traits.get(t).getName(abc).getName(abc.constants, fullyQualifiedNames, true); + } + } + + } + return slotname; + } + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -2; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PopIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PopIns.java index b0466e1bd..4865b4ff0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PopIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PopIns.java @@ -1,60 +1,62 @@ -/* - * Copyright (C) 2010-2015 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.abc.avm2.instructions.stack; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.TranslateStack; -import java.util.HashMap; -import java.util.List; - -public class PopIns extends InstructionDefinition { - - public PopIns() { - super(0x29, "pop", new int[]{}, false); - } - - @Override - public void execute(LocalDataArea lda, AVM2ConstantPool constants, List arguments) { - lda.operandStack.pop(); - } - - @Override - public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { - if (stack.size() > 0) { - GraphTargetItem top = stack.pop(); - //Note: Commands like "5;" - numbers are unsupported as it collide with try..finally block decompilation. TODO: allow this somehow - if (!(top instanceof IntegerValueAVM2Item)) { - output.add(top); - } - } - } - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -1; - } -} +/* + * Copyright (C) 2010-2015 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.abc.avm2.instructions.stack; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.MarkItem; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.TranslateStack; +import java.util.HashMap; +import java.util.List; + +public class PopIns extends InstructionDefinition { + + public PopIns() { + super(0x29, "pop", new int[]{}, false); + } + + @Override + public void execute(LocalDataArea lda, AVM2ConstantPool constants, List arguments) { + lda.operandStack.pop(); + } + + @Override + public void translate(boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { + if (stack.size() > 0) { + GraphTargetItem top = stack.pop(); + //Note: Commands like "5;" - numbers are unsupported as it collide with try..finally block decompilation. TODO: allow this somehow + + if (/*!(top instanceof IntegerValueAVM2Item) &&*/(!(top instanceof MarkItem))) { + output.add(top); + } + } + } + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -1; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java index a84e4cb22..c6903ee60 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.abc.avm2.model; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ThrowAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ThrowAVM2Item.java index b1c1565b6..7ae818eb8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ThrowAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ThrowAVM2Item.java @@ -25,10 +25,11 @@ import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.ExitItem; import com.jpexs.decompiler.graph.model.LocalData; import java.util.List; -public class ThrowAVM2Item extends AVM2Item { +public class ThrowAVM2Item extends AVM2Item implements ExitItem { //public GraphTargetItem value; public ThrowAVM2Item(AVM2Instruction instruction, GraphTargetItem value) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ExceptionAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ExceptionAVM2Item.java index 048f032b0..18a51db69 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ExceptionAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/ExceptionAVM2Item.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.abc.avm2.model.clauses; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java index 27cc7814c..3e223f4a1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java @@ -50,6 +50,8 @@ public class TryAVM2Item extends AVM2Item implements Block { public List> catchVariables = new ArrayList<>(); + public String finCatchName = ""; + @Override public List> getSubs() { List> ret = new ArrayList<>(); @@ -63,12 +65,13 @@ public class TryAVM2Item extends AVM2Item implements Block { return ret; } - public TryAVM2Item(List tryCommands, List catchExceptions, List> catchCommands, List finallyCommands) { + public TryAVM2Item(List tryCommands, List catchExceptions, List> catchCommands, List finallyCommands, String finCatchName) { super(null, NOPRECEDENCE); this.tryCommands = tryCommands; this.catchExceptions = catchExceptions; this.catchCommands = catchCommands; this.finallyCommands = finallyCommands; + this.finCatchName = finCatchName; } @Override @@ -84,6 +87,9 @@ public class TryAVM2Item extends AVM2Item implements Block { writer.newLine(); writer.append("catch("); String localName = catchExceptions.get(e).getVarName(localData.constantsAvm2, localData.fullyQualifiedNames); + if (localName.isEmpty()) { + localName = finCatchName; + } HighlightData data = new HighlightData(); data.localName = localName; data.declaration = true; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java index 7a41c4ff4..bb2148713 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java @@ -103,6 +103,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.CompilationException; @@ -569,8 +570,11 @@ public class AVM2SourceGenerator implements SourceGenerator { List forExpr = new ArrayList<>(); List ex = new ArrayList<>(); - ex.add(item.expression); - + if (item.expression != null) { + ex.add(item.expression); + } else { + ex.add(new BooleanAVM2Item(null, true)); + } GraphTargetItem lastItem = null; if (!ex.isEmpty()) { lastItem = ex.remove(ex.size() - 1); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java index 9c0abb716..e302990ae 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java @@ -1415,7 +1415,10 @@ public class ActionScriptParser { } forExpr = (expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); expectedType(SymbolType.SEMICOLON); - forFinalCommands.add(command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + GraphTargetItem fcom = command(thisType, pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); + if (fcom != null) { + forFinalCommands.add(fcom); + } } expectedType(SymbolType.PARENT_CLOSE); List forBody = new ArrayList<>(); @@ -1627,7 +1630,7 @@ public class ActionScriptParser { expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); } lexer.pushback(s); - TryAVM2Item tai = new TryAVM2Item(tryCommands, null, catchCommands, finallyCommands); + TryAVM2Item tai = new TryAVM2Item(tryCommands, null, catchCommands, finallyCommands, ""); tai.catchVariables = catchesVars; tai.catchExceptions2 = catchExceptions; ret = tai; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index e01a14714..390ab7a40 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -1,327 +1,328 @@ -/* - * Copyright (C) 2010-2015 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.abc.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ABCInputStream; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.CodeStats; -import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFField; -import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.logging.Level; -import java.util.logging.Logger; - -public final class MethodBody implements Cloneable { - - private static final Logger logger = Logger.getLogger(MethodBody.class.getName()); - - @Internal - public boolean deleted; - - boolean debugMode = false; - - public int method_info; - - public int max_stack; - - public int max_regs; - - public int init_scope_depth; - - public int max_scope_depth; - - @SWFField - private byte[] codeBytes; - - private AVM2Code code; - - public ABCException[] exceptions; - - public Traits traits; - - @Internal - public transient List convertedItems; - - @Internal - public transient Throwable convertException; - - public MethodBody() { - this.traits = new Traits(); - this.codeBytes = new byte[0]; - this.exceptions = new ABCException[0]; - } - - public MethodBody(Traits traits, byte[] codeBytes, ABCException[] exceptions) { - this.traits = traits; - this.codeBytes = codeBytes; - this.exceptions = exceptions; - } - - public synchronized void setCodeBytes(byte codeBytes[]) { - this.codeBytes = codeBytes; - this.code = null; - } - - public synchronized byte[] getCodeBytes() { - if (code == null) { - return codeBytes; - } else { - return code.getBytes(); - } - } - - public synchronized AVM2Code getCode() { - if (code == null) { - AVM2Code avm2Code; - try { - ABCInputStream ais = new ABCInputStream(new MemoryInputStream(codeBytes)); - avm2Code = new AVM2Code(ais); - } catch (UnknownInstructionCode | IOException ex) { - avm2Code = new AVM2Code(); - logger.log(Level.SEVERE, null, ex); - } - avm2Code.compact(); - code = avm2Code; - } - return code; - } - - public void setCode(AVM2Code code) { - this.code = code; - } - - public List getExceptionEntries() { - List ret = new ArrayList<>(); - for (ABCException e : exceptions) { - ret.add(getCode().adr2pos(e.start)); - ret.add(getCode().adr2pos(e.end)); - ret.add(getCode().adr2pos(e.target)); - } - return ret; - } - - public void markOffsets() { - long offset = 0; - for (int i = 0; i < getCode().code.size(); i++) { - getCode().code.get(i).offset = offset; - offset += getCode().code.get(i).getBytes().length; - } - } - - @Override - public String toString() { - String s = ""; - s += "method_info=" + method_info + " max_stack=" + max_stack + " max_regs=" + max_regs + " scope_depth=" + init_scope_depth + " max_scope=" + max_scope_depth; - s += "\r\nCode:\r\n" + getCode().toString(); - return s; - } - - public int removeDeadCode(AVM2ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException { - return getCode().removeDeadCode(constants, trait, info, this); - } - - public void restoreControlFlow(AVM2ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException { - getCode().restoreControlFlow(constants, trait, info, this); - } - - public int removeTraps(AVM2ConstantPool constants, ABC abc, Trait trait, int scriptIndex, int classIndex, boolean isStatic, String path) throws InterruptedException { - return getCode().removeTraps(constants, trait, abc.method_info.get(method_info), this, abc, scriptIndex, classIndex, isStatic, path); - } - - public HashMap getLocalRegNames(ABC abc) { - HashMap ret = new HashMap<>(); - for (int i = 1; i <= abc.method_info.get(this.method_info).param_types.length; i++) { - String paramName = "param" + i; - if (abc.method_info.get(this.method_info).flagHas_paramnames() && Configuration.paramNamesEnable.get()) { - paramName = abc.constants.getString(abc.method_info.get(this.method_info).paramNames[i - 1]); - } - ret.put(i, paramName); - } - int pos = abc.method_info.get(this.method_info).param_types.length + 1; - if (abc.method_info.get(this.method_info).flagNeed_arguments()) { - ret.put(pos, "arguments"); - pos++; - } - if (abc.method_info.get(this.method_info).flagNeed_rest()) { - ret.put(pos, "rest"); - pos++; - } - - if (Configuration.getLocalNamesFromDebugInfo.get()) { - Map debugRegNames = getCode().getLocalRegNamesFromDebug(abc); - for (int k : debugRegNames.keySet()) { - ret.put(k, debugRegNames.get(k)); - } - } - return ret; - } - - public void convert(final String path, ScriptExportMode exportMode, final boolean isStatic, final int scriptIndex, final int classIndex, final ABC abc, final Trait trait, final AVM2ConstantPool constants, final List method_info, final ScopeStack scopeStack, final boolean isStaticInitializer, final GraphTextWriter writer, final List fullyQualifiedNames, final Traits initTraits, boolean firstLevel) throws InterruptedException { - if (debugMode) { - System.err.println("Decompiling " + path); - } - if (exportMode != ScriptExportMode.AS) { - getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); - } else { - if (!Configuration.decompile.get()) { - writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); - return; - } - int timeout = Configuration.decompilationTimeoutSingleMethod.get(); - convertException = null; - try { - Callable callable = new Callable() { - @Override - public Void call() throws InterruptedException { - MethodBody converted = convertMethodBody(path, isStatic, scriptIndex, classIndex, abc, trait, constants, method_info, scopeStack, isStaticInitializer, fullyQualifiedNames, initTraits); - HashMap localRegNames = getLocalRegNames(abc); - convertedItems = converted.getCode().toGraphTargetItems(path, isStatic, scriptIndex, classIndex, abc, constants, method_info, converted, localRegNames, scopeStack, isStaticInitializer, fullyQualifiedNames, initTraits, Graph.SOP_USE_STATIC, new HashMap(), converted.getCode().visitCode(converted)); - Graph.graphToString(convertedItems, writer, LocalData.create(constants, localRegNames, fullyQualifiedNames)); - return null; - } - }; - if (firstLevel) { - CancellableWorker.call(callable, timeout, TimeUnit.SECONDS); - } else { - callable.call(); - } - } catch (InterruptedException ex) { - throw ex; - } catch (Exception | OutOfMemoryError | StackOverflowError ex) { - if (ex instanceof TimeoutException) { - logger.log(Level.SEVERE, "Decompilation timeout in " + path, ex); - } else { - logger.log(Level.SEVERE, "Decompilation error in " + path, ex); - } - convertException = ex; - Throwable cause = ex.getCause(); - if (ex instanceof ExecutionException && cause instanceof Exception) { - convertException = (Exception) cause; - } - } - } - } - - public GraphTextWriter toString(final String path, ScriptExportMode exportMode, final ABC abc, final Trait trait, final AVM2ConstantPool constants, final List method_info, final GraphTextWriter writer, final List fullyQualifiedNames) throws InterruptedException { - if (exportMode != ScriptExportMode.AS) { - getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); - } else { - if (!Configuration.decompile.get()) { - //writer.startMethod(this.method_info); - writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); - //writer.endMethod(); - return writer; - } - int timeout = Configuration.decompilationTimeoutSingleMethod.get(); - - if (convertException == null) { - HashMap localRegNames = getLocalRegNames(abc); - //writer.startMethod(this.method_info); - if (Configuration.showMethodBodyId.get()) { - writer.appendNoHilight("// method body id: "); - writer.appendNoHilight(abc.findBodyIndex(this.method_info)); - writer.newLine(); - } - Graph.graphToString(convertedItems, writer, LocalData.create(constants, localRegNames, fullyQualifiedNames)); - //writer.endMethod(); - } else if (convertException instanceof TimeoutException) { - // exception was logged in convert method - Helper.appendTimeoutComment(writer, timeout); - } else { - // exception was logged in convert method - Helper.appendErrorComment(writer, convertException); - } - } - return writer; - } - - public MethodBody convertMethodBody(String path, boolean isStatic, int scriptIndex, int classIndex, ABC abc, Trait trait, AVM2ConstantPool constants, List method_info, ScopeStack scopeStack, boolean isStaticInitializer, List fullyQualifiedNames, Traits initTraits) throws InterruptedException { - MethodBody b = clone(); - AVM2Code deobfuscated = b.getCode(); - deobfuscated.markMappedOffsets(); - if (Configuration.autoDeobfuscate.get()) { - try { - deobfuscated.removeTraps(constants, trait, method_info.get(this.method_info), b, abc, scriptIndex, classIndex, isStatic, path); - } catch (StackOverflowError ex) { - logger.log(Level.SEVERE, "Error during remove traps in " + path, ex); - } - } - //deobfuscated.restoreControlFlow(constants, b); - - return b; - } - - @Override - public MethodBody clone() { - try { - MethodBody ret = (MethodBody) super.clone(); - if (code != null) { - ret.code = code.clone(); - } - //maybe deep clone traits - return ret; - } catch (CloneNotSupportedException ex) { - throw new RuntimeException(); - } - } - - public boolean autoFillStats(ABC abc, int initScope, boolean hasThis) { - //System.out.println("--------------"); - CodeStats stats = getCode().getStats(abc, this, initScope); - if (stats == null) { - return false; - } - if (stats.has_activation) { - initScope++; - } - max_stack = stats.maxstack; - max_scope_depth = stats.maxscope + (stats.has_activation ? 1 : 0); - max_regs = stats.maxlocal; - init_scope_depth = initScope; - abc.method_info.get(method_info).setFlagSetsdxns(stats.has_set_dxns); - abc.method_info.get(method_info).setFlagNeed_activation(stats.has_activation); - MethodInfo mi = abc.method_info.get(method_info); - int min_regs = mi.param_types.length + 1 + (mi.flagNeed_rest() ? 1 : 0); - if (max_regs < min_regs) { - max_regs = min_regs; - } - return true; - } -} +/* + * Copyright (C) 2010-2015 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.abc.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ABCInputStream; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.CodeStats; +import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFField; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +public final class MethodBody implements Cloneable { + + private static final Logger logger = Logger.getLogger(MethodBody.class.getName()); + + @Internal + public boolean deleted; + + boolean debugMode = false; + + public int method_info; + + public int max_stack; + + public int max_regs; + + public int init_scope_depth; + + public int max_scope_depth; + + @SWFField + private byte[] codeBytes; + + private AVM2Code code; + + public ABCException[] exceptions; + + public Traits traits; + + @Internal + public transient List convertedItems; + + @Internal + public transient Throwable convertException; + + public MethodBody() { + this.traits = new Traits(); + this.codeBytes = new byte[0]; + this.exceptions = new ABCException[0]; + } + + public MethodBody(Traits traits, byte[] codeBytes, ABCException[] exceptions) { + this.traits = traits; + this.codeBytes = codeBytes; + this.exceptions = exceptions; + } + + public synchronized void setCodeBytes(byte codeBytes[]) { + this.codeBytes = codeBytes; + this.code = null; + } + + public synchronized byte[] getCodeBytes() { + if (code == null) { + return codeBytes; + } else { + return code.getBytes(); + } + } + + public synchronized AVM2Code getCode() { + if (code == null) { + AVM2Code avm2Code; + try { + ABCInputStream ais = new ABCInputStream(new MemoryInputStream(codeBytes)); + avm2Code = new AVM2Code(ais); + } catch (UnknownInstructionCode | IOException ex) { + avm2Code = new AVM2Code(); + logger.log(Level.SEVERE, null, ex); + } + avm2Code.compact(); + code = avm2Code; + } + return code; + } + + public void setCode(AVM2Code code) { + this.code = code; + } + + public List getExceptionEntries() { + List ret = new ArrayList<>(); + for (ABCException e : exceptions) { + ret.add(getCode().adr2pos(e.start)); + ret.add(getCode().adr2pos(e.end)); + ret.add(getCode().adr2pos(e.target)); + } + return ret; + } + + public void markOffsets() { + long offset = 0; + for (int i = 0; i < getCode().code.size(); i++) { + getCode().code.get(i).offset = offset; + offset += getCode().code.get(i).getBytes().length; + } + } + + @Override + public String toString() { + String s = ""; + s += "method_info=" + method_info + " max_stack=" + max_stack + " max_regs=" + max_regs + " scope_depth=" + init_scope_depth + " max_scope=" + max_scope_depth; + s += "\r\nCode:\r\n" + getCode().toString(); + return s; + } + + public int removeDeadCode(AVM2ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException { + return getCode().removeDeadCode(constants, trait, info, this); + } + + public void restoreControlFlow(AVM2ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException { + getCode().restoreControlFlow(constants, trait, info, this); + } + + public int removeTraps(AVM2ConstantPool constants, ABC abc, Trait trait, int scriptIndex, int classIndex, boolean isStatic, String path) throws InterruptedException { + return getCode().removeTraps(constants, trait, abc.method_info.get(method_info), this, abc, scriptIndex, classIndex, isStatic, path); + } + + public HashMap getLocalRegNames(ABC abc) { + HashMap ret = new HashMap<>(); + for (int i = 1; i <= abc.method_info.get(this.method_info).param_types.length; i++) { + String paramName = "param" + i; + if (abc.method_info.get(this.method_info).flagHas_paramnames() && Configuration.paramNamesEnable.get()) { + paramName = abc.constants.getString(abc.method_info.get(this.method_info).paramNames[i - 1]); + } + ret.put(i, paramName); + } + int pos = abc.method_info.get(this.method_info).param_types.length + 1; + if (abc.method_info.get(this.method_info).flagNeed_arguments()) { + ret.put(pos, "arguments"); + pos++; + } + if (abc.method_info.get(this.method_info).flagNeed_rest()) { + ret.put(pos, "rest"); + pos++; + } + + if (Configuration.getLocalNamesFromDebugInfo.get()) { + Map debugRegNames = getCode().getLocalRegNamesFromDebug(abc); + for (int k : debugRegNames.keySet()) { + ret.put(k, debugRegNames.get(k)); + } + } + return ret; + } + + public void convert(final String path, ScriptExportMode exportMode, final boolean isStatic, final int scriptIndex, final int classIndex, final ABC abc, final Trait trait, final AVM2ConstantPool constants, final List method_info, final ScopeStack scopeStack, final boolean isStaticInitializer, final GraphTextWriter writer, final List fullyQualifiedNames, final Traits initTraits, boolean firstLevel) throws InterruptedException { + if (debugMode) { + System.err.println("Decompiling " + path); + } + if (exportMode != ScriptExportMode.AS) { + getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); + } else { + if (!Configuration.decompile.get()) { + writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); + return; + } + int timeout = Configuration.decompilationTimeoutSingleMethod.get(); + convertException = null; + try { + Callable callable = new Callable() { + @Override + public Void call() throws InterruptedException { + MethodBody converted = convertMethodBody(path, isStatic, scriptIndex, classIndex, abc, trait, constants, method_info, scopeStack, isStaticInitializer, fullyQualifiedNames, initTraits); + HashMap localRegNames = getLocalRegNames(abc); + convertedItems = converted.getCode().toGraphTargetItems(path, isStatic, scriptIndex, classIndex, abc, constants, method_info, converted, localRegNames, scopeStack, isStaticInitializer, fullyQualifiedNames, initTraits, Graph.SOP_USE_STATIC, new HashMap(), converted.getCode().visitCode(converted)); + Graph.graphToString(convertedItems, writer, LocalData.create(constants, localRegNames, fullyQualifiedNames)); + return null; + } + }; + if (firstLevel) { + CancellableWorker.call(callable, timeout, TimeUnit.SECONDS); + } else { + callable.call(); + } + } catch (InterruptedException ex) { + throw ex; + } catch (Exception | OutOfMemoryError | StackOverflowError ex) { + if (ex instanceof TimeoutException) { + logger.log(Level.SEVERE, "Decompilation timeout in " + path, ex); + } else { + logger.log(Level.SEVERE, "Decompilation error in " + path, ex); + } + convertException = ex; + Throwable cause = ex.getCause(); + if (ex instanceof ExecutionException && cause instanceof Exception) { + convertException = (Exception) cause; + } + } + } + } + + public GraphTextWriter toString(final String path, ScriptExportMode exportMode, final ABC abc, final Trait trait, final AVM2ConstantPool constants, final List method_info, final GraphTextWriter writer, final List fullyQualifiedNames) throws InterruptedException { + if (exportMode != ScriptExportMode.AS) { + getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); + } else { + if (!Configuration.decompile.get()) { + //writer.startMethod(this.method_info); + writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); + //writer.endMethod(); + return writer; + } + int timeout = Configuration.decompilationTimeoutSingleMethod.get(); + + if (convertException == null) { + HashMap localRegNames = getLocalRegNames(abc); + //writer.startMethod(this.method_info); + if (Configuration.showMethodBodyId.get()) { + writer.appendNoHilight("// method body id: "); + writer.appendNoHilight(abc.findBodyIndex(this.method_info)); + writer.newLine(); + } + Graph.graphToString(convertedItems, writer, LocalData.create(constants, localRegNames, fullyQualifiedNames)); + //writer.endMethod(); + } else if (convertException instanceof TimeoutException) { + // exception was logged in convert method + Helper.appendTimeoutComment(writer, timeout); + } else { + // exception was logged in convert method + Helper.appendErrorComment(writer, convertException); + } + } + return writer; + } + + public MethodBody convertMethodBody(String path, boolean isStatic, int scriptIndex, int classIndex, ABC abc, Trait trait, AVM2ConstantPool constants, List method_info, ScopeStack scopeStack, boolean isStaticInitializer, List fullyQualifiedNames, Traits initTraits) throws InterruptedException { + MethodBody b = clone(); + AVM2Code deobfuscated = b.getCode(); + deobfuscated.markMappedOffsets(); + //deobfuscated.inlineJumpExit(); + if (Configuration.autoDeobfuscate.get()) { + try { + deobfuscated.removeTraps(constants, trait, method_info.get(this.method_info), b, abc, scriptIndex, classIndex, isStatic, path); + } catch (StackOverflowError ex) { + logger.log(Level.SEVERE, "Error during remove traps in " + path, ex); + } + } + //deobfuscated.restoreControlFlow(constants, b); + + return b; + } + + @Override + public MethodBody clone() { + try { + MethodBody ret = (MethodBody) super.clone(); + if (code != null) { + ret.code = code.clone(); + } + //maybe deep clone traits + return ret; + } catch (CloneNotSupportedException ex) { + throw new RuntimeException(); + } + } + + public boolean autoFillStats(ABC abc, int initScope, boolean hasThis) { + //System.out.println("--------------"); + CodeStats stats = getCode().getStats(abc, this, initScope); + if (stats == null) { + return false; + } + if (stats.has_activation) { + initScope++; + } + max_stack = stats.maxstack; + max_scope_depth = stats.maxscope + (stats.has_activation ? 1 : 0); + max_regs = stats.maxlocal; + init_scope_depth = initScope; + abc.method_info.get(method_info).setFlagSetsdxns(stats.has_set_dxns); + abc.method_info.get(method_info).setFlagNeed_activation(stats.has_activation); + MethodInfo mi = abc.method_info.get(method_info); + int min_regs = mi.param_types.length + 1 + (mi.flagNeed_rest() ? 1 : 0); + if (max_regs < min_regs) { + max_regs = min_regs; + } + return true; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java index b13b9df6a..3d067f888 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java @@ -1074,7 +1074,10 @@ public class ActionScriptParser { } forExpr = (expression(inFunction, inMethod, true, variables)); expectedType(SymbolType.SEMICOLON); - forFinalCommands.add(command(inFunction, inMethod, forinlevel, true, variables)); + GraphTargetItem fcom = command(inFunction, inMethod, forinlevel, true, variables); + if (fcom != null) { + forFinalCommands.add(fcom); + } } expectedType(SymbolType.PARENT_CLOSE); List forBody = new ArrayList<>(); 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 8676e9c7b..620b9dd18 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -1,2157 +1,2160 @@ -/* - * Copyright (C) 2010-2015 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.ecma.EcmaScript; -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.DoWhileItem; -import com.jpexs.decompiler.graph.model.ExitItem; -import com.jpexs.decompiler.graph.model.ForItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.IntegerValueItem; -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.ScriptEndItem; -import com.jpexs.decompiler.graph.model.SwitchItem; -import com.jpexs.decompiler.graph.model.TernarOpItem; -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.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @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; - - 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, List allParts) { - if (allParts.contains(part)) { - return; - } - allParts.add(part); - for (GraphPart p : part.nextParts) { - populateParts(p, allParts); - } - } - - public GraphPart deepCopy(GraphPart part, List visited, List copies) { - if (visited == null) { - visited = new ArrayList<>(); - } - if (copies == null) { - copies = new ArrayList<>(); - } - if (visited.contains(part)) { - return copies.get(visited.indexOf(part)); - } - visited.add(part); - GraphPart copy = new GraphPart(part.start, part.end); - copy.path = part.path; - copies.add(copy); - copy.nextParts = new ArrayList<>(); - for (int i = 0; i < part.nextParts.size(); i++) { - copy.nextParts.add(deepCopy(part.nextParts.get(i), visited, copies)); - } - for (int i = 0; i < part.refs.size(); i++) { - copy.refs.add(deepCopy(part.refs.get(i), visited, copies)); - } - return copy; - } - - public void resetGraph(GraphPart part, List 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, List ret, List loops) { - getReachableParts(part, ret, loops, true); - } - - private void getReachableParts(GraphPart part, List ret, List loops, boolean first) { - - if (first) { - 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) { - return; - } - if (l.loopBreak == part) { - return; - } - if (l.loopPreContinue == part) { - return; - } - } - if (l.reachableMark == 0) { - if (l.loopContinue == part) { - l.reachableMark = 1; - currentLoop = l; - } - } - } - - List newparts = new ArrayList<>(); - 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); - for (GraphPart next : newparts) { - getReachableParts(next, ret, loops); - } - - if (currentLoop != null) { - if (currentLoop.loopBreak != null) { - if (!ret.contains(currentLoop.loopBreak)) { - ret.add(currentLoop.loopBreak); - currentLoop.reachableMark = 2; - getReachableParts(currentLoop.loopBreak, ret, loops); - } - } - } - } - - /* public GraphPart getNextCommonPart(GraphPart part, List loops) { - return getNextCommonPart(part, new ArrayList(),loops); - }*/ - public GraphPart getNextCommonPart(BaseLocalData localData, GraphPart part, List loops) throws InterruptedException { - return getCommonPart(localData, part.nextParts, loops); - } - - 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) { - List r1 = new ArrayList<>(); - getReachableParts(p, r1, loops); - r1.add(p); - reachable.add(r1); - } - List 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 (List 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) { - List r1 = new ArrayList<>(); - getReachableParts(p, r1, loops); - r1.add(0, p); - reachable.add(r1); - } - ///List first = reachable.get(0); - int commonLevel; - Map levelMap = new HashMap<>(); - for (List first : reachable) { - int maxclevel = 0; - Set visited = new HashSet<>(); - for (GraphPart p : first) { - if (loopContinues.contains(p)) { - break; - } - if (visited.contains(p)) { - continue; - } - visited.add(p); - boolean common = true; - commonLevel = 1; - for (List r : reachable) { - if (r == first) { - continue; - } - if (r.contains(p)) { - commonLevel++; - } - } - if (commonLevel <= maxclevel) { - continue; - } - maxclevel = commonLevel; - if (levelMap.containsKey(p)) { - if (levelMap.get(p) > commonLevel) { - commonLevel = levelMap.get(p); - } - } - levelMap.put(p, commonLevel); - if (common) { - //return p; - } - } - } - for (int i = reachable.size() - 1; i >= 2; i--) { - for (GraphPart p : levelMap.keySet()) { - if (levelMap.get(p) == i) { - return p; - } - } - } - for (GraphPart p : levelMap.keySet()) { - if (levelMap.get(p) == parts.size()) { - return p; - } - } - return null; - } - - public GraphPart getNextNoJump(GraphPart part, 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 { - List allParts = new ArrayList<>(); - for (GraphPart head : heads) { - populateParts(head, allParts); - } - TranslateStack stack = new TranslateStack(); - List loops = new ArrayList<>(); - getLoops(localData, heads.get(0), loops, null); - /*System.out.println(""); - for (Loop el : loops) { - System.out.println(el); - } - System.out.println("");*/ - 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(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) { - 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); - } - } - } - } - - protected void finalProcess(List list, int level, FinalProcessLocalData localData) { - - //For detection based on debug line information - Set removeFromList = new HashSet<>(); - for (int i = 0; i < list.size(); i++) { - - if (list.get(i) instanceof ForItem) { - ForItem fori = (ForItem) list.get(i); - 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) { - forFirstCommands.add(0, list.get(j)); - removeFromList.add(j); - } else { - break; - } - } - fori.firstCommands.addAll(0, forFirstCommands); - } - } - - if (list.get(i) instanceof WhileItem) { - WhileItem whi = (WhileItem) list.get(i); - int whileExprLine = whi.getLine(); - if (whileExprLine > 0) { - List forFirstCommands = new ArrayList<>(); - List forFinalCommands = new ArrayList<>(); - - for (int j = i - 1; j >= 0; j--) { - if (list.get(j).getLine() == whileExprLine) { - forFirstCommands.add(0, list.get(j)); - removeFromList.add(j); - } else { - break; - } - } - for (int j = whi.commands.size() - 1; j >= 0; j--) { - if (whi.commands.get(j).getLine() == whileExprLine) { - forFinalCommands.add(0, whi.commands.remove(j)); - } else { - break; - } - } - if (!forFirstCommands.isEmpty() || !forFinalCommands.isEmpty()) { - GraphTargetItem lastExpr = whi.expression.remove(whi.expression.size() - 1); - forFirstCommands.addAll(whi.expression); - list.set(i, new ForItem(whi.src, whi.loop, forFirstCommands, lastExpr, forFinalCommands, whi.commands)); - } - } - } - } - - for (int i = list.size() - 1; i >= 0; i--) { - if (removeFromList.contains(i)) { - list.remove(i); - } - } - } - - private void processIfs(List list) { - //if(true) return; - for (int i = 0; i < list.size(); i++) { - GraphTargetItem item = list.get(i); - if (item instanceof Block) { - List> subs = ((Block) item).getSubs(); - for (List sub : subs) { - processIfs(sub); - } - } - if ((item instanceof LoopItem) && (item instanceof Block)) { - List> subs = ((Block) item).getSubs(); - for (List sub : subs) { - processIfs(sub); - checkContinueAtTheEnd(sub, ((LoopItem) item).loop); - } - } - 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; - } - for (Loop l : loops) { - if (l.loopContinue == part) { - return (new ContinueItem(null, l.id)); - } - if (l.loopBreak == part) { - return (new BreakItem(null, 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(GraphSource code, BaseLocalData localData, List 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, List 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(BaseLocalData localData, TranslateStack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, int staticOperation, String path) throws InterruptedException { - List visited = new ArrayList<>(); - return printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops, null, staticOperation, path, 0); - } - - protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List loops) { - return loopItem; - } - - private void getPrecontinues(String path, BaseLocalData localData, GraphPart parent, GraphPart part, List allParts, List loops, List stopPart) throws InterruptedException { - markLevels(path, localData, part, allParts, loops); - //Note: this also marks part as precontinue when there is if - /* - while(k<10){ - if(k==7){ - trace(a); - }else{ - trace(b); - } - //precontinue - k++; - } - - */ - looploops: - for (Loop l : loops) { - if (l.loopContinue != null) { - Set uniqueRefs = new HashSet<>(); - uniqueRefs.addAll(l.loopContinue.refs); - if (uniqueRefs.size() == 2) { //only one path - from precontinue - List uniqueRefsList = new ArrayList<>(uniqueRefs); - if (uniqueRefsList.get(0).discoveredTime > uniqueRefsList.get(1).discoveredTime) { //latch node is discovered later - part = uniqueRefsList.get(0); - } else { - part = uniqueRefsList.get(1); - } - if (part == l.loopContinue) { - continue looploops; - } - - while (part.refs.size() == 1) { - if (part.refs.get(0).nextParts.size() != 1) { - continue looploops; - } - - part = part.refs.get(0); - if (part == l.loopContinue) { - break; - } - } - if (part.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, List allParts, List loops) throws InterruptedException { - clearLoops(loops); - markLevels(path, localData, part, allParts, loops, new ArrayList(), 1, new ArrayList(), 0); - clearLoops(loops); - } - - private void markLevels(String path, BaseLocalData localData, GraphPart part, List allParts, List loops, List stopPart, int level, List visited, int recursionLevel) throws InterruptedException { - boolean debugMode = false; - if (stopPart == null) { - stopPart = new ArrayList<>(); - } - if (recursionLevel > allParts.size() + 1) { - Logger.getLogger(Graph.class.getName()).log(Level.WARNING, "{0} : markLevels max recursion level reached", path); - return; - } - - 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.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 (stopPart == null) { - stopPart = new ArrayList<>(); - } - 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.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 = 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); - } - } - if (part.nextParts.size() > 2) { - GraphPart next = getNextCommonPart(localData, part, loops); - - for (GraphPart p : part.nextParts) { - List stopPart2 = 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); - } - } - 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) { - 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) { - 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(List visited, BaseLocalData localData, TranslateStack stack, List 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 (visited.contains(part)) { - //return new ArrayList(); - } else { - visited.add(part); - } - 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, 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, 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, el.id)); - return ret; - } - } - - if (stopPart.contains(part)) { - if (currentLoop != null) { - currentLoop.phase = 0; - } - switch (part.stopPartType) { - case AND_OR: - part.setAndOrStack(stack); //Save stack for later use - break; - - case COMMONPART: - part.setCommonPartStack(stack); //Save stack for later use - break; - - case NONE: - break; - } - return ret; - } - - if ((part != null) && (code.size() <= part.start)) { - ret.add(new ScriptEndItem()); - return ret; - } - List currentRet = ret; - UniversalLoopItem loopItem = null; - if (isLoop) { - loopItem = new UniversalLoopItem(null, 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()); - } - } - - //Assuming part with two nextparts is an IF - - /* //If with both branches empty - if (part.nextParts.size() == 2) { - if (part.nextParts.get(0) == part.nextParts.get(1)) { - if (!stack.isEmpty()) { - GraphTargetItem expr = stack.pop(); - if (expr instanceof LogicalOpItem) { - expr = ((LogicalOpItem) expr).invert(); - } else { - expr = new NotItem(null, expr); - } - output.add(new IfItem(null, expr, new ArrayList(), new ArrayList())); - } - part.nextParts.remove(0); - } - }*/ - if (parseNext) { - List retCheck = check(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); - } - } - - /** - * AND / OR detection - */ - if (parseNext && part.nextParts.size() == 2) { - if ((stack.size() >= 2) && (stack.get(stack.size() - 1) instanceof NotItem) && (((NotItem) (stack.get(stack.size() - 1))).getOriginal().getNotCoerced() == stack.get(stack.size() - 2).getNotCoerced())) { - GraphPart sp0 = getNextNoJump(part.nextParts.get(0), localData); - GraphPart sp1 = getNextNoJump(part.nextParts.get(1), localData); - boolean reversed = false; - loopContinues = getLoopsContinues(loops); - loopContinues.add(part);//??? - if (sp1.leadsTo(localData, this, code, sp0, loops)) { - } else if (sp0.leadsTo(localData, this, code, sp1, loops)) { - reversed = true; - } - GraphPart next = reversed ? sp0 : sp1; - GraphTargetItem ti; - if ((ti = checkLoop(next, stopPart, loops)) != null) { - currentRet.add(ti); - } else { - List stopPart2 = new ArrayList<>(stopPart); - GraphPart andOrStopPart = reversed ? sp1 : sp0; - andOrStopPart.stopPartType = GraphPart.StopPartType.AND_OR; - stopPart2.add(andOrStopPart); - printGraph(visited, localData, stack, allParts, parent, next, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - stack = andOrStopPart.andOrStack; // Use stack that was stored upon reaching AND_OR stopPart - GraphTargetItem second = stack.pop(); - GraphTargetItem first = stack.pop(); - andOrStopPart.stopPartType = GraphPart.StopPartType.NONE; // Reset stopPartType - - if (!reversed) { - AndItem a = new AndItem(null, first, second); - stack.push(a); - a.firstPart = part; - if (second instanceof AndItem) { - a.firstPart = ((AndItem) second).firstPart; - } - if (second instanceof OrItem) { - a.firstPart = ((OrItem) second).firstPart; - } - } else { - OrItem o = new OrItem(null, first, second); - stack.push(o); - o.firstPart = part; - if (second instanceof AndItem) { - o.firstPart = ((AndItem) second).firstPart; - } - if (second instanceof OrItem) { - o.firstPart = ((OrItem) second).firstPart; - } - } - next = reversed ? sp1 : sp0; - if ((ti = checkLoop(next, stopPart, loops)) != null) { - currentRet.add(ti); - } else { - currentRet.addAll(printGraph(visited, localData, stack, allParts, parent, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); - } - } - parseNext = false; - //return ret; - } else if ((stack.size() >= 2) && (stack.get(stack.size() - 1).getNotCoerced() == stack.get(stack.size() - 2).getNotCoerced())) { - GraphPart sp0 = getNextNoJump(part.nextParts.get(0), localData); - GraphPart sp1 = getNextNoJump(part.nextParts.get(1), localData); - boolean reversed = false; - loopContinues = getLoopsContinues(loops); - loopContinues.add(part);//??? - if (sp1.leadsTo(localData, this, code, sp0, loops)) { - } else if (sp0.leadsTo(localData, this, code, sp1, loops)) { - reversed = true; - } - GraphPart next = reversed ? sp0 : sp1; - GraphTargetItem ti; - if ((ti = checkLoop(next, stopPart, loops)) != null) { - currentRet.add(ti); - } else { - List stopPart2 = new ArrayList<>(stopPart); - GraphPart andOrStopPart = reversed ? sp1 : sp0; - andOrStopPart.stopPartType = GraphPart.StopPartType.AND_OR; - stopPart2.add(andOrStopPart); - printGraph(visited, localData, stack, allParts, parent, next, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - stack = andOrStopPart.andOrStack; // Use stack that was stored upon reaching AND_OR stopPart - GraphTargetItem second = stack.pop(); - GraphTargetItem first = stack.pop(); - andOrStopPart.stopPartType = GraphPart.StopPartType.NONE; // Reset stopPartType - - if (reversed) { - AndItem a = new AndItem(null, first, second); - stack.push(a); - a.firstPart = part; - if (second instanceof AndItem) { - a.firstPart = ((AndItem) second).firstPart; - } - if (second instanceof OrItem) { - a.firstPart = ((OrItem) second).firstPart; - } - } else { - OrItem o = new OrItem(null, first, second); - stack.push(o); - o.firstPart = part; - if (second instanceof OrItem) { - o.firstPart = ((OrItem) second).firstPart; - } - if (second instanceof AndItem) { - o.firstPart = ((AndItem) second).firstPart; - } - } - - next = reversed ? sp1 : sp0; - if ((ti = checkLoop(next, stopPart, loops)) != null) { - currentRet.add(ti); - } else { - currentRet.addAll(printGraph(visited, localData, stack, allParts, parent, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); - } - } - parseNext = false; - //return ret; - } - } -//********************************END PART DECOMPILING - - if (parseNext) { - - if (false && part.nextParts.size() > 2) {//alchemy direct switch - GraphPart next = getMostCommonPart(localData, part.nextParts, loops); - List vis = new ArrayList<>(); - GraphTargetItem switchedItem = stack.pop(); - List caseValues = new ArrayList<>(); - List> caseCommands = new ArrayList<>(); - List defaultCommands = 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 = 0; - for (GraphPart p : part.nextParts) { - - if (!first) { - caseValues.add(new IntegerValueItem(null, pos++)); - 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 (first) { - defaultCommands = printGraph(visited, prepareBranchLocalData(localData), stack, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - } else { - caseCommands.add(printGraph(visited, prepareBranchLocalData(localData), stack, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1)); - } - vis.add(p); - } - first = false; - } - SwitchItem sw = new SwitchItem(null, swLoop, switchedItem, caseValues, caseCommands, defaultCommands, valueMappings); - currentRet.add(sw); - swLoop.phase = 2; - if (next != null) { - currentRet.addAll(printGraph(visited, localData, stack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); - } - } //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 (staticOperation != SOP_USE_STATIC) { - if (expr.isCompileTime()) { - boolean doJump = EcmaScript.toBoolean(expr.getResult()); - if (doJump) { - nextOnePart = part.nextParts.get(0); - } else { - nextOnePart = part.nextParts.get(1); - } - if (staticOperation == SOP_REMOVE_STATIC) { - //TODO - } - } - } - if (nextOnePart == null) { - - List nps; - /*nps = new ArrayList<>(part.nextParts); - for(int i=0;i onTrue = new ArrayList<>(); - boolean isEmpty = nps.get(0) == nps.get(1); - - if (isEmpty) { - next = nps.get(0); - } - - List stopPart2 = new ArrayList<>(stopPart); - GraphPart.CommonPartStack commonPartStack = null; - if ((!isEmpty) && (next != null)) { - commonPartStack = next.new CommonPartStack(); - if (next.commonPartStacks == null) { - next.commonPartStacks = new ArrayList<>(); - } - next.stopPartType = GraphPart.StopPartType.COMMONPART; - stopPart2.add(next); - } - if (!isEmpty) { - if (next != null) { - next.commonPartStacks.add(commonPartStack); - commonPartStack.isTrueStack = true; //stopPart must know it needs to store trueStack - } - onTrue = printGraph(visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - } - List onFalse = new ArrayList<>(); - - if (!isEmpty) { - if (next != null) { - commonPartStack.isTrueStack = false; //stopPart must know it needs to store falseStack - } - onFalse = printGraph(visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); - } - - /* if there is a stopPart (next), then Graph will be further analyzed starting from the stopPart: - * trueStack and falseStack must be set equal to corresponding stack that was built upon reaching stopPart. */ - if ((!isEmpty) && (next != null)) { - if ((commonPartStack.trueStack != null) && (commonPartStack.falseStack != null)) { - trueStack = commonPartStack.trueStack; - falseStack = commonPartStack.falseStack; - } - next.commonPartStacks.remove(next.commonPartStacks.size() - 1); - if (next.commonPartStacks.isEmpty()) { - next.stopPartType = GraphPart.StopPartType.NONE; // reset StopPartType - } - } - - if (isEmpty(onTrue) && isEmpty(onFalse) && (trueStack.size() == trueStackSizeBefore + 1) && (falseStack.size() == falseStackSizeBefore + 1)) { - stack.push(new TernarOpItem(null, expr, trueStack.pop(), falseStack.pop())); - } else { - currentRet.add(new IfItem(null, expr, onTrue, onFalse)); - } - if (next != null) { - if (trueStack.size() != trueStackSizeBefore || falseStack.size() != falseStackSizeBefore) { - // it's a hack, because duplicates all instructions in the next part, but better than EmptyStackException - onTrue = printGraph(visited, localData, trueStack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1); - onFalse = printGraph(visited, localData, falseStack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1); - if (isEmpty(onTrue) && isEmpty(onFalse) && (trueStack.size() == trueStackSizeBefore + 1) && (falseStack.size() == falseStackSizeBefore + 1)) { - stack.push(new TernarOpItem(null, expr, trueStack.pop(), falseStack.pop())); - } else { - currentRet.add(new IfItem(null, expr, onTrue, onFalse)); - } - } else { - printGraph(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(visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); - } - - } - if (isLoop) { - - LoopItem li = loopItem; - boolean loopTypeFound = false; - - boolean hasContinue = false; - processIfs(loopItem.commands); - checkContinueAtTheEnd(loopItem.commands, currentLoop); - List continues = loopItem.getContinues(); - for (ContinueItem c : continues) { - if (c.loopId == currentLoop.id) { - hasContinue = true; - break; - } - } - if (!hasContinue) { - if (currentLoop.loopPreContinue != null) { - List stopContPart = new ArrayList<>(); - stopContPart.add(currentLoop.loopContinue); - GraphPart precoBackup = currentLoop.loopPreContinue; - currentLoop.loopPreContinue = null; - loopItem.commands.addAll(printGraph(visited, localData, new TranslateStack(), 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) { - bodyBranch = 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(); - } else { - expr = new NotItem(null, 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(visited, localData, new TranslateStack(), 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.src, currentLoop, new ArrayList(), exprList.get(exprList.size() - 1), finalComm, commands)); - } else { - ret.add(index, li = new WhileItem(expr.src, 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) { - if (expr instanceof LogicalOpItem) { - expr = ((LogicalOpItem) expr).invert(); - } else { - expr = new NotItem(null, expr); - } - } - - 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, 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(visited, localData, new TranslateStack(), 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.onTrue.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.onTrue.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) { - if (expr instanceof LogicalOpItem) { - expr = ((LogicalOpItem) expr).invert(); - } else { - expr = new NotItem(null, expr); - } - } - exprList.add(expr); - ret.add(index, li = new DoWhileItem(null, 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(visited, localData, stack, 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; - } -} +/* + * Copyright (C) 2010-2015 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.ecma.EcmaScript; +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.DoWhileItem; +import com.jpexs.decompiler.graph.model.ExitItem; +import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.IntegerValueItem; +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.ScriptEndItem; +import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.TernarOpItem; +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.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @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; + + 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, List allParts) { + if (allParts.contains(part)) { + return; + } + allParts.add(part); + for (GraphPart p : part.nextParts) { + populateParts(p, allParts); + } + } + + public GraphPart deepCopy(GraphPart part, List visited, List copies) { + if (visited == null) { + visited = new ArrayList<>(); + } + if (copies == null) { + copies = new ArrayList<>(); + } + if (visited.contains(part)) { + return copies.get(visited.indexOf(part)); + } + visited.add(part); + GraphPart copy = new GraphPart(part.start, part.end); + copy.path = part.path; + copies.add(copy); + copy.nextParts = new ArrayList<>(); + for (int i = 0; i < part.nextParts.size(); i++) { + copy.nextParts.add(deepCopy(part.nextParts.get(i), visited, copies)); + } + for (int i = 0; i < part.refs.size(); i++) { + copy.refs.add(deepCopy(part.refs.get(i), visited, copies)); + } + return copy; + } + + public void resetGraph(GraphPart part, List 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, List ret, List loops) { + getReachableParts(part, ret, loops, true); + } + + private void getReachableParts(GraphPart part, List ret, List loops, boolean first) { + + if (first) { + 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) { + return; + } + if (l.loopBreak == part) { + return; + } + if (l.loopPreContinue == part) { + return; + } + } + if (l.reachableMark == 0) { + if (l.loopContinue == part) { + l.reachableMark = 1; + currentLoop = l; + } + } + } + + List newparts = new ArrayList<>(); + 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); + for (GraphPart next : newparts) { + getReachableParts(next, ret, loops); + } + + if (currentLoop != null) { + if (currentLoop.loopBreak != null) { + if (!ret.contains(currentLoop.loopBreak)) { + ret.add(currentLoop.loopBreak); + currentLoop.reachableMark = 2; + getReachableParts(currentLoop.loopBreak, ret, loops); + } + } + } + } + + /* public GraphPart getNextCommonPart(GraphPart part, List loops) { + return getNextCommonPart(part, new ArrayList(),loops); + }*/ + public GraphPart getNextCommonPart(BaseLocalData localData, GraphPart part, List loops) throws InterruptedException { + return getCommonPart(localData, part.nextParts, loops); + } + + 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) { + List r1 = new ArrayList<>(); + getReachableParts(p, r1, loops); + r1.add(p); + reachable.add(r1); + } + List 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 (List 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) { + List r1 = new ArrayList<>(); + getReachableParts(p, r1, loops); + r1.add(0, p); + reachable.add(r1); + } + ///List first = reachable.get(0); + int commonLevel; + Map levelMap = new HashMap<>(); + for (List first : reachable) { + int maxclevel = 0; + Set visited = new HashSet<>(); + for (GraphPart p : first) { + if (loopContinues.contains(p)) { + break; + } + if (visited.contains(p)) { + continue; + } + visited.add(p); + boolean common = true; + commonLevel = 1; + for (List r : reachable) { + if (r == first) { + continue; + } + if (r.contains(p)) { + commonLevel++; + } + } + if (commonLevel <= maxclevel) { + continue; + } + maxclevel = commonLevel; + if (levelMap.containsKey(p)) { + if (levelMap.get(p) > commonLevel) { + commonLevel = levelMap.get(p); + } + } + levelMap.put(p, commonLevel); + if (common) { + //return p; + } + } + } + for (int i = reachable.size() - 1; i >= 2; i--) { + for (GraphPart p : levelMap.keySet()) { + if (levelMap.get(p) == i) { + return p; + } + } + } + for (GraphPart p : levelMap.keySet()) { + if (levelMap.get(p) == parts.size()) { + return p; + } + } + return null; + } + + public GraphPart getNextNoJump(GraphPart part, 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 { + List allParts = new ArrayList<>(); + for (GraphPart head : heads) { + populateParts(head, allParts); + } + TranslateStack stack = new TranslateStack(); + List loops = new ArrayList<>(); + getLoops(localData, heads.get(0), loops, null); + /*System.out.println(""); + for (Loop el : loops) { + System.out.println(el); + } + System.out.println("");*/ + 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(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) { + 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); + } + + protected void finalProcessAfter(List list, int level, FinalProcessLocalData localData) { + + } + + protected void finalProcess(List list, int level, FinalProcessLocalData localData) { + + //For detection based on debug line information + Set removeFromList = new HashSet<>(); + for (int i = 0; i < list.size(); i++) { + + if (list.get(i) instanceof ForItem) { + ForItem fori = (ForItem) list.get(i); + 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) { + forFirstCommands.add(0, list.get(j)); + removeFromList.add(j); + } else { + break; + } + } + fori.firstCommands.addAll(0, forFirstCommands); + } + } + + if (list.get(i) instanceof WhileItem) { + WhileItem whi = (WhileItem) list.get(i); + int whileExprLine = whi.getLine(); + if (whileExprLine > 0) { + List forFirstCommands = new ArrayList<>(); + List forFinalCommands = new ArrayList<>(); + + for (int j = i - 1; j >= 0; j--) { + if (list.get(j).getLine() == whileExprLine) { + forFirstCommands.add(0, list.get(j)); + removeFromList.add(j); + } else { + break; + } + } + for (int j = whi.commands.size() - 1; j >= 0; j--) { + if (whi.commands.get(j).getLine() == whileExprLine) { + forFinalCommands.add(0, whi.commands.remove(j)); + } else { + break; + } + } + if (!forFirstCommands.isEmpty() || !forFinalCommands.isEmpty()) { + GraphTargetItem lastExpr = whi.expression.remove(whi.expression.size() - 1); + forFirstCommands.addAll(whi.expression); + list.set(i, new ForItem(whi.src, whi.loop, forFirstCommands, lastExpr, forFinalCommands, whi.commands)); + } + } + } + } + + for (int i = list.size() - 1; i >= 0; i--) { + if (removeFromList.contains(i)) { + list.remove(i); + } + } + } + + private void processIfs(List list) { + //if(true) return; + for (int i = 0; i < list.size(); i++) { + GraphTargetItem item = list.get(i); + if (item instanceof Block) { + List> subs = ((Block) item).getSubs(); + for (List sub : subs) { + processIfs(sub); + } + } + if ((item instanceof LoopItem) && (item instanceof Block)) { + List> subs = ((Block) item).getSubs(); + for (List sub : subs) { + processIfs(sub); + checkContinueAtTheEnd(sub, ((LoopItem) item).loop); + } + } + 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; + } + for (Loop l : loops) { + if (l.loopContinue == part) { + return (new ContinueItem(null, l.id)); + } + if (l.loopBreak == part) { + return (new BreakItem(null, 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(GraphSource code, BaseLocalData localData, List 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, List 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(BaseLocalData localData, TranslateStack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, int staticOperation, String path) throws InterruptedException { + List visited = new ArrayList<>(); + return printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops, null, staticOperation, path, 0); + } + + protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List loops) { + return loopItem; + } + + private void getPrecontinues(String path, BaseLocalData localData, GraphPart parent, GraphPart part, List allParts, List loops, List stopPart) throws InterruptedException { + markLevels(path, localData, part, allParts, loops); + //Note: this also marks part as precontinue when there is if + /* + while(k<10){ + if(k==7){ + trace(a); + }else{ + trace(b); + } + //precontinue + k++; + } + + */ + looploops: + for (Loop l : loops) { + if (l.loopContinue != null) { + Set uniqueRefs = new HashSet<>(); + uniqueRefs.addAll(l.loopContinue.refs); + if (uniqueRefs.size() == 2) { //only one path - from precontinue + List uniqueRefsList = new ArrayList<>(uniqueRefs); + if (uniqueRefsList.get(0).discoveredTime > uniqueRefsList.get(1).discoveredTime) { //latch node is discovered later + part = uniqueRefsList.get(0); + } else { + part = uniqueRefsList.get(1); + } + if (part == l.loopContinue) { + continue looploops; + } + + while (part.refs.size() == 1) { + if (part.refs.get(0).nextParts.size() != 1) { + continue looploops; + } + + part = part.refs.get(0); + if (part == l.loopContinue) { + break; + } + } + if (part.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, List allParts, List loops) throws InterruptedException { + clearLoops(loops); + markLevels(path, localData, part, allParts, loops, new ArrayList(), 1, new ArrayList(), 0); + clearLoops(loops); + } + + private void markLevels(String path, BaseLocalData localData, GraphPart part, List allParts, List loops, List stopPart, int level, List visited, int recursionLevel) throws InterruptedException { + boolean debugMode = false; + if (stopPart == null) { + stopPart = new ArrayList<>(); + } + if (recursionLevel > allParts.size() + 1) { + Logger.getLogger(Graph.class.getName()).log(Level.WARNING, "{0} : markLevels max recursion level reached", path); + return; + } + + 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.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 (stopPart == null) { + stopPart = new ArrayList<>(); + } + 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.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 = 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); + } + } + if (part.nextParts.size() > 2) { + GraphPart next = getNextCommonPart(localData, part, loops); + + for (GraphPart p : part.nextParts) { + List stopPart2 = 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); + } + } + 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) { + 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) { + 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(List visited, BaseLocalData localData, TranslateStack stack, List 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 (visited.contains(part)) { + //return new ArrayList(); + } else { + visited.add(part); + } + 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, 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, 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, el.id)); + return ret; + } + } + + if (stopPart.contains(part)) { + if (currentLoop != null) { + currentLoop.phase = 0; + } + /*switch (part.stopPartType) { + case AND_OR: + part.setAndOrStack(stack); //Save stack for later use + break; + + case COMMONPART: + part.setCommonPartStack(stack); //Save stack for later use + break; + + case NONE: + break; + }*/ + return ret; + } + + if ((part != null) && (code.size() <= part.start)) { + ret.add(new ScriptEndItem()); + return ret; + } + List currentRet = ret; + UniversalLoopItem loopItem = null; + if (isLoop) { + loopItem = new UniversalLoopItem(null, 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()); + } + } + + //Assuming part with two nextparts is an IF + + /* //If with both branches empty + if (part.nextParts.size() == 2) { + if (part.nextParts.get(0) == part.nextParts.get(1)) { + if (!stack.isEmpty()) { + GraphTargetItem expr = stack.pop(); + if (expr instanceof LogicalOpItem) { + expr = ((LogicalOpItem) expr).invert(); + } else { + expr = new NotItem(null, expr); + } + output.add(new IfItem(null, expr, new ArrayList(), new ArrayList())); + } + part.nextParts.remove(0); + } + }*/ + if (parseNext) { + List retCheck = check(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); + } + } + + /** + * AND / OR detection + */ + if (parseNext && part.nextParts.size() == 2) { + if ((stack.size() >= 2) && (stack.get(stack.size() - 1) instanceof NotItem) && (((NotItem) (stack.get(stack.size() - 1))).getOriginal().getNotCoerced() == stack.get(stack.size() - 2).getNotCoerced())) { + GraphPart sp0 = getNextNoJump(part.nextParts.get(0), localData); + GraphPart sp1 = getNextNoJump(part.nextParts.get(1), localData); + boolean reversed = false; + loopContinues = getLoopsContinues(loops); + loopContinues.add(part);//??? + if (sp1.leadsTo(localData, this, code, sp0, loops)) { + } else if (sp0.leadsTo(localData, this, code, sp1, loops)) { + reversed = true; + } + GraphPart next = reversed ? sp0 : sp1; + GraphTargetItem ti; + if ((ti = checkLoop(next, stopPart, loops)) != null) { + currentRet.add(ti); + } else { + List stopPart2 = new ArrayList<>(stopPart); + GraphPart andOrStopPart = reversed ? sp1 : sp0; + stopPart2.add(andOrStopPart); + GraphTargetItem first = ((NotItem) stack.pop()).getOriginal(); + stack.pop(); + stack.push(new MarkItem("disposable")); + printGraph(visited, localData, stack, allParts, parent, next, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + GraphTargetItem second = stack.pop(); + + if (!reversed) { + AndItem a = new AndItem(null, first, second); + stack.push(a); + a.firstPart = part; + if (second instanceof AndItem) { + a.firstPart = ((AndItem) second).firstPart; + } + if (second instanceof OrItem) { + a.firstPart = ((OrItem) second).firstPart; + } + } else { + OrItem o = new OrItem(null, first, second); + stack.push(o); + o.firstPart = part; + if (second instanceof AndItem) { + o.firstPart = ((AndItem) second).firstPart; + } + if (second instanceof OrItem) { + o.firstPart = ((OrItem) second).firstPart; + } + } + next = reversed ? sp1 : sp0; + if ((ti = checkLoop(next, stopPart, loops)) != null) { + currentRet.add(ti); + } else { + currentRet.addAll(printGraph(visited, localData, stack, allParts, parent, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); + } + } + parseNext = false; + //return ret; + } else if ((stack.size() >= 2) && (stack.get(stack.size() - 1).getNotCoerced() == stack.get(stack.size() - 2).getNotCoerced())) { + GraphPart sp0 = getNextNoJump(part.nextParts.get(0), localData); + GraphPart sp1 = getNextNoJump(part.nextParts.get(1), localData); + boolean reversed = false; + loopContinues = getLoopsContinues(loops); + loopContinues.add(part);//??? + if (sp1.leadsTo(localData, this, code, sp0, loops)) { + } else if (sp0.leadsTo(localData, this, code, sp1, loops)) { + reversed = true; + } + GraphPart next = reversed ? sp0 : sp1; + GraphTargetItem ti; + if ((ti = checkLoop(next, stopPart, loops)) != null) { + currentRet.add(ti); + } else { + List stopPart2 = new ArrayList<>(stopPart); + GraphPart andOrStopPart = reversed ? sp1 : sp0; + //andOrStopPart.stopPartType = GraphPart.StopPartType.AND_OR; + stopPart2.add(andOrStopPart); + GraphTargetItem first = stack.pop(); + stack.pop(); + stack.push(new MarkItem("disposable")); + printGraph(visited, localData, stack, allParts, parent, next, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + //stack = andOrStopPart.andOrStack; // Use stack that was stored upon reaching AND_OR stopPart + GraphTargetItem second = stack.pop(); + //GraphTargetItem first = stack.pop(); + //andOrStopPart.stopPartType = GraphPart.StopPartType.NONE; // Reset stopPartType + + if (reversed) { + AndItem a = new AndItem(null, first, second); + stack.push(a); + a.firstPart = part; + if (second instanceof AndItem) { + a.firstPart = ((AndItem) second).firstPart; + } + if (second instanceof OrItem) { + a.firstPart = ((OrItem) second).firstPart; + } + } else { + OrItem o = new OrItem(null, first, second); + stack.push(o); + o.firstPart = part; + if (second instanceof OrItem) { + o.firstPart = ((OrItem) second).firstPart; + } + if (second instanceof AndItem) { + o.firstPart = ((AndItem) second).firstPart; + } + } + + next = reversed ? sp1 : sp0; + if ((ti = checkLoop(next, stopPart, loops)) != null) { + currentRet.add(ti); + } else { + currentRet.addAll(printGraph(visited, localData, stack, allParts, parent, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); + } + } + parseNext = false; + //return ret; + } + } +//********************************END PART DECOMPILING + + if (parseNext) { + + if (part.nextParts.size() > 2) {//direct switch, seen in the wild... + GraphPart next = getMostCommonPart(localData, part.nextParts, loops); + List vis = new ArrayList<>(); + GraphTargetItem switchedItem = stack.pop(); + List caseValues = new ArrayList<>(); + List> caseCommands = new ArrayList<>(); + List defaultCommands = 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 = 0; + for (GraphPart p : part.nextParts) { + + if (!first) { + caseValues.add(new IntegerValueItem(null, pos++)); + 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 (first) { + defaultCommands = printGraph(visited, prepareBranchLocalData(localData), stack, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + } else { + caseCommands.add(printGraph(visited, prepareBranchLocalData(localData), stack, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1)); + } + vis.add(p); + } + first = false; + } + SwitchItem sw = new SwitchItem(null, swLoop, switchedItem, caseValues, caseCommands, defaultCommands, valueMappings); + currentRet.add(sw); + swLoop.phase = 2; + if (next != null) { + currentRet.addAll(printGraph(visited, localData, stack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); + } + } //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 (staticOperation != SOP_USE_STATIC) { + if (expr.isCompileTime()) { + boolean doJump = EcmaScript.toBoolean(expr.getResult()); + if (doJump) { + nextOnePart = part.nextParts.get(0); + } else { + nextOnePart = part.nextParts.get(1); + } + if (staticOperation == SOP_REMOVE_STATIC) { + //TODO + } + } + } + if (nextOnePart == null) { + + List nps; + /*nps = new ArrayList<>(part.nextParts); + for(int i=0;i onTrue = new ArrayList<>(); + boolean isEmpty = nps.get(0) == nps.get(1); + + if (isEmpty) { + next = nps.get(0); + } + + List stopPart2 = new ArrayList<>(stopPart); + //GraphPart.CommonPartStack commonPartStack = null; + if ((!isEmpty) && (next != null)) { + /*commonPartStack = next.new CommonPartStack(); + if (next.commonPartStacks == null) { + next.commonPartStacks = new ArrayList<>(); + } + next.stopPartType = GraphPart.StopPartType.COMMONPART; + */ + stopPart2.add(next); + } + + if (!isEmpty) { + onTrue = printGraph(visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + } + List onFalse = new ArrayList<>(); + + if (!isEmpty) { + /*if (next != null) { + commonPartStack.isTrueStack = false; //stopPart must know it needs to store falseStack + }*/ + onFalse = printGraph(visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + } + + /* if there is a stopPart (next), then Graph will be further analyzed starting from the stopPart: + * trueStack and falseStack must be set equal to corresponding stack that was built upon reaching stopPart. + if ((!isEmpty) && (next != null)) { + if ((commonPartStack.trueStack != null) && (commonPartStack.falseStack != null)) { + trueStack = commonPartStack.trueStack; + falseStack = commonPartStack.falseStack; + } + next.commonPartStacks.remove(next.commonPartStacks.size() - 1); + if (next.commonPartStacks.isEmpty()) { + next.stopPartType = GraphPart.StopPartType.NONE; // reset StopPartType + } + }*/ + if (isEmpty(onTrue) && isEmpty(onFalse) && (trueStack.size() == trueStackSizeBefore + 1) && (falseStack.size() == falseStackSizeBefore + 1)) { + stack.push(new TernarOpItem(null, expr, trueStack.pop(), falseStack.pop())); + } else { + currentRet.add(new IfItem(null, expr, onTrue, onFalse)); + } + if (next != null) { + if (trueStack.size() != trueStackSizeBefore || falseStack.size() != falseStackSizeBefore) { + // it's a hack, because duplicates all instructions in the next part, but better than EmptyStackException + onTrue = printGraph(visited, localData, trueStack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1); + onFalse = printGraph(visited, localData, falseStack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1); + if (isEmpty(onTrue) && isEmpty(onFalse) && (trueStack.size() == trueStackSizeBefore + 1) && (falseStack.size() == falseStackSizeBefore + 1)) { + stack.push(new TernarOpItem(null, expr, trueStack.pop(), falseStack.pop())); + } else { + currentRet.add(new IfItem(null, expr, onTrue, onFalse)); + } + } else { + printGraph(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(visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); + } + + } + if (isLoop) { + + LoopItem li = loopItem; + boolean loopTypeFound = false; + + boolean hasContinue = false; + processIfs(loopItem.commands); + checkContinueAtTheEnd(loopItem.commands, currentLoop); + List continues = loopItem.getContinues(); + for (ContinueItem c : continues) { + if (c.loopId == currentLoop.id) { + hasContinue = true; + break; + } + } + if (!hasContinue) { + if (currentLoop.loopPreContinue != null) { + List stopContPart = new ArrayList<>(); + stopContPart.add(currentLoop.loopContinue); + GraphPart precoBackup = currentLoop.loopPreContinue; + currentLoop.loopPreContinue = null; + loopItem.commands.addAll(printGraph(visited, localData, new TranslateStack(), 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) { + bodyBranch = 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(); + } else { + expr = new NotItem(null, 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(visited, localData, new TranslateStack(), 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.src, currentLoop, new ArrayList(), exprList.get(exprList.size() - 1), finalComm, commands)); + } else { + ret.add(index, li = new WhileItem(expr.src, 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) { + if (expr instanceof LogicalOpItem) { + expr = ((LogicalOpItem) expr).invert(); + } else { + expr = new NotItem(null, expr); + } + } + + 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, 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(visited, localData, new TranslateStack(), 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.onTrue.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.onTrue.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) { + if (expr instanceof LogicalOpItem) { + expr = ((LogicalOpItem) expr).invert(); + } else { + expr = new NotItem(null, expr); + } + } + exprList.add(expr); + ret.add(index, li = new DoWhileItem(null, 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(visited, localData, stack, 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; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java index b79ab7e64..5c96b3830 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java @@ -1,317 +1,314 @@ -/* - * Copyright (C) 2010-2015 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 java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class GraphPart implements Serializable { - - public int start = 0; - - public int end = 0; - - public int instanceCount = 0; - - public List nextParts = new ArrayList<>(); - - public int posX = -1; - - public int posY = -1; - - public GraphPath path = new GraphPath(); - - public List refs = new ArrayList<>(); - - public boolean ignored = false; - - public List forContinues = new ArrayList<>(); - - public int level; - - public int discoveredTime; - - public int finishedTime; - - public int order; - - public List throwParts = new ArrayList<>(); - - public enum StopPartType { - - NONE, AND_OR, COMMONPART - } - - public StopPartType stopPartType = StopPartType.NONE; - - public TranslateStack andOrStack; // Stores stack when AND_OR stopPart has been reached - - public class CommonPartStack { // Stores stack when COMMONPART stopPart has been reached - - boolean isTrueStack; - - TranslateStack trueStack; - - TranslateStack falseStack; - } - - public ArrayList commonPartStacks; - - public void setAndOrStack(TranslateStack stack) { - andOrStack = stack; - } - - public void setCommonPartStack(TranslateStack stack) { - CommonPartStack currentStack = commonPartStacks.get(commonPartStacks.size() - 1); - if (currentStack.isTrueStack) { - currentStack.trueStack = stack; - } else { - currentStack.falseStack = stack; - } - } - - public int setTime(int time, List ordered, List visited) { - if (visited.contains(this)) { - return time; - } - discoveredTime = time; - visited.add(this); - for (GraphPart next : nextParts) { - if (!visited.contains(next)) { - time = next.setTime(time + 1, ordered, visited); - } - } - time++; - finishedTime = time; - order = ordered.size(); - ordered.add(this); - return time; - } - - private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List visited, List loops) throws InterruptedException { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - - GraphPart tpart = gr.checkPart(null, localData, this, null); - if (tpart == null) { - return false; - } - if (tpart != this) { - return tpart.leadsTo(localData, gr, code, part, visited, loops); - } - Loop currentLoop = null; - for (Loop l : loops) { - /*if(l.phase==0){ - if(l.loopContinue==this){ - l.leadsToMark = 1; - next = l.loopBreak; - currentLoop = l; - continue; - } - }*/ - if (l.phase == 1) { - if (l.loopContinue == this) { - return false; - } - if (l.loopPreContinue == this) { - return false; - } - if (l.loopBreak == this) { - //return false; //? - } - } - } - if (visited.contains(this)) { - return false; - } - /*if (loops.contains(this)) { - return false; - }*/ - visited.add(this); - if (end < code.size() && code.get(end).isBranch() && (code.get(end).ignoredLoops())) { - return false; - } - for (GraphPart p : nextParts) { - if (p == part) { - return true; - } else { - if (p.leadsTo(localData, gr, code, part, visited, loops)) { - return true; - } - } - } - for (GraphPart p : throwParts) { - if (p == part) { - return true; - } else { - if (p.leadsTo(localData, gr, code, part, visited, loops)) { - return true; - } - } - } - return false; - } - - public boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List loops) throws InterruptedException { - for (Loop l : loops) { - l.leadsToMark = 0; - } - return leadsTo(localData, gr, code, part, new ArrayList(), loops); - } - - public GraphPart(int start, int end) { - this.start = start; - this.end = end; - } - - private GraphPart getNextPartPath(GraphPart original, GraphPath path, List visited) { - if (visited.contains(this) && (this == original)) { - return null; - } - if (visited.contains(this) && (this != original)) { - return null; - } - visited.add(this); - for (GraphPart p : nextParts) { - if (p == original) { - continue; - } - if (p.path.equals(path)) { - return p; - } else if (p.path.length() >= path.length()) { - GraphPart gp = p.getNextPartPath(original, path, visited); - if (gp != null) { - return gp; - } - } - } - return null; - } - - public GraphPart getNextPartPath(List ignored) { - List visited = new ArrayList<>(); - visited.addAll(ignored); - if (visited.contains(this)) { - visited.remove(this); - } - return getNextPartPath(this, path, visited); - } - - public GraphPart getNextSuperPartPath(List ignored) { - List visited = new ArrayList<>(); - visited.addAll(ignored); - return getNextSuperPartPath(this, path, visited); - } - - private GraphPart getNextSuperPartPath(GraphPart original, GraphPath path, List visited) { - if (visited.contains(this)) { - return null; - } - visited.add(this); - for (GraphPart p : nextParts) { - if (p == original) { - continue; - } - if (p.path.length() < path.length()) { - return p; - } else { - GraphPart gp = p.getNextSuperPartPath(original, path, visited); - if (gp != null) { - return gp; - } - } - } - return null; - } - - @Override - public String toString() { - if (end < start) { - return "<-> " + (start + 1) + "-" + (end + 1); - } - return "" + (start + 1) + "-" + (end + 1) + (instanceCount > 1 ? "(" + instanceCount + " links)" : "");// + " p" + path; - } - - public boolean containsIP(int ip) { - return (ip >= start) && (ip <= end); - } - - private boolean containsPart(GraphPart part, GraphPart what, List used) { - if (used.contains(part)) { - return false; - } - used.add(part); - for (GraphPart subpart : part.nextParts) { - if (subpart == what) { - return true; - } - if (containsPart(subpart, what, used)) { - return true; - } - } - return false; - } - - public int getHeight() { - return end - start + 1; - } - - public int getPosAt(int offset) { - return start + offset; - } - - public boolean containsPart(GraphPart what) { - return containsPart(this, what, new ArrayList()); - } - - public List getSubParts() { - List ret = new ArrayList<>(); - ret.add(this); - return ret; - } - - @Override - public int hashCode() { - int hash = 7; - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (!(obj instanceof GraphPart)) { - return false; - } - final GraphPart other = (GraphPart) obj; - if (this.start != other.start) { - return false; - } - if (this.end != other.end) { - return false; - } - return true; - } -} +/* + * Copyright (C) 2010-2015 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 java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class GraphPart implements Serializable { + + public int start = 0; + + public int end = 0; + + public int instanceCount = 0; + + public List nextParts = new ArrayList<>(); + + public int posX = -1; + + public int posY = -1; + + public GraphPath path = new GraphPath(); + + public List refs = new ArrayList<>(); + + public boolean ignored = false; + + public List forContinues = new ArrayList<>(); + + public int level; + + public int discoveredTime; + + public int finishedTime; + + public int order; + + public List throwParts = new ArrayList<>(); + + public enum StopPartType { + + NONE, AND_OR, COMMONPART + } + + //public StopPartType stopPartType = StopPartType.NONE; + //public TranslateStack andOrStack; // Stores stack when AND_OR stopPart has been reached + /*public class CommonPartStack { // Stores stack when COMMONPART stopPart has been reached + + boolean isTrueStack; + + TranslateStack trueStack; + + TranslateStack falseStack; + }/ + + //public ArrayList commonPartStacks; + + /* public void setAndOrStack(TranslateStack stack) { + andOrStack = stack; + } + + public void setCommonPartStack(TranslateStack stack) { + CommonPartStack currentStack = commonPartStacks.get(commonPartStacks.size() - 1); + if (currentStack.isTrueStack) { + currentStack.trueStack = stack; + } else { + currentStack.falseStack = stack; + } + }*/ + public int setTime(int time, List ordered, List visited) { + if (visited.contains(this)) { + return time; + } + discoveredTime = time; + visited.add(this); + for (GraphPart next : nextParts) { + if (!visited.contains(next)) { + time = next.setTime(time + 1, ordered, visited); + } + } + time++; + finishedTime = time; + order = ordered.size(); + ordered.add(this); + return time; + } + + private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List visited, List loops) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + + GraphPart tpart = gr.checkPart(null, localData, this, null); + if (tpart == null) { + return false; + } + if (tpart != this) { + return tpart.leadsTo(localData, gr, code, part, visited, loops); + } + Loop currentLoop = null; + for (Loop l : loops) { + /*if(l.phase==0){ + if(l.loopContinue==this){ + l.leadsToMark = 1; + next = l.loopBreak; + currentLoop = l; + continue; + } + }*/ + if (l.phase == 1) { + if (l.loopContinue == this) { + return false; + } + if (l.loopPreContinue == this) { + return false; + } + if (l.loopBreak == this) { + //return false; //? + } + } + } + if (visited.contains(this)) { + return false; + } + /*if (loops.contains(this)) { + return false; + }*/ + visited.add(this); + if (end < code.size() && code.get(end).isBranch() && (code.get(end).ignoredLoops())) { + return false; + } + for (GraphPart p : nextParts) { + if (p == part) { + return true; + } else { + if (p.leadsTo(localData, gr, code, part, visited, loops)) { + return true; + } + } + } + for (GraphPart p : throwParts) { + if (p == part) { + return true; + } else { + if (p.leadsTo(localData, gr, code, part, visited, loops)) { + return true; + } + } + } + return false; + } + + public boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List loops) throws InterruptedException { + for (Loop l : loops) { + l.leadsToMark = 0; + } + return leadsTo(localData, gr, code, part, new ArrayList(), loops); + } + + public GraphPart(int start, int end) { + this.start = start; + this.end = end; + } + + private GraphPart getNextPartPath(GraphPart original, GraphPath path, List visited) { + if (visited.contains(this) && (this == original)) { + return null; + } + if (visited.contains(this) && (this != original)) { + return null; + } + visited.add(this); + for (GraphPart p : nextParts) { + if (p == original) { + continue; + } + if (p.path.equals(path)) { + return p; + } else if (p.path.length() >= path.length()) { + GraphPart gp = p.getNextPartPath(original, path, visited); + if (gp != null) { + return gp; + } + } + } + return null; + } + + public GraphPart getNextPartPath(List ignored) { + List visited = new ArrayList<>(); + visited.addAll(ignored); + if (visited.contains(this)) { + visited.remove(this); + } + return getNextPartPath(this, path, visited); + } + + public GraphPart getNextSuperPartPath(List ignored) { + List visited = new ArrayList<>(); + visited.addAll(ignored); + return getNextSuperPartPath(this, path, visited); + } + + private GraphPart getNextSuperPartPath(GraphPart original, GraphPath path, List visited) { + if (visited.contains(this)) { + return null; + } + visited.add(this); + for (GraphPart p : nextParts) { + if (p == original) { + continue; + } + if (p.path.length() < path.length()) { + return p; + } else { + GraphPart gp = p.getNextSuperPartPath(original, path, visited); + if (gp != null) { + return gp; + } + } + } + return null; + } + + @Override + public String toString() { + if (end < start) { + return "<-> " + (start + 1) + "-" + (end + 1); + } + return "" + (start + 1) + "-" + (end + 1) + (instanceCount > 1 ? "(" + instanceCount + " links)" : "");// + " p" + path; + } + + public boolean containsIP(int ip) { + return (ip >= start) && (ip <= end); + } + + private boolean containsPart(GraphPart part, GraphPart what, List used) { + if (used.contains(part)) { + return false; + } + used.add(part); + for (GraphPart subpart : part.nextParts) { + if (subpart == what) { + return true; + } + if (containsPart(subpart, what, used)) { + return true; + } + } + return false; + } + + public int getHeight() { + return end - start + 1; + } + + public int getPosAt(int offset) { + return start + offset; + } + + public boolean containsPart(GraphPart what) { + return containsPart(this, what, new ArrayList()); + } + + public List getSubParts() { + List ret = new ArrayList<>(); + ret.add(this); + return ret; + } + + @Override + public int hashCode() { + int hash = 7; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof GraphPart)) { + return false; + } + final GraphPart other = (GraphPart) obj; + if (this.start != other.start) { + return false; + } + if (this.end != other.end) { + return false; + } + return true; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BinaryOpItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BinaryOpItem.java index b32deafb0..cc4f85a45 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BinaryOpItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BinaryOpItem.java @@ -51,7 +51,7 @@ public abstract class BinaryOpItem extends GraphTargetItem implements BinaryOp { @Override public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - if (leftSide.getPrecedence() > precedence) { + if (leftSide.getPrecedence() > precedence && leftSide.getPrecedence() != GraphTargetItem.NOPRECEDENCE) { writer.append("("); leftSide.toString(writer, localData); writer.append(")"); @@ -63,7 +63,7 @@ public abstract class BinaryOpItem extends GraphTargetItem implements BinaryOp { writer.append(operator); writer.append(" "); - if (rightSide.getPrecedence() > precedence) { + if (rightSide.getPrecedence() > precedence && rightSide.getPrecedence() != GraphTargetItem.NOPRECEDENCE) { writer.append("("); rightSide.toString(writer, localData); writer.append(")"); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 88deb8023..64ff16596 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -455,7 +455,9 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener