diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1bdf88e..8483f1d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ All notable changes to this project will be documented in this file. - [#1888] AS3 - Coerces, module operator - [#1937] AS3 - declarations vs null - [#1458] Quick find bar overlaying horizontal scrollbar +- [#1842] AS1/2 Better handling obfuscated code, for..in ## [18.3.2] - 2023-01-10 ### Removed @@ -2893,6 +2894,7 @@ All notable changes to this project will be documented in this file. [#1936]: https://www.free-decompiler.com/flash/issues/1936 [#1937]: https://www.free-decompiler.com/flash/issues/1937 [#1458]: https://www.free-decompiler.com/flash/issues/1458 +[#1842]: https://www.free-decompiler.com/flash/issues/1842 [#1935]: https://www.free-decompiler.com/flash/issues/1935 [#1931]: https://www.free-decompiler.com/flash/issues/1931 [#1934]: https://www.free-decompiler.com/flash/issues/1934 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java index 02c368eb1..b6f94bb3e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegisters.java @@ -37,6 +37,7 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSource; import com.jpexs.decompiler.graph.GraphSourceItem; @@ -290,7 +291,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple { } @Override - public List translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { + public List translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegistersOld.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegistersOld.java index 235f6b5d7..f3838c8bc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegistersOld.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorRegistersOld.java @@ -298,7 +298,7 @@ public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld { } @Override - public List translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { + public List translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } 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 e76cff188..75182546c 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 @@ -2427,7 +2427,7 @@ public class AVM2Graph extends Graph { } } - public static void makeAllCommands(List commands, TranslateStack stack) { + public void makeAllCommands(List commands, TranslateStack stack) { for (int i = 0; i < stack.size(); i++) { //These are often obfuscated, so ignore them if (stack.get(i) instanceof NewFunctionAVM2Item) { @@ -2435,7 +2435,7 @@ public class AVM2Graph extends Graph { i--; } } - Graph.makeAllCommands(commands, stack); + super.makeAllCommands(commands, stack); } @Override 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 18506d38c..a5f34a6e0 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 @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.avm2.ConvertOutput; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSource; import com.jpexs.decompiler.graph.GraphSourceItem; @@ -115,7 +116,7 @@ public class AVM2GraphSource extends GraphSource { } @Override - public List translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { + 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<>(); ScopeStack newstack = ((AVM2LocalData) localData).scopeStack; Reference lineStartItem = new Reference<>(localData.lineStartInstruction); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java index db062f56e..82cd74ccf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java @@ -1010,7 +1010,7 @@ public abstract class Action implements GraphSourceItem { return variables2; } - public static List actionsPartToTree(Set switchParts, SecondPassData secondPassData, boolean insideDoInitAction, Reference fi, HashMap registerNames, HashMap variables, HashMap functions, TranslateStack stack, List actions, int start, int end, int version, int staticOperation, String path, String charset) throws InterruptedException, GraphPartChangeException { + public static List actionsPartToTree(ActionGraph graph, Set switchParts, SecondPassData secondPassData, boolean insideDoInitAction, Reference fi, HashMap registerNames, HashMap variables, HashMap functions, TranslateStack stack, List actions, int start, int end, int version, int staticOperation, String path, String charset) throws InterruptedException, GraphPartChangeException { if (start < actions.size() && (end > 0) && (start > 0)) { logger.log(Level.FINE, "Entering {0}-{1}{2}", new Object[]{start, end, actions.size() > 0 ? (" (" + actions.get(start).toString() + " - " + actions.get(end == actions.size() ? end - 1 : end) + ")") : ""}); } @@ -1135,7 +1135,7 @@ public abstract class Action implements GraphSourceItem { if (((action instanceof ActionSetTarget) || (action instanceof ActionSetTarget2)) && (!stack.isEmpty())) { GraphTargetItem lastItem = output.remove(output.size() - 1); - Graph.makeAllCommands(output, stack); + graph.makeAllCommands(output, stack); output.add(lastItem); } 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 f83f121a4..5db237de8 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 @@ -22,13 +22,16 @@ import com.jpexs.decompiler.flash.SWF; import static com.jpexs.decompiler.flash.action.Action.adr2ip; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.model.EnumerateActionItem; +import com.jpexs.decompiler.flash.action.model.EnumeratedValueActionItem; import com.jpexs.decompiler.flash.action.model.FunctionActionItem; import com.jpexs.decompiler.flash.action.model.GetPropertyActionItem; import com.jpexs.decompiler.flash.action.model.SetTarget2ActionItem; import com.jpexs.decompiler.flash.action.model.SetTargetActionItem; import com.jpexs.decompiler.flash.action.model.SetTypeActionItem; +import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem; import com.jpexs.decompiler.flash.action.model.TemporaryRegister; +import com.jpexs.decompiler.flash.action.model.TemporaryRegisterMark; import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem; import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem; import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem; @@ -93,7 +96,7 @@ public class ActionGraph extends Graph { this.insideDoInitAction = insideDoInitAction; this.insideFunction = insideFunction; } - + @Override public ActionGraphSource getGraphCode() { return (ActionGraphSource) code; @@ -121,7 +124,7 @@ public class ActionGraph extends Graph { for (ActionList al : outs) { subgraphs.put("loc" + Helper.formatAddress(code.pos2adr(ip)) + ": function " + functionName, - new ActionGraph("", false, false, al, new HashMap<>(), new HashMap<>(), new HashMap<>(), SWF.DEFAULT_VERSION, ((ActionGraphSource)getGraphCode()).getCharset()) + new ActionGraph("", false, false, al, new HashMap<>(), new HashMap<>(), new HashMap<>(), SWF.DEFAULT_VERSION, ((ActionGraphSource) getGraphCode()).getCharset()) ); } } @@ -353,29 +356,65 @@ public class ActionGraph extends Graph { checkedLoop = null; } } - if (checkedBody != null && (!checkedBody.isEmpty()) && (checkedBody.get(0) instanceof SetTypeActionItem)) { - SetTypeActionItem sti = (SetTypeActionItem) checkedBody.get(0); - if (checkedCondition instanceof NeqActionItem) { - NeqActionItem ne = (NeqActionItem) checkedCondition; - if (ne.rightSide instanceof DirectValueActionItem) { - DirectValueActionItem dv = (DirectValueActionItem) ne.rightSide; - if (dv.value == Null.INSTANCE) { - GraphTargetItem en = list.get(t - 1); - if (en instanceof EnumerateActionItem) { - EnumerateActionItem eti = (EnumerateActionItem) en; - list.remove(t); - checkedBody.remove(0); - if (checkedLoop == null) { - checkedLoop = new Loop(localData.loops.size(), null, null); - checkedBody.add(new BreakItem(null, null, checkedLoop.id)); + + if (checkedCondition instanceof NeqActionItem) { + NeqActionItem ne = (NeqActionItem) checkedCondition; + if (ne.rightSide instanceof DirectValueActionItem) { + DirectValueActionItem dv = (DirectValueActionItem) ne.rightSide; + if (dv.value == Null.INSTANCE) { + GraphTargetItem en = list.get(t - 1); + if (en instanceof EnumerateActionItem) { + EnumerateActionItem eti = (EnumerateActionItem) en; + if (checkedCondition instanceof NeqActionItem) { + NeqActionItem neq = (NeqActionItem) checkedCondition; + if (neq.leftSide instanceof StoreRegisterActionItem) { + if (checkedBody != null && (!checkedBody.isEmpty()) && (checkedBody.get(0) instanceof SetTypeActionItem)) { + SetTypeActionItem sti = (SetTypeActionItem) checkedBody.get(0); + + if ((sti.getValue() instanceof DirectValueActionItem) && (((DirectValueActionItem) sti.getValue()).value instanceof RegisterNumber)) { + if ((neq.rightSide instanceof DirectValueActionItem) && (((DirectValueActionItem) neq.rightSide).value instanceof Null)) { + if (neq.leftSide.value instanceof EnumeratedValueActionItem) { + if (((StoreRegisterActionItem) neq.leftSide).register.number == ((RegisterNumber) (((DirectValueActionItem) sti.getValue()).value)).number) { + list.remove(t); + checkedBody.remove(0); + if (checkedLoop == null) { + checkedLoop = new Loop(localData.loops.size(), null, null); + checkedBody.add(new BreakItem(null, null, checkedLoop.id)); + } + sti.setValue(new DirectValueActionItem(Null.INSTANCE)); + list.add(t, new ForInActionItem(null, null, checkedLoop, (GraphTargetItem) sti, eti.object, checkedBody)); + //sti.getObject() + list.remove(t - 1); + t--; + continue; + } + } + } + } + } + + if (checkedBody != null) { + list.remove(t); + if (checkedLoop == null) { + checkedLoop = new Loop(localData.loops.size(), null, null); + checkedBody.add(new BreakItem(null, null, checkedLoop.id)); + } + list.remove(t - 1); + t--; + if (eti.object instanceof SetTypeActionItem) { + list.add(t++, eti.object); + eti.object = ((SetTypeActionItem)eti.object).getObject(); + } + list.add(t, new ForInActionItem(null, null, checkedLoop, (GraphTargetItem) neq.leftSide, eti.object, checkedBody)); + if (t + 1 < list.size()) { + if (list.get(t + 1) instanceof EnumeratedValueActionItem) { + list.remove(t + 1); + } + } + } } - sti.setValue(new DirectValueActionItem(Null.INSTANCE)); - list.add(t, new ForInActionItem(null, null, checkedLoop, (GraphTargetItem) sti/*sti.getObject()*/, eti.object, checkedBody)); - list.remove(t - 1); - t--; } } - } } } @@ -386,6 +425,44 @@ public class ActionGraph extends Graph { } + public void makeAllCommands(List commands, TranslateStack stack) { + super.makeAllCommands(commands, stack); + //ags.getVariables() + ActionGraphSource ags = (ActionGraphSource) code; + for (String varName : ags.getVariables().keySet()) { + if (varName.matches("^__register.*")) { + GraphTargetItem varValue = ags.getVariables().get(varName); + if (varValue instanceof TemporaryRegister) { + TemporaryRegister tempReg = (TemporaryRegister) varValue; + + if (!tempReg.used) { + if (varValue.value instanceof SetTypeActionItem) { + SetTypeActionItem st = (SetTypeActionItem) varValue.value; + for (int i = 0; i < commands.size(); i++) { + if (commands.get(i) instanceof TemporaryRegisterMark) { + TemporaryRegisterMark trm = (TemporaryRegisterMark) commands.get(i); + if (st.getValue() instanceof StoreRegisterActionItem) { + StoreRegisterActionItem sr = (StoreRegisterActionItem) st.getValue(); + if (sr.register.number == tempReg.getRegId()) { + sr.temporary = false; + } + } + if (trm.tempReg == tempReg) { + commands.set(i, (GraphTargetItem) st); + ags.getVariables().put(varName, null); + break; + } + } + } + } + } + //System.err.println(varValue.value.getClass().getSimpleName()); + } + } + } + + } + @Override public List translate(BaseLocalData localData, int staticOperation, String path) throws InterruptedException { List ret = super.translate(localData, staticOperation, path); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java index 71b6d2ee8..08dba9a16 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.action; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphPartChangeException; import com.jpexs.decompiler.graph.GraphSource; @@ -109,10 +110,10 @@ public class ActionGraphSource extends GraphSource { } @Override - public List translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException { + public List translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException { Reference fi = new Reference<>(localData.lineStartInstruction); - List r = Action.actionsPartToTree(localData.allSwitchParts, localData.secondPassData, this.insideDoInitAction, fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path, charset); + List r = Action.actionsPartToTree((ActionGraph)graph, localData.allSwitchParts, localData.secondPassData, this.insideDoInitAction, fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path, charset); localData.lineStartInstruction = fi.getVal(); return r; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java index dc9bfce4b..0ff407e43 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java @@ -279,7 +279,7 @@ public class ActionScript2ClassDetector { } private boolean checkClassContent(List parts, HashMap variables, int partsPos, int commandsStartPos, int commandsEndPos, List commands, List classNamePath, String scriptPath) { - + try { GraphTargetItem extendsOp = null; @@ -299,29 +299,7 @@ public class ActionScript2ClassDetector { StoreRegisterActionItem sr = (StoreRegisterActionItem) item; definedRegisters.add(sr.register.number); } - } - - /* - Hack: - When register is not used after setting, then FFDec discards its value, - but the value is crucial as there is setmember, atc. - This will bypass the situation. - - This happens when there are no methods/vars, constructor only. - */ - int numr = 0; - for (String s : variables.keySet()) { - Matcher m = regPattern.matcher(s); - if (m.matches()) { - if (variables.get(s) instanceof TemporaryRegister) { - int regId = Integer.parseInt(m.group(1)); - if (!definedRegisters.contains(regId)) { - parts.add(partsPos + numr, variables.get(s).value); - numr++; - } - } - } - } + } if (parts.size() > partsPos) { item = parts.get(partsPos); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TemporaryRegister.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TemporaryRegister.java index 9f5e8ed1c..673659304 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TemporaryRegister.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TemporaryRegister.java @@ -31,6 +31,8 @@ import java.util.Objects; public class TemporaryRegister extends ActionItem { private final int regId; + + public boolean used = false; public TemporaryRegister(int regId, GraphTargetItem value) { super(value.getSrc(), value.getLineStartItem(), value.getPrecedence(), value); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TemporaryRegisterMark.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TemporaryRegisterMark.java new file mode 100644 index 000000000..f79922210 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TemporaryRegisterMark.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.action.model; + +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.model.LocalData; + +/** + * + * @author JPEXS + */ +public class TemporaryRegisterMark extends ActionItem { + + public TemporaryRegister tempReg; + + public TemporaryRegisterMark(TemporaryRegister tempReg) { + this.tempReg = tempReg; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + @Override + public boolean needsNewLine() { + return false; + } + + @Override + public boolean needsSemicolon() { + return false; + } + + @Override + public boolean hasReturnValue() { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/clauses/ForInActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/clauses/ForInActionItem.java index d762eeb75..3b83711d0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/clauses/ForInActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/clauses/ForInActionItem.java @@ -128,7 +128,13 @@ public class ForInActionItem extends LoopActionItem implements Block { } writer.append(" in "); + if (enumVariable.getPrecedence() > PRECEDENCE_PRIMARY) { + writer.append("("); + } enumVariable.toString(writer, localData); + if (enumVariable.getPrecedence() > PRECEDENCE_PRIMARY) { + writer.append(")"); + } writer.append(")").startBlock(); for (GraphTargetItem ti : commands) { ti.toStringSemicoloned(writer, localData).newLine(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java index b29db74d9..c437bdf23 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java @@ -25,6 +25,7 @@ import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.model.TemporaryRegister; +import com.jpexs.decompiler.flash.action.model.TemporaryRegisterMark; import com.jpexs.decompiler.flash.action.model.UnresolvedConstantActionItem; import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.action.parser.pcode.ASMParsedSymbol; @@ -448,6 +449,16 @@ public class ActionPush extends Action { } } if (dvt.computedRegValue instanceof TemporaryRegister) { + ((TemporaryRegister)dvt.computedRegValue).used = true; + for(int i = 0; i < output.size(); i++) { + if(output.get(i) instanceof TemporaryRegisterMark) { + TemporaryRegisterMark trm = (TemporaryRegisterMark)output.get(i); + if(trm.tempReg == dvt.computedRegValue) { + output.remove(i); + break; + } + } + } stack.push(new TemporaryRegister(((RegisterNumber) o).number, ((TemporaryRegister) dvt.computedRegValue).value)); } else { stack.push(dvt); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetProperty.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetProperty.java index be7e39f38..65f0496ab 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetProperty.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetProperty.java @@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem; import com.jpexs.decompiler.flash.action.model.SetPropertyActionItem; import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem; import com.jpexs.decompiler.flash.action.model.TemporaryRegister; +import com.jpexs.decompiler.flash.action.model.TemporaryRegisterMark; import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem; import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; @@ -164,7 +165,9 @@ public class ActionSetProperty extends Action { sr.temporary = true; ((SetPropertyActionItem) ret).setValue(sr); } - variables.put("__register" + sr.register.number, new TemporaryRegister(sr.register.number, ret)); + TemporaryRegister tr = new TemporaryRegister(sr.register.number, ret); + variables.put("__register" + sr.register.number, tr); + output.add(new TemporaryRegisterMark(tr)); return; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetVariable.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetVariable.java index bb2dd1c92..4b86810d6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetVariable.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionSetVariable.java @@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem; import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem; import com.jpexs.decompiler.flash.action.model.TemporaryRegister; +import com.jpexs.decompiler.flash.action.model.TemporaryRegisterMark; import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem; import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem; import com.jpexs.decompiler.flash.ecma.EcmaScript; @@ -172,7 +173,9 @@ public class ActionSetVariable extends Action implements StoreTypeAction { ((SetVariableActionItem) ret).setValue(sr); } - variables.put("__register" + sr.register.number, new TemporaryRegister(sr.register.number, ret)); + TemporaryRegister tr = new TemporaryRegister(sr.register.number, ret); + variables.put("__register" + sr.register.number, tr); + output.add(new TemporaryRegisterMark(tr)); return; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java index b8475943c..dd2b73bf6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java @@ -28,6 +28,7 @@ import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem; import com.jpexs.decompiler.flash.action.model.SetMemberActionItem; import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem; import com.jpexs.decompiler.flash.action.model.TemporaryRegister; +import com.jpexs.decompiler.flash.action.model.TemporaryRegisterMark; import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem; import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; @@ -156,7 +157,9 @@ public class ActionSetMember extends Action { sr.temporary = true; ((SetMemberActionItem) ret).setValue(sr); } - variables.put("__register" + sr.register.number, new TemporaryRegister(sr.register.number, ret)); + TemporaryRegister tr = new TemporaryRegister(sr.register.number, ret); + variables.put("__register" + sr.register.number, tr); + output.add(new TemporaryRegisterMark(tr)); return; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java index 3b1a71b6e..67c4ca80d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStoreRegister.java @@ -161,7 +161,9 @@ public class ActionStoreRegister extends Action implements StoreTypeAction { } if ((value instanceof EnumeratedValueActionItem)) { - variables.put("__register" + registerNumber, new TemporaryRegister(registerNumber, new EnumerationAssignmentValueActionItem())); + //variables.put("__register" + registerNumber, new TemporaryRegister(registerNumber, new EnumerationAssignmentValueActionItem())); + //variables.put("__register" + registerNumber, null); + variables.remove("__register" + registerNumber); } StoreRegisterActionItem ret = new StoreRegisterActionItem(this, lineStartAction, rn, value, define); if (value.getNotCoercedNoDup() instanceof CompoundableBinaryOp) { 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 24396db1b..0ef4b0a93 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -1485,7 +1485,7 @@ public class Graph { } end = p.end; int start = p.start; - ret.addAll(code.translatePart(part, localData, stack, start, end, staticOperation, path)); + ret.addAll(code.translatePart(this, part, localData, stack, start, end, staticOperation, path)); } return ret; } @@ -2039,6 +2039,7 @@ public class Graph { } protected List printGraph(List foundGotos, Map> partCodes, Map partCodePos, Set visited, BaseLocalData localData, TranslateStack stack, Set allParts, GraphPart parent, GraphPart part, List stopPart, List stopPartKind, List loops, List throwStates, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { + loopPrintGraph:while(true) { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } @@ -2273,7 +2274,7 @@ public class Graph { do { exHappened = false; try { - output.addAll(code.translatePart(part, localData, stack, ipStart, part.end, staticOperation, path)); + output.addAll(code.translatePart(this, part, localData, stack, ipStart, part.end, staticOperation, path)); } catch (GraphPartChangeException ex) { //Special case for ifFrameLoaded when it's over multiple parts output.addAll(ex.getOutput()); for (GraphPart p : allParts) { @@ -2282,7 +2283,8 @@ public class Graph { currentRet.addAll(output); //to check for stopparts,etc. we need to call printGraph again part = p; - return printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel); + //return printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel); + continue loopPrintGraph; } exHappened = true; ipStart = ex.getIp(); @@ -2932,7 +2934,8 @@ public class Graph { printGraph(foundGotos, partCodes, partCodePos, visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel + 1); } } - + break; + } return ret; } @@ -3169,8 +3172,8 @@ public class Graph { i--; } } - - public static void makeAllCommands(List commands, TranslateStack stack) { + + public void makeAllCommands(List commands, TranslateStack stack) { int clen = commands.size(); boolean isExit = false; if (clen > 0) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java index bdf849c91..00dd14d95 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java @@ -21,6 +21,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Stack; /** * @@ -73,11 +74,10 @@ public class GraphPart implements Serializable { public int finishedTime; public int order; - + public int numBlocks = Integer.MAX_VALUE; //public List throwParts = new ArrayList<>(); - public enum StopPartType { NONE, AND_OR, COMMONPART @@ -125,7 +125,7 @@ public class GraphPart implements Serializable { ordered.add(this); return time; } - + public void setNumblocks(int numBlocks) { this.numBlocks = numBlocks; numBlocks++; @@ -133,7 +133,7 @@ public class GraphPart implements Serializable { if (next.numBlocks > numBlocks) { next.setNumblocks(numBlocks); } - } + } } private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet visited, List loops, List throwStates, boolean useThrow) throws InterruptedException { @@ -141,6 +141,74 @@ public class GraphPart implements Serializable { throw new InterruptedException(); } + Stack todo = new Stack<>(); + todo.push(this); + + looptodo:while (!todo.isEmpty()) { + GraphPart thisPart = todo.pop(); + + GraphPart tpart = gr.checkPart(null, localData, prev, thisPart, null); + if (tpart == null) { + continue; + } + if (tpart != thisPart) { + todo.push(tpart); + continue; + } + for (Loop l : loops) { + if (l.phase == 1) { + if (l.loopContinue == thisPart) { + continue looptodo; + } + if (l.loopPreContinue == thisPart) { + continue looptodo; + } + if (l.loopBreak == thisPart) { + //return false; //? + } + } + } + if (visited.contains(thisPart)) { + continue; + } + visited.add(thisPart); + if (thisPart.end < code.size() && code.get(thisPart.end).isBranch() && (code.get(thisPart.end).ignoredLoops())) { + continue; + } + for (GraphPart p : thisPart.nextParts) { + if (p == part) { + return true; + } + if (visited.contains(p)) { + continue; + } + todo.push(p); + } + for (ThrowState ts : throwStates) { + if (ts.state != 1) { + if (ts.throwingParts.contains(thisPart)) { + GraphPart p = ts.targetPart; + + if (p == part) { + return true; + } + if (visited.contains(p)) { + continue; + } + + todo.push(p); + } + } + } + } + return false; + } + + private boolean leadsToRecursive(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet visited, List loops, List throwStates, boolean useThrow) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + GraphPart tpart = gr.checkPart(null, localData, prev, this, null); if (tpart == null) { return false; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java index c40de4d80..71a0c3c3e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java @@ -36,7 +36,7 @@ public abstract class GraphSource implements Serializable { public abstract boolean isEmpty(); - public abstract List translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException; + public abstract List translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException; public abstract Set getImportantAddresses(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/CommaExpressionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/CommaExpressionItem.java index fa14be3b0..430750690 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/CommaExpressionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/CommaExpressionItem.java @@ -48,6 +48,9 @@ public class CommaExpressionItem extends GraphTargetItem { public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { boolean first = true; for (GraphTargetItem t : commands) { + if (t.isEmpty()) { + continue; + } if (!first) { writer.append(", "); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/DoWhileItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/DoWhileItem.java index e9440ddb7..7beff622d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/DoWhileItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/DoWhileItem.java @@ -101,13 +101,15 @@ public class DoWhileItem extends LoopItem implements Block { } writer.append("("); + boolean first = true; for (int i = 0; i < expression.size(); i++) { if (expression.get(i).isEmpty()) { continue; } - if (i != 0) { + if (!first) { writer.append(", "); } + first = false; if (i == expression.size() - 1) { expression.get(i).toStringBoolean(writer, localData); } else { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/WhileItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/WhileItem.java index 6b4d1b788..e3e3b7583 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/WhileItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/WhileItem.java @@ -92,13 +92,15 @@ public class WhileItem extends LoopItem implements Block { writer.append(" "); } writer.append("("); + boolean first = true; for (int i = 0; i < expression.size(); i++) { if (expression.get(i).isEmpty()) { continue; } - if (i != 0) { + if (!first) { writer.append(", "); } + first = false; if (i == expression.size() - 1) { expression.get(i).toStringBoolean(writer, localData); } else { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java index c0270e732..d38af6adf 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java @@ -293,4 +293,39 @@ public class ActionScript2AssemblerTest extends ActionScript2TestBase { + "}\n" + "}"); } + + @Test + public void testNonStandardForIn() { + String res = decompileClassPcode("ConstantPool\n" + + "Push \"x\" 0 \"Object\"\n" + + "NewObject\n" + + "StoreRegister 1\n" + + "SetVariable\n" + + "Push \"_global\"\n" + + "GetVariable\n" + + "Push \"x\"\n" + + "GetMember\n" + + "StoreRegister 2\n" + + "Enumerate2\n" + + "loc003d:StoreRegister 0\n" + + "Push null\n" + + "Equals2\n" + + "If loc0066\n" + + "Push register1 register0 register2 register0\n" + + "GetMember\n" + + "SetMember\n" + + "Jump loc003d\n" + + "loc0066:Pop\n" + + "Push \"after\"\n" + + "Trace"); + res = cleanPCode(res); + assertEquals(res, "var _loc1_ = null;\n" + + "x = _loc1_ = new Object();\n" + + "var _loc2_ = _global.x;\n" + + "for(var _loc0_ in _loc2_)\n" + + "{\n" + + "_loc1_[_loc0_] = _loc2_[_loc0_];\n" + + "}\n" + + "trace(\"after\");"); + } }