diff --git a/libsrc/ffdec_lib/graphviz/try_finally_asc2.gv b/libsrc/ffdec_lib/graphviz/try_finally_asc2.gv new file mode 100644 index 000000000..f03124823 --- /dev/null +++ b/libsrc/ffdec_lib/graphviz/try_finally_asc2.gv @@ -0,0 +1,60 @@ + +digraph try_finally_asc2 { + label = "Try..Finally clause in ASC2"; + node[shape=rect]; + + start->before; + before->finally_init->in_try; + try_f_begin->in_try; + try_c_begin->in_try; + in_try->finally; + try_c_end->finally; + in_catch->finally; + try_c_target->in_catch; + try_f_end->finally; + try_f_target->catch_f->finally:ne; + + finally->lookupswitch; + + lookupswitch->after; + lookupswitch->finally_throw[label="X"]; + + after->end; + + finally_init[label="loc_N=0;"]; + catch_f[label="loc_E = exception;\lloc_N = X;\l"]; + finally_throw[label="throw loc_E;"]; + + try_c_target[shape=ellipse]; + try_c_begin[shape=ellipse]; + try_c_end[shape=ellipse]; + + try_f_target[shape=ellipse]; + try_f_begin[shape=ellipse]; + try_f_end[shape=ellipse]; + + start[shape=ellipse]; + end[shape=ellipse]; + + lookupswitch[label="lookupswitch(loc_N)"] + + in_try->in_try_ret[label=return]; + in_try_ret[label="loc_R = returnedValue;\lloc_N = Y;\l"] + in_try_ret->finally; + lookupswitch->finally_return[label="Y"]; + finally_return[label="return loc_R;"]; + + in_try->in_try_continue[label=continue]; + in_try_continue[label="loc_N = Z;"] + in_try_continue->finally; + lookupswitch->finally_continue[label="Z"]; + finally_continue[label="continue label;"]; + + /* + All ways go through finally clause. There is also lookupswitch with getlocal. + Local register is set before entering in_try block. + For every return/continue, there is local register set to specific value. + Lookupswitch then decides what will happen. + */ + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/graphviz/try_finally_asc2.png b/libsrc/ffdec_lib/graphviz/try_finally_asc2.png new file mode 100644 index 000000000..5a726b776 Binary files /dev/null and b/libsrc/ffdec_lib/graphviz/try_finally_asc2.png differ diff --git a/libsrc/ffdec_lib/graphviz/try_finally_flex.gv b/libsrc/ffdec_lib/graphviz/try_finally_flex.gv new file mode 100644 index 000000000..f3611d0e0 --- /dev/null +++ b/libsrc/ffdec_lib/graphviz/try_finally_flex.gv @@ -0,0 +1,60 @@ +digraph try_finally_flex { + label = "Try..Finally clause in Adobe Flex"; + node[shape=rect]; + + start->before; + before->in_try; + try_f_begin->in_try; + try_c_begin->in_try; + in_try->pushbyteminusone; + try_c_end->pushbyteminusone; + in_catch->pushbyteminusone; + pushbyteminusone->finally; + try_c_target->in_catch; + try_f_end->finally; + try_f_target->catch_f->finally:ne; + + finally->lookupswitch; + + lookupswitch->after; + lookupswitch->finally_throw[label="X"]; + + after->end; + + pushbyteminusone[label="pushbyte -1"]; + catch_f[label="loc_E = exception;\lpushbyte X;\l"]; + finally_throw[label="throw loc_E;"]; + + try_c_target[shape=ellipse]; + try_c_begin[shape=ellipse]; + try_c_end[shape=ellipse]; + + try_f_target[shape=ellipse]; + try_f_begin[shape=ellipse]; + try_f_end[shape=ellipse]; + + start[shape=ellipse]; + end[shape=ellipse]; + + lookupswitch[label="lookupswitch(§§pop())"] + + in_try->in_try_ret[label=return]; + in_try_ret[label="loc_R = returnedValue;\lpushbyte Y;\l"] + in_try_ret->finally; + lookupswitch->finally_return[label="Y"]; + finally_return[label="return loc_R;"]; + + in_try->in_try_continue[label=continue]; + in_try_continue[label="pushbyte Z;"] + in_try_continue->finally; + lookupswitch->finally_continue[label="Z"]; + finally_continue[label="continue label;"]; + + /* + All ways go through finally clause. There is also lookupswitch which uses value on stack.. + Value -1 is pushed on stack after in_try and after catch bodys. + For every return/continue, there is also specific value pushed to stack. + Lookupswitch then decides what will happen. + */ + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/graphviz/try_finally_flex.png b/libsrc/ffdec_lib/graphviz/try_finally_flex.png new file mode 100644 index 000000000..4c097bb39 Binary files /dev/null and b/libsrc/ffdec_lib/graphviz/try_finally_flex.png differ diff --git a/libsrc/ffdec_lib/graphviz/try_finally_swftools.gv b/libsrc/ffdec_lib/graphviz/try_finally_swftools.gv new file mode 100644 index 000000000..0d45ca414 --- /dev/null +++ b/libsrc/ffdec_lib/graphviz/try_finally_swftools.gv @@ -0,0 +1,44 @@ +digraph try_finally_swftools { + label = "Try..Finally clause in swftools"; + node[shape=rect]; + + start->before; + before->in_try; + try_f_begin->in_try; + try_c_begin->in_try; + in_try->finally; + try_c_end->finally; + in_catch->finally; + try_c_target->in_catch; + try_f_end->finally; + try_f_target->catch_f; + + finally->after; + + + + after->end; + + catch_f[label="finally;\lthrow exception;\l"]; + try_c_target[shape=ellipse]; + try_c_begin[shape=ellipse]; + try_c_end[shape=ellipse]; + + try_f_target[shape=ellipse]; + try_f_begin[shape=ellipse]; + try_f_end[shape=ellipse]; + + start[shape=ellipse]; + end[shape=ellipse]; + + in_try->in_try_ret[label=return]; + in_try_ret[label="§§push(returnedValue);\lfinally;\lreturn §§pop();\l"] + + in_try->in_try_continue[label=continue]; + in_try_continue[label="finally;\lcontinue label;\l"]; + + /* + Finally is inlined in every its usage. + In try_f_target there is finnally alone with throw - we can take it from there. + */ +} diff --git a/libsrc/ffdec_lib/graphviz/try_finally_swftools.png b/libsrc/ffdec_lib/graphviz/try_finally_swftools.png new file mode 100644 index 000000000..dbd3826fa Binary files /dev/null and b/libsrc/ffdec_lib/graphviz/try_finally_swftools.png differ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java index 646b8923b..acf1ace6e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java @@ -12,18 +12,21 @@ * 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; import com.jpexs.decompiler.flash.BaseLocalData; 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.types.ABCException; import com.jpexs.decompiler.flash.abc.types.InstanceInfo; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; import java.util.ArrayList; @@ -57,11 +60,17 @@ public class AVM2LocalData extends BaseLocalData { public ArrayList parsedExceptions; - public Map> finallyJumps; + //public Map> finallyJumps; + /** + * Mapped jumps from pushbyte xx part to apropriate lookupswitch branch + */ + public Map finallyJumps = new HashMap<>(); - public Map ignoredSwitches; + //public Map ignoredSwitches; + //exception index => switchPart + public Map ignoredSwitches; - public List ignoredSwitches2; + //public List ignoredSwitches2; public Integer scriptIndex; @@ -77,6 +86,8 @@ public class AVM2LocalData extends BaseLocalData { public Map> setLocalPosToGetLocalPos = new HashMap<>(); + public CodeStats codeStats; + public AVM2LocalData() { } @@ -103,7 +114,7 @@ public class AVM2LocalData extends BaseLocalData { parsedExceptions = localData.parsedExceptions; finallyJumps = localData.finallyJumps; ignoredSwitches = localData.ignoredSwitches; - ignoredSwitches2 = localData.ignoredSwitches2; + //ignoredSwitches2 = localData.ignoredSwitches2; scriptIndex = localData.scriptIndex; localRegAssignmentIps = localData.localRegAssignmentIps; ip = localData.ip; @@ -111,6 +122,7 @@ public class AVM2LocalData extends BaseLocalData { code = localData.code; thisHasDefaultToPrimitive = localData.thisHasDefaultToPrimitive; setLocalPosToGetLocalPos = localData.setLocalPosToGetLocalPos; + codeStats = localData.codeStats; } public AVM2ConstantPool getConstants() { 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 76e844fba..d9ff67275 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 @@ -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; import com.jpexs.decompiler.flash.EndOfStreamException; @@ -1493,21 +1494,30 @@ public class AVM2Code implements Cloneable { } } - public int fixIPAfterDebugLine(int ip) { + public int getIpThroughJumpAndDebugLine(int ip) { if (code.isEmpty()) { return ip; } if (ip >= code.size()) { return code.size() - 1; } - while (code.get(ip).definition instanceof DebugLineIns) { - ip++; + while (ip < code.size()) { + if (code.get(ip).definition instanceof DebugLineIns) { + ip++; + } else if (code.get(ip).definition instanceof JumpIns) { + ip = adr2pos(pos2adr(ip + 1) + code.get(ip).operands[0]); + } else { + break; + } + } + if (ip >= code.size()) { + return code.size() - 1; } return ip; } - public long fixAddrAfterDebugLine(long addr) throws ConvertException { - return pos2adr(fixIPAfterDebugLine(adr2pos(addr, true))); + public long getAddrThroughJumpAndDebugLine(long addr) throws ConvertException { + return pos2adr(getIpThroughJumpAndDebugLine(adr2pos(addr, true))); } public ConvertOutput toSourceOutput(Map> setLocalPosToGetLocalPos, boolean thisHasDefaultToPrimitive, Reference lineStartItem, String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ABC abc, MethodBody body, int start, int end, HashMap localRegNames, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps, HashMap> refs) throws ConvertException, InterruptedException { @@ -2373,7 +2383,7 @@ public class AVM2Code implements Cloneable { } } - private boolean walkCode(CodeStats stats, int pos, int stack, int scope, ABC abc) { + private boolean walkCode(CodeStats stats, int pos, int stack, int scope, ABC abc, boolean autoFill) { while (pos < code.size()) { AVM2Instruction ins = code.get(pos); if (stats.instructionStats[pos].seen) { @@ -2383,7 +2393,9 @@ public class AVM2Code implements Cloneable { if (ins.definition instanceof NewFunctionIns) { MethodBody innerBody = abc.findBody(ins.operands[0]); - innerBody.autoFillStats(abc, stats.initscope + (stats.has_activation ? 1 : 0), false); + if (autoFill) { + innerBody.autoFillStats(abc, stats.initscope + (stats.has_activation ? 1 : 0), false); + } } stats.instructionStats[pos].seen = true; @@ -2445,7 +2457,7 @@ public class AVM2Code implements Cloneable { } else if (ins.definition instanceof IfTypeIns) { try { int newpos = adr2pos(ins.getTargetAddress()); - walkCode(stats, newpos, stack, scope, abc); + walkCode(stats, newpos, stack, scope, abc, autoFill); } catch (ConvertException ex) { return false; } @@ -2457,7 +2469,7 @@ public class AVM2Code implements Cloneable { } try { int newpos = adr2pos(pos2adr(pos) + ins.operands[i]); - if (!walkCode(stats, newpos, stack, scope, abc)) { + if (!walkCode(stats, newpos, stack, scope, abc, autoFill)) { return false; } } catch (ConvertException ex) { @@ -2470,10 +2482,10 @@ public class AVM2Code implements Cloneable { return true; } - public CodeStats getStats(ABC abc, MethodBody body, int initScope) { + public CodeStats getStats(ABC abc, MethodBody body, int initScope, boolean autoFill) { CodeStats stats = new CodeStats(this); stats.initscope = initScope; - if (!walkCode(stats, 0, 0, initScope, abc)) { + if (!walkCode(stats, 0, 0, initScope, abc, autoFill)) { return null; } int scopePos = -1; @@ -2490,7 +2502,7 @@ public class AVM2Code implements Cloneable { visited.add(i); } } - if (!walkCode(stats, adr2pos(ex.target), 1 + (ex.isFinally() ? 1 : 0), scopePos, abc)) { + if (!walkCode(stats, adr2pos(ex.target), 1, scopePos, abc, autoFill)) { return null; } int maxIp = 0; @@ -2516,7 +2528,7 @@ public class AVM2Code implements Cloneable { stats.instructionStats[i].seen = false; } // Rerun rest with new scopePos, stackPos - if (!walkCode(stats, nextIp, origStackPos + 1/*magic!*/, scopePos - 1 /*magic!*/, abc)) { + if (!walkCode(stats, nextIp, origStackPos + 1/*magic!*/, scopePos - 1 /*magic!*/, abc, autoFill)) { return null; } scopePos--; 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 2f44e29f1..99e330526 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 @@ -36,6 +36,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIn import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins; import com.jpexs.decompiler.flash.abc.avm2.instructions.other2.DecLocalPIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other2.IncLocalPIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; 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.FullMultinameAVM2Item; @@ -70,6 +71,8 @@ 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.ContinueItem; import com.jpexs.decompiler.graph.model.ExitItem; import com.jpexs.decompiler.graph.model.GotoItem; import com.jpexs.decompiler.graph.model.IfItem; @@ -138,56 +141,119 @@ public class AVM2Graph extends Graph { } @Override - protected void beforePrintGraph(BaseLocalData localData, String path, Set allParts, List loops) { - Map ignoredSwitches = getIgnoredSwitches(allParts); - Map> setLocalPosToGetLocalPos = calculateLocalRegsUsage(new HashSet(ignoredSwitches.values()), path, allParts); + protected void beforePrintGraph(BaseLocalData localData, String path, Set allParts, List loops) throws InterruptedException { AVM2LocalData avm2LocalData = ((AVM2LocalData) localData); + avm2LocalData.codeStats = avm2LocalData.code.getStats(avm2LocalData.abc, avm2LocalData.methodBody, avm2LocalData.methodBody.init_scope_depth, false); + getIgnoredSwitches((AVM2LocalData) localData, allParts); + Set integerSwitchesIps = new HashSet<>(); + for (GraphPart p : ((AVM2LocalData) localData).ignoredSwitches.values()) { + integerSwitchesIps.add(p.end); + } + Map> setLocalPosToGetLocalPos = calculateLocalRegsUsage(integerSwitchesIps, path, allParts); avm2LocalData.setLocalPosToGetLocalPos = setLocalPosToGetLocalPos; - } - private Map getIgnoredSwitches(Set allParts) { - Map ignoredSwitches = new HashMap<>(); - int finStart; + private void getIgnoredSwitches(AVM2LocalData localData, Set allParts) throws InterruptedException { + for (int e = 0; e < body.exceptions.length; e++) { - if (body.exceptions[e].isFinally()) { - { - { - AVM2Instruction jmpIns = avm2code.code.get(code.adr2pos(avm2code.fixAddrAfterDebugLine(body.exceptions[e].end))); + ABCException ex = body.exceptions[e]; + if (!ex.isFinally()) { + continue; + } - 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; - } - } - 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) { - swPos = f; - - break; - } - } - } - } - } - ignoredSwitches.put(e, swPos); - break; - } - } + GraphPart finallyTryTargetPart = null; + int targetIp = code.adr2pos(ex.target); + for (GraphPart p : allParts) { + if (targetIp >= p.start && targetIp <= p.end) { + finallyTryTargetPart = p; + break; } } + + GraphPart finallyPart = finallyTryTargetPart.nextParts.size() > 0 ? finallyTryTargetPart.nextParts.get(0) : null; + + TranslateStack finallyTryTargetStack = (TranslateStack) new TranslateStack("try_target"); + + AVM2LocalData localData2 = new AVM2LocalData(localData); + localData2.scopeStack = new ScopeStack(); + + List targetOutput = translatePart(localData2, finallyTryTargetPart, finallyTryTargetStack, 0 /*??*/, "try_target"); + + final int FINALLY_KIND_STACK_BASED = 0; + final int FINALLY_KIND_REGISTER_BASED = 1; + final int FINALLY_KIND_INLINED = 2; + final int FINALLY_KIND_UNKNOWN = -1; + + int switchedReg = -1; + int finallyKind = FINALLY_KIND_UNKNOWN; + if (finallyTryTargetStack.size() == 1) { + finallyKind = FINALLY_KIND_STACK_BASED; + } else if (targetOutput.size() >= 2 + && (targetOutput.get(targetOutput.size() - 1) instanceof SetLocalAVM2Item) + && (targetOutput.get(targetOutput.size() - 2) instanceof SetLocalAVM2Item)) { + switchedReg = ((SetLocalAVM2Item) targetOutput.get(targetOutput.size() - 1)).regIndex; + finallyKind = FINALLY_KIND_REGISTER_BASED; + } else if (!targetOutput.isEmpty() && (targetOutput.get(targetOutput.size() - 1) instanceof ThrowAVM2Item)) { + //inlined to single part + //TODO: maybe replace all instances of exit nodes of try block + finallyKind = FINALLY_KIND_INLINED; + } else { + //probably inlined code in more parts, cannot do :-( + } + + if (finallyKind == FINALLY_KIND_STACK_BASED) { + + /* + Search for a lookupswitch which first pops from the stack + */ + List foundIps = new ArrayList<>(); + List foundParts = new ArrayList<>(); + int stackAfter = localData.codeStats.instructionStats[finallyTryTargetPart.end].stackpos_after; + + //int stackAfter = localData.codeStats.instructionStats[finallyPart.start].stackpos; + findAllPops(localData, stackAfter, finallyPart, foundIps, foundParts, new HashSet<>()); + int switchIp = -1; + GraphPart switchPart = null; + for (int i = 0; i < foundIps.size(); i++) { + int ip = foundIps.get(i); + if (avm2code.code.get(ip).definition instanceof LookupSwitchIns) { + switchIp = ip; + switchPart = foundParts.get(i); + } + } + if (switchIp > -1 && switchPart != null) { + for (GraphPart r : finallyPart.refs) { + for (int ip = r.end; ip >= r.start; ip--) { + AVM2Instruction ins = avm2code.code.get(ip); + if (ins.definition instanceof JumpIns) { + continue; + } else if (ins.definition instanceof PushByteIns) { + int val = ins.operands[0]; + if (val < 0 || val > switchPart.nextParts.size() - 2) { + localData.finallyJumps.put(r, switchPart.nextParts.get(0)); //default branch + } else { + localData.finallyJumps.put(r, switchPart.nextParts.get(1 + val)); + } + } + } + } + + //return in finally block is joined after switch decision + for (GraphPart p : switchPart.nextParts) { + for (GraphPart r : p.refs) { + if (r != switchPart) { + localData.finallyJumps.put(r, p); + } + } + } + + localData.ignoredSwitches.put(e, switchPart); + } else { + //there is probably return in all branches and no other way outside finally + } + + } } - return ignoredSwitches; } public Map> calculateLocalRegsUsage(Set ignoredSwitches, String path, Set allParts) { @@ -345,315 +411,231 @@ public class AVM2Graph extends Graph { for (GraphPart p : allBlocks) { if (avm2code.pos2adr(p.start) >= ex.start && avm2code.pos2adr(p.end) <= ex.end && target != null) { //Logger.getLogger(Graph.class.getName()).fine("ADDING throwpart " + target + " to " + p); - p.throwParts.add(target); - target.refs.add(p); + //p.throwParts.add(target); + //target.refs.add(p); } } } } + private void findNearestPartOutsideTry(AVM2LocalData localData, GraphPart part, int tryEndIp, Set visited, Set result) { + if (visited.contains(part)) { + return; + } + + if (part.start >= tryEndIp || part.end >= tryEndIp) { + result.add(part); + return; + } + + if (localData.finallyJumps.containsKey(part)) { + GraphPart afterSwitchPart = localData.finallyJumps.get(part); + GraphPart switchPart = null; + for (GraphPart r : afterSwitchPart.refs) { + if (localData.ignoredSwitches.containsValue(r)) { + switchPart = r; + } + } + + if (switchPart != null) { + return; + } + } + + for (GraphPart n : part.nextParts) { + findNearestPartOutsideTry(localData, n, tryEndIp, visited, result); + } + } + + private Set findNearestPartOutsideTry(AVM2LocalData localData, GraphPart start, int tryEndIp) { + Set result = new HashSet<>(); + findNearestPartOutsideTry(localData, start, tryEndIp, new HashSet<>(), result); + return result; + } + + private void findAllPops(AVM2LocalData localData, int stackLevel, GraphPart part, List foundIps, List foundParts, Set visited) { + if (visited.contains(part)) { + return; + } + visited.add(part); + for (int ip = part.start; ip <= part.end; ip++) { + if (localData.codeStats.instructionStats[ip].stackpos_after == stackLevel - 1) { + foundIps.add(ip); + foundParts.add(part); + return; + } + } + for (GraphPart n : part.nextParts) { + findAllPops(localData, stackLevel, n, foundIps, foundParts, visited); + } + } + + private List checkTry(List output, List foundGotos, Map> partCodes, Map partCodePos, AVM2LocalData localData, GraphPart part, List stopPart, List loops, Set allParts, TranslateStack stack, int staticOperation, String path) throws InterruptedException { + if (localData.parsedExceptions == null) { + localData.parsedExceptions = new ArrayList<>(); + } + List parsedExceptions = localData.parsedExceptions; + if (localData.finallyJumps == null) { + localData.finallyJumps = new HashMap<>(); + } + if (localData.ignoredSwitches == null) { + localData.ignoredSwitches = new HashMap<>(); + } + long addr = avm2code.getAddrThroughJumpAndDebugLine(avm2code.pos2adr(part.start)); + long maxEndAddr = -1; + List catchedExceptions = new ArrayList<>(); + ABCException finallyException = null; + int endIp = -1; + int finallyIndex = -1; + for (int e = 0; e < body.exceptions.length; e++) { + if (addr == avm2code.getAddrThroughJumpAndDebugLine(body.exceptions[e].start)) { + if (!parsedExceptions.contains(body.exceptions[e])) { + long endAddr = avm2code.getAddrThroughJumpAndDebugLine(body.exceptions[e].end); + if (endAddr > maxEndAddr) { + catchedExceptions.clear(); + finallyException = null; + finallyIndex = -1; + maxEndAddr = avm2code.getAddrThroughJumpAndDebugLine(body.exceptions[e].end); + endIp = avm2code.adr2pos(maxEndAddr); + catchedExceptions.add(body.exceptions[e]); + } else if (endAddr == maxEndAddr) { + catchedExceptions.add(body.exceptions[e]); + } + if (body.exceptions[e].isFinally()) { + finallyException = body.exceptions[e]; + finallyIndex = e; + } + } + } + } + if (catchedExceptions.size() > 0) { + parsedExceptions.addAll(catchedExceptions); + if (finallyException != null) { + catchedExceptions.remove(finallyException); + } + List tryCommands = new ArrayList<>(); + List> catchCommands = new ArrayList<>(); + List finallyCommands = new ArrayList<>(); + + GraphPart afterPart = null; + for (GraphPart p : allParts) { + if (endIp >= p.start && endIp <= p.end) { + afterPart = p; + break; + } + } + + stack.clear(); //If the original code (before check()) had "if" in it, there would be something on stack + + for (ABCException ex : catchedExceptions) { + + TranslateStack st2 = (TranslateStack) stack.clone(); + st2.clear(); + st2.add(new ExceptionAVM2Item(ex)); + + GraphPart catchPart = null; + for (GraphPart p : allParts) { + if (p.start == avm2code.adr2pos(ex.target)) { + catchPart = p; + break; + } + } + AVM2LocalData localData2 = new AVM2LocalData(localData); + localData2.scopeStack = new ScopeStack(); + + List stopPart2 = new ArrayList<>(stopPart); + stopPart2.add(afterPart); + + List currentCatchCommands = printGraph(foundGotos, partCodes, partCodePos, localData2, st2, allParts, null, catchPart, stopPart2, loops, staticOperation, path); + if (!currentCatchCommands.isEmpty() && (currentCatchCommands.get(0) instanceof SetLocalAVM2Item)) { + if (currentCatchCommands.get(0).value.getNotCoerced() instanceof ExceptionAVM2Item) { + currentCatchCommands.remove(0); + } + } + catchCommands.add(currentCatchCommands); + } + + if (finallyException == null) { + List stopPart2 = new ArrayList<>(stopPart); + stopPart2.add(afterPart); + tryCommands = printGraph(foundGotos, partCodes, partCodePos, localData, stack, allParts, null, part, stopPart2, loops, staticOperation, path); + } + + if (finallyException != null) { + afterPart = null; + GraphPart finallyTryTargetPart = null; + int targetPos = avm2code.adr2pos(finallyException.target); + for (GraphPart p : allParts) { + if (p.start == targetPos) { + finallyTryTargetPart = p; + break; + } + } + + GraphPart finallyPart = finallyTryTargetPart.nextParts.isEmpty() ? null : finallyTryTargetPart.nextParts.get(0); + + List tryStopPart = new ArrayList<>(stopPart); + if (finallyPart != null) { + tryStopPart.add(finallyPart); + } + tryCommands = printGraph(foundGotos, partCodes, partCodePos, localData, stack, allParts, null, part, tryStopPart, loops, staticOperation, path); + makeAllCommands(tryCommands, stack); + processIfs(tryCommands); + + //there should be §§push(-1) left + if (!tryCommands.isEmpty() + && (tryCommands.get(tryCommands.size() - 1) instanceof PushItem) + && (tryCommands.get(tryCommands.size() - 1).value instanceof IntegerValueAVM2Item)) { + tryCommands.remove(tryCommands.size() - 1); + } + + List finallyStopPart = new ArrayList<>(stopPart); + GraphPart switchPart = localData.ignoredSwitches.containsKey(finallyIndex) ? localData.ignoredSwitches.get(finallyIndex) : null; + if (switchPart != null) { + finallyStopPart.add(switchPart); + } + if (finallyPart != null) { + finallyCommands = printGraph(foundGotos, partCodes, partCodePos, localData, stack, allParts, null, finallyPart, finallyStopPart, loops, staticOperation, path); + } + if (switchPart != null) { + finallyCommands.addAll(translatePart(localData, switchPart, stack, staticOperation, path)); + afterPart = switchPart.nextParts.get(0); //take the default branch + } + stack.pop(); + + if (tryCommands.size() == 1 + && (tryCommands.get(0) instanceof TryAVM2Item) + && catchCommands.isEmpty() + && ((TryAVM2Item) tryCommands.get(0)).finallyCommands.isEmpty()) { + catchCommands = ((TryAVM2Item) tryCommands.get(0)).catchCommands; + catchedExceptions = ((TryAVM2Item) tryCommands.get(0)).catchExceptions; + tryCommands = ((TryAVM2Item) tryCommands.get(0)).tryCommands; + } + } + if (catchCommands.isEmpty() && finallyCommands.isEmpty() && tryCommands.isEmpty()) { + return null; + } + List ret = new ArrayList<>(); + ret.add(new TryAVM2Item(tryCommands, catchedExceptions, catchCommands, finallyCommands, "TODO")); + + if (afterPart != null) { + ret.addAll(printGraph(foundGotos, partCodes, partCodePos, localData, stack, allParts, null, afterPart, stopPart, loops, staticOperation, path)); + } + return ret; + } + return null; + } + @Override protected List check(List foundGotos, Map> partCodes, Map partCodePos, GraphSource code, BaseLocalData localData, Set allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { List ret = null; AVM2LocalData aLocalData = (AVM2LocalData) localData; - if (aLocalData.parsedExceptions == null) { - aLocalData.parsedExceptions = new ArrayList<>(); - } - List parsedExceptions = aLocalData.parsedExceptions; - if (aLocalData.finallyJumps == null) { - aLocalData.finallyJumps = new HashMap<>(); - } - Map> finallyJumps = aLocalData.finallyJumps; - if (aLocalData.ignoredSwitches == null) { - aLocalData.ignoredSwitches = new HashMap<>(); - } - Map ignoredSwitches = aLocalData.ignoredSwitches; - if (aLocalData.ignoredSwitches2 == null) { - aLocalData.ignoredSwitches2 = new ArrayList<>(); - } - List ignoredSwitches2 = aLocalData.ignoredSwitches2; - int ip = part.start; - long addr = avm2code.fixAddrAfterDebugLine(avm2code.pos2adr(part.start)); - long maxend = -1; - List catchedFinallys = new ArrayList<>(); - List 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> 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() { - @Override - public int compare(ABCException o1, ABCException o2) { - return (int) (t.avm2code.fixAddrAfterDebugLine(o1.target) - t.avm2code.fixAddrAfterDebugLine(o2.target)); - } - }); - - List 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 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(); - Map> oldFinallyJumps = new HashMap<>(finallyJumps); - finallyJumps.clear(); - ignoredSwitches.put(e, swPos); - st.push(new PopItem(null, aLocalData.lineStartInstruction)); - finallyCommands = printGraph(foundGotos, 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 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(localData2.scriptIndex); - List stopPart2 = new ArrayList<>(stopPart); - stopPart2.add(nepart); - if (retPart != null) { - stopPart2.add(retPart); - } - - List ncatchedCommands = printGraph(foundGotos, partCodes, partCodePos, localData2, st2, allParts, parent, npart, stopPart2, loops, staticOperation, path); - //hack for findGotos - FIXME - if (hasFinally && !ncatchedCommands.isEmpty()) { - for (int k = 0; k < ncatchedCommands.size(); k++) { - if (ncatchedCommands.get(k) instanceof GotoItem) { - GotoItem gi = (GotoItem) ncatchedCommands.get(k); - for (GotoItem g : foundGotos) { - if (gi.labelName.equals(g.labelName) && g.targetCommands != null) { - if (!g.targetCommands.isEmpty()) { - if (g.targetCommands.get(0) instanceof PushItem) { - if (g.targetCommands.get(0).value instanceof IntegerValueAVM2Item) { - if (((IntegerValueAVM2Item) g.targetCommands.get(0).value).value == -1) { - ncatchedCommands.remove(gi); - } - } - } - } - break; - } - } - } - } - } - 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); - } - } - } else { - //No kill ins - if (!ncatchedCommands.isEmpty() && (ncatchedCommands.get(0) instanceof SetLocalAVM2Item)) { - SetLocalAVM2Item sl = (SetLocalAVM2Item) ncatchedCommands.get(0); - if (sl.value.getThroughDuplicate().getNotCoerced() instanceof ExceptionAVM2Item) { - ncatchedCommands.remove(0); - } - } - } - } - } - - GraphPart nepart = null; - - for (GraphPart p : allParts) { - if (p.start == endposStartBlock) { - nepart = p; - break; - } - } - List 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 tryCommands = printGraph(foundGotos, 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(); - makeAllCommands(tryCommands, st); - 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 ps = p.getSubParts(); - for (GraphPart p2 : ps) { - if (p2.start == ip) { - part = p2; - break; - } - } - } - ret = new ArrayList<>(); - ret.addAll(output); - GraphTargetItem lop = checkLoop(new ArrayList(), part, stopPart, loops); - if (lop == null) { - TranslateStack st = (TranslateStack) stack.clone(); - st.clear(); - - ret.addAll(printGraph(foundGotos, partCodes, partCodePos, localData, st, allParts, null, part, stopPart, loops, staticOperation, path)); - } else { - ret.add(lop); - } + ret = checkTry(output, foundGotos, partCodes, partCodePos, aLocalData, part, stopPart, loops, allParts, stack, staticOperation, path); + if (ret != null) { 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; - } - + //Detect switch if ((part.nextParts.size() == 2) && (!stack.isEmpty()) && (stack.peek() instanceof StrictEqAVM2Item)) { GraphSourceItem switchStartItem = code.get(part.start); @@ -737,7 +719,6 @@ public class AVM2Graph extends Graph { defaultPart = defaultPart.nextParts.get(0); } - ret = new ArrayList<>(); ret.addAll(output); Reference nextRef = new Reference<>(null); @@ -758,13 +739,37 @@ public class AVM2Graph extends Graph { return ret; } + @Override + protected List getNextParts(BaseLocalData localData, GraphPart part) { + AVM2LocalData aLocalData = (AVM2LocalData) localData; + /*if (aLocalData.finallyJumps.containsKey(part)) { + List ret = new ArrayList<>(); + ret.add(aLocalData.finallyJumps.get(part)); + return ret; + }*/ + return super.getNextParts(localData, part); + } + @Override protected GraphPart checkPart(TranslateStack stack, BaseLocalData localData, GraphPart prev, GraphPart next, Set allParts) { AVM2LocalData aLocalData = (AVM2LocalData) localData; if (aLocalData.finallyJumps == null) { aLocalData.finallyJumps = new HashMap<>(); } - Map> finallyJumps = aLocalData.finallyJumps; + if (aLocalData.ignoredSwitches == null) { + aLocalData.ignoredSwitches = new HashMap<>(); + } + + if (prev != null) { + if (aLocalData.ignoredSwitches.containsValue(prev)) { + return null; + } + if (aLocalData.finallyJumps.containsKey(prev)) { + return aLocalData.finallyJumps.get(prev); + } + } + + /*Map> finallyJumps = aLocalData.finallyJumps; if (aLocalData.ignoredSwitches == null) { aLocalData.ignoredSwitches = new HashMap<>(); } @@ -786,7 +791,7 @@ public class AVM2Graph extends Graph { nip = branches.get(1 + val); } for (GraphPart p : allParts) { - if (avm2code.fixIPAfterDebugLine(p.start) == avm2code.fixIPAfterDebugLine(nip)) { + if (avm2code.getIpThroughJumpAndDebugLine(p.start) == avm2code.getIpThroughJumpAndDebugLine(nip)) { return p; } } @@ -801,14 +806,14 @@ public class AVM2Graph extends Graph { } int pos = next.start; - long addr = avm2code.fixAddrAfterDebugLine(avm2code.pos2adr(pos)); + long addr = avm2code.getAddrThroughJumpAndDebugLine(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 (addr == avm2code.getAddrThroughJumpAndDebugLine(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))); + AVM2Instruction jmpIns = avm2code.code.get(avm2code.adr2pos(avm2code.getAddrThroughJumpAndDebugLine(body.exceptions[e].end))); if (jmpIns.definition instanceof JumpIns) { - int finStart = avm2code.adr2pos(avm2code.fixAddrAfterDebugLine(body.exceptions[e].end) + jmpIns.getBytesLength() + jmpIns.operands[0]); + int finStart = avm2code.adr2pos(avm2code.getAddrThroughJumpAndDebugLine(body.exceptions[e].end) + jmpIns.getBytesLength() + jmpIns.operands[0]); if (!finallyJumps.containsKey(e)) { finallyJumps.put(e, new ArrayList<>()); } @@ -832,8 +837,7 @@ public class AVM2Graph extends Graph { } } } - } - + }*/ return next; } @@ -1050,6 +1054,7 @@ public class AVM2Graph extends Graph { } return false; } + @Override protected void finalProcess(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { @@ -1061,13 +1066,6 @@ public class AVM2Graph extends Graph { } } - /*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); @@ -1105,30 +1103,42 @@ public class AVM2Graph extends Graph { } } - if (i + 2 < list.size()) { - if (isIntegerOrPopInteger(list.get(i + 1)) && (list.get(i + 2) instanceof ReturnValueAVM2Item) - && (list.get(i + 2).value instanceof LocalRegAVM2Item) - && (((LocalRegAVM2Item) list.get(i + 2).value).regIndex == ri.regIndex)) { - ReturnValueAVM2Item r = (ReturnValueAVM2Item) list.get(i + 2); - r.value = ri.value; - list.remove(i + 1); - list.remove(i); - i--; - continue; - } - if (isIntegerOrPopInteger(list.get(i + 1)) && (list.get(i + 2) instanceof ThrowAVM2Item) - && (list.get(i + 2).value instanceof LocalRegAVM2Item) - && (((LocalRegAVM2Item) list.get(i + 2).value).regIndex == ri.regIndex)) { - ThrowAVM2Item t = (ThrowAVM2Item) list.get(i + 2); - t.value = ri.value; - list.remove(i + 1); - list.remove(i); - i--; - continue; - } - } else if (i + 1 < list.size() && usages.isEmpty()) { - if (isIntegerOrPopInteger(list.get(i + 1))) { - list.remove(i + 1); + //§§push(int) in every return/throw in try..finally block + //there may be multiple pushes as finnaly clauses may be nested + int numPushes = 0; + while (i + 1 + numPushes < list.size() && isIntegerOrPopInteger(list.get(i + 1 + numPushes))) { + numPushes++; + } + if (numPushes > 0) { + if (i + 1 + numPushes < list.size()) { + if (numPushes > 0 && (list.get(i + 1 + numPushes) instanceof ReturnValueAVM2Item) + && (list.get(i + 1 + numPushes).value instanceof LocalRegAVM2Item) + && (((LocalRegAVM2Item) list.get(i + 1 + numPushes).value).regIndex == ri.regIndex)) { + ReturnValueAVM2Item r = (ReturnValueAVM2Item) list.get(i + 1 + numPushes); + r.value = ri.value; + for (int n = 0; n < numPushes; n++) { + list.remove(i + 1); + } + list.remove(i); + i--; + continue; + } + if (numPushes > 0 && (list.get(i + 1 + numPushes) instanceof ThrowAVM2Item) + && (list.get(i + 1 + numPushes).value instanceof LocalRegAVM2Item) + && (((LocalRegAVM2Item) list.get(i + 1 + numPushes).value).regIndex == ri.regIndex)) { + ThrowAVM2Item t = (ThrowAVM2Item) list.get(i + 1 + numPushes); + t.value = ri.value; + for (int n = 0; n < numPushes; n++) { + list.remove(i + 1); + } + list.remove(i); + i--; + continue; + } + } else if (i + numPushes < list.size() && usages.isEmpty()) { + for (int n = 0; n < numPushes; n++) { + list.remove(i + 1); + } list.remove(i); i--; continue; @@ -1136,6 +1146,20 @@ public class AVM2Graph extends Graph { } } } + + //§§push(int) before every continue/returnvoid in try..finally block + //there may be multiple pushes as finnaly clauses may be nested + //TODO: handle this better - actually remove only really needed + if ((list.get(i) instanceof ContinueItem) || (list.get(i) instanceof BreakItem) || (list.get(i) instanceof ReturnVoidAVM2Item)) { + for (int j = i - 1; j >= 0; j--) { + if (isIntegerOrPopInteger(list.get(j))) { + list.remove(j); + i--; + } else { + break; + } + } + } } List ret = list; @@ -1241,4 +1265,12 @@ public class AVM2Graph extends Graph { switchItem.switchedObject = setLocal.value; } } + + @Override + protected boolean partIsSwitch(GraphPart part) { + if (part.end < 0) { + return false; + } + return avm2code.code.get(part.end).definition instanceof LookupSwitchIns; + } } 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 ae5da289e..32d7c7ded 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 @@ -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.types; import com.jpexs.decompiler.flash.SWFInputStream; @@ -445,7 +446,7 @@ public final class MethodBody implements Cloneable { public boolean autoFillStats(ABC abc, int initScope, boolean hasThis) { //System.out.println("--------------"); - CodeStats stats = getCode().getStats(abc, this, initScope); + CodeStats stats = getCode().getStats(abc, this, initScope, true); if (stats == null) { return false; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java index 016437aaa..b75277638 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.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.action; import com.jpexs.decompiler.flash.BaseLocalData; @@ -53,6 +54,7 @@ import com.jpexs.decompiler.graph.model.ContinueItem; import com.jpexs.decompiler.graph.model.DefaultItem; import com.jpexs.decompiler.graph.model.GotoItem; import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.ScriptEndItem; import com.jpexs.decompiler.graph.model.SwitchItem; import com.jpexs.decompiler.graph.model.WhileItem; import com.jpexs.helpers.Helper; @@ -159,6 +161,21 @@ public class ActionGraph extends Graph { @Override protected void finalProcess(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { + if (level == 0) { + List removed = new ArrayList<>(); + for (int i = list.size() - 1; i >= 0; i--) { + if (list.get(i) instanceof ScriptEndItem) { + continue; + } + if (list.get(i) instanceof FunctionActionItem) { + removed.add(0, list.remove(i)); + } else { + break; + } + } + list.addAll(0, removed); + } + if (insideDoInitAction) { ActionScript2ClassDetector detector = new ActionScript2ClassDetector(); detector.checkClass(list, path); 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 a4e445958..c6871fc45 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -238,7 +238,7 @@ public class Graph { GraphPartQueue newParts = new GraphPartQueue(); loopnext: - for (GraphPart nextRaw : part.nextParts) { + for (GraphPart nextRaw : getNextParts(localData, part)) { GraphPart next = checkPart(null, localData, part, nextRaw, null); if (next == null) { @@ -276,7 +276,7 @@ public class Graph { } public GraphPart getNextCommonPart(BaseLocalData localData, GraphPart part, List loops) throws InterruptedException { - return getCommonPart(localData, part, part.nextParts, loops); + return getCommonPart(localData, part, getNextParts(localData, part), loops); } //TODO: Make this faster! @@ -374,7 +374,7 @@ public class Graph { } } - loopi: + /*loopi: for (int i = 0; i < parts.size(); i++) { for (int j = 0; j < parts.size(); j++) { if (j == i) { @@ -386,7 +386,7 @@ public class Graph { continue loopi; } } - } + }*/ List> reachable = new ArrayList<>(); Set allReachable = new LinkedHashSet<>(); for (GraphPart p : parts) { @@ -410,7 +410,7 @@ public class Graph { continue; } visited.add(p); - int commonLevel = 1; + int commonLevel = 0; for (Set r : reachable) { if (r.contains(p)) { commonLevel++; @@ -426,6 +426,9 @@ public class Graph { //System.err.println("maxclevel = " + maxCommonLevel); //System.err.println("maxclevelpart = " + maxCommonLevelPart); + if (maxCommonLevel <= 1) { + return null; + } return maxCommonLevelPart; } @@ -440,7 +443,7 @@ public class Graph { for (int i = part.start; i <= part.end; i++) { GraphSourceItem src = code.get(i); if (src.isJump()) { - part = part.nextParts.get(0); + part = getNextParts(localData, part).get(0); if(st.isEmpty()){ startPart = part; } @@ -480,11 +483,11 @@ public class Graph { System.err.println("parts:"); for (GraphPart p : allParts) { System.err.print(p); - if (!p.nextParts.isEmpty()) { + if (!getNextParts(localData, p).isEmpty()) { System.err.print(", next: "); } boolean first = true; - for (GraphPart n : p.nextParts) { + for (GraphPart n : getNextParts(localData, p)) { if (!first) { System.err.print(","); } @@ -552,7 +555,7 @@ public class Graph { return new FinalProcessLocalData(loops); } - protected void beforePrintGraph(BaseLocalData localData, String path, Set allParts, List loops) { + protected void beforePrintGraph(BaseLocalData localData, String path, Set allParts, List loops) throws InterruptedException { } @@ -604,7 +607,7 @@ public class Graph { /**/ - //if (ref.nextParts) + //if (getNextParts(localData, ref)) private void getBackEdges(BaseLocalData localData, List loops) throws InterruptedException { clearLoops(loops); for (Loop el : loops) { @@ -843,7 +846,7 @@ public class Graph { } } - private void processIfs(List list) { + protected void processIfs(List list) { for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); if ((item instanceof LoopItem) && (item instanceof Block)) { @@ -1192,7 +1195,7 @@ public class Graph { //loopContinues.add(part); } - if (part.nextParts.size() == 2) { + if (part.nextParts.size() == 2 && !partIsSwitch(part)) { List nps;/* = new ArrayList<>(part.nextParts); for(int i=0;i 2) { + } else if (part.nextParts.size() > 2 || partIsSwitch(part)) { GraphPart next = getNextCommonPart(localData, part, loops); for (GraphPart p : part.nextParts) { @@ -1411,6 +1414,10 @@ public class Graph { } } + protected List getNextParts(BaseLocalData localData, GraphPart part) { + return part.nextParts; + } + protected List printGraph(List foundGotos, Map> partCodes, Map partCodePos, Set visited, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List loops, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); @@ -1428,7 +1435,7 @@ public class Graph { //try { if (debugPrintGraph) { - System.err.println("PART " + part + " nextsize:" + part.nextParts.size()); + System.err.println("PART " + part + " nextsize:" + getNextParts(localData, part).size()); } /*while (((part != null) && (part.getHeight() == 1)) && (code.size() > part.start) && (code.get(part.start).isJump())) { //Parts with only jump in it gets ignored @@ -1436,9 +1443,9 @@ public class Graph { if (part == stopPart) { return ret; } - GraphTargetItem lop = checkLoop(part.nextParts.get(0), stopPart, loops); + GraphTargetItem lop = checkLoop(getNextParts(localData, part).get(0), stopPart, loops); if (lop == null) { - part = part.nextParts.get(0); + part = getNextParts(localData, part).get(0); } else { break; } @@ -1584,8 +1591,11 @@ public class Graph { parts = ((GraphPartMulti) part).parts; } else { parts.add(part); - while (part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) { - part = part.nextParts.get(0); + while (getNextParts(localData, part).size() == 1 && getNextParts(localData, part).get(0).refs.size() == 1) { + if (stopPart.contains(getNextParts(localData, part).get(0))) { //it might be referenced with try statement + break; + } + part = getNextParts(localData, part).get(0); parts.add(part); } } @@ -1594,7 +1604,7 @@ public class Graph { int start = p.start; output.addAll(code.translatePart(p, localData, stack, start, end, staticOperation, path)); - if ((end >= code.size() - 1) && p.nextParts.isEmpty()) { + if ((end >= code.size() - 1) && getNextParts(localData, p).isEmpty()) { output.add(new ScriptEndItem()); } } @@ -1614,7 +1624,7 @@ public class Graph { //********************************END PART DECOMPILING if (parseNext) { - if (part.nextParts.size() > 2) { + if (getNextParts(localData, part).size() > 2 || partIsSwitch(part)) { GraphTargetItem originalSwitchedItem = stack.pop(); makeAllCommands(currentRet, stack); GraphTargetItem switchedItem = originalSwitchedItem; @@ -1742,11 +1752,11 @@ public class Graph { pos = 0; //This is tied to AS3 switch implementation which has nextparts switched from index 1. TODO: Make more universal - GraphPart defaultPart = hasExpr ? part.nextParts.get(1 + defaultBranch) : part.nextParts.get(0); + GraphPart defaultPart = hasExpr ? getNextParts(localData, part).get(1 + defaultBranch) : getNextParts(localData, part).get(0); List caseBodyParts = new ArrayList<>(); - for (int i = 1; i < part.nextParts.size(); i++) { + for (int i = 1; i < getNextParts(localData, part).size(); i++) { if (!hasExpr) { - if (part.nextParts.get(i) == defaultPart) { + if (getNextParts(localData, part).get(i) == defaultPart) { pos++; continue; } @@ -1763,7 +1773,7 @@ public class Graph { pos++; continue; } - caseBodyParts.add(part.nextParts.get(i)); + caseBodyParts.add(getNextParts(localData, part).get(i)); pos++; } Reference nextRef = new Reference<>(null); @@ -1783,7 +1793,7 @@ public class Graph { pos++; } //else GraphPart nextOnePart = null; - if (part.nextParts.size() == 2) { + if (getNextParts(localData, part).size() == 2 && !partIsSwitch(part)) { GraphTargetItem expr = stack.pop(); /*if (expr instanceof LogicalOpItem) { expr = ((LogicalOpItem) expr).invert(); @@ -1793,7 +1803,7 @@ public class Graph { if (nextOnePart == null) { List nps; - nps = part.nextParts; + nps = getNextParts(localData, part); boolean isEmpty = nps.get(0) == nps.get(1); GraphPart next = getCommonPart(localData, part, nps, loops); @@ -1896,11 +1906,16 @@ public class Graph { } } } //else - if (part.nextParts.size() == 1) { - nextOnePart = part.nextParts.get(0); + if (getNextParts(localData, part).size() == 1) { + nextOnePart = getNextParts(localData, part).get(0); } + + if (getNextParts(localData, part).isEmpty()) { + makeAllCommands(currentRet, stack); + } + if (nextOnePart != null) { - printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); + printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, getNextParts(localData, part).get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); } } @@ -2415,29 +2430,17 @@ public class Graph { if (p instanceof FunctionActionItem) { commands.add(clen, p); } else { - if (isExit) { + /*if (isExit) { //ASC2 leaves some function calls unpopped on stack before returning from a method commands.add(clen, p); - } else { + } else {*/ commands.add(clen, new PushItem(p)); - } + //} } } } } - protected void removeEdgeToFromList(List edges, GraphPart to) { - for (int i = edges.size() - 1; i >= 0; i--) { - if (edges.get(i).to.equals(to)) { - edges.remove(i); - } - } - while (isPartEmpty(to) && !to.nextParts.isEmpty()) { - to = to.nextParts.get(0); - removeEdgeToFromList(edges, to); - } - } - protected SwitchItem handleSwitch(GraphTargetItem switchedObject, GraphSourceItem switchStartItem, List foundGotos, Map> partCodes, Map partCodePos, Set allParts, TranslateStack stack, List stopPart, List loops, BaseLocalData localData, int staticOperation, String path, List caseValuesMap, GraphPart defaultPart, List caseBodyParts, Reference nextRef, Reference tiRef) throws InterruptedException { @@ -2570,16 +2573,12 @@ public class Graph { for (int i = 0; i < caseBodies.size(); i++) { List currentCaseCommands = new ArrayList<>(); - GraphPart nextCase = next; - if (next != null) { - if (i < caseBodies.size() - 1) { - if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops)) { - currentCaseCommands.add(new BreakItem(null, localData.lineStartInstruction, currentLoop.id)); - } else { - nextCase = caseBodies.get(i + 1); - } + if (i < caseBodies.size() - 1) { + if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops)) { + currentCaseCommands.add(new BreakItem(null, localData.lineStartInstruction, currentLoop.id)); } } + List stopPart2x = new ArrayList<>(stopPart); for (GraphPart b : caseBodies) { if (b != caseBodies.get(i)) { @@ -2638,4 +2637,8 @@ public class Graph { return new SwitchItem(null, switchStartItem, currentLoop, switchedObject, caseValuesMap, caseCommands, valuesMapping); } + + protected boolean partIsSwitch(GraphPart part) { + return false; + } } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java index 56947bf35..1fff7833f 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java @@ -2006,6 +2006,8 @@ public class ActionScript2Test extends ActionScript2TestBase { + "case \"C\":\r\n" + "trace(\"Ret 5\");\r\n" + "return 5;\r\n" + + "default:\r\n" + + "continue;\r\n" + "}\r\n" + "}\r\n" + "trace(\"Final\");\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java index aabbd2a7f..a2a007374 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -1415,6 +1415,7 @@ public class ActionScript3Test extends ActionScriptTestBase { + "{\r\n" + "trace(\"a\");\r\n" + "}\r\n" + + "continue;\r\n" + "}\r\n" + "catch(e:EOFError)\r\n" + "{\r\n" diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.air.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.air.swf new file mode 100644 index 000000000..217d90f13 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex.swf new file mode 100644 index 000000000..a327e86c8 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex_apache.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex_apache.swf new file mode 100644 index 000000000..cb2c2c142 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex_apache.swf differ diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.swftools.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.swftools.swf new file mode 100644 index 000000000..7c6ef7ed9 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.swftools.swf differ diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/expressInstall.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/expressInstall.swf new file mode 100644 index 000000000..86958bf3a Binary files /dev/null and b/libsrc/ffdec_lib/testdata/cross_compile/bin/expressInstall.swf differ diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/index.html b/libsrc/ffdec_lib/testdata/cross_compile/bin/index.html new file mode 100644 index 000000000..0096db17c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/bin/index.html @@ -0,0 +1,40 @@ + + + + + asc2 + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/js/swfobject.js b/libsrc/ffdec_lib/testdata/cross_compile/bin/js/swfobject.js new file mode 100644 index 000000000..8eafe9dd8 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/bin/js/swfobject.js @@ -0,0 +1,4 @@ +/* SWFObject v2.2 + is released under the MIT License +*/ +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab buildlog.%COMPILERKIND%.txt 2>&1 \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/build_debug.bat b/libsrc/ffdec_lib/testdata/cross_compile/build_debug.bat new file mode 100644 index 000000000..914701057 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/build_debug.bat @@ -0,0 +1,6 @@ +@echo off +call build_air_debug.bat +call build_flex_debug.bat +call build_flex_apache_debug.bat +call build_swftools_debug.bat +pause \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/build_flex_apache_debug.bat b/libsrc/ffdec_lib/testdata/cross_compile/build_flex_apache_debug.bat new file mode 100644 index 000000000..c7617f10c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/build_flex_apache_debug.bat @@ -0,0 +1,3 @@ +@echo off +set COMPILERKIND=flex_apache +call c:\flex_apache\bin\mxmlc.bat -warnings=false -debug=true -output bin/Main.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1 \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/build_flex_debug.bat b/libsrc/ffdec_lib/testdata/cross_compile/build_flex_debug.bat new file mode 100644 index 000000000..76c857b14 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/build_flex_debug.bat @@ -0,0 +1,3 @@ +@echo off +set COMPILERKIND=flex +c:\flex\bin\mxmlc.exe -warnings=false -debug=true -output bin/Main.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1 \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/build_stub.bat b/libsrc/ffdec_lib/testdata/cross_compile/build_stub.bat new file mode 100644 index 000000000..b153e4d13 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/build_stub.bat @@ -0,0 +1,19 @@ +@echo off +set ISDEBUG=false +if "%1" == "debug" goto blockset +goto block2 +:blockset +set ISDEBUG=true +:block2 +set COMPILERPATH=%2 +set COMPILERKIND=%3 +rem if not exist %COMPILERPATH% goto notex +%COMPILERPATH% -warnings=false -debug=%ISDEBUG% -output bin/Main.%COMPILERKIND%.swf src/Main.as > buildlog.%COMPILERKIND%.txt +if errorlevel==1 goto failed +goto end +:notex +echo Flex/AIR SDK not found. Download and unpack Flex/AIR SDK into some directory and add it to PATH variable +goto end +:failed +pause +:end diff --git a/libsrc/ffdec_lib/testdata/cross_compile/build_swftools_debug.bat b/libsrc/ffdec_lib/testdata/cross_compile/build_swftools_debug.bat new file mode 100644 index 000000000..2ccf2fb56 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/build_swftools_debug.bat @@ -0,0 +1,4 @@ +@echo off +set COMPILERKIND=swftools +cd src +c:\swftools\as3compile.exe Main.as -o ..\bin\Main.%COMPILERKIND%.swf 1> ../buildlog.%COMPILERKIND%.txt 2>&1 \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/buildlog.air.txt b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.air.txt new file mode 100644 index 000000000..758330a34 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.air.txt @@ -0,0 +1,3 @@ +Loading configuration: c:\air\frameworks\flex-config.xml + +2753 bytes written to C:\Dropbox\Programovani\JavaSE\FFDec\libsrc\ffdec_lib\testdata\cross_compile\bin\Main.air.swf in 0,459 seconds diff --git a/libsrc/ffdec_lib/testdata/cross_compile/buildlog.flex.txt b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.flex.txt new file mode 100644 index 000000000..f6277bc21 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.flex.txt @@ -0,0 +1,2 @@ +Loading configuration file C:\flex\frameworks\flex-config.xml +C:\Dropbox\Programovani\JavaSE\FFDec\libsrc\ffdec_lib\testdata\asc2\bin\Main.flex.swf (1083 bytes) diff --git a/libsrc/ffdec_lib/testdata/cross_compile/buildlog.flex_apache.txt b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.flex_apache.txt new file mode 100644 index 000000000..258b0a3de --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.flex_apache.txt @@ -0,0 +1,2 @@ +Loading configuration file C:\flex_apache\frameworks\flex-config.xml +C:\Dropbox\Programovani\JavaSE\FFDec\libsrc\ffdec_lib\testdata\cross_compile\bin\Main.flex_apache.swf (3617 bytes) diff --git a/libsrc/ffdec_lib/testdata/cross_compile/buildlog.swftools.txt b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.swftools.txt new file mode 100644 index 000000000..18f232b14 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/buildlog.swftools.txt @@ -0,0 +1,4 @@ +Stack mismatch at pos 61 +Should be: 1:1, is: 0:1 +Stack mismatch at pos 61 +Should be: 1:1, is: 0:1 diff --git a/libsrc/ffdec_lib/testdata/cross_compile/cross_compile.as3proj b/libsrc/ffdec_lib/testdata/cross_compile/cross_compile.as3proj new file mode 100644 index 000000000..19d21f07d --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/cross_compile.as3proj @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + build_air_debug.bat +build_flex_apache_debug.bat +build_swftools_debug.bat + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.old b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.old new file mode 100644 index 000000000..90790e11c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.old @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '31.01.2021' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\Dropbox\Programovani\JavaSE\FFDec\libsrc\ffdec_lib\testdata\cross_compile\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\Dropbox\Programovani\JavaSE\FFDec\libsrc\ffdec_lib\testdata\cross_compile\src\Main.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.xml b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.xml new file mode 100644 index 000000000..90790e11c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.xml @@ -0,0 +1,48 @@ + + + + + 25.0 + false + true + + + CONFIG::debug + true + + + CONFIG::release + false + + + CONFIG::timeStamp + '31.01.2021' + + + CONFIG::air + false + + + CONFIG::mobile + false + + + CONFIG::desktop + false + + true + + C:\Dropbox\Programovani\JavaSE\FFDec\libsrc\ffdec_lib\testdata\cross_compile\src + C:\Program Files (x86)\FlashDevelop\Library\AS3\classes + + + + C:\Dropbox\Programovani\JavaSE\FFDec\libsrc\ffdec_lib\testdata\cross_compile\src\Main.as + + #FFFFFF + 30 + + 800 + 600 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/Main.as b/libsrc/ffdec_lib/testdata/cross_compile/src/Main.as new file mode 100644 index 000000000..257c1c562 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/Main.as @@ -0,0 +1,34 @@ +package +{ + import flash.display.MovieClip; + import flash.events.Event; + import tests.*; + + /** + * ... + * @author JPEXS + */ + public class Main extends MovieClip + { + TestTryCatch; + TestTryCatchIfInTry; + TestTryCatchLoop; + TestTryCatchExceptionUsage + TestTryFinally; + TestTryFinallyDirectReturnInFinally; + TestTryFinallyLoop; + TestTryFinallyLoopInFinally; + TestTryFinallyMultipleCatch; + TestTryFinallyReturn; + TestTryFinallyReturnInFinally; + TestTryFinallyReturnNested; + TestTryFinallyReturnVoid; + + public function Main() + { + + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatch.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatch.as new file mode 100644 index 000000000..18490e38a --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatch.as @@ -0,0 +1,26 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryCatch + { + + public function run() : void + { + trace("before try"); + try + { + trace("in try"); + } + catch (e:Error) + { + trace("in catch"); + } + trace("after"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchExceptionUsage.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchExceptionUsage.as new file mode 100644 index 000000000..6a8c98a9d --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchExceptionUsage.as @@ -0,0 +1,26 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryCatchExceptionUsage + { + + public function run() : void + { + trace("before try"); + try + { + trace("in try"); + } + catch (e:Error) + { + trace("catched exception: "+e.message); + } + trace("after"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchIfInTry.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchIfInTry.as new file mode 100644 index 000000000..306e8a995 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchIfInTry.as @@ -0,0 +1,32 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryCatchIfInTry + { + + public function run() : void + { + var a:Boolean = true; + trace("before"); + try + { + if (a) + { + trace("ret"); + return; + } + trace("in try"); + } + catch (e:Error) + { + trace("in catch"); + } + trace("after"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchLoop.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchLoop.as new file mode 100644 index 000000000..1d7cad3af --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchLoop.as @@ -0,0 +1,39 @@ +package tests +{ + import flash.errors.EOFError; + + /** + * ... + * @author JPEXS + */ + public class TestTryCatchLoop + { + + public function run() : void + { + var j:* = undefined; + for (var i:* = 0; i < 100; i++) + { + try + { + for (j = 0; j < 20; j++) + { + trace("a"); + } + } + catch (e:EOFError) + { + continue; + } + catch (e:Error) + { + continue; + } + trace("after_try"); + } + trace("end"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinally.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinally.as new file mode 100644 index 000000000..33192f187 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinally.as @@ -0,0 +1,30 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinally + { + + public function run() : void + { + trace("before try"); + try + { + trace("in try"); + } + catch (e:Error) + { + trace("in catch"); + } + finally + { + trace("in finally"); + } + trace("after"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyDirectReturnInFinally.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyDirectReturnInFinally.as new file mode 100644 index 000000000..815c73f6c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyDirectReturnInFinally.as @@ -0,0 +1,34 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyDirectReturnInFinally + { + + public function run() : String + { + + var str:String = "xxx"; + try + { + } + catch (e:Error) + { + trace("error"); + } + finally + { + trace("hi "); + if (5 == 4) + { + return str; + } + return "hu" + str; + } + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyLoop.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyLoop.as new file mode 100644 index 000000000..06a3d7056 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyLoop.as @@ -0,0 +1,38 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyLoop + { + + public function run() : void + { + for (var i:int = 0; i < 10; i++) + { + trace("before try"); + try + { + trace("in try"); + if (i == 5) + { + trace("continue for"); + continue; + } + } + catch (e:Error) + { + trace("in catch"); + } + finally + { + trace("in finally"); + } + trace("after"); + } + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyLoopInFinally.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyLoopInFinally.as new file mode 100644 index 000000000..4c3342857 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyLoopInFinally.as @@ -0,0 +1,38 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyLoopInFinally + { + + public function run() : void + { + for (var i:int = 0; i < 10; i++) + { + trace("before try"); + try + { + trace("in try"); + } + catch (e:Error) + { + trace("in catch"); + } + finally + { + if (i == 5) + { + trace("continue for"); + continue; + } + trace("in finally"); + } + trace("after"); + } + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyMultipleCatch.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyMultipleCatch.as new file mode 100644 index 000000000..4cd44de0c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyMultipleCatch.as @@ -0,0 +1,35 @@ +package tests +{ + import flash.errors.EOFError; + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyMultipleCatch + { + + public function run() : void + { + trace("before try"); + try + { + trace("in try"); + } + catch (e:Error) + { + trace("in catch Error"); + } + catch (e:EOFError) + { + trace("in catch EOFError"); + } + finally + { + trace("in finally"); + } + trace("after"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturn.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturn.as new file mode 100644 index 000000000..4c75f1b84 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturn.as @@ -0,0 +1,42 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyReturn + { + + public function run() : String + { + trace("before try"); + try + { + trace("in try"); + var a:int = 5; + if (a > 4) + { + return "RET"; + } + trace("between"); + if (a < 3) + { + return "RE2"; + } + trace("in try2"); + } + catch (e:Error) + { + trace("in catch"); + } + finally + { + trace("in finally"); + } + trace("after"); + return "RETFINAL"; + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnInFinally.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnInFinally.as new file mode 100644 index 000000000..045d50786 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnInFinally.as @@ -0,0 +1,44 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyReturnInFinally + { + + public function run() : String + { + trace("before try"); + try + { + trace("in try"); + var a:int = 5; + if (a > 4) + { + return "RET"; + } + } + catch (e:Error) + { + trace("in catch"); + } + finally + { + trace("in finally"); + if (a > 6){ + return "FINRET1"; + } + trace("xx"); + if (a > 5){ + return "FINRET2"; + } + trace("nofinret"); + } + trace("after"); + return "RETEXIT"; + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnNested.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnNested.as new file mode 100644 index 000000000..dc9d6d4da --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnNested.as @@ -0,0 +1,43 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyReturnNested + { + + public function run() : String + { + try + { + trace("before try2"); + try + { + trace("in try2"); + var a:int = 5; + if (a > 4) + { + return "RET"; + } + } + catch (e:Error) + { + trace("in catch"); + } + finally + { + trace("in finally2"); + } + trace("after"); + } + finally + { + trace("in finally1"); + } + return "RETFINAL"; + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnVoid.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnVoid.as new file mode 100644 index 000000000..c18fb4b7e --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryFinallyReturnVoid.as @@ -0,0 +1,36 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryFinallyReturnVoid + { + + public function run() : void + { + trace("before try"); + try + { + trace("in try"); + var a:int = 5; + if (a > 4) + { + return; + } + trace("in try2"); + } + catch (e:Error) + { + trace("in catch"); + } + finally + { + trace("in finally"); + } + trace("after"); + } + + } + +} \ No newline at end of file diff --git a/nbproject/project.xml b/nbproject/project.xml index 405584824..dbfce67c3 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -249,6 +249,10 @@ libsrc/ffdec_lib/lexers + + + libsrc/ffdec_lib/graphviz + build.xml