diff --git a/CHANGELOG.md b/CHANGELOG.md index 686c22401..77e3b09db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ All notable changes to this project will be documented in this file. - Asking more than once for the same imported (ImportAssets) URL - ABC Explorer problems when index out of bounds (Usually in obfuscated code) - Go to document class for classes with obfuscated name +- [#2270] AS3 decompilation - unnnecessary local registers assignments as part + of expressions when using optimization like `dup, setlocal N` + instead of `setlocal N, getlocal N` ## [21.0.1] - 2024-08-08 ### Added @@ -3487,6 +3490,7 @@ Major version of SWF to XML export changed to 2. [alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 [alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 [#2269]: https://www.free-decompiler.com/flash/issues/2269 +[#2270]: https://www.free-decompiler.com/flash/issues/2270 [#2221]: https://www.free-decompiler.com/flash/issues/2221 [#2267]: https://www.free-decompiler.com/flash/issues/2267 [#2268]: https://www.free-decompiler.com/flash/issues/2268 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 7cfa5410d..2f788fd9a 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 @@ -20,6 +20,7 @@ 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.avm2.model.SetLocalAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing; import com.jpexs.decompiler.flash.abc.types.ABCException; import com.jpexs.decompiler.flash.abc.types.InstanceInfo; @@ -30,6 +31,7 @@ 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 com.jpexs.helpers.LinkedIdentityHashSet; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -221,6 +223,12 @@ public class AVM2LocalData extends BaseLocalData { */ public Set seenMethods = new HashSet<>(); + + /** + * Bottom set locals + */ + public LinkedIdentityHashSet bottomSetLocals = new LinkedIdentityHashSet<>(); + /** * Constructs a new AVM2LocalData */ @@ -287,6 +295,7 @@ public class AVM2LocalData extends BaseLocalData { pushDefaultPart = localData.pushDefaultPart; finallyKinds = localData.finallyKinds; seenMethods = localData.seenMethods; + bottomSetLocals = localData.bottomSetLocals; } /** 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 92f20ed7a..26d4448d2 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 @@ -289,6 +289,7 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import com.jpexs.decompiler.graph.AbstractGraphTargetRecursiveVisitor; import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor; import com.jpexs.decompiler.graph.Block; import com.jpexs.decompiler.graph.DottedChain; @@ -302,6 +303,7 @@ import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.decompiler.graph.TypeItem; import com.jpexs.decompiler.graph.model.ScriptEndItem; import com.jpexs.helpers.Helper; +import com.jpexs.helpers.LinkedIdentityHashSet; import com.jpexs.helpers.Reference; import com.jpexs.helpers.ReflectionTools; import com.jpexs.helpers.stat.Statistics; @@ -320,6 +322,7 @@ import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; +import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; @@ -1842,11 +1845,12 @@ public class AVM2Code implements Cloneable { * @param fullyQualifiedNames Fully qualified names * @param visited Visited * @param localRegAssigmentIps Local register assignment IPs + * @param bottomStackSetLocals Set locals on bottom of the stack * @return Convert output * @throws ConvertException On convert error * @throws InterruptedException On interrupt */ - public ConvertOutput toSourceOutput(Set switchParts, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, boolean thisHasDefaultToPrimitive, Reference lineStartItem, String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, ABC abc, MethodBody body, int start, int end, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps) throws ConvertException, InterruptedException { + public ConvertOutput toSourceOutput(Set switchParts, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, boolean thisHasDefaultToPrimitive, Reference lineStartItem, String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, ABC abc, MethodBody body, int start, int end, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps, LinkedIdentityHashSet bottomStackSetLocals) throws ConvertException, InterruptedException { boolean debugMode = DEBUG_MODE; if (debugMode) { System.err.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); @@ -1901,26 +1905,13 @@ public class AVM2Code implements Cloneable { } } } - /*if ((ins.definition instanceof SetLocalTypeIns) && (ip + 1 <= end)) { // set_local_x,get_local_x.. no other local_x get - - AVM2Instruction insAfter = code.get(ip + 1); - Set usages = setLocalPosToGetLocalPos.containsKey(ip) ? setLocalPosToGetLocalPos.get(ip) : new HashSet<>(); - - if (!(stack.peek().getNotCoercedNoDup() instanceof DuplicateItem) && !AVM2Item.mustStayIntact2(stack.peek()) && usages.size() == 1 && (usages.iterator().next().equals(ip + 1)) && (insAfter.definition instanceof GetLocalTypeIns) && (((GetLocalTypeIns) insAfter.definition).getRegisterId(insAfter) == ((SetLocalTypeIns) ins.definition).getRegisterId(ins))) { - ip += 2; - continue iploop; - } else { - ins.definition.translate(setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); - ip++; - continue iploop; - } - } else*/ + /* if (ins.definition instanceof DupIns) { int nextPos; do { AVM2Instruction insAfter = ip + 1 < code.size() ? code.get(ip + 1) : null; if (insAfter == null) { - ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); ip++; break; } @@ -1942,14 +1933,16 @@ public class AVM2Code implements Cloneable { //stack.add("(" + stack.pop() + ")||"); isAnd = false; } else { - ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); ip++; break; //throw new ConvertException("Unknown pattern after DUP:" + insComparsion.toString()); } } while (ins.definition instanceof DupIns); - } else if ((ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ThrowIns)) { - ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive); + } else + */ + if ((ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ThrowIns)) { + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); //ip = end + 1; break; } else if (ins.definition instanceof NewFunctionIns) { @@ -1985,13 +1978,18 @@ public class AVM2Code implements Cloneable { } } // What to do when hasDup is false? - ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); NewFunctionAVM2Item nft = (NewFunctionAVM2Item) stack.peek(); nft.functionName = functionName; ip++; } else { - try { - ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive); + try { + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); + + + if (stack.size() == 1 && (stack.peek() instanceof SetLocalAVM2Item)) { + bottomStackSetLocals.add((SetLocalAVM2Item) stack.peek()); + } } catch (RuntimeException re) { /*String last=""; int len=5; @@ -2281,9 +2279,9 @@ public class AVM2Code implements Cloneable { break; }*/ Reference hasPrevReference = new Reference<>(false); - value.visitRecursivelyNoBlock(new AbstractGraphTargetVisitor() { + value.visitRecursivelyNoBlock(new AbstractGraphTargetRecursiveVisitor() { @Override - public void visit(GraphTargetItem subItem) { + public void visit(GraphTargetItem subItem, Stack parentStack) { Multiname propertyMultiName; String propertyName; if (subItem instanceof GetPropertyAVM2Item) { @@ -2343,9 +2341,9 @@ public class AVM2Code implements Cloneable { GraphTargetItem currentItem = items.get(i); List itemsOnLine = new ArrayList<>(); itemsOnLine.add(currentItem); - currentItem.visitRecursivelyNoBlock(new AbstractGraphTargetVisitor() { + currentItem.visitRecursivelyNoBlock(new AbstractGraphTargetRecursiveVisitor() { @Override - public void visit(GraphTargetItem item) { + public void visit(GraphTargetItem item, Stack parentStack) { itemsOnLine.add(item); } }); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2FinalProcessLocalData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2FinalProcessLocalData.java index 5c1396f07..c44eb1d31 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2FinalProcessLocalData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2FinalProcessLocalData.java @@ -17,7 +17,9 @@ package com.jpexs.decompiler.flash.abc.avm2; import com.jpexs.decompiler.flash.FinalProcessLocalData; +import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; import com.jpexs.decompiler.graph.Loop; +import com.jpexs.helpers.LinkedIdentityHashSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -40,6 +42,11 @@ public class AVM2FinalProcessLocalData extends FinalProcessLocalData { * Set local position to get local position mapping. */ public Map> setLocalPosToGetLocalPos = new HashMap<>(); + + /** + * Bottom set locals + */ + public LinkedIdentityHashSet bottomSetLocals = new LinkedIdentityHashSet<>(); /** * Constructs AVM2 final process local data. @@ -49,11 +56,13 @@ public class AVM2FinalProcessLocalData extends FinalProcessLocalData { * mapping * @param setLocalPosToGetLocalPos Set local position to get local position * mapping + * @param bottomSetLocals Bottom set locals */ - public AVM2FinalProcessLocalData(List loops, HashMap localRegNames, Map> setLocalPosToGetLocalPos) { + public AVM2FinalProcessLocalData(List loops, HashMap localRegNames, Map> setLocalPosToGetLocalPos, LinkedIdentityHashSet bottomSetLocals) { super(loops); this.localRegNames = localRegNames; this.setLocalPosToGetLocalPos = setLocalPosToGetLocalPos; + this.bottomSetLocals = bottomSetLocals; } /** 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 611771194..fd506d059 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 @@ -48,7 +48,9 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.ConstructAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ConvertAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.FilteredCheckAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.FindPropertyAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; @@ -81,6 +83,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictEqAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing; import com.jpexs.decompiler.flash.abc.types.ABCException; import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.graph.AbstractGraphTargetRecursiveVisitor; import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.Graph; @@ -122,6 +125,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.Stack; import java.util.TreeMap; import java.util.TreeSet; import java.util.logging.Level; @@ -2641,6 +2645,51 @@ public class AVM2Graph extends Graph { } } + + AVM2FinalProcessLocalData adata = (AVM2FinalProcessLocalData) localData; + //if(false) + if (!adata.bottomSetLocals.isEmpty()) { + for (int i = 0; i < list.size(); i++) { + + if (list.get(i) instanceof LoopItem) { + continue; + } + if (list.get(i) instanceof WithEndAVM2Item) { + continue; + } + + Reference foundSetLoc = new Reference<>(null); + list.get(i).visitRecursivelyNoBlock(new AbstractGraphTargetRecursiveVisitor() { + @Override + public void visit(GraphTargetItem item, Stack parentStack) { + + if (item instanceof SetLocalAVM2Item) { + if (adata.bottomSetLocals.contains((SetLocalAVM2Item) item)) { + int s = parentStack.size() - 1; + if (parentStack.get(s) instanceof CoerceAVM2Item) { + s--; + } else if (parentStack.get(s) instanceof ConvertAVM2Item) { + s--; + } + if (s >= 0) { + GraphTargetItem parent = parentStack.get(s); + if (!(parent instanceof SetTypeAVM2Item)) { //not a chained assignment + foundSetLoc.setVal((SetLocalAVM2Item) item); + } + } + } + } + } + }); + if (foundSetLoc.getVal() != null) { + SetLocalAVM2Item setLoc = foundSetLoc.getVal(); + list.add(i, setLoc.clone()); + setLoc.hideValue = true; + i++; + } + } + } + /* convert this situation: @@ -2656,13 +2705,13 @@ public class AVM2Graph extends Graph { It's TestSwapAssignment assembled test case. */ - for (int i = 0; i < list.size(); i++) { + /*for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); Map> setRegisters = new HashMap<>(); - item.visitRecursivelyNoBlock(new AbstractGraphTargetVisitor() { + item.visitRecursivelyNoBlock(new AbstractGraphTargetRecursiveVisitor() { @Override - public void visit(GraphTargetItem item) { + public void visit(GraphTargetItem item, GraphTargetItem parent) { if (item instanceof SetLocalAVM2Item) { SetLocalAVM2Item setLoc = (SetLocalAVM2Item) item; if (setLoc.causedByDup) { @@ -2770,15 +2819,15 @@ public class AVM2Graph extends Graph { }); i = newI.getVal(); } - + */ //Handle for loops at the end: super.finalProcess(list, level, localData, path); } @Override protected FinalProcessLocalData getFinalData(BaseLocalData localData, List loops, List throwStates) { - FinalProcessLocalData finalProcess = new AVM2FinalProcessLocalData(loops, ((AVM2LocalData) localData).localRegNames, ((AVM2LocalData) localData).setLocalPosToGetLocalPos); - finalProcess.registerUsage = ((AVM2LocalData) localData).setLocalPosToGetLocalPos; + FinalProcessLocalData finalProcess = new AVM2FinalProcessLocalData(loops, ((AVM2LocalData) localData).localRegNames, ((AVM2LocalData) localData).setLocalPosToGetLocalPos, ((AVM2LocalData) localData).bottomSetLocals); + finalProcess.registerUsage = ((AVM2LocalData) localData).setLocalPosToGetLocalPos; return finalProcess; } @@ -3081,4 +3130,37 @@ public class AVM2Graph extends Graph { protected SecondPassData prepareSecondPass(List list) { return new SecondPassData(); } + + @Override + protected GraphTargetItem getIfExpression(BaseLocalData localData, TranslateStack stack, List output) { + GraphTargetItem result = stack.pop(); + + /* + Fixes this case: + + var i:int; + if ((i = 5) > 2 && i < 10) { + ... + } + + when instead + setlocal x + getlocal x + + there is: + dup + setlocal x + + */ + + if (stack.getMark("firstSetLocal") != null) { + SetLocalAVM2Item setLocal = (SetLocalAVM2Item) stack.getMark("firstSetLocal"); + if (setLocal.directlyCausedByDup) { + output.add(setLocal.clone()); + setLocal.hideValue = true; + } + } + + return result; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java index f5f790aa7..569e3ec8a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.abc.AVM2LocalData; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.ConvertOutput; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.Graph; @@ -30,6 +31,7 @@ import com.jpexs.decompiler.graph.GraphSource; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; +import com.jpexs.helpers.LinkedIdentityHashSet; import com.jpexs.helpers.Reference; import java.util.ArrayList; import java.util.HashMap; @@ -203,7 +205,7 @@ public class AVM2GraphSource extends GraphSource { public List translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { List ret = new ArrayList<>(); Reference lineStartItem = new Reference<>(localData.lineStartInstruction); - ConvertOutput co = code.toSourceOutput(localData.allSwitchParts, ((AVM2LocalData) localData).callStack, ((AVM2LocalData) localData).abcIndex, ((AVM2LocalData) localData).setLocalPosToGetLocalPos, ((AVM2LocalData) localData).thisHasDefaultToPrimitive, lineStartItem, path, part, false, isStatic, scriptIndex, classIndex, localRegs, stack, ((AVM2LocalData) localData).scopeStack, ((AVM2LocalData) localData).localScopeStack, abc, body, start, end, localRegNames, ((AVM2LocalData) localData).localRegTypes, fullyQualifiedNames, new boolean[size()], localRegAssigmentIps); + ConvertOutput co = code.toSourceOutput(localData.allSwitchParts, ((AVM2LocalData) localData).callStack, ((AVM2LocalData) localData).abcIndex, ((AVM2LocalData) localData).setLocalPosToGetLocalPos, ((AVM2LocalData) localData).thisHasDefaultToPrimitive, lineStartItem, path, part, false, isStatic, scriptIndex, classIndex, localRegs, stack, ((AVM2LocalData) localData).scopeStack, ((AVM2LocalData) localData).localScopeStack, abc, body, start, end, localRegNames, ((AVM2LocalData) localData).localRegTypes, fullyQualifiedNames, new boolean[size()], localRegAssigmentIps, ((AVM2LocalData) localData).bottomSetLocals); localData.lineStartInstruction = lineStartItem.getVal(); ret.addAll(co.output); return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java index 88484e064..3f9b7c320 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java @@ -62,6 +62,7 @@ import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.decompiler.graph.model.DuplicateItem; +import com.jpexs.helpers.LinkedIdentityHashSet; import com.jpexs.helpers.Reference; import java.io.Serializable; import java.util.ArrayList; @@ -290,9 +291,10 @@ public abstract class InstructionDefinition implements Serializable { * @param ip IP * @param code AVM2 code * @param thisHasDefaultToPrimitive This has default to primitive + * @param bottomSetLocals Bottom set locals * @throws InterruptedException On interrupt */ - public void translate(Set switchParts, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, Reference lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, AVM2Instruction ins, List output, MethodBody body, ABC abc, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, AVM2Code code, boolean thisHasDefaultToPrimitive) throws InterruptedException { + public void translate(Set switchParts, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, Reference lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, AVM2Instruction ins, List output, MethodBody body, ABC abc, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, AVM2Code code, boolean thisHasDefaultToPrimitive, LinkedIdentityHashSet bottomSetLocals) throws InterruptedException { AVM2LocalData localData = new AVM2LocalData(); localData.allSwitchParts = switchParts; localData.isStatic = isStatic; @@ -314,6 +316,7 @@ public abstract class InstructionDefinition implements Serializable { localData.code = code; localData.thisHasDefaultToPrimitive = thisHasDefaultToPrimitive; localData.setLocalPosToGetLocalPos = setLocalPosToGetLocalPos; + localData.bottomSetLocals = bottomSetLocals; translate(localData, stack, ins, output, path); lineStartItem.setVal(localData.lineStartInstruction); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java index fc0dd9cf8..8893bb86e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/localregs/SetLocalTypeIns.java @@ -36,6 +36,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.decompiler.graph.model.CompoundableBinaryOp; +import com.jpexs.decompiler.graph.model.DuplicateItem; import com.jpexs.decompiler.graph.model.PopItem; import java.util.List; @@ -145,6 +146,10 @@ public abstract class SetLocalTypeIns extends InstructionDefinition implements S } } } + + if (value instanceof DuplicateItem) { + result.directlyCausedByDup = true; + } SetTypeIns.handleResult(value, stack, output, localData, result, regId, value.returnType()); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/SetLocalAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/SetLocalAVM2Item.java index ebec39c65..da3f628db 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/SetLocalAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/SetLocalAVM2Item.java @@ -72,6 +72,11 @@ public class SetLocalAVM2Item extends AVM2Item implements SetTypeAVM2Item, Assig * Caused by duplicate */ public boolean causedByDup = false; + + /** + * Directly caused by duplicate + */ + public boolean directlyCausedByDup = false; @Override public DeclarationAVM2Item getDeclaration() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/AbstractGraphTargetRecursiveVisitor.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/AbstractGraphTargetRecursiveVisitor.java new file mode 100644 index 000000000..479d380a9 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/AbstractGraphTargetRecursiveVisitor.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010-2024 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.graph; + +import java.util.Collection; +import java.util.Stack; + +/** + * Abstract graph target recursive visitor. + * + * @author JPEXS + */ +public abstract class AbstractGraphTargetRecursiveVisitor implements GraphTargetRecursiveVisitorInterface { + + /** + * Constructs new AbstractGraphTargetVisitor + */ + public AbstractGraphTargetRecursiveVisitor() { + } + + @Override + public abstract void visit(GraphTargetItem item, Stack parentStack); + + @Override + public final void visitAll(Collection items, Stack parentStack) { + for (GraphTargetItem item : items) { + visit(item, parentStack); + } + } +} 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 9c17751b8..a0532d714 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -2715,6 +2715,18 @@ public class Graph { return printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, stopPartKind, loops, throwStates, null, staticOperation, path, 0); } + /** + * Gets if expression from stack. + * Can be overriden for custom handling + * @param localData Local data + * @param stack Stack + * @param output Output + * @return Expression + */ + protected GraphTargetItem getIfExpression(BaseLocalData localData, TranslateStack stack, List output) { + return stack.pop(); + } + /** * Walks graph parts and converts them to target items. * @@ -3254,7 +3266,7 @@ public class Graph { } //else GraphPart nextOnePart = null; if (getNextParts(localData, part).size() == 2 && !partIsSwitch(part)) { - GraphTargetItem expr = stack.pop(); + GraphTargetItem expr = getIfExpression(localData, stack, currentRet); if (nextOnePart == null) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java index 20318096b..73764e1a7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java @@ -48,6 +48,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.Stack; /** * Graph target item - an item in high level representation of the code. @@ -963,15 +964,19 @@ public abstract class GraphTargetItem implements Serializable, Cloneable { * * @param visitor Visitor */ - public final void visitRecursivelyNoBlock(GraphTargetVisitorInterface visitor) { + public final void visitRecursivelyNoBlock(GraphTargetRecursiveVisitorInterface visitor) { Set visitedItems = new HashSet<>(); + Stack parentStack = new Stack<>(); + parentStack.add(this); visitNoBlock(new AbstractGraphTargetVisitor() { @Override public void visit(GraphTargetItem item) { if (item != null && !visitedItems.contains(item)) { visitedItems.add(item); - visitor.visit(item); + visitor.visit(item, parentStack); + parentStack.push(item); item.visitNoBlock(this); + parentStack.pop(); } } }); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetRecursiveVisitorInterface.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetRecursiveVisitorInterface.java new file mode 100644 index 000000000..a64fbbf0c --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetRecursiveVisitorInterface.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010-2024 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.graph; + +import java.util.Collection; +import java.util.Stack; + +/** + * Recursive graph target visitor interface. + * + * @author JPEXS + */ +public interface GraphTargetRecursiveVisitorInterface { + + /** + * Visits a graph target item. + * + * @param item Graph target item + * @param parentStack Stack of parents + */ + public void visit(GraphTargetItem item, Stack parentStack); + + /** + * Visits all graph target items. + * + * @param items Collection of graph target items + * @param parentStack Stack of parents + */ + public void visitAll(Collection items, Stack parentStack); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetVisitorInterface.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetVisitorInterface.java index f32669e22..559f999fb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetVisitorInterface.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetVisitorInterface.java @@ -19,7 +19,7 @@ package com.jpexs.decompiler.graph; import java.util.Collection; /** - * Graph source visitor interface. + * Graph target visitor interface. * * @author JPEXS */ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java index 9b0bc1b8d..9c6b0d8a4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TranslateStack.java @@ -17,6 +17,10 @@ package com.jpexs.decompiler.graph; import com.jpexs.decompiler.graph.model.PopItem; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; @@ -38,6 +42,27 @@ public class TranslateStack extends Stack { */ private final String path; + private Map marks = new HashMap<>(); + + + /** + * Sets mark. + * @param name Name + * @param value Value + */ + public void setMark(String name, GraphTargetItem value) { + marks.put(name, value); + } + + /** + * Gets mark. + * @param name Name + * @return Value + */ + public GraphTargetItem getMark(String name) { + return marks.get(name); + } + /** * Simplifies all items in the stack. */ diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java index 919a3868c..484a45ddf 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java @@ -100,7 +100,8 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT public void testDupAssignment() { decompileMethod("assembled", "testDupAssignment", "var _loc1_:int = 0;\r\n" + "var _loc2_:int = 10;\r\n" - + "if(_loc1_ = _loc2_)\r\n" + + "_loc1_ = _loc2_;\r\n" + + "if(_loc1_)\r\n" + "{\r\n" + "trace(_loc2_);\r\n" + "}\r\n", @@ -154,6 +155,16 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT false); } + @Test + public void testLocalRegIf() { + decompileMethod("assembled", "testLocalRegIf", "var _loc1_:int = 8;\r\n" + + "if(_loc1_ > 5 && _loc1_ < 10)\r\n" + + "{\r\n" + + "trace(\"I\");\r\n" + + "}\r\n", + false); + } + @Test public void testMutatingSwitch() { decompileMethod("assembled", "testMutatingSwitch", "switch(this.k)\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java index 1253e43c3..5ebfefddc 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java @@ -280,8 +280,8 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile + "var dict:Dictionary = new Dictionary();\r\n" + "s = \"a\";\r\n" + "i = int(s);\r\n" - + "var j:int;\r\n" - + "s = String(j = n);\r\n" + + "var j:int = n;\r\n" + + "s = String(j);\r\n" + "s = ns;\r\n" + "s = String(i == 4 ? \"\" : i);\r\n" + "s = i == 4 ? \"\" : String(i);\r\n" @@ -296,8 +296,8 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile + "2:\"C\"\r\n" + "};\r\n" + "i = int(s.charAt(10));\r\n" - + "var v:Vector.;\r\n" - + "(v = new Vector.()).push(\"A\");\r\n" + + "var v:Vector. = new Vector.();\r\n" + + "v.push(\"A\");\r\n" + "v.push(\"B\");\r\n" + "i = int(v[0]);\r\n" + "s = v[1];\r\n" @@ -321,11 +321,11 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile + "trace(\"c\");\r\n" + "i = int(x.item[i].@id);\r\n" + "dict[String(x.item[i].@id)] = \"Hello\";\r\n" - + "var lc:LocalClass;\r\n" - + "i = (lc = new LocalClass()).attr;\r\n" + + "var lc:LocalClass = new LocalClass();\r\n" + + "i = lc.attr;\r\n" + "s = String(lc.attr);\r\n" - + "var f:Function;\r\n" - + "if(Boolean(f = this.f))\r\n" + + "var f:Function = this.f;\r\n" + + "if(Boolean(f))\r\n" + "{\r\n" + "trace(\"OK\");\r\n" + "}\r\n" @@ -537,7 +537,8 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile decompileMethod("classic_air", "testExpressions", "var arr:Array = null;\r\n" + "var i:int = 5;\r\n" + "var j:int = 5;\r\n" - + "if((i = i /= 2) == 1 || i == 2)\r\n" + + "i = i /= 2;\r\n" + + "if(i == 1 || i == 2)\r\n" + "{\r\n" + "arguments.concat(i);\r\n" + "}\r\n" @@ -920,7 +921,8 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile + "var b:int = 6;\r\n" + "for(i = 0; i < len; k = myXML.book.(@isbn == \"12345\"))\r\n" + "{\r\n" - + "if((c = 1) == 2)\r\n" + + "c = 1;\r\n" + + "if(c == 2)\r\n" + "{\r\n" + "trace(\"A\");\r\n" + "}\r\n" @@ -1667,6 +1669,20 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile false); } + @Test + public void testOptimization() { + decompileMethod("classic_air", "testOptimization", "var f:int = 0;\r\n" + + "var g:* = 0;\r\n" + + "var h:int = 0;\r\n" + + "var a:int = 1;\r\n" + + "var b:int = 2;\r\n" + + "var c:int = 3;\r\n" + + "var d:int = 4;\r\n" + + "var e:int = d + 5;\r\n" + + "var i:int = h = g = f;\r\n", + false); + } + @Test public void testParamNames() { decompileMethod("classic_air", "testParamNames", "return firstp + secondp + thirdp;\r\n", @@ -2327,12 +2343,12 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile @Test public void testXml() { decompileMethod("classic_air", "testXml", "var name:String = \"ahoj\";\r\n" - + "var myXML:XML;\r\n" - + "var k:* = (myXML = \r\n" + + "var myXML:XML = \r\n" + "\r\n" + "{name}\r\n" + "\r\n" - + ").@id;\r\n" + + ";\r\n" + + "var k:* = myXML.@id;\r\n" + "var all:String = myXML.@*.toXMLString();\r\n" + "k = myXML.book;\r\n" + "k = myXML.book.(@isbn == \"12345\");\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java index 646e40a5a..d36ba7a28 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java @@ -539,7 +539,8 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes decompileMethod("classic", "testExpressions", "var arr:Array = null;\r\n" + "var i:int = 5;\r\n" + "var j:int = 5;\r\n" - + "if((i = i = i / 2) == 1 || i == 2)\r\n" + + "i = i = i / 2;\r\n" + + "if(i == 1 || i == 2)\r\n" + "{\r\n" + "arguments.concat(i);\r\n" + "}\r\n" @@ -1654,6 +1655,20 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes false); } + @Test + public void testOptimization() { + decompileMethod("classic", "testOptimization", "var f:int = 0;\r\n" + + "var g:int = 0;\r\n" + + "var h:int = 0;\r\n" + + "var a:int = 1;\r\n" + + "var b:int = 2;\r\n" + + "var c:int = 3;\r\n" + + "var d:int = 4;\r\n" + + "var e:int = d + 5;\r\n" + + "var i:int = h = g = f;\r\n", + false); + } + @Test public void testParamNames() { decompileMethod("classic", "testParamNames", "return firstp + secondp + thirdp;\r\n", diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java index 7416a5465..2722d7350 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java @@ -522,8 +522,8 @@ public class ActionScript3CrossCompileSwfToolsDecompileTest extends ActionScript public void testTryCatchWith() { decompileMethod("swftools", "testTryCatchWith", "var _loc1_:* = new MyTest();\r\n" + "trace(\"before with\");\r\n" - + "var _loc2_:*;\r\n" - + "with(_loc2_ = _loc1_)\r\n" + + "var _loc2_:* = _loc1_;\r\n" + + "with(_loc2_)\r\n" + "{\r\n" + "trace(\"before try\");\r\n" + "try\r\n" diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc index 486f97e2a..b39578ba0 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc and b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc differ diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm index 5c4ff8069..6cbcdf71a 100644 --- a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm @@ -34,5 +34,6 @@ program #include "tests/TestMutatingSwitch.script.asasm" #include "tests/TestSwitchMostCommon.script.asasm" #include "tests/TestXmlStar.script.asasm" + #include "tests/TestLocalRegIf.script.asasm" ; place to add next end ; program diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestLocalRegIf.class.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestLocalRegIf.class.asasm new file mode 100644 index 000000000..e7b64a867 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestLocalRegIf.class.asasm @@ -0,0 +1,80 @@ +class + refid "tests:TestLocalRegIf" + instance QName(PackageNamespace("tests"), "TestLocalRegIf") + extends QName(PackageNamespace(""), "Object") + flag SEALED + flag PROTECTEDNS + protectedns ProtectedNamespace("tests:TestLocalRegIf") + iinit + refid "tests:TestLocalRegIf/instance/init" + body + maxstack 1 + localcount 1 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + + getlocal0 + constructsuper 0 + + returnvoid + end ; code + end ; body + end ; method + trait method QName(PackageNamespace(""), "run") + method + refid "tests:TestLocalRegIf/instance/run" + returns QName(PackageNamespace(""), "void") + body + maxstack 2 + localcount 7 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + pushbyte 8 + dup + setlocal1 + pushbyte 5 + greaterthan + dup + iffalse ofs0020 + pop + getlocal1 + pushbyte 10 + lessthan + ofs0020: + iffalse ofs0030 + getlex QName(PackageNamespace(""),"trace") + getglobalscope + pushstring "I" + call 1 + pop + ofs0030: + returnvoid + + returnvoid + end ; code + end ; body + end ; method + end ; trait + end ; instance + cinit + refid "tests:TestLocalRegIf/class/init" + body + maxstack 1 + localcount 1 + initscopedepth 3 + maxscopedepth 4 + code + getlocal0 + pushscope + + returnvoid + end ; code + end ; body + end ; method +end ; class diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestLocalRegIf.script.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestLocalRegIf.script.asasm new file mode 100644 index 000000000..002d46d64 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestLocalRegIf.script.asasm @@ -0,0 +1,29 @@ +script + sinit + refid "tests:TestLocalRegIf/init" + body + maxstack 2 + localcount 1 + initscopedepth 1 + maxscopedepth 3 + code + getlocal0 + pushscope + + findpropstrict Multiname("TestLocalRegIf", [PackageNamespace("tests")]) + getlex QName(PackageNamespace(""), "Object") + pushscope + + getlex Multiname("Object", [PrivateNamespace(null, "tests:TestLocalRegIf"), PackageNamespace(""), PackageNamespace("tests"), PackageInternalNs("tests"), Namespace("http://adobe.com/AS3/2006/builtin")]) + newclass "tests:TestLocalRegIf" + popscope + initproperty QName(PackageNamespace("tests"), "TestLocalRegIf") + + returnvoid + end ; code + end ; body + end ; method + trait class QName(PackageNamespace("tests"), "TestLocalRegIf") + #include "TestLocalRegIf.class.asasm" + end ; trait +end ; script diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf index f4f0ef099..a98b47fe1 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf and b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf index ed73ce35a..831fbc68a 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf index ca08b6cb4..d6e060c7c 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/build_air_debug.bat b/libsrc/ffdec_lib/testdata/as3_new/build_air_debug.bat index 96921eac3..a8d3c50ed 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/build_air_debug.bat +++ b/libsrc/ffdec_lib/testdata/as3_new/build_air_debug.bat @@ -2,4 +2,6 @@ set COMPILERKIND=air set SWFNAME=as3_new call c:\air\bin\mxmlc.bat -debug=true -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1 -rem -warnings=false \ No newline at end of file +rem set COMPILERKIND=air.optimize +rem call c:\air\bin\mxmlc.bat -compiler.optimize -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1 +rem -warnings=false diff --git a/libsrc/ffdec_lib/testdata/as3_new/build_flex_debug.bat b/libsrc/ffdec_lib/testdata/as3_new/build_flex_debug.bat index 89088bc66..aac51a318 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/build_flex_debug.bat +++ b/libsrc/ffdec_lib/testdata/as3_new/build_flex_debug.bat @@ -2,4 +2,6 @@ set COMPILERKIND=flex set SWFNAME=as3_new call c:\flex\bin\mxmlc.bat -debug=true -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1 +rem set COMPILERKIND=flex.optimize +rem call c:\flex\bin\mxmlc.bat -compiler.optimize -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1 rem -warnings=false diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as index aa885d3d9..d3e2cc993 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as +++ b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as @@ -97,6 +97,7 @@ package TestNegate; TestNumberCall; TestOperations; + TestOptimization; TestOptionalParameters; TestParamNames; TestParamsCount; diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestHello.as b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestHello.as index 1b4e68385..bab1fbc87 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestHello.as +++ b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestHello.as @@ -5,7 +5,7 @@ package tests { public function run():* { - trace("hello"); + trace("hello"); } } } diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestOptimization.as b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestOptimization.as new file mode 100644 index 000000000..6d1e2ede8 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestOptimization.as @@ -0,0 +1,25 @@ +package tests +{ + + public class TestOptimization + { + public function run():* + { + // Add more than 3 variables. + // Optimization happens from register 4 on. + // (setlocal X takes more bytes than dup) + var a:int = 1; + var b:int = 2; + var c:int = 3; + + var d:int = 4; //setlocal N + var e:int = d + 5; //getlocal N is replaced with dup before setlocal N + + //We must leave this case intact: + var f:int; + var g:int; + var h:int; + var i:int = h = g = f; + } + } +}