/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.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.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.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.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.types.ABCException;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphPart;
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.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.PopItem;
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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @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<Integer, GraphTargetItem> localRegs, ScopeStack scopeStack, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames, HashMap<Integer, Integer> localRegAssigmentIps, HashMap<Integer, List<Integer>> 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<GraphPart>(), body);
this.code = code;
this.abc = abc;
this.body = body;
for (GraphPart head : heads) {
fixGraph(head);
makeMulti(head, new ArrayList<GraphPart>());
}*/
}
public static List<GraphTargetItem> translateViaGraph(String path, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, ScopeStack scopeStack, HashMap<Integer, String> localRegNames, List<DottedChain> fullyQualifiedNames, int staticOperation, HashMap<Integer, Integer> localRegAssigmentIps, HashMap<Integer, List<Integer>> refs, boolean thisHasDefaultToPrimitive) throws InterruptedException {
AVM2Graph g = new AVM2Graph(code, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs);
AVM2LocalData localData = new AVM2LocalData();
localData.thisHasDefaultToPrimitive = thisHasDefaultToPrimitive;
localData.isStatic = isStatic;
localData.classIndex = classIndex;
localData.localRegs = localRegs;
localData.scopeStack = scopeStack;
localData.methodBody = body;
localData.abc = abc;
localData.localRegNames = localRegNames;
localData.fullyQualifiedNames = fullyQualifiedNames;
localData.scriptIndex = scriptIndex;
localData.ip = 0;
localData.refs = refs;
localData.code = code;
g.init(localData);
Set<GraphPart> allParts = new HashSet<>();
for (GraphPart head : g.heads) {
populateParts(head, allParts);
}
return g.translate(localData, staticOperation, path);
}
@Override
protected void checkGraph(List<GraphPart> allBlocks) {
for (ABCException ex : body.exceptions) {
/*int startAddr = avm2code.adr2pos(ex.start);
int endAddr = avm2code.adr2pos(ex.end);
int targetIp = avm2code.adr2pos(ex.target);*/
GraphPart target = null;
for (GraphPart p : allBlocks) {
if (avm2code.pos2adr(p.start) == ex.target) {
target = p;
break;
}
}
for (GraphPart p : allBlocks) {
if (avm2code.pos2adr(p.start) >= ex.start && avm2code.pos2adr(p.end) <= ex.end && target != null) {
p.throwParts.add(target);
target.refs.add(p);
}
}
}
}
@Override
protected List<GraphTargetItem> check(Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos, GraphSource code, BaseLocalData localData, Set<GraphPart> allParts, TranslateStack stack, GraphPart parent, GraphPart part, List<GraphPart> stopPart, List<Loop> loops, List<GraphTargetItem> output, Loop currentLoop, int staticOperation, String path) throws InterruptedException {
List<GraphTargetItem> ret = null;
AVM2LocalData aLocalData = (AVM2LocalData) localData;
if (aLocalData.parsedExceptions == null) {
aLocalData.parsedExceptions = new ArrayList<>();
}
List<ABCException> parsedExceptions = aLocalData.parsedExceptions;
if (aLocalData.finallyJumps == null) {
aLocalData.finallyJumps = new HashMap<>();
}
Map<Integer, List<Integer>> finallyJumps = aLocalData.finallyJumps;
if (aLocalData.ignoredSwitches == null) {
aLocalData.ignoredSwitches = new HashMap<>();
}
Map<Integer, Integer> ignoredSwitches = aLocalData.ignoredSwitches;
if (aLocalData.ignoredSwitches2 == null) {
aLocalData.ignoredSwitches2 = new ArrayList<>();
}
List<Integer> ignoredSwitches2 = aLocalData.ignoredSwitches2;
int ip = part.start;
long addr = avm2code.fixAddrAfterDebugLine(avm2code.pos2adr(part.start));
long maxend = -1;
List<Integer> catchedFinallys = new ArrayList<>();
List<ABCException> catchedExceptions = new ArrayList<>();
for (int e = 0; e < body.exceptions.length; e++) {
if (addr == 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();
catchedFinallys.clear();
maxend = avm2code.fixAddrAfterDebugLine(body.exceptions[e].end);
catchedExceptions.add(body.exceptions[e]);
} else if (avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) == maxend) {
catchedExceptions.add(body.exceptions[e]);
}
catchedFinallys.add(e);
}
} else if (body.exceptions[e].isFinally()) {
parsedExceptions.add(body.exceptions[e]);
}
}
}
if (catchedExceptions.size() > 0) {
parsedExceptions.addAll(catchedExceptions);
int endpos = code.adr2pos(avm2code.fixAddrAfterDebugLine(catchedExceptions.get(0).end));
int endposStartBlock = code.adr2pos(catchedExceptions.get(0).end);
String finCatchName = "";
List<List<GraphTargetItem>> catchedCommands = new ArrayList<>();
if (avm2code.code.get(endpos).definition instanceof JumpIns) {
long afterCatchAddr = avm2code.pos2adr(endpos + 1) + avm2code.code.get(endpos).operands[0];
int afterCatchPos = avm2code.adr2pos(afterCatchAddr);
final AVM2Graph t = this;
Collections.sort(catchedExceptions, new Comparator<ABCException>() {
@Override
public int compare(ABCException o1, ABCException o2) {
return (int) (t.avm2code.fixAddrAfterDebugLine(o1.target) - t.avm2code.fixAddrAfterDebugLine(o2.target));
}
});
List<GraphTargetItem> finallyCommands = new ArrayList<>();
boolean hasFinally = false;
int returnPos = afterCatchPos;
int finStart;
for (int e = 0; e < body.exceptions.length; e++) {
if (body.exceptions[e].isFinally()) {
if (addr == avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) {
if (afterCatchPos + 1 == code.adr2pos(avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))) {
catchedFinallys.add(e);
AVM2Instruction jmpIns = avm2code.code.get(code.adr2pos(avm2code.fixAddrAfterDebugLine(body.exceptions[e].end)));
if (jmpIns.definition instanceof JumpIns) {
finStart = code.adr2pos(avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytesLength() + jmpIns.operands[0]);
GraphPart fpart = null;
for (GraphPart p : allParts) {
if (p.start == finStart) {
fpart = p;
break;
}
}
TranslateStack st = (TranslateStack) stack.clone();
st.clear();
int swPos = -1;
for (int f = finStart; f < avm2code.code.size(); f++) {
if (avm2code.code.get(f).definition instanceof LookupSwitchIns) {
AVM2Instruction swins = avm2code.code.get(f);
if (swins.operands.length >= 3) {
if (swins.operands[0] == swins.getBytesLength()) {
if (code.adr2pos(code.pos2adr(f) + swins.operands[2]) < finStart) {
//st.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<GraphPart> stopPart2 = new ArrayList<>(stopPart);
stopPart2.add(fepart);
//finallyCommands = printGraph(new ArrayList<GraphPart>(), localData, stack, allParts, parent, fpart, stopPart2, loops, staticOperation, path);
returnPos = f + 1;
break;
}
}
}
}
}
//ignoredSwitches.add(-1);
//int igs_size=ignoredSwitches.size();
Map<Integer, List<Integer>> oldFinallyJumps = new HashMap<>(finallyJumps);
finallyJumps.clear();
ignoredSwitches.put(e, swPos);
st.push(new PopItem(null, aLocalData.lineStartInstruction));
finallyCommands = printGraph(partCodes, partCodePos, localData, st, allParts, parent, fpart, null, loops, staticOperation, path);
//ignoredSwitches.remove(igs_size-1);
finallyJumps.putAll(oldFinallyJumps);
if (!finallyJumps.containsKey(e)) {
finallyJumps.put(e, new ArrayList<>());
}
finallyJumps.get(e).add(finStart);
hasFinally = true;
break;
}
}
}
}
}
GraphPart retPart = null;
for (GraphPart p : allParts) {
if (p.start == returnPos) {
retPart = p;
break;
}
}
List<GraphPart> catchParts = new ArrayList<>();
for (int e = 0; e < catchedExceptions.size(); e++) {
int eendpos;
if (e < catchedExceptions.size() - 1) {
eendpos = code.adr2pos(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;
}
}
TranslateStack st2 = (TranslateStack) stack.clone();
st2.clear();
st2.add(new ExceptionAVM2Item(catchedExceptions.get(e)));
AVM2LocalData localData2 = new AVM2LocalData(aLocalData);
localData2.scopeStack = new ScopeStack();
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
stopPart2.add(nepart);
if (retPart != null) {
stopPart2.add(retPart);
}
List<GraphTargetItem> ncatchedCommands = printGraph(partCodes, partCodePos, localData2, st2, 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, st2, ncatchedCommands, staticOperation, path);
}
if (catchedExceptions.get(e).isFinally()) {
//endposStartBlock = -1;
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<GraphPart> stopPart2 = new ArrayList<>();//stopPart);
if (nepart != null) {
stopPart2.add(nepart);
}
stopPart2.addAll(catchParts);
if (retPart != null) {
stopPart2.add(retPart);
}
TranslateStack st = (TranslateStack) stack.clone();
st.clear();
List<GraphTargetItem> tryCommands = printGraph(partCodes, partCodePos, localData, st, 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, st, tryCommands, staticOperation, path);
}
output.clear();
stack.clear();
output.add(new TryAVM2Item(tryCommands, catchedExceptions, catchedCommands, finallyCommands, finCatchName));
for (int fin_e : catchedFinallys) {
if (finallyJumps.containsKey(fin_e)) {
finallyJumps.get(fin_e).clear();
}
//.remove((Integer) finStart);
}
ip = returnPos;
}
}
if (ip != part.start) {
part = null;
for (GraphPart p : allParts) {
List<GraphPart> 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) {
TranslateStack st = (TranslateStack) stack.clone();
st.clear();
ret.addAll(printGraph(partCodes, partCodePos, localData, st, allParts, null, part, stopPart, loops, staticOperation, path));
} else {
ret.add(lop);
}
return ret;
}
if (part.nextParts.isEmpty()) {
if (avm2code.code.get(part.end).definition instanceof ReturnValueIns) { //returns in finally clause
if (part.getHeight() >= 3) {
if (avm2code.code.get(part.getPosAt(part.getHeight() - 2)).definition instanceof KillIns) {
if (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);
GraphTargetItem v = ((SetLocalAVM2Item) output.get(output.size() - 2)).value;
ret.add(new ReturnValueAVM2Item(avm2code.code.get(part.end), (AVM2Instruction) v.getLineStartItem(), v));
return ret;
}
}
}
}
}
}
}
if ((avm2code.code.get(part.end).definition instanceof LookupSwitchIns) && (ignoredSwitches.containsValue(part.end) || ignoredSwitches2.contains(part.end))) {
ret = new ArrayList<>();
ret.addAll(output);
return ret;
}
return ret;
}
@Override
protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart next, Set<GraphPart> allParts) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
if (aLocalData.finallyJumps == null) {
aLocalData.finallyJumps = new HashMap<>();
}
Map<Integer, List<Integer>> finallyJumps = aLocalData.finallyJumps;
if (aLocalData.ignoredSwitches == null) {
aLocalData.ignoredSwitches = new HashMap<>();
}
Map<Integer, Integer> ignoredSwitches = aLocalData.ignoredSwitches;
GraphPart ret = next;
for (int f : finallyJumps.keySet()) {//int f = 0; f < finallyJumps.size(); f++) {
int swip = ignoredSwitches.get(f);
for (int fip : finallyJumps.get(f)) {
if (next.start == fip) {
if (stack != null && swip != -1) {
AVM2Instruction swIns = avm2code.code.get(swip);
GraphTargetItem t = stack.pop();
Double dval = t.getResultAsNumber();
int val = (int) (double) dval;
if (swIns.definition instanceof LookupSwitchIns) {
List<Integer> 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;
}
}
}
if (ret != next) {
return ret;
}
int pos = next.start;
long addr = avm2code.fixAddrAfterDebugLine(avm2code.pos2adr(pos));
for (int e = 0; e < body.exceptions.length; e++) {
if (body.exceptions[e].isFinally()) {
if (addr == avm2code.fixAddrAfterDebugLine(body.exceptions[e].start)) {
if (true) { //afterCatchPos + 1 == code.adr2pos(this.code.fixAddrAfterDebugLine(body.exceptions[e].end))) {
AVM2Instruction jmpIns = avm2code.code.get(avm2code.adr2pos(avm2code.fixAddrAfterDebugLine(body.exceptions[e].end)));
if (jmpIns.definition instanceof JumpIns) {
int finStart = avm2code.adr2pos(avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytesLength() + jmpIns.operands[0]);
if (!finallyJumps.containsKey(e)) {
finallyJumps.put(e, new ArrayList<>());
}
finallyJumps.get(e).add(finStart);
if (!ignoredSwitches.containsKey(e)) {
ignoredSwitches.put(e, -1);
}
//ignoredSwitches.put(e, -1);
break;
}
}
}
}
}
return next;
}
@Override
protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List<Loop> 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)).obj != null) {
if (((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).obj.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) {
pos++;
while (w.commands.get(pos) instanceof SetTypeAVM2Item) {
pos++;
}
ft = w.commands.get(pos);
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<Integer, GraphTargetItem> localRegs = aLocalData.localRegs;
localRegs.put(regIndex, new FilterAVM2Item(null, null, iti.obj.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.getSrc(), w.getLineStartItem(), w.loop, new InAVM2Item(hn.getInstruction(), hn.getLineStartIns(), sti.getObject(), ((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).obj), w.commands);
} else if (gti instanceof NextNameAVM2Item) {
return new ForInAVM2Item(w.getSrc(), w.getLineStartItem(), w.loop, new InAVM2Item(hn.getInstruction(), hn.getLineStartIns(), sti.getObject(), ((HasNextAVM2Item) w.expression.get(w.expression.size() - 1)).obj), w.commands);
}
}
}
}
}
}
return loopItem;
}
@Override
protected void finalProcessAfter(List<GraphTargetItem> 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<GraphTargetItem> list, int level, FinalProcessLocalData localData) throws InterruptedException {
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 + 1 < list.size()) {
if (list.get(i + 1) instanceof SwitchItem) {
SwitchItem si = (SwitchItem) list.get(i + 1);
if (si.switchedObject instanceof LocalRegAVM2Item) {
if (((LocalRegAVM2Item) si.switchedObject).regIndex == ri.regIndex) {
si.switchedObject = ri.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<GraphTargetItem> 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;
List<GraphTargetItem> body = new ArrayList<>();
List<GraphTargetItem> 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.getSrc(), ifi.getLineStartItem(), new Loop(0, null, null), new InAVM2Item(null, null, sti.getObject(), hnt.obj), body);
} else if (gti instanceof NextNameAVM2Item) {
repl = new ForInAVM2Item(ifi.getSrc(), ifi.getLineStartItem(), new Loop(0, null, null), new InAVM2Item(null, null, sti.getObject(), hnt.obj), body);
}
if (repl != null) {
list.remove(i);
list.add(i, repl);
list.addAll(i + 1, nextbody);
}
}
}
}
}
}
//Handle for loops at the end:
super.finalProcess(list, level, localData);
}
@Override
protected boolean isEmpty(List<GraphTargetItem> 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;
}
@Override
protected List<GraphTargetItem> filter(List<GraphTargetItem> list) {
return avm2code.clearTemporaryRegisters(list);
}
}