/*
* Copyright (C) 2010-2013 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.jpexs.asdec.abc.avm2.flowgraph;
import com.jpexs.asdec.abc.ABC;
import com.jpexs.asdec.abc.avm2.AVM2Code;
import com.jpexs.asdec.abc.avm2.ConvertException;
import com.jpexs.asdec.abc.avm2.ConvertOutput;
import com.jpexs.asdec.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.asdec.abc.avm2.instructions.IfTypeIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.IfFalseIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.IfStrictNeIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.IfTrueIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.JumpIns;
import com.jpexs.asdec.abc.avm2.instructions.jumps.LookupSwitchIns;
import com.jpexs.asdec.abc.avm2.instructions.localregs.GetLocalTypeIns;
import com.jpexs.asdec.abc.avm2.instructions.localregs.KillIns;
import com.jpexs.asdec.abc.avm2.instructions.localregs.SetLocalTypeIns;
import com.jpexs.asdec.abc.avm2.instructions.other.LabelIns;
import com.jpexs.asdec.abc.avm2.instructions.other.NextNameIns;
import com.jpexs.asdec.abc.avm2.instructions.other.NextValueIns;
import com.jpexs.asdec.abc.avm2.instructions.other.ReturnValueIns;
import com.jpexs.asdec.abc.avm2.instructions.other.ReturnVoidIns;
import com.jpexs.asdec.abc.avm2.instructions.other.ThrowIns;
import com.jpexs.asdec.abc.avm2.instructions.stack.*;
import com.jpexs.asdec.abc.avm2.treemodel.BooleanTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.BreakTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.CommentTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.ContinueTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.FilteredCheckTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.HasNextTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.InTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.IntegerValueTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.LocalRegTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.NextNameTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.NextValueTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.ReturnValueTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.ReturnVoidTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.SetLocalTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.SetPropertyTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.SetTypeTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.TreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.WithTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.DoWhileTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.ExceptionTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.FilterTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.ForEachInTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.ForInTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.ForTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.IfTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.SwitchTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.TernarOpTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.TryTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.clauses.WhileTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.operations.AndTreeItem;
import com.jpexs.asdec.abc.avm2.treemodel.operations.LogicalOp;
import com.jpexs.asdec.abc.avm2.treemodel.operations.OrTreeItem;
import com.jpexs.asdec.abc.types.ABCException;
import com.jpexs.asdec.abc.types.MethodBody;
import com.jpexs.asdec.helpers.Highlighting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class Graph {
public List heads;
private AVM2Code code;
private ABC abc;
private MethodBody body;
public Graph(AVM2Code code, ABC abc, MethodBody 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());
}
}
private void makeMulti(GraphPart part, List visited) {
if (visited.contains(part)) {
return;
}
visited.add(part);
GraphPart p = part;
List multiList = new ArrayList();
multiList.add(p);
while ((p.nextParts.size() == 1) && (p.nextParts.get(0).refs.size() == 1)) {
p = p.nextParts.get(0);
multiList.add(p);
}
if (multiList.size() > 1) {
GraphPartMulti gpm = new GraphPartMulti(multiList);
gpm.refs = part.refs;
GraphPart lastPart = multiList.get(multiList.size() - 1);
gpm.nextParts = lastPart.nextParts;
for (GraphPart next : gpm.nextParts) {
int index = next.refs.indexOf(lastPart);
if (index == -1) {
continue;
}
next.refs.remove(lastPart);
next.refs.add(index, gpm);
}
for (GraphPart parent : part.refs) {
if (parent.start == -1) {
continue;
}
int index = parent.nextParts.indexOf(part);
if (index == -1) {
continue;
}
parent.nextParts.remove(part);
parent.nextParts.add(index, gpm);
}
}
for (int i = 0; i < part.nextParts.size(); i++) {
makeMulti(part.nextParts.get(i), visited);
}
}
public static List translateViaGraph(String path, AVM2Code code, ABC abc, MethodBody body) {
Graph g = new Graph(code, abc, body);
List allParts = new ArrayList();
for (GraphPart head : g.heads) {
populateParts(head, allParts);
}
return g.printGraph(path, new Stack(), new Stack(), allParts, new ArrayList(), new ArrayList(), 0, null, g.heads.get(0), null, new ArrayList(), new HashMap(), body, new ArrayList());
}
private static void populateParts(GraphPart part, List allParts) {
if (allParts.contains(part)) {
return;
}
allParts.add(part);
for (GraphPart p : part.nextParts) {
populateParts(p, allParts);
}
}
private String strOfChars(int len, String chars) {
String ret = "";
for (int i = 0; i < len; i++) {
ret += chars;
}
return ret;
}
private static final String TAB = " ";
private String printOutput(int level, List output, List fqn, HashMap lrn) {
String s = Highlighting.stripHilights(code.listToString(output, abc.constants, lrn, fqn));
String parts[] = s.split("\n");
String ret = "";
for (String p : parts) {
if (p.trim().equals("")) {
continue;
}
ret += (strOfChars(level, TAB) + p.trim()) + "\r\n";
}
return ret;
}
private TreeItem checkLoop(GraphPart part, GraphPart stopPart, List loops) {
if (part == stopPart) {
return null;
}
for (Loop l : loops) {
if (l.loopContinue == part) {
return (new ContinueTreeItem(null, l.loopBreak == null ? -1 : l.loopBreak.start));
}
if (l.loopBreak == part) {
return (new BreakTreeItem(null, part.start));
}
}
return null;
}
private boolean doDecompile = true;
private List printGraph(String methodPath, Stack stack, Stack scopeStack, List allParts, List parsedExceptions, List finallyJumps, int level, GraphPart parent, GraphPart part, GraphPart stopPart, List loops, HashMap localRegs, MethodBody body, List ignoredSwitches) {
List ret = new ArrayList();
boolean debugMode = false;
try {
if (!doDecompile) {
ret.add(new CommentTreeItem(null, "not decompiled"));
return ret;
}
if (debugMode) {
System.err.println("PART " + part);
}
if (part == stopPart) {
return ret;
}
if (part.ignored) {
return ret;
}
List fqn = new ArrayList();
HashMap lrn = new HashMap();
List output = new ArrayList();
boolean isSwitch = false;
try {
code.initToSource();
List parts = new ArrayList();
if (part instanceof GraphPartMulti) {
parts = ((GraphPartMulti) part).parts;
} else {
parts.add(part);
}
boolean isIf = false;
int end = part.end;
for (GraphPart p : parts) {
end = p.end;
int start = p.start;
isIf = false;
if (code.code.get(end).definition instanceof JumpIns) {
end--;
} else if (code.code.get(end).definition instanceof IfTypeIns) {
end--;
isIf = true;
} else if (code.code.get(end).definition instanceof LookupSwitchIns) {
isSwitch = true;
end--;
}
ConvertOutput co = code.toSourceOutput(false, false, 0, localRegs, stack, scopeStack, abc, abc.constants, abc.method_info, body, start, end, lrn, fqn, new boolean[code.code.size()]);
output.addAll(co.output);
}
if (isIf) {
AVM2Instruction ins = code.code.get(end + 1);
if ((stack.size() >= 2) && (ins.definition instanceof IfFalseIns) && (stack.get(stack.size() - 1) == stack.get(stack.size() - 2))) {
printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, parent, part.nextParts.get(1), part.nextParts.get(0), loops, localRegs, body, ignoredSwitches);
TreeItem second = stack.pop();
TreeItem first = stack.pop();
stack.push(new AndTreeItem(ins, first, second));
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, parent, part.nextParts.get(0), stopPart, loops, localRegs, body, ignoredSwitches));
return ret;
} else if ((stack.size() >= 2) && (ins.definition instanceof IfTrueIns) && (stack.get(stack.size() - 1) == stack.get(stack.size() - 2))) {
printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, parent, part.nextParts.get(1), part.nextParts.get(0), loops, localRegs, body, ignoredSwitches);
TreeItem second = stack.pop();
TreeItem first = stack.pop();
stack.push(new OrTreeItem(ins, first, second));
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, parent, part.nextParts.get(0), stopPart, loops, localRegs, body, ignoredSwitches));
return ret;
} else if (((ins.definition instanceof IfStrictNeIns)) && ((part.nextParts.get(1).getHeight() == 2) && (code.code.get(part.nextParts.get(1).start).definition instanceof PushByteIns) && (code.code.get(part.nextParts.get(1).nextParts.get(0).end).definition instanceof LookupSwitchIns))) {
TreeItem switchedObject = null;
if (!output.isEmpty()) {
if (output.get(output.size() - 1) instanceof SetLocalTreeItem) {
switchedObject = ((SetLocalTreeItem) output.get(output.size() - 1)).value;
}
}
HashMap caseValuesMap = new HashMap();
stack.pop();
caseValuesMap.put(code.code.get(part.nextParts.get(1).start).operands[0], stack.pop());
GraphPart switchLoc = part.nextParts.get(1).nextParts.get(0);
while (code.code.get(part.nextParts.get(0).end).definition instanceof IfStrictNeIns) {
part = part.nextParts.get(0);
code.toSourceOutput(false, false, 0, localRegs, stack, scopeStack, abc, abc.constants, abc.method_info, body, part.start, part.end - 1, lrn, fqn, new boolean[code.code.size()]);
stack.pop();
caseValuesMap.put(code.code.get(part.nextParts.get(1).start).operands[0], stack.pop());
}
boolean hasDefault = false;
if (code.code.get(part.nextParts.get(0).start).definition instanceof PushByteIns) {
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;
List loopContinues = new ArrayList();
for (Loop l : loops) {
if (l.loopContinue != null) {
loopContinues.add(l.loopContinue);
}
}
next = switchLoc.getNextPartPath(loopContinues);
/*for (GraphPart p : allParts) {
if (p.start == switchLoc.end + 1) {
next = p;
break;
}
}*/
TreeItem ti = checkLoop(next, stopPart, loops);
loops.add(new Loop(null, next));
//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);
defaultCommands = printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, switchLoc, defaultPart, next, loops, localRegs, body, ignoredSwitches);
}
List ignored = new ArrayList();
for (Loop l : loops) {
ignored.add(l.loopContinue);
}
for (int i = 0; i < caseBodies.size(); i++) {
List cc = new ArrayList();
GraphPart nextCase = null;
nextCase = next;
if (next != null) {
if (i < caseBodies.size() - 1) {
if (!caseBodies.get(i).leadsTo(caseBodies.get(i + 1), ignored)) {
cc.add(new BreakTreeItem(null, next.start));
} else {
nextCase = caseBodies.get(i + 1);
}
} else if (hasDefault) {
if (!caseBodies.get(i).leadsTo(defaultPart, ignored)) {
cc.add(new BreakTreeItem(null, next.start));
} else {
nextCase = defaultPart;
}
}
}
cc.addAll(0, printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, switchLoc, caseBodies.get(i), nextCase, loops, localRegs, body, ignoredSwitches));
caseCommands.add(cc);
}
SwitchTreeItem sti = new SwitchTreeItem(null, next == null ? -1 : next.start, switchedObject, caseValues, caseCommands, defaultCommands, valuesMapping);
ret.add(sti);
if (next != null) {
if (ti != null) {
ret.add(ti);
} else {
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, null, next, stopPart, loops, localRegs, body, ignoredSwitches));
}
}
return ret;
} else {
try {
ins.definition.translate(false, 0, new HashMap(), stack, new Stack(), abc.constants, ins, abc.method_info, output, body, abc, lrn, fqn);
} catch (Exception ex) {
System.err.println("ip:" + (end + 1));
ex.printStackTrace();
}
}
//((IfTypeIns)ins.definition).translateInverted(new HashMap(), co.stack, ins);
}
} catch (ConvertException ex) {
Logger.getLogger(Graph.class.getName()).log(Level.SEVERE, null, ex);
}
int ip = part.start;
int addr = code.fixAddrAfterDebugLine(code.pos2adr(part.start));
int maxend = -1;
List catchedExceptions = new ArrayList();
for (int e = 0; e < body.exceptions.length; e++) {
if (addr == code.fixAddrAfterDebugLine(body.exceptions[e].start)) {
if (!body.exceptions[e].isFinally()) {
if (((body.exceptions[e].end) > maxend) && (!parsedExceptions.contains(body.exceptions[e]))) {
catchedExceptions.clear();
maxend = code.fixAddrAfterDebugLine(body.exceptions[e].end);
catchedExceptions.add(body.exceptions[e]);
} else if (code.fixAddrAfterDebugLine(body.exceptions[e].end) == maxend) {
catchedExceptions.add(body.exceptions[e]);
}
}
}
}
if (catchedExceptions.size() > 0) {
parsedExceptions.addAll(catchedExceptions);
int endpos = code.adr2pos(code.fixAddrAfterDebugLine(catchedExceptions.get(0).end));
int endposStartBlock = code.adr2pos(catchedExceptions.get(0).end);
List> catchedCommands = new ArrayList>();
if (code.code.get(endpos).definition instanceof JumpIns) {
int afterCatchAddr = code.pos2adr(endpos + 1) + code.code.get(endpos).operands[0];
int afterCatchPos = code.adr2pos(afterCatchAddr);
Collections.sort(catchedExceptions, new Comparator() {
public int compare(ABCException o1, ABCException o2) {
try {
return code.fixAddrAfterDebugLine(o1.target) - code.fixAddrAfterDebugLine(o2.target);
} catch (ConvertException ex) {
return 0;
}
}
});
List finallyCommands = new ArrayList();
int returnPos = afterCatchPos;
for (int e = 0; e < body.exceptions.length; e++) {
if (body.exceptions[e].isFinally()) {
if (addr == code.fixAddrAfterDebugLine(body.exceptions[e].start)) {
if (afterCatchPos + 1 == code.adr2pos(code.fixAddrAfterDebugLine(body.exceptions[e].end))) {
AVM2Instruction jmpIns = code.code.get(code.adr2pos(code.fixAddrAfterDebugLine(body.exceptions[e].end)));
if (jmpIns.definition instanceof JumpIns) {
int finStart = code.adr2pos(code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytes().length + jmpIns.operands[0]);
finallyJumps.add(finStart);
/*if (unknownJumps.contains(finStart)) {
unknownJumps.remove((Integer) finStart);
}*/
for (int f = finStart; f < code.code.size(); f++) {
if (code.code.get(f).definition instanceof LookupSwitchIns) {
AVM2Instruction swins = code.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) {
GraphPart fpart = null;
for (GraphPart p : allParts) {
if (p.start == finStart) {
fpart = p;
break;
}
}
stack.push(new ExceptionTreeItem(body.exceptions[e]));
GraphPart fepart = null;
for (GraphPart p : allParts) {
if (p.start == f + 1) {
fepart = p;
break;
}
}
//code.code.get(f).ignored = true;
ignoredSwitches.add(f);
finallyCommands = printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, parent, fpart, fepart, loops, localRegs, body, ignoredSwitches);
returnPos = f + 1;
break;
}
}
}
}
}
break;
}
}
}
}
}
for (int e = 0; e < catchedExceptions.size(); e++) {
int eendpos;
if (e < catchedExceptions.size() - 1) {
eendpos = code.adr2pos(code.fixAddrAfterDebugLine(catchedExceptions.get(e + 1).target)) - 2;
} else {
eendpos = afterCatchPos - 1;
}
Stack substack = new Stack();
substack.add(new ExceptionTreeItem(catchedExceptions.get(e)));
GraphPart npart = null;
int findpos = code.adr2pos(code.fixAddrAfterDebugLine(catchedExceptions.get(e).target));
for (GraphPart p : allParts) {
if (p.start == findpos) {
npart = p;
break;
}
}
GraphPart nepart = null;
for (GraphPart p : allParts) {
if (p.start == eendpos + 1) {
nepart = p;
break;
}
}
stack.add(new ExceptionTreeItem(catchedExceptions.get(e)));
catchedCommands.add(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, parent, npart, nepart, loops, localRegs, body, ignoredSwitches));
}
GraphPart nepart = null;
for (GraphPart p : allParts) {
if (p.start == endposStartBlock) {
nepart = p;
break;
}
}
List tryCommands = printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, parent, part, nepart, loops, localRegs, body, ignoredSwitches);
output.clear();
output.add(new TryTreeItem(tryCommands, catchedExceptions, catchedCommands, finallyCommands));
ip = returnPos;
addr = code.pos2adr(ip);
}
}
if (ip != part.start) {
part = null;
for (GraphPart p : allParts) {
if (p.start == ip) {
part = p;
break;
}
}
ret.addAll(output);
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, null, part, stopPart, loops, localRegs, body, ignoredSwitches));
return ret;
}
List loopContinues = new ArrayList();
for (Loop l : loops) {
if (l.loopContinue != null) {
loopContinues.add(l.loopContinue);
}
}
boolean loop = false;
if ((!part.nextParts.isEmpty()) && part.nextParts.get(0).leadsTo(part, loopContinues)) {
loop = true;
}
if (((part.nextParts.size() == 2) || ((part.nextParts.size() == 1) && loop)) && (!isSwitch)) {
boolean doWhile = loop;
if (loop && output.isEmpty()) {
doWhile = false;
}
Loop currentLoop = new Loop(part, null);
if (loop) {
loops.add(currentLoop);
}
loopContinues = new ArrayList();
for (Loop l : loops) {
if (l.loopContinue != null) {
loopContinues.add(l.loopContinue);
}
}
if (part.nextParts.size() > 1) {
currentLoop.loopBreak = part.nextParts.get(1);
}
int breakIp = -1;
if (currentLoop.loopBreak != null) {
breakIp = currentLoop.loopBreak.start;
}
TreeItem expr = null;
if ((code.code.get(part.end).definition instanceof JumpIns) || (!(code.code.get(part.end).definition instanceof IfTypeIns))) {
expr = new BooleanTreeItem(null, true);
} else {
if (stack.isEmpty()) {
}
expr = stack.pop();
}
if (doWhile) {
ret.add(new DoWhileTreeItem(null, breakIp, part.start, output, expr));
} else {
ret.addAll(output);
}
GraphPart next = part.getNextPartPath(loopContinues);
if (loop && (!doWhile)) {
List loopBody = null;
List finalCommands = null;
GraphPart finalPart = null;
boolean isFor = false;
try {
loopBody = printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level + 1, part, part.nextParts.get(0), stopPart, loops, localRegs, body, ignoredSwitches);
} catch (ForException fex) {
loopBody = fex.output;
finalCommands = fex.finalOutput;
if (!finalCommands.isEmpty()) {
finalCommands.remove(finalCommands.size() - 1); //remove continue
}
finalPart = fex.continuePart;
isFor = true;
for (ContinueTreeItem cti : finalPart.forContinues) {
cti.loopPos = breakIp;
}
}
if (isFor) {
ret.add(new ForTreeItem(null, breakIp, finalPart.start, new ArrayList(), expr, finalCommands, loopBody));
} else if ((expr instanceof HasNextTreeItem) && ((HasNextTreeItem) expr).collection.getNotCoerced().getThroughRegister() instanceof FilteredCheckTreeItem) {
TreeItem gti = ((HasNextTreeItem) expr).collection.getNotCoerced().getThroughRegister();
boolean found = false;
if ((loopBody.size() == 3) || (loopBody.size() == 4)) {
TreeItem ft = loopBody.get(0);
if (ft instanceof WithTreeItem) {
ft = loopBody.get(1);
if (ft instanceof IfTreeItem) {
IfTreeItem ift = (IfTreeItem) ft;
if (ift.onTrue.size() > 0) {
ft = ift.onTrue.get(0);
if (ft instanceof SetPropertyTreeItem) {
SetPropertyTreeItem spt = (SetPropertyTreeItem) ft;
if (spt.object instanceof LocalRegTreeItem) {
int regIndex = ((LocalRegTreeItem) spt.object).regIndex;
HasNextTreeItem iti = (HasNextTreeItem) expr;
localRegs.put(regIndex, new FilterTreeItem(null, iti.collection.getThroughRegister(), ift.expression));
}
}
}
}
}
}
} else if ((expr instanceof HasNextTreeItem) && (!loopBody.isEmpty()) && (loopBody.get(0) instanceof SetTypeTreeItem) && (((SetTypeTreeItem) loopBody.get(0)).getValue().getNotCoerced() instanceof NextValueTreeItem)) {
TreeItem obj = ((SetTypeTreeItem) loopBody.get(0)).getObject();
loopBody.remove(0);
ret.add(new ForEachInTreeItem(null, breakIp, part.start, new InTreeItem(expr.instruction, obj, ((HasNextTreeItem) expr).collection), loopBody));
} else if ((expr instanceof HasNextTreeItem) && (!loopBody.isEmpty()) && (loopBody.get(0) instanceof SetTypeTreeItem) && (((SetTypeTreeItem) loopBody.get(0)).getValue().getNotCoerced() instanceof NextNameTreeItem)) {
TreeItem obj = ((SetTypeTreeItem) loopBody.get(0)).getObject();
loopBody.remove(0);
ret.add(new ForInTreeItem(null, breakIp, part.start, new InTreeItem(expr.instruction, obj, ((HasNextTreeItem) expr).collection), loopBody));
} else {
ret.add(new WhileTreeItem(null, breakIp, part.start, expr, loopBody));
}
} else if (!loop) {
if (expr instanceof LogicalOp) {
expr = ((LogicalOp) expr).invert();
}
int stackSizeBefore = stack.size();
Stack trueStack = (Stack) stack.clone();
Stack falseStack = (Stack) stack.clone();
TreeItem lopTrue = checkLoop(part.nextParts.get(1), stopPart, loops);
TreeItem lopFalse = null;
if (next != part.nextParts.get(0)) {
lopFalse = checkLoop(part.nextParts.get(0), stopPart, loops);
}
List onTrue = new ArrayList();
if (lopTrue != null) {
onTrue.add(lopTrue);
} else {
if (debugMode) {
System.err.println("ONTRUE: (inside " + part + ")");
}
onTrue = printGraph(methodPath, trueStack, scopeStack, allParts, parsedExceptions, finallyJumps, level + 1, part, part.nextParts.get(1), next == null ? stopPart : next, loops, localRegs, body, ignoredSwitches);
if (debugMode) {
System.err.println("/ONTRUE (inside " + part + ")");
}
}
List onFalse = new ArrayList();
if (lopFalse != null) {
onFalse.add(lopFalse);
} else {
if (debugMode) {
System.err.println("ONFALSE: (inside " + part + ")");
}
onFalse = (((next == part.nextParts.get(0)) || (part.nextParts.get(0).path.equals(part.path) || part.nextParts.get(0).path.length() < part.path.length())) ? new ArrayList() : printGraph(methodPath, falseStack, scopeStack, allParts, parsedExceptions, finallyJumps, level + 1, part, part.nextParts.get(0), next == null ? stopPart : next, loops, localRegs, body, ignoredSwitches));
if (debugMode) {
System.err.println("/ONFALSE (inside " + part + ")");
}
}
if (onTrue.isEmpty() && onFalse.isEmpty() && (trueStack.size() > stackSizeBefore) && (falseStack.size() > stackSizeBefore)) {
stack.push(new TernarOpTreeItem(null, expr, trueStack.pop(), falseStack.pop()));
} else {
ret.add(new IfTreeItem(null, expr, onTrue, onFalse));
}
}
if (loop && (part.nextParts.size() > 1)) {
loops.remove(currentLoop); //remove loop so no break shows up
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, part, part.nextParts.get(1), stopPart, loops, localRegs, body, ignoredSwitches));
}
if (next != null) {
boolean finallyJump = false;
for (int f : finallyJumps) {
if (next.start == f) {
finallyJump = true;
break;
}
}
if (!finallyJump) {
TreeItem ti = checkLoop(next, stopPart, loops);
if (ti != null) {
ret.add(ti);
} else {
if (debugMode) {
System.err.println("NEXT: (inside " + part + ")");
}
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, part, next, stopPart, loops, localRegs, body, ignoredSwitches));
if (debugMode) {
System.err.println("/NEXT: (inside " + part + ")");
}
}
}
}
} else {
ret.addAll(output);
}
onepart:
if (part.nextParts.size() == 1 && (!loop)) {
if (part.end - part.start > 4) {
if (code.code.get(part.end).definition instanceof PopIns) {
if (code.code.get(part.end - 1).definition instanceof LabelIns) {
if (code.code.get(part.end - 2).definition instanceof PushByteIns) {
//if (code.code.get(part.end - 3).definition instanceof SetLocalTypeIns) {
if (part.nextParts.size() == 1) {
GraphPart sec = part.nextParts.get(0);
if (code.code.get(sec.end).definition instanceof ReturnValueIns) {
if (sec.end - sec.start >= 3) {
if (code.code.get(sec.end - 1).definition instanceof KillIns) {
if (code.code.get(sec.end - 2).definition instanceof GetLocalTypeIns) {
if (!output.isEmpty()) {
if (output.get(output.size() - 1) instanceof SetLocalTreeItem) {
sec.ignored = true;
ret.add(new ReturnValueTreeItem(code.code.get(sec.end), ((SetLocalTreeItem) output.get(output.size() - 1)).value));
break onepart;
}
}
}
}
}
} else if (code.code.get(sec.end).definition instanceof ReturnVoidIns) {
ret.add(new ReturnVoidTreeItem(code.code.get(sec.end)));
break onepart;
}
//}
}
}
}
}
}
for (int f : finallyJumps) {
if (part.nextParts.get(0).start == f) {
if ((!output.isEmpty()) && (output.get(output.size() - 1) instanceof SetLocalTreeItem)) {
ret.add(new ReturnValueTreeItem(null, ((SetLocalTreeItem) output.get(output.size() - 1)).value));
} else {
ret.add(new ReturnVoidTreeItem(null));
}
break onepart;
}
}
GraphPart p = part.nextParts.get(0);
TreeItem lop = checkLoop(p, stopPart, loops);
if (lop == null) {
if (p.path.length() == part.path.length()) {
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, part, p, stopPart, loops, localRegs, body, ignoredSwitches));
} else {
if ((p != stopPart) && (p.refs.size() > 1)) {
ContinueTreeItem cti = new ContinueTreeItem(null, -1);
p.forContinues.add(cti);
ret.add(cti);
}
}
} else {
ret.add(lop);
}
//}
//ret += (strOfChars(level, TAB) + "continue;\r\n");
//}
}
if (isSwitch && (!ignoredSwitches.contains(part.end))) {
//ret.add(new CommentTreeItem(code.code.get(part.end), "Switch not supported"));
TreeItem switchedObject = stack.pop();
List caseValues = new ArrayList();
List valueMappings = new ArrayList();
List> caseCommands = new ArrayList>();
GraphPart next = part.getNextPartPath(loopContinues);
int breakPos = -1;
if (next != null) {
breakPos = next.start;
}
List defaultCommands = printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, part, part.nextParts.get(0), stopPart, loops, localRegs, body, ignoredSwitches);
for (int i = 0; i < part.nextParts.size() - 1; i++) {
caseValues.add(new IntegerValueTreeItem(null, (Long) (long) i));
valueMappings.add(i);
GraphPart nextCase = next;
List caseBody = new ArrayList();
if (i < part.nextParts.size() - 1 - 1) {
if (!part.nextParts.get(1 + i).leadsTo(part.nextParts.get(1 + i + 1), new ArrayList())) {
caseBody.add(new BreakTreeItem(null, breakPos));
} else {
nextCase = part.nextParts.get(1 + i + 1);
}
} else if (!part.nextParts.get(1 + i).leadsTo(part.nextParts.get(0), new ArrayList())) {
caseBody.add(new BreakTreeItem(null, breakPos));
} else {
nextCase = part.nextParts.get(0);
}
caseBody.addAll(0, printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, part, part.nextParts.get(1 + i), nextCase, loops, localRegs, body, ignoredSwitches));
caseCommands.add(caseBody);
}
SwitchTreeItem swt = new SwitchTreeItem(null, breakPos, switchedObject, caseValues, caseCommands, defaultCommands, valueMappings);
ret.add(swt);
TreeItem lopNext = checkLoop(next, stopPart, loops);
if (lopNext != null) {
ret.add(lopNext);
} else {
ret.addAll(printGraph(methodPath, stack, scopeStack, allParts, parsedExceptions, finallyJumps, level, part, next, stopPart, loops, localRegs, body, ignoredSwitches));
}
}
} catch (ForException fex) {
ret.addAll(fex.output);
fex.output = ret;
throw fex;
}
code.clearTemporaryRegisters(ret);
if (!part.forContinues.isEmpty()) {
throw new ForException(new ArrayList(), ret, part);
}
return ret;
}
private void fixGraph(GraphPart part) {
while (fixGraphOnce(part, new ArrayList(), false)) {
}
}
private boolean fixGraphOnce(GraphPart part, List visited, boolean doChildren) {
if (visited.contains(part)) {
return false;
}
visited.add(part);
boolean fixed = false;
/*System.out.print("Part " + part.start + "-" + part.end + " refs:");
for (GraphPart gp : part.refs) {
System.out.print(gp.path + " ");
}
System.out.println("");*/
int i = 1;
String lastpref = null;
boolean modify = true;
int prvni = -1;
if (!doChildren) {
List uniqueRefs = new ArrayList();
for (GraphPart r : part.refs) {
if (!uniqueRefs.contains(r)) {
uniqueRefs.add(r);
}
}
loopi:
for (; i <= part.path.length(); i++) {
lastpref = null;
int pos = -1;
for (GraphPart r : uniqueRefs) {
pos++;
if (r.path.startsWith("e")) {
continue;
}
if (part.leadsTo(r, new ArrayList())) {
//modify=false;
//continue;
}
prvni = pos;
if (i > r.path.length()) {
i--;
break loopi;
}
if (lastpref == null) {
lastpref = r.path.substring(0, i);
} else {
if (!r.path.startsWith(lastpref)) {
i--;
break loopi;
}
}
}
}
if (i > part.path.length()) {
i = part.path.length();
}
if (modify && ((uniqueRefs.size() > 1) && (prvni >= 0))) {
String newpath = uniqueRefs.get(prvni).path.substring(0, i);
if (!part.path.equals(newpath)) {
if (part.path.startsWith(newpath)) {
String origPath = part.path;
GraphPart p = part;
part.path = newpath;
while (p.nextParts.size() == 1) {
p = p.nextParts.get(0);
if (!p.path.equals(origPath)) {
break;
}
p.path = newpath;
}
fixGraphOnce(part, new ArrayList(), true);
fixed = true;
}
}
}
} else {
if (!fixed) {
if (part.nextParts.size() == 1) {
if (!(part.path.startsWith("e") && (!part.nextParts.get(0).path.startsWith("e")))) {
if (part.nextParts.get(0).path.length() > part.path.length()) {
part.nextParts.get(0).path = part.path;
fixed = true;
}
}
}
if (part.nextParts.size() > 1) {
for (int j = 0; j < part.nextParts.size(); j++) {
GraphPart npart = part.nextParts.get(j);
if (npart.path.length() > part.path.length() + 1) {
npart.path = part.path + "" + j;
fixed = true;
}
}
}
}
}
/* if (part.nextParts.size() == 2) {
GraphPart left = part.nextParts.get(0);
GraphPart right = part.nextParts.get(1);
if ((left.nextParts.size() == 1) && (right.nextParts.size() == 1)) {
if (left.nextParts.get(0) == right.nextParts.get(0)) {
if (!left.nextParts.get(0).path.equals(part.path)) {
String origPath = left.nextParts.get(0).path;
GraphPart p = left;
while (p.nextParts.size() == 1) {
p = p.nextParts.get(0);
if (!p.path.equals(origPath)) {
break;
}
p.path = part.path;
}
fixed = true;
}
}
}
}*/
for (GraphPart p : part.nextParts) {
fixGraphOnce(p, visited, doChildren);
}
return fixed;
}
private List makeGraph(AVM2Code code, List allBlocks, MethodBody body) {
HashMap> refs = code.visitCode(body);
List ret = new ArrayList();
boolean visited[] = new boolean[code.code.size()];
ret.add(makeGraph(null, "0", code, 0, 0, allBlocks, refs, visited));
for (ABCException ex : body.exceptions) {
GraphPart e1 = new GraphPart(-1, -1);
e1.path = "e";
GraphPart e2 = new GraphPart(-1, -1);
e2.path = "e";
GraphPart e3 = new GraphPart(-1, -1);
e3.path = "e";
makeGraph(e1, "e", code, code.adr2pos(ex.start), code.adr2pos(ex.start), allBlocks, refs, visited);
makeGraph(e2, "e", code, code.adr2pos(ex.end), code.adr2pos(ex.end), allBlocks, refs, visited);
ret.add(makeGraph(e3, "e", code, code.adr2pos(ex.target), code.adr2pos(ex.target), allBlocks, refs, visited));
}
return ret;
}
private GraphPart makeGraph(GraphPart parent, String path, AVM2Code code, int startip, int lastIp, List allBlocks, HashMap> refs, boolean visited2[]) {
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.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;
}
}
lastIp = ip;
AVM2Instruction ins = code.code.get(ip);
if ((ins.definition instanceof ThrowIns) || (ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ReturnVoidIns)) {
part.end = ip;
allBlocks.add(part);
break;
}
if (ins.definition instanceof LookupSwitchIns) {
part.end = ip;
allBlocks.add(part);
try {
part.nextParts.add(g = makeGraph(part, path + "0", code, code.adr2pos(code.pos2adr(ip) + ins.operands[0]), ip, allBlocks, refs, visited2));
g.refs.add(part);
for (int i = 2; i < ins.operands.length; i++) {
part.nextParts.add(g = makeGraph(part, path + (i - 1), code, code.adr2pos(code.pos2adr(ip) + ins.operands[i]), ip, allBlocks, refs, visited2));
g.refs.add(part);
}
break;
} catch (ConvertException ex) {
}
}
if (ins.definition instanceof JumpIns) {
try {
part.end = ip;
allBlocks.add(part);
ip = code.adr2pos(code.pos2adr(ip) + ins.getBytes().length + ins.operands[0]);
part.nextParts.add(g = makeGraph(part, path, code, ip, lastIp, allBlocks, refs, visited2));
g.refs.add(part);
break;
} catch (ConvertException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex);
}
} else if (ins.definition instanceof IfTypeIns) {
part.end = ip;
allBlocks.add(part);
try {
part.nextParts.add(g = makeGraph(part, path + "0", code, code.adr2pos(code.pos2adr(ip) + ins.getBytes().length + ins.operands[0]), ip, allBlocks, refs, visited2));
g.refs.add(part);
part.nextParts.add(g = makeGraph(part, path + "1", code, ip + 1, ip, allBlocks, refs, visited2));
g.refs.add(part);
} catch (ConvertException ex) {
Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex);
}
break;
}
ip++;
};
return ret;
}
}