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 8a60834cb..abb457414 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 @@ -698,6 +698,11 @@ public abstract class Action implements GraphSourceItem { return toString(); } + public boolean execute(LocalDataArea lda) { + //throw new UnsupportedOperationException("Action " + toString() + " not implemented"); + return false; + } + /** * Translates this function to stack and output. * diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/LocalDataArea.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/LocalDataArea.java new file mode 100644 index 000000000..2dc527f0e --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/LocalDataArea.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-2015 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; + +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import java.util.HashMap; +import java.util.Stack; + +public class LocalDataArea { + + public Stack stack = new Stack<>(); + + public HashMap localVariables = new HashMap<>(); + + public Long jump; + + public Object returnValue; + + public String executionException; + + public void clear() { + stack.clear(); + localVariables.clear(); + jump = null; + returnValue = null; + executionException = null; + } + + public Object pop() { + return stack.pop(); + } + + public Double popAsNumber() { + return EcmaScript.toNumber(stack.pop()); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/AsciiToCharActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/AsciiToCharActionItem.java index 4a7f71f8c..432aa92f7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/AsciiToCharActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/AsciiToCharActionItem.java @@ -51,12 +51,7 @@ public class AsciiToCharActionItem extends ActionItem { @Override public Object getResult() { - int res = (int) (double) (value.getResultAsNumber()); - if (res == 0) { - return ""; - } - - return ((Character) (char) res).toString(); + return ActionAsciiToChar.getResult(value.getResultAsNumber()); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TypeOfActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TypeOfActionItem.java index 8b71de962..3da1f4038 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TypeOfActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/TypeOfActionItem.java @@ -18,8 +18,6 @@ package com.jpexs.decompiler.flash.action.model; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.flash.ecma.EcmaType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.GraphSourceItem; @@ -53,24 +51,7 @@ public class TypeOfActionItem extends ActionItem { @Override public Object getResult() { - Object res = value.getResult(); - EcmaType type = EcmaScript.type(res); - switch (type) { - case STRING: - return "string"; - case BOOLEAN: - return "boolean"; - case NUMBER: - return "number"; - case OBJECT: - return "object"; - case UNDEFINED: - return "undefined"; - case NULL: - return "null"; - } - //TODO: function,movieclip - return "object"; + return ActionTypeOf.getResult(value.getResult()); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StringAddActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StringAddActionItem.java index 35394e840..6f9ed0c96 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StringAddActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/operations/StringAddActionItem.java @@ -18,7 +18,6 @@ package com.jpexs.decompiler.flash.action.model.operations; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd; -import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; @@ -41,7 +40,7 @@ public class StringAddActionItem extends BinaryOpItem { @Override public Object getResult() { - return EcmaScript.toString(leftSide.getResult()) + EcmaScript.toString(rightSide.getResult()); + return ActionStringAdd.getResult(leftSide.getResult(), rightSide.getResult()); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionAsciiToChar.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionAsciiToChar.java index 724bda0f5..7e877dfc8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionAsciiToChar.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionAsciiToChar.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.action.swf4; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; @@ -35,6 +36,25 @@ public class ActionAsciiToChar extends Action { return "AsciiToChar"; } + @Override + public boolean execute(LocalDataArea lda) { + if (lda.stack.size() == 0) { + return false; + } + + lda.stack.push(getResult(lda.popAsNumber())); + return true; + } + + public static String getResult(Double ascii) { + int res = (int) (double) ascii; + if (res == 0) { + return ""; + } + + return ((Character) (char) res).toString(); + } + @Override public void translate(TranslateStack stack, List output, HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { GraphTargetItem a = stack.pop(); 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 e961569b0..86a0945ae 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 @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.action.Action; 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.parser.ActionParseException; @@ -373,6 +374,15 @@ public class ActionPush extends Action { return writer; } + @Override + public boolean execute(LocalDataArea lda) { + for (Object value : values) { + lda.stack.push(value); + } + + return true; + } + @Override public void translate(TranslateStack stack, List output, HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { int pos = 0; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionStringAdd.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionStringAdd.java index 37b4b7352..016f0d216 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionStringAdd.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf4/ActionStringAdd.java @@ -18,7 +18,9 @@ package com.jpexs.decompiler.flash.action.swf4; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.model.operations.StringAddActionItem; +import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import java.util.HashMap; @@ -35,6 +37,20 @@ public class ActionStringAdd extends Action { return "StringAdd"; } + @Override + public boolean execute(LocalDataArea lda) { + if (lda.stack.size() < 2) { + return false; + } + + lda.stack.push(getResult(lda.pop(), lda.pop())); + return true; + } + + public static String getResult(Object obj1, Object obj2) { + return EcmaScript.toString(obj1) + EcmaScript.toString(obj2); + } + @Override public void translate(TranslateStack stack, List output, HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { GraphTargetItem a = stack.pop(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionPushDuplicate.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionPushDuplicate.java index 7ee61818f..9778b971a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionPushDuplicate.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionPushDuplicate.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.action.swf5; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.graph.GraphSourceItemPos; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; @@ -36,6 +37,16 @@ public class ActionPushDuplicate extends Action { return "PushDuplicate"; } + @Override + public boolean execute(LocalDataArea lda) { + if (lda.stack.size() == 0) { + return false; + } + + lda.stack.push(lda.stack.peek()); + return true; + } + @Override public void translate(TranslateStack stack, List output, HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { GraphTargetItem value = stack.peek(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStackSwap.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStackSwap.java index 579850203..9ad440683 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStackSwap.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionStackSwap.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.action.swf5; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.graph.GraphSourceItemPos; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; @@ -35,6 +36,19 @@ public class ActionStackSwap extends Action { return "StackSwap"; } + @Override + public boolean execute(LocalDataArea lda) { + if (lda.stack.size() < 2) { + return false; + } + + Object obj1 = lda.stack.pop(); + Object obj2 = lda.stack.pop(); + lda.stack.push(obj1); + lda.stack.push(obj2); + return true; + } + @Override public void translate(TranslateStack stack, List output, HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { GraphTargetItem a = stack.pop(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionTypeOf.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionTypeOf.java index b7299c3be..031be737f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionTypeOf.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionTypeOf.java @@ -18,7 +18,10 @@ package com.jpexs.decompiler.flash.action.swf5; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.model.TypeOfActionItem; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.ecma.EcmaType; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import java.util.HashMap; @@ -35,6 +38,37 @@ public class ActionTypeOf extends Action { return "TypeOf"; } + @Override + public boolean execute(LocalDataArea lda) { + if (lda.stack.size() == 0) { + return false; + } + + lda.stack.push(getResult(lda.pop())); + return true; + } + + public static String getResult(Object obj) { + Object res = obj; + EcmaType type = EcmaScript.type(res); + switch (type) { + case STRING: + return "string"; + case BOOLEAN: + return "boolean"; + case NUMBER: + return "number"; + case OBJECT: + return "object"; + case UNDEFINED: + return "undefined"; + case NULL: + return "null"; + } + //TODO: function,movieclip + return "object"; + } + @Override public void translate(TranslateStack stack, List output, HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { GraphTargetItem object = stack.pop(); diff --git a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java index d07cc7980..2dfaf037b 100644 --- a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java +++ b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java @@ -25,6 +25,7 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.ActionLocalData; +import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.deobfuscation.FixItemCounterTranslateStack; import com.jpexs.decompiler.flash.action.swf4.ActionAdd; import com.jpexs.decompiler.flash.action.swf4.ActionAnd; @@ -92,6 +93,7 @@ import java.util.List; import java.util.Map; import java.util.Random; import org.testng.Assert; +import static org.testng.Assert.fail; /** * @@ -274,6 +276,16 @@ public class FlashPlayerTest { Object ffdecResult = stack.pop().getResult(); System.out.println("FFDec result: " + ffdecResult); + LocalDataArea lda = new LocalDataArea(); + for (Action a : newActions) { + if (!a.execute(lda)) { + fail(); + } + } + + Object ffdecExecuteResult = lda.stack.pop(); + System.out.println("FFDec execte result: " + ffdecExecuteResult); + asm.setActions(actions); asm.setModified(); try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f2))) { @@ -307,8 +319,10 @@ public class FlashPlayerTest { if (checkOnlyStart) { Assert.assertTrue(((String) ffdecResult).startsWith(flashResult)); + Assert.assertTrue(((String) ffdecExecuteResult).startsWith(flashResult)); } else { Assert.assertEquals(ffdecResult, flashResult); + Assert.assertEquals(ffdecExecuteResult, flashResult); } f2.delete();