From 97447968b1d744c2c7ab27a59aaefccc3d3beb6f Mon Sep 17 00:00:00 2001 From: honfika Date: Fri, 22 Aug 2014 20:16:15 +0200 Subject: [PATCH] better actionlist deobfuscation --- src/com/jpexs/decompiler/flash/SWF.java | 6 +- .../jpexs/decompiler/flash/action/Action.java | 36 +- .../flash/action/ActionDeobfuscator.java | 357 ++++ .../decompiler/flash/action/ActionList.java | 121 +- .../flash/action/ActionListReader.java | 253 ++- ...nDeobfuscation.java => Deobfuscation.java} | 2 +- .../flash/action/parser/pcode/ASMParser.java | 3 + .../flash/action/swf3/ActionWaitForFrame.java | 4 +- .../flash/action/swf4/ActionIf.java | 7 +- .../flash/action/swf4/ActionJump.java | 7 +- .../flash/action/swf4/ActionPush.java | 5 +- .../action/swf4/ActionWaitForFrame2.java | 6 +- .../action/swf5/ActionDefineFunction.java | 8 +- .../flash/action/swf5/ActionWith.java | 4 +- .../action/swf7/ActionDefineFunction2.java | 8 +- .../flash/action/swf7/ActionTry.java | 4 +- src/com/jpexs/decompiler/flash/gui/Main.java | 3 +- .../flash/gui/action/ActionPanel.java | 5 +- .../helpers/EmptySWFDecompilerListener.java | 2 +- .../flash/helpers/SWFDecompilerPlugin.java | 1 - .../flash/tags/DefineButtonTag.java | 5 +- .../decompiler/flash/tags/DoActionTag.java | 5 +- .../flash/tags/DoInitActionTag.java | 5 +- .../decompiler/flash/tags/base/ASMSource.java | 2 +- .../flash/types/BUTTONCONDACTION.java | 5 +- .../flash/types/CLIPACTIONRECORD.java | 5 +- .../flash/ActionScript2ModificationTest.java | 130 +- .../decompiler/flash/ActionScript2Test.java | 1616 +++++++-------- .../decompiler/flash/ActionScript3Test.java | 1826 ++++++++--------- .../flash/ActionScriptTestBase.java | 30 + .../flash/ActionStript2TestBase.java | 77 +- 31 files changed, 2611 insertions(+), 1937 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/action/ActionDeobfuscator.java rename src/com/jpexs/decompiler/flash/action/{ActionDeobfuscation.java => Deobfuscation.java} (99%) create mode 100644 test/com/jpexs/decompiler/flash/ActionScriptTestBase.java diff --git a/src/com/jpexs/decompiler/flash/SWF.java b/src/com/jpexs/decompiler/flash/SWF.java index f7f0c231d..b58e40b24 100644 --- a/src/com/jpexs/decompiler/flash/SWF.java +++ b/src/com/jpexs/decompiler/flash/SWF.java @@ -24,7 +24,7 @@ import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionDeobfuscation; +import com.jpexs.decompiler.flash.action.Deobfuscation; import com.jpexs.decompiler.flash.action.ActionGraphSource; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.ActionLocalData; @@ -1672,7 +1672,7 @@ public final class SWF implements TreeItem, Timelined { private List allFunctions = new ArrayList<>(); private HashMap allStrings = new HashMap<>(); private final HashMap usageTypes = new HashMap<>(); - private final ActionDeobfuscation deobfuscation = new ActionDeobfuscation(); + private final Deobfuscation deobfuscation = new Deobfuscation(); private static void getVariables(ConstantPool constantPool, BaseLocalData localData, Stack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { boolean debugMode = false; @@ -1683,7 +1683,7 @@ public final class SWF implements TreeItem, Timelined { GraphSourceItem ins = code.get(ip); if (debugMode) { - System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); + System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ActionList(), new ArrayList(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); } if (ins.isExit()) { break; diff --git a/src/com/jpexs/decompiler/flash/action/Action.java b/src/com/jpexs/decompiler/flash/action/Action.java index cbd6a8ba7..8e8cc46a3 100644 --- a/src/com/jpexs/decompiler/flash/action/Action.java +++ b/src/com/jpexs/decompiler/flash/action/Action.java @@ -398,25 +398,9 @@ public class Action implements GraphSourceItem { * @param version SWF version * @param exportMode PCode or hex? * @param writer - * @return HilightedTextWriter + * @return GraphTextWriter */ - public static GraphTextWriter actionsToString(List listeners, long address, List list, int version, ScriptExportMode exportMode, GraphTextWriter writer) { - return actionsToString(listeners, address, list, new ArrayList(), version, exportMode, writer); - } - - /** - * Converts list of actions to ASM source - * - * @param listeners - * @param address - * @param list List of actions - * @param importantOffsets List of important offsets to mark as labels - * @param constantPool Constant pool - * @param version SWF version - * @param hex Add hexadecimal? - * @return HilightedTextWriter - */ - private static GraphTextWriter actionsToString(List listeners, long address, List list, List constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer) { + public static GraphTextWriter actionsToString(List listeners, long address, ActionList list, int version, ScriptExportMode exportMode, GraphTextWriter writer) { long offset; List importantOffsets = getActionsAllRefs(list); /*List cps = SWFInputStream.getConstantPool(new ArrayList(), new ActionGraphSource(list, version, new HashMap(), new HashMap(), new HashMap()), 0, version, path); @@ -467,8 +451,11 @@ public class Action implements GraphSourceItem { if (containers.containsKey(offset)) { for (int i = 0; i < containers.get(offset).size(); i++) { + if (lastPush) { + writer.newLine(); + lastPush = false; + } writer.appendNoHilight("}").newLine(); - lastPush = false; GraphSourceItemContainer cnt = containers.get(offset).get(i); int cntPos = containersPos.get(cnt); writer.appendNoHilight(cnt.getASMSourceBetween(cntPos)); @@ -517,7 +504,7 @@ public class Action implements GraphSourceItem { add = "";*/ if ((a instanceof ActionPush) && lastPush) { writer.appendNoHilight(" "); - ((ActionPush) a).paramsToStringReplaced(list, importantOffsets, constantPool, version, exportMode, writer); + ((ActionPush) a).paramsToStringReplaced(list, importantOffsets, exportMode, writer); } else { if (lastPush) { writer.newLine(); @@ -547,7 +534,7 @@ public class Action implements GraphSourceItem { //nojump, ignore } } else { - a.getASMSourceReplaced(list, importantOffsets, constantPool, exportMode, writer); + a.getASMSourceReplaced(list, importantOffsets, exportMode, writer); } writer.appendNoHilight(a.isIgnored() ? "; ignored" : ""); writer.appendNoHilight(add); @@ -592,11 +579,10 @@ public class Action implements GraphSourceItem { * * @param container * @param knownAddreses List of important offsets to mark as labels - * @param constantPool Constant pool * @param exportMode PCode or hex? * @return String of P-code source */ - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { return toString(); } @@ -1209,8 +1195,8 @@ public class Action implements GraphSourceItem { } } - public GraphTextWriter getASMSourceReplaced(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode, GraphTextWriter writer) { - writer.appendNoHilight(getASMSource(container, knownAddreses, constantPool, exportMode)); + public GraphTextWriter getASMSourceReplaced(ActionList container, List knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) { + writer.appendNoHilight(getASMSource(container, knownAddreses, exportMode)); return writer; } diff --git a/src/com/jpexs/decompiler/flash/action/ActionDeobfuscator.java b/src/com/jpexs/decompiler/flash/action/ActionDeobfuscator.java new file mode 100644 index 000000000..a6c9b3394 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/action/ActionDeobfuscator.java @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.action; + +import com.jpexs.decompiler.flash.action.special.ActionStore; +import com.jpexs.decompiler.flash.action.swf4.ActionAdd; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; +import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionReturn; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerListener; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TranslateException; +import java.util.ArrayList; +import java.util.EmptyStackException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +/** + * + * @author JPEXS + */ +public class ActionDeobfuscator implements SWFDecompilerListener { + + @Override + public void actionListParsed(ActionList actions) { + combinePushs(actions); + removeFakeFunction(actions); + removeUnreachableActions(actions); + removeObfuscationIfs(actions); + removeUnreachableActions(actions); + removeZeroJumps(actions); + } + + private void combinePushs(ActionList actions) { + for (int i = 0; i < actions.size() - 1; i++) { + Action action = actions.get(i); + Action action2 = actions.get(i + 1); + if (action instanceof ActionPush && action2 instanceof ActionPush) { + if (actions.getReferencesFor(action2).hasNext()) { + ActionPush push = (ActionPush) action; + ActionPush push2 = (ActionPush) action2; + push.values.addAll(push2.values); + actions.remove(i + 1); + i--; + } + } + } + } + + private boolean removeUnreachableActions(ActionList actions) { + Set reachableActions = new HashSet<>(); + Set processedActions = new HashSet<>(); + reachableActions.add(actions.get(0)); + boolean modified = true; + while (modified) { + modified = false; + for (int i = 0; i < actions.size(); i++) { + Action action = actions.get(i); + if (reachableActions.contains(action) && !processedActions.contains(action)) { + if (!action.isExit() && !(action instanceof ActionJump) && i != actions.size() - 1) { + Action next = actions.get(i + 1); + if (!reachableActions.contains(next)) { + reachableActions.add(next); + } + } + + if (action instanceof ActionJump) { + ActionJump aJump = (ActionJump) action; + long ref = aJump.getAddress() + aJump.getTotalActionLength() + aJump.getJumpOffset(); + Action target = actions.getByAddress(ref); + if (target != null && !reachableActions.contains(target)) { + reachableActions.add(target); + } + } else if (action instanceof ActionIf) { + ActionIf aIf = (ActionIf) action; + long ref = aIf.getAddress() + aIf.getTotalActionLength() + aIf.getJumpOffset(); + Action target = actions.getByAddress(ref); + if (target != null && !reachableActions.contains(target)) { + reachableActions.add(target); + } + } else if (action instanceof ActionStore) { + ActionStore aStore = (ActionStore) action; + int storeSize = aStore.getStoreSize(); + if (actions.size() > i + storeSize) { + Action target = actions.get(i + storeSize); + if (!reachableActions.contains(target)) { + reachableActions.add(target); + } + } + } else if (action instanceof GraphSourceItemContainer) { + GraphSourceItemContainer container = (GraphSourceItemContainer) action; + long ref = action.getAddress() + action.getTotalActionLength(); + for (Long size : container.getContainerSizes()) { + ref += size; + Action target = actions.getByAddress(ref); + if (target != null && !reachableActions.contains(target)) { + reachableActions.add(target); + } + } + } + + processedActions.add(action); + modified = true; + } + } + } + + boolean result = false; + for (int i = 0; i < actions.size(); i++) { + if (!reachableActions.contains(actions.get(i))) { + actions.removeAction(i); + i--; + result = true; + } + } + + return result; + } + + private boolean removeZeroJumps(ActionList actions) { + boolean result = false; + for (int i = 0; i < actions.size(); i++) { + Action action = actions.get(i); + if (action instanceof ActionJump && ((ActionJump) action).getJumpOffset() == 0) { + actions.removeAction(i); + i--; + result = true; + } + } + return result; + } + + private boolean removeObfuscationIfs(ActionList actions) { + if (actions.size() == 0) { + return false; + } + + for (int i = 0; i < actions.size(); i++) { + int idx = i; + List output = new ArrayList<>(); + ActionLocalData localData = new ActionLocalData(); + Stack stack = new Stack<>(); + + try { + int lastOkIdx = -1; + int lastOkInstructionsProcessed = -1; + int instructionsProcessed = 0; + Map variables = new HashMap<>(); + Set defines = new HashSet<>(); + ActionConstantPool constantPool = null; + while (true) { + Action action = actions.get(idx); + instructionsProcessed++; + + System.out.print(action.getASMSource(actions, new ArrayList(), ScriptExportMode.PCODE)); + for (int j = 0; j < stack.size(); j++) { + System.out.print(" '" + stack.get(j).getResult() + "'"); + } + System.out.println(); + + if (action instanceof ActionConstantPool) { + constantPool = (ActionConstantPool) action; + } + + if (action instanceof ActionDefineLocal) { + GraphTargetItem top = stack.pop(); + String variableName = stack.peek().getResult().toString(); + defines.add(variableName); + stack.push(top); + } + + action.translate(localData, stack, output, Graph.SOP_USE_STATIC, ""); + + if (!(action instanceof ActionPush || + action instanceof ActionAdd || + action instanceof ActionAdd2 || + action instanceof ActionSubtract || + action instanceof ActionDefineLocal || + action instanceof ActionJump || + action instanceof ActionGetVariable || + action instanceof ActionSetVariable || + action instanceof ActionEquals || + action instanceof ActionNot || + action instanceof ActionIf || + action instanceof ActionConstantPool)) { + break; + } + + /*for (String variable : localData.variables.keySet()) { + System.out.println(Helper.byteArrToString(variable.getBytes())); + }*/ + + idx++; + + if (action instanceof ActionJump) { + ActionJump jump = (ActionJump) action; + long address = jump.getAddress() + jump.getTotalActionLength() + jump.getJumpOffset(); + idx = actions.indexOf(actions.getByAddress(address)); + if (idx == -1) { + int a = 1; + } + } + + if (action instanceof ActionIf) { + ActionIf aif = (ActionIf) action; + if (EcmaScript.toBoolean(stack.pop().getResult())) { + System.out.println("if true"); + long address = aif.getAddress() + aif.getTotalActionLength() + aif.getJumpOffset(); + idx = actions.indexOf(actions.getByAddress(address)); + if (idx == -1) { + int a = 1; + } + } + } + + if (/*localData.variables.size() == 1 && */stack.empty()) { + lastOkIdx = idx; + lastOkInstructionsProcessed = instructionsProcessed; + variables.clear(); + for (String variableName : localData.variables.keySet()) { + Object value = localData.variables.get(variableName).getResult(); + variables.put(variableName, value); + } + } + } + + if (lastOkIdx != -1) { + int newIstructionCount = constantPool != null ? 2 : 1; + newIstructionCount += 2 * variables.size(); + + if (newIstructionCount < lastOkInstructionsProcessed) { + Action target = actions.get(lastOkIdx); + Action prevAction = actions.get(i); + + if (constantPool != null) { + actions.addAction(i++, constantPool); + prevAction = constantPool; + } + + for (String variableName : variables.keySet()) { + Object value = variables.get(variableName); + ActionPush push = new ActionPush(variableName); + push.values.add(value); + push.setAddress(prevAction.getAddress()); + actions.addAction(i++, push); + prevAction = push; + + if (defines.contains(variableName)) { + Action defineLocal = new ActionDefineLocal(); + defineLocal.setAddress(prevAction.getAddress()); + actions.addAction(i++, defineLocal); + prevAction = defineLocal; + } else { + Action setVariable = new ActionSetVariable(); + setVariable.setAddress(prevAction.getAddress()); + actions.addAction(i++, setVariable); + prevAction = setVariable; + } + } + + ActionJump jump = new ActionJump(0); + jump.setAddress(prevAction.getAddress()); + jump.setJumpOffset((int) (target.getAddress() - jump.getAddress() - jump.getTotalActionLength())); + actions.addAction(i++, jump); + + return true; + } + } + } catch (EmptyStackException | TranslateException | InterruptedException ex) { + } + } + + return false; + } + + private boolean removeFakeFunction(ActionList actions) { + /* + DefineFunction "fakeName" 0 { + Push 1777 + Return + } + */ + + for (int i = 0; i < actions.size() - 2; i++) { + Action action = actions.get(i); + if (action instanceof ActionDefineFunction) { + Action action2 = actions.get(i + 1); + Action action3 = actions.get(i + 2); + if (action2 instanceof ActionPush && action3 instanceof ActionReturn) { + ActionDefineFunction def = (ActionDefineFunction) action; + ActionPush push = (ActionPush) action2; + if (def.paramNames.isEmpty() && push.values.size() == 1) { + Object pushValueObj = push.values.get(0); + if (pushValueObj instanceof Long) { + String functionName = def.functionName; + long pushValue = (Long) pushValueObj; + for (int j = 0; j < 3; j++) { + actions.removeAction(i); + } + + for (int j = 0; j < actions.size() - 1; j++) { + action = actions.get(j); + if (action instanceof ActionPush) { + push = (ActionPush) action; + int pushValuesCount = push.values.size(); + if (pushValuesCount >= 2 + && push.values.get(pushValuesCount - 1).equals(functionName)) { + push.values.remove(pushValuesCount - 1); + push.values.remove(pushValuesCount - 2); + push.values.add(pushValue); + actions.removeAction(j + 1); + } + } + } + + return true; + } + } + } + } + } + + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/ActionList.java b/src/com/jpexs/decompiler/flash/action/ActionList.java index e8f751a17..56b958704 100644 --- a/src/com/jpexs/decompiler/flash/action/ActionList.java +++ b/src/com/jpexs/decompiler/flash/action/ActionList.java @@ -16,15 +16,38 @@ */ package com.jpexs.decompiler.flash.action; +import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.action.special.ActionNop; +import com.jpexs.decompiler.flash.action.special.ActionStore; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.EmptySWFDecompilerListener; +import com.jpexs.decompiler.flash.helpers.FileTextWriter; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; /** * * @author JPEXS */ public class ActionList extends ArrayList { + + public ActionList() { + } + + public ActionList(Collection actions) { + super(actions); + } public void removeAction(int index) { ActionListReader.removeAction(this, index, SWF.DEFAULT_VERSION, true); @@ -42,7 +65,70 @@ public class ActionList extends ArrayList { } public void addAction(int index, Action action) { - ActionListReader.removeAction(this, index, SWF.DEFAULT_VERSION, true); + ActionListReader.addAction(this, index, action, SWF.DEFAULT_VERSION, false, false); + } + + public void fixActionList() { + ActionListReader.fixActionList(this, null, SWF.DEFAULT_VERSION); + } + + public Iterator getReferencesFor(final Action target) { + final ActionList diz = this; + return new Iterator() { + + private final Iterator iterator = diz.iterator(); + + private Action action = getNext(); + + @Override + public boolean hasNext() { + return action != null; + } + + @Override + public Action next() { + Action a = action; + action = getNext(); + return a; + } + + private Action getNext() { + while (iterator.hasNext()) { + Action a = iterator.next(); + if (a instanceof ActionJump) { + ActionJump aJump = (ActionJump) a; + long ref = aJump.getAddress() + aJump.getTotalActionLength() + aJump.getJumpOffset(); + if (target.getAddress() == ref) { + return aJump; + } + } else if (a instanceof ActionIf) { + ActionIf aIf = (ActionIf) a; + long ref = aIf.getAddress() + aIf.getTotalActionLength() + aIf.getJumpOffset(); + if (target.getAddress() == ref) { + return aIf; + } + } else if (a instanceof ActionStore) { + ActionStore aStore = (ActionStore) a; + int storeSize = aStore.getStoreSize(); + int idx = indexOf(a); + int idx2 = indexOf(target); + if (idx != -1 && idx2 == idx + storeSize) { + return a; + } + } else if (a instanceof GraphSourceItemContainer) { + GraphSourceItemContainer container = (GraphSourceItemContainer) a; + long ref = a.getAddress() + a.getTotalActionLength(); + for (Long size : container.getContainerSizes()) { + ref += size; + if (target.getAddress() == ref) { + return a; + } + } + } + } + return null; + } + }; } public void removeNops() { @@ -54,11 +140,36 @@ public class ActionList extends ArrayList { } public Action getByAddress(long address) { - for (Action action : this) { - if (action.getAddress() == address) { - return action; + int idx = getIndexByAddress(address); + return idx == -1 ? null : get(idx); + } + + public int getIndexByAddress(long address) { + int min = 0; + int max = size() - 1; + + while (max >= min) { + int mid = (min + max) / 2; + long midValue = get(mid).getAddress(); + if (midValue == address) { + return mid; + } else if (midValue < address) { + min = mid + 1; + } else { + max = mid - 1; } } - return null; + + return -1; } + + public void saveToFile(String fileName) { + File file = new File(fileName); + try (FileTextWriter writer = new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(file))) { + Action.actionsToString(new ArrayList(), 0, this, SWF.DEFAULT_VERSION, ScriptExportMode.PCODE, writer); + } catch (IOException ex) { + Logger.getLogger(EmptySWFDecompilerListener.class.getName()).log(Level.SEVERE, null, ex); + } + } + } diff --git a/src/com/jpexs/decompiler/flash/action/ActionListReader.java b/src/com/jpexs/decompiler/flash/action/ActionListReader.java index 6949f587e..f8958bb48 100644 --- a/src/com/jpexs/decompiler/flash/action/ActionListReader.java +++ b/src/com/jpexs/decompiler/flash/action/ActionListReader.java @@ -41,7 +41,6 @@ import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.NotCompileTimeItem; @@ -132,90 +131,56 @@ public class ActionListReader { ConstantPool cpool = new ConstantPool(); // Map of the actions. Use TreeMap to sort the keys in ascending order + // actionMap and nextOffsets should contain exaclty the same keys Map actionMap = new TreeMap<>(); Map nextOffsets = new HashMap<>(); Action entryAction = readActionListAtPos(listeners, cpool, sis, actionMap, nextOffsets, ip, 0, endIp, path, false, new ArrayList()); - ActionList actions = new ActionList(); if (actionMap.isEmpty()) { - return actions; + return new ActionList(); } - Map> containerLastActions = new HashMap<>(); List addresses = new ArrayList<>(actionMap.keySet()); - getContainerLastActions(actionMap, addresses, containerLastActions); - - // jump to the entry action when it is diffrent from the first action in the map - long index = addresses.get(0); - if (index != -1 && entryAction != actionMap.get(index)) { - ActionJump jump = new ActionDeobfuscateJump(0); - int size = jump.getTotalActionLength(); - jump.setJumpOffset((int) (entryAction.getAddress() - size)); - actions.add(jump); - } - - // remove nulls - index = getNearAddress(addresses, index, true); - while (index > -1) { - Action action = actionMap.get(index); - long nextOffset = nextOffsets.get(index); - long nextIndex = getNearAddress(addresses, index + 1, true); - actions.add(action); - if (nextIndex != -1 && nextOffset != nextIndex) { - if (!action.isExit() && !(action instanceof ActionJump)) { - ActionJump jump = new ActionDeobfuscateJump(0); - jump.setAddress(action.getAddress()); - int size = jump.getTotalActionLength(); - jump.setJumpOffset((int) (nextOffset - action.getAddress() - size)); - actions.add(jump); - } - } - index = nextIndex; - } - - // Map for storing the targers of the "jump" actions - // "jump" action can be ActionIf, ActionJump and any ActionStore - Map jumps = new HashMap<>(); - getJumps(actions, jumps); - - updateActionLengths(actions, version); - long endAddress = updateAddresses(actions, 0); // add end action - Action lastAction = actions.get(actions.size() - 1); - Action aEnd = new ActionEnd(); + Action lastAction = actionMap.get(addresses.get(addresses.size() - 1)); + long endAddress; if (!(lastAction instanceof ActionEnd)) { - aEnd.setAddress(endAddress); - actions.add(aEnd); - } else { - endAddress -= aEnd.getTotalActionLength(); + Action aEnd = new ActionEnd(); + aEnd.setAddress(nextOffsets.get(lastAction.getAddress())); + endAddress = aEnd.getAddress(); + actionMap.put(aEnd.getAddress(), aEnd); + nextOffsets.put(endAddress, endAddress + 1); } + + ActionList actions = fixActionList(new ActionList(actionMap.values()), nextOffsets, version); - updateJumps(actions, jumps, containerLastActions, endAddress); - updateActionStores(actions, jumps); - updateContainerSizes(actions, containerLastActions); + // jump to the entry action when it is diffrent from the first action in the map + if (entryAction != actions.get(0)) { + ActionJump jump = new ActionDeobfuscateJump(0); + actions.addAction(0, jump); + jump.setJumpOffset((int) (entryAction.getAddress() - jump.getTotalActionLength())); + } if (SWFDecompilerPlugin.listener != null) { try { SWFDecompilerPlugin.listener.actionListParsed(actions); - updateActionLengths(actions, version); - updateAddresses(actions, 0); - updateJumps(actions, jumps, containerLastActions, endAddress); - updateActionStores(actions, jumps); - updateContainerSizes(actions, containerLastActions); + actions = fixActionList(actions, null, version); } catch (Throwable e) { + Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, e); View.showMessageDialog(null, "Failed to call plugin method actionListParsed. Exception: " + e.getMessage()); } } if (Configuration.autoDeobfuscate.get()) { try { - actions = deobfuscateActionList(listeners, actions, version, 0, path); + new ActionDeobfuscator().actionListParsed(actions); + /*actions = deobfuscateActionList(listeners, actions, version, 0, path); updateActionLengths(actions, version); - removeZeroJumps(actions, version); + removeZeroJumps(actions, version);*/ } catch (OutOfMemoryError | StackOverflowError | TranslateException ex) { // keep orignal (not deobfuscated) actions Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, ex); @@ -224,6 +189,51 @@ public class ActionListReader { return actions; } + + public static ActionList fixActionList(ActionList actions, Map nextOffsets, int version) { + Map> containerLastActions = new HashMap<>(); + getContainerLastActions(actions, containerLastActions); + + ActionList ret = new ActionList(); + + if (nextOffsets != null) { + int index = 0; + while (index != -1 && index < actions.size()) { + Action action = actions.get(index); + ret.add(action); + index++; + if (index < actions.size()) { + long nextAddress = nextOffsets.get(action.getAddress()); + if (actions.get(index).getAddress() != nextAddress) { + if (!action.isExit() && !(action instanceof ActionJump)) { + ActionJump jump = new ActionDeobfuscateJump(0); + jump.setAddress(action.getAddress()); + int size = jump.getTotalActionLength(); + jump.setJumpOffset((int) (nextAddress - action.getAddress() - size)); + ret.add(jump); + } + } + } + } + } else { + ret.addAll(actions); + } + + // Map for storing the targers of the "jump" actions + // "jump" action can be ActionIf, ActionJump and any ActionStore + Map jumps = new HashMap<>(); + getJumps(ret, jumps); + + updateActionLengths(ret, version); + updateAddresses(ret, 0); + long endAddress = ret.get(ret.size() - 1).getAddress(); + + updateJumps(ret, jumps, containerLastActions, endAddress); + updateActionStores(ret, jumps); + updateContainerSizes(ret, containerLastActions); + + return ret; + } public static List getOriginalActions(SWFInputStream sis, int startIp, int endIp) throws IOException, InterruptedException { ConstantPool cpool = new ConstantPool(); @@ -313,13 +323,13 @@ public class ActionListReader { return reta; } - private static long getNearAddress(List addresses, long address, boolean next) { + private static long getNearAddress(ActionList actions, long address, boolean next) { int min = 0; - int max = addresses.size() - 1; + int max = actions.size() - 1; while (max >= min) { int mid = (min + max) / 2; - long midValue = addresses.get(mid); + long midValue = actions.get(mid).getAddress(); if (midValue == address) { return address; } else if (midValue < address) { @@ -330,8 +340,8 @@ public class ActionListReader { } return next - ? (min < addresses.size() ? addresses.get(min) : -1) - : (max > 0 ? addresses.get(max) : -1); + ? (min < actions.size() ? actions.get(min).getAddress() : -1) + : (max > 0 ? actions.get(max).getAddress() : -1); } private static Map actionListToMap(List actions) { @@ -378,10 +388,8 @@ public class ActionListReader { } } - private static void getContainerLastActions(Map actionMap, List addresses, Map> lastActions) { - for (Long address : actionMap.keySet()) { - Action a = actionMap.get(address); - + private static void getContainerLastActions(ActionList actions, Map> lastActions) { + for (Action a : actions) { if (a instanceof GraphSourceItemContainer) { GraphSourceItemContainer container = (GraphSourceItemContainer) a; List sizes = container.getContainerSizes(); @@ -389,10 +397,10 @@ public class ActionListReader { List lasts = new ArrayList<>(sizes.size()); for (long size : sizes) { endAddress += size; - long lastActionIndex = getNearAddress(addresses, endAddress - 1, false); + long lastActionAddress = getNearAddress(actions, endAddress - 1, false); Action lastAction = null; - if (lastActionIndex != -1) { - lastAction = actionMap.get(lastActionIndex); + if (lastActionAddress != -1) { + lastAction = actions.getByAddress(lastActionAddress); } lasts.add(lastAction); } @@ -518,20 +526,6 @@ public class ActionListReader { } } - private static void removeZeroJumps(List actions, int version) { - for (int i = 0; i < actions.size(); i++) { - Action a = actions.get(i); - if (a instanceof ActionJump) { - ActionJump aJump = (ActionJump) a; - if (aJump.getJumpOffset() == 0) { - if (removeAction(actions, i, version, false)) { - i--; - } - } - } - } - } - /** * Removes an action from the action list, and updates all references * This method will keep the inner actions of the container when you remove the container @@ -542,7 +536,7 @@ public class ActionListReader { * @param removeWhenLast * @return */ - public static boolean removeAction(List actions, int index, int version, boolean removeWhenLast) { + public static boolean removeAction(ActionList actions, int index, int version, boolean removeWhenLast) { if (index < 0 || actions.size() <= index) { return false; @@ -552,14 +546,8 @@ public class ActionListReader { Action lastAction = actions.get(actions.size() - 1); long endAddress = lastAction.getAddress() + lastAction.getTotalActionLength(); - Map actionMap = new TreeMap<>(); - for (Action a : actions) { - actionMap.put(a.getAddress(), a); - } - List addresses = new ArrayList<>(actionMap.keySet()); - Map> containerLastActions = new HashMap<>(); - getContainerLastActions(actionMap, addresses, containerLastActions); + getContainerLastActions(actions, containerLastActions); Map jumps = new HashMap<>(); getJumps(actions, jumps); @@ -602,6 +590,75 @@ public class ActionListReader { return true; } + /** + * Adds an action to the action list to the specified location, and updates all references + * + * @param actions + * @param index + * @param action + * @param version + * @param addToContainer + * @param replaceJump + * @return + */ + public static boolean addAction(ActionList actions, int index, Action action, + int version, boolean addToContainer, boolean replaceJump) { + + if (index < 0 || actions.size() < index) { + return false; + } + + long startIp = actions.get(0).getAddress(); + Action lastAction = actions.get(actions.size() - 1); + if (!(lastAction instanceof ActionEnd)) { + Action aEnd = new ActionEnd(); + aEnd.setAddress(lastAction.getAddress() + lastAction.getTotalActionLength()); + actions.add(aEnd); + lastAction = aEnd; + } + + long endAddress = lastAction.getAddress(); + + Map> containerLastActions = new HashMap<>(); + getContainerLastActions(actions, containerLastActions); + + Map jumps = new HashMap<>(); + List tempActions = new ArrayList<>(actions); + tempActions.add(action); + getJumps(tempActions, jumps); + + Action prevAction = actions.get(index); + if (addToContainer) { + for (Action a : containerLastActions.keySet()) { + List lastActions = containerLastActions.get(a); + for (int i = 0; i < lastActions.size(); i++) { + if (lastActions.get(i) == prevAction) { + lastActions.set(i, action); + } + } + } + } + + if (replaceJump) { + for (Action a : jumps.keySet()) { + Action targetAction = jumps.get(a); + if (targetAction == prevAction) { + jumps.put(a, action); + } + } + } + + actions.add(index, action); + + updateActionLengths(actions, version); + updateAddresses(actions, startIp); + updateJumps(actions, jumps, containerLastActions, endAddress); + updateActionStores(actions, jumps); + updateContainerSizes(actions, containerLastActions); + + return true; + } + private static Action readActionListAtPos(List listeners, ConstantPool cpool, SWFInputStream sis, Map actions, Map nextOffsets, long ip, long startIp, long endIp, String path, boolean indeterminate, List visitedContainers) throws IOException { @@ -742,7 +799,7 @@ public class ActionListReader { } if (debugMode) { - String atos = a.getASMSource(new ArrayList(), new ArrayList(), cpool.constants, ScriptExportMode.PCODE); + String atos = a.getASMSource(new ActionList(), new ArrayList(), ScriptExportMode.PCODE); if (a instanceof GraphSourceItemContainer) { atos = a.toString(); } @@ -928,8 +985,10 @@ public class ActionListReader { } @SuppressWarnings("unchecked") - Stack substack = (Stack) stack.clone(); - deobfustaceActionListAtPosRecursive(listeners, output, containers, prepareLocalBranch(localData), substack, cpool, actions, ip + actionLen + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel); + Stack subStack = (Stack) stack.clone(); + ActionLocalData subLocalData = new ActionLocalData(new HashMap<>(localData.regNames), + new HashMap<>(localData.variables), new HashMap<>(localData.functions)); + deobfustaceActionListAtPosRecursive(listeners, output, containers, subLocalData, subStack, cpool, actions, ip + actionLen + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel); } if (newip > -1) { @@ -946,10 +1005,4 @@ public class ActionListReader { listener.progress(AppStrings.translate("disassemblingProgress.deobfuscating"), ip, actions.size()); } } - - private static ActionLocalData prepareLocalBranch(ActionLocalData localData) { - - return new ActionLocalData(new HashMap<>(localData.regNames), - new HashMap<>(localData.variables), new HashMap<>(localData.functions)); - } } diff --git a/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java b/src/com/jpexs/decompiler/flash/action/Deobfuscation.java similarity index 99% rename from src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java rename to src/com/jpexs/decompiler/flash/action/Deobfuscation.java index bea9842fb..330b8c67e 100644 --- a/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java +++ b/src/com/jpexs/decompiler/flash/action/Deobfuscation.java @@ -32,7 +32,7 @@ import java.util.regex.Pattern; * * @author JPEXS */ -public class ActionDeobfuscation { +public class Deobfuscation { private final Random rnd = new Random(); private final int DEFAULT_FOO_SIZE = 10; diff --git a/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java b/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java index 1244b08b0..aed21f520 100644 --- a/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java +++ b/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.action.flashlite.ActionStrictMode; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.special.ActionDeobfuscateJump; import com.jpexs.decompiler.flash.action.special.ActionDeobfuscatePop; +import com.jpexs.decompiler.flash.action.special.ActionEnd; import com.jpexs.decompiler.flash.action.special.ActionNop; import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; import com.jpexs.decompiler.flash.action.swf3.ActionGoToLabel; @@ -408,6 +409,8 @@ public class ASMParser { a = (new ActionStrictMode(lexer)); } else if (instructionName.compareToIgnoreCase("Nop") == 0) { a = (new ActionNop()); + } else if (instructionName.compareToIgnoreCase("End") == 0) { + a = (new ActionEnd()); } else if (instructionName.compareToIgnoreCase("FFDec_DeobfuscatePop") == 0) { a = (new ActionDeobfuscatePop()); } else if (instructionName.compareToIgnoreCase("FFDec_DeobfuscateJump") == 0) { diff --git a/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java b/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java index 4e0752779..b47e3350a 100644 --- a/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java +++ b/src/com/jpexs/decompiler/flash/action/swf3/ActionWaitForFrame.java @@ -21,6 +21,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.ActionGraph; +import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.model.ConstantPool; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem; @@ -28,7 +29,6 @@ import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.action.special.ActionStore; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -56,7 +56,7 @@ public class ActionWaitForFrame extends Action implements ActionStore { } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { String ret = "WaitForFrame " + frame + " " + skipCount; return ret; } diff --git a/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java b/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java index 2af322d75..694c20fdc 100644 --- a/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java +++ b/src/com/jpexs/decompiler/flash/action/swf4/ActionIf.java @@ -19,11 +19,11 @@ package com.jpexs.decompiler.flash.action.swf4; 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.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.graph.GraphSource; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.helpers.Helper; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -74,8 +74,9 @@ public class ActionIf extends Action { } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { - String ofsStr = Helper.formatAddress(getAddress() + getTotalActionLength() + offset); + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { + long address = getAddress() + getTotalActionLength() + offset; + String ofsStr = Helper.formatAddress(address); return "If loc" + ofsStr + (!jumpUsed ? " ;compileTimeIgnore" : (!ignoreUsed ? " ;compileTimeJump" : "")); } diff --git a/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java b/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java index 2e1487a51..beac8cfb3 100644 --- a/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java +++ b/src/com/jpexs/decompiler/flash/action/swf4/ActionJump.java @@ -20,11 +20,11 @@ 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.ActionGraphSource; +import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.graph.GraphSource; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.helpers.Helper; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -75,8 +75,9 @@ public class ActionJump extends Action { } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { - String ofsStr = Helper.formatAddress(getAddress() + getTotalActionLength() + offset); + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { + long address = getAddress() + getTotalActionLength() + offset; + String ofsStr = Helper.formatAddress(address); return "Jump loc" + ofsStr; } diff --git a/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java b/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java index c8c528692..1a684322d 100644 --- a/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java +++ b/src/com/jpexs/decompiler/flash/action/swf4/ActionPush.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.EndOfStreamException; 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.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.model.TemporaryRegister; import com.jpexs.decompiler.flash.action.parser.ParseException; @@ -231,7 +232,7 @@ public class ActionPush extends Action { } @Override - public GraphTextWriter getASMSourceReplaced(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode, GraphTextWriter writer) { + public GraphTextWriter getASMSourceReplaced(ActionList container, List knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) { if (replacement == null || replacement.size() < values.size()) { return toString(writer); } @@ -242,7 +243,7 @@ public class ActionPush extends Action { return writer; } - public GraphTextWriter paramsToStringReplaced(List container, List knownAddreses, List constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer) { + public GraphTextWriter paramsToStringReplaced(List container, List knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) { if (replacement == null || replacement.size() < values.size()) { return paramsToString(writer); } diff --git a/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java b/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java index ae2d093d5..04e15e1bc 100644 --- a/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java +++ b/src/com/jpexs/decompiler/flash/action/swf4/ActionWaitForFrame2.java @@ -21,13 +21,13 @@ 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.ActionGraph; +import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.model.ConstantPool; import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.action.special.ActionStore; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -116,13 +116,13 @@ public class ActionWaitForFrame2 extends Action implements ActionStore { } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { String ret = "WaitForFrame2 " + skipCount; /*for (int i = 0; i < skipped.size(); i++) { if (skipped.get(i) instanceof ActionEnd) { break; } - ret += "\r\n" + skipped.get(i).getASMSource(container, knownAddreses, constantPool, version, exportMode); + ret += "\r\n" + skipped.get(i).getASMSource(container, knownAddreses, version, exportMode); }*/ return ret; } diff --git a/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java b/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java index a6a255baa..f0553ee1d 100644 --- a/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java +++ b/src/com/jpexs/decompiler/flash/action/swf5/ActionDefineFunction.java @@ -19,13 +19,13 @@ package com.jpexs.decompiler.flash.action.swf5; 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.model.FunctionActionItem; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.action.parser.script.VariableActionItem; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.helpers.Helper; @@ -146,7 +146,7 @@ public class ActionDefineFunction extends Action implements GraphSourceItemConta } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { StringBuilder paramStr = new StringBuilder(); for (int i = 0; i < paramNames.size(); i++) { paramStr.append("\"").append(Helper.escapeString(paramNames.get(i))).append("\" "); @@ -156,7 +156,7 @@ public class ActionDefineFunction extends Action implements GraphSourceItemConta } @Override - public GraphTextWriter getASMSourceReplaced(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode, GraphTextWriter writer) { + public GraphTextWriter getASMSourceReplaced(ActionList container, List knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) { List oldParamNames = paramNames; if (replacedParamNames != null) { paramNames = replacedParamNames; @@ -165,7 +165,7 @@ public class ActionDefineFunction extends Action implements GraphSourceItemConta if (replacedFunctionName != null) { functionName = replacedFunctionName; } - String ret = getASMSource(container, knownAddreses, constantPool, exportMode); + String ret = getASMSource(container, knownAddreses, exportMode); paramNames = oldParamNames; functionName = oldFunctionName; writer.appendNoHilight(ret); diff --git a/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java b/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java index 325987e55..4919dff8d 100644 --- a/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java +++ b/src/com/jpexs/decompiler/flash/action/swf5/ActionWith.java @@ -19,11 +19,11 @@ package com.jpexs.decompiler.flash.action.swf5; 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.model.clauses.WithActionItem; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; import java.io.ByteArrayOutputStream; @@ -75,7 +75,7 @@ public class ActionWith extends Action implements GraphSourceItemContainer { } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { return "With {"; } diff --git a/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java b/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java index 331869632..504e8a26e 100644 --- a/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java +++ b/src/com/jpexs/decompiler/flash/action/swf7/ActionDefineFunction2.java @@ -19,13 +19,13 @@ package com.jpexs.decompiler.flash.action.swf7; 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.model.FunctionActionItem; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.action.parser.script.VariableActionItem; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.helpers.Helper; @@ -226,7 +226,7 @@ public class ActionDefineFunction2 extends Action implements GraphSourceItemCont } @Override - public GraphTextWriter getASMSourceReplaced(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode, GraphTextWriter writer) { + public GraphTextWriter getASMSourceReplaced(ActionList container, List knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) { List oldParamNames = paramNames; if (replacedParamNames != null) { paramNames = replacedParamNames; @@ -235,7 +235,7 @@ public class ActionDefineFunction2 extends Action implements GraphSourceItemCont if (replacedFunctionName != null) { functionName = replacedFunctionName; } - String ret = getASMSource(container, knownAddreses, constantPool, exportMode); + String ret = getASMSource(container, knownAddreses, exportMode); paramNames = oldParamNames; functionName = oldFunctionName; writer.appendNoHilight(ret); @@ -244,7 +244,7 @@ public class ActionDefineFunction2 extends Action implements GraphSourceItemCont } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { StringBuilder paramStr = new StringBuilder(); for (int i = 0; i < paramNames.size(); i++) { paramStr.append(paramRegisters.get(i)).append(" \"").append(Helper.escapeString(paramNames.get(i))).append("\" "); diff --git a/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java b/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java index 35abd8be9..a6e1cbb8f 100644 --- a/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java +++ b/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.action.swf7; 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.model.ActionItem; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; import com.jpexs.decompiler.flash.action.model.clauses.TryActionItem; @@ -27,7 +28,6 @@ import com.jpexs.decompiler.flash.action.parser.pcode.ASMParsedSymbol; import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.helpers.Helper; @@ -148,7 +148,7 @@ public class ActionTry extends Action implements GraphSourceItemContainer { } @Override - public String getASMSource(List container, List knownAddreses, List constantPool, ScriptExportMode exportMode) { + public String getASMSource(ActionList container, List knownAddreses, ScriptExportMode exportMode) { String ret = ""; ret += "Try "; if (catchBlockFlag) { diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 5e22adce1..c9817a4ba 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.console.CommandLineArgumentParser; import com.jpexs.decompiler.flash.console.ContextMenuTools; import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; +import com.jpexs.decompiler.flash.helpers.EmptySWFDecompilerListener; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.treeitems.SWFList; @@ -880,7 +881,7 @@ public class Main { public static void main(String[] args) throws IOException { String pluginPath = Configuration.pluginPath.get(); - if (pluginPath != null) { + if (pluginPath != null && !pluginPath.isEmpty()) { try { SWFDecompilerPlugin.loadPlugin(pluginPath); } catch (Throwable e) { diff --git a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index d80b77181..54156b208 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionGraph; +import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser; @@ -112,7 +113,7 @@ public class ActionPanel extends JPanel implements ActionListener, SearchListene private boolean ignoreCarret = false; private boolean editMode = false; private boolean editDecompiledMode = false; - private List lastCode; + private ActionList lastCode; private ASMSource src; public JPanel topButtonsPan; private HilightedText srcWithHex; @@ -395,7 +396,7 @@ public class ActionPanel extends JPanel implements ActionListener, SearchListene } DisassemblyListener listener = getDisassemblyListener(); asm.addDisassemblyListener(listener); - List actions = asm.getActions(); + ActionList actions = asm.getActions(); lastCode = actions; asm.removeDisassemblyListener(listener); srcWithHex = null; diff --git a/src/com/jpexs/decompiler/flash/helpers/EmptySWFDecompilerListener.java b/src/com/jpexs/decompiler/flash/helpers/EmptySWFDecompilerListener.java index 820723f7c..205f21248 100644 --- a/src/com/jpexs/decompiler/flash/helpers/EmptySWFDecompilerListener.java +++ b/src/com/jpexs/decompiler/flash/helpers/EmptySWFDecompilerListener.java @@ -28,5 +28,5 @@ public class EmptySWFDecompilerListener implements SWFDecompilerListener { public void actionListParsed(ActionList actions) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } - + } diff --git a/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java b/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java index fd5294121..0aec2e1c7 100644 --- a/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java +++ b/src/com/jpexs/decompiler/flash/helpers/SWFDecompilerPlugin.java @@ -71,7 +71,6 @@ public class SWFDecompilerPlugin { // Creating an instance of our compiled class and try { listener = (SWFDecompilerListener) fileManager.getClassLoader(null).loadClass(fullName).newInstance(); - listener = new EmptySWFDecompilerListener(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) { Logger.getLogger(SWFDecompilerPlugin.class.getName()).log(Level.SEVERE, null, ex); } diff --git a/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index b0e2c6453..b64ba0591 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -136,13 +136,14 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { /** * Converts actions to ASM source * - * @param actions + * @param exportMode PCode or hex? * @param writer + * @param actions * @return ASM source * @throws java.lang.InterruptedException */ @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException { if (actions == null) { actions = getActions(); } diff --git a/src/com/jpexs/decompiler/flash/tags/DoActionTag.java b/src/com/jpexs/decompiler/flash/tags/DoActionTag.java index 12dc4ec71..e7168f233 100644 --- a/src/com/jpexs/decompiler/flash/tags/DoActionTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DoActionTag.java @@ -86,13 +86,14 @@ public class DoActionTag extends Tag implements ASMSource { /** * Converts actions to ASM source * - * @param actions + * @param exportMode PCode or hex? * @param writer + * @param actions * @return ASM source * @throws java.lang.InterruptedException */ @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException { if (actions == null) { actions = getActions(); } diff --git a/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java b/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java index e0b81a2c4..0ad674ce9 100644 --- a/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java @@ -100,13 +100,14 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource { /** * Converts actions to ASM source * - * @param actions + * @param exportMode PCode or hex? * @param writer + * @param actions * @return ASM source * @throws java.lang.InterruptedException */ @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException { if (actions == null) { actions = getActions(); } diff --git a/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java b/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java index 527df3523..276fbe4e0 100644 --- a/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java +++ b/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java @@ -41,7 +41,7 @@ public interface ASMSource extends TreeItem { * @return ASM source * @throws java.lang.InterruptedException */ - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException; + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException; /** * Whether or not this object contains ASM source diff --git a/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java b/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java index 27bc560de..34404b456 100644 --- a/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java +++ b/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java @@ -155,13 +155,14 @@ public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem, S /** * Converts actions to ASM source * - * @param actions + * @param exportMode PCode or hex? * @param writer + * @param actions * @return ASM source * @throws java.lang.InterruptedException */ @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException { if (actions == null) { actions = getActions(); } diff --git a/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java b/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java index 19e6c9bd8..627b7675a 100644 --- a/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java +++ b/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java @@ -171,13 +171,14 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem, S /** * Converts actions to ASM source * - * @param actions + * @param exportMode PCode or hex? * @param writer + * @param actions * @return ASM source * @throws java.lang.InterruptedException */ @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, ActionList actions) throws InterruptedException { if (actions == null) { actions = getActions(); } diff --git a/test/com/jpexs/decompiler/flash/ActionScript2ModificationTest.java b/test/com/jpexs/decompiler/flash/ActionScript2ModificationTest.java index c59febbee..46da723ed 100644 --- a/test/com/jpexs/decompiler/flash/ActionScript2ModificationTest.java +++ b/test/com/jpexs/decompiler/flash/ActionScript2ModificationTest.java @@ -20,6 +20,8 @@ import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.gui.Main; @@ -80,8 +82,29 @@ public class ActionScript2ModificationTest extends ActionStript2TestBase { doa.getASMSource(ScriptExportMode.PCODE, writer, doa.getActions()); String actualResult = normalizeLabels(writer.toString()); - actualResult = actualResult.replaceAll("[ \r\n]", ""); - expectedResult = expectedResult.replaceAll("[ \r\n]", ""); + actualResult = cleanPCode(actualResult); + expectedResult = cleanPCode(expectedResult); + + Assert.assertEquals(actualResult, expectedResult); + } catch (IOException | ParseException | InterruptedException ex) { + fail(); + } + } + + public void testAddAction(String actionsString, String expectedResult, Action action, int index) { + try { + ActionList actions = ASMParser.parse(0, true, actionsString, swf.version, false); + + actions.addAction(index, action); + + DoActionTag doa = getFirstActionTag(); + doa.setActionBytes(Action.actionsToBytes(actions, true, swf.version)); + HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); + doa.getASMSource(ScriptExportMode.PCODE, writer, doa.getActions()); + String actualResult = normalizeLabels(writer.toString()); + + actualResult = cleanPCode(actualResult); + expectedResult = cleanPCode(expectedResult); Assert.assertEquals(actualResult, expectedResult); } catch (IOException | ParseException | InterruptedException ex) { @@ -204,4 +227,107 @@ public class ActionScript2ModificationTest extends ActionStript2TestBase { "label_1:"; testRemoveAction(actionsString, expectedResult, new int[] {7}); } + + @Test + public void testAddAtion1() { + String actionsString = + "ConstantPool\n" + + "DefineFunction \"test\" 1 \"p1\" {\n" + + "Push 1\n" + + "GetVariable\n" + + "}\n" + + "Push 2\n" + + "If label_1\n" + + "Push 3\n" + + "label_1:Push 4"; + String expectedResult = + "ConstantPool\n" + + "GetMember\n" + + "DefineFunction \"test\" 1 \"p1\" {\n" + + "Push 1\n" + + "GetVariable\n" + + "}\n" + + "Push 2\n" + + "If label_1\n" + + "Push 3\n" + + "label_1:Push 4"; + testAddAction(actionsString, expectedResult, new ActionGetMember(), 1); + } + + @Test + public void testAddAtionToContainer() { + String actionsString = + "ConstantPool\n" + + "DefineFunction \"test\" 1 \"p1\" {\n" + + "Push 1\n" + + "GetVariable\n" + + "}\n" + + "Push 2\n" + + "If label_1\n" + + "Push 3\n" + + "label_1:Push 4"; + String expectedResult = + "ConstantPool\n" + + "DefineFunction \"test\" 1 \"p1\" {\n" + + "GetMember\n" + + "Push 1\n" + + "GetVariable\n" + + "}\n" + + "Push 2\n" + + "If label_1\n" + + "Push 3\n" + + "label_1:Push 4"; + testAddAction(actionsString, expectedResult, new ActionGetMember(), 2); + } + + @Test + public void testAddActionIf() { + String actionsString = + "ConstantPool\n" + + "DefineFunction \"test\" 1 \"p1\" {\n" + + "Push 1\n" + + "GetVariable\n" + + "}\n" + + "Push 2\n" + + "If label_1\n" + + "Push 3\n" + + "label_1:Push 4"; + String expectedResult = + "ConstantPool\n" + + "DefineFunction \"test\" 1 \"p1\" {\n" + + "Push 1\n" + + "GetVariable\n" + + "}\n" + + "Push 2\n" + + "If label_1\n" + + "Push 3\n" + + "GetMember\n" + + "label_1:Push 4"; + testAddAction(actionsString, expectedResult, new ActionGetMember(), 7); + } + + @Test + public void testAddToJumpTarget() { + String actionsString = + "ConstantPool\n" + + "If label_1\n" + + "GetMember\n" + + "label_1:Jump label_2\n" + // address 9 + "label_2:Jump label_3\n" + + "label_3:Jump labelend\n" + + "labelend:End"; // address 24 + String expectedResult = + "ConstantPool\n" + + "If label_1\n" + + "GetMember\n" + + "Jump label_4\n" + + "label_1:Jump label_2\n" + + "label_2:Jump label_3\n" + + "label_3:Jump label_4\n" + + "label_4:"; + ActionJump jump = new ActionJump(0); + jump.setAddress(9); + jump.setJumpOffset(24 - 9 - 5); + testAddAction(actionsString, expectedResult, jump, 3); + } } diff --git a/test/com/jpexs/decompiler/flash/ActionScript2Test.java b/test/com/jpexs/decompiler/flash/ActionScript2Test.java index f4aaa0ecc..3b9149503 100644 --- a/test/com/jpexs/decompiler/flash/ActionScript2Test.java +++ b/test/com/jpexs/decompiler/flash/ActionScript2Test.java @@ -1,808 +1,808 @@ -/* - * Copyright (C) 2010-2014 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash; - -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.helpers.CodeFormatting; -import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.Tag; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -/** - * - * @author JPEXS - */ -public class ActionScript2Test extends ActionStript2TestBase { - - @BeforeClass - public void init() throws IOException, InterruptedException { - Main.initLogging(false); - Configuration.autoDeobfuscate.set(false); - Configuration.decompile.set(true); - Configuration.registerNameFormat.set("_loc%d_"); - swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as2/as2.swf")), false); - } - - private void compareSrc(int frame, String expectedResult) { - DoActionTag doa = getFrameSource(frame); - assertNotNull(doa); - HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); - try { - Action.actionsToSource(doa, doa.getActions(), "", writer); - } catch (InterruptedException ex) { - fail(); - } - String actualResult = writer.toString().replaceAll("[ \r\n]", ""); - expectedResult = expectedResult.replaceAll("[ \r\n]", ""); - assertEquals(actualResult, expectedResult); - - } - - private DoActionTag getFrameSource(int frame) { - int f = 0; - DoActionTag lastDoa = null; - - for (Tag t : swf.tags) { - if (t instanceof DoActionTag) { - lastDoa = (DoActionTag) t; - } - if (t instanceof ShowFrameTag) { - f++; - if (f == frame) { - return lastDoa; - } - lastDoa = null; - } - } - return null; - } - - @Test - public void frame23_Test() { - compareSrc(23, "stop();\r\n" - ); - } - - @Test - public void frame24_unicodeTest() { - compareSrc(24, "trace(\"unicodeTest\");\r\n" - + "var k = \"היפופוטמי, או א\";\r\n" - + "trace(k);\r\n" - ); - } - - @Test - public void frame25_ifWithElseTest() { - compareSrc(25, "trace(\"ifWithElseTest\");\r\n" - + "var i = 5;\r\n" - + "if(i == 258)\r\n" - + "{\r\n" - + "trace(\"onTrue\");\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "trace(\"onFalse\" + i);\r\n" - + "}\r\n" - ); - } - - @Test - public void frame26_forTest() { - compareSrc(26, "trace(\"forTest\");\r\n" - + "var i = 0;\r\n" - + "while(i < 10)\r\n" - + "{\r\n" - + "trace(\"hello:\" + i);\r\n" - + "i++;\r\n" - + "}\r\n" - ); - } - - @Test - public void frame27_whileTest() { - compareSrc(27, "trace(\"whileTest\");\r\n" - + "var i = 0;\r\n" - + "while(i < 10)\r\n" - + "{\r\n" - + "trace(\"hello:\" + i);\r\n" - + "i++;\r\n" - + "}\r\n" - ); - } - - @Test - public void frame28_forWithContinueTest() { - compareSrc(28, "trace(\"forWithContinueTest\");\r\n" - + "var i = 0;\r\n" - + "for(;i < 10;i++)\r\n" - + "{\r\n" - + "trace(\"hello:\" + i);\r\n" - + "if(i == 5)\r\n" - + "{\r\n" - + "trace(\"i==5\");\r\n" - + "if(i == 7)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "trace(\"hawk\");\r\n" - + "}\r\n" - + "trace(\"end of the loop\");\r\n" - + "}\r\n" - ); - } - - @Test - public void frame29_doWhileTest() { - compareSrc(29, "trace(\"doWhileTest\");\r\n" - + "var i = 0;\r\n" - + "do\r\n" - + "{\r\n" - + "trace(\"i=\" + i);\r\n" - + "i++;\r\n" - + "}\r\n" - + "while(i < 10);\r\n" - + "trace(\"end\");\r\n" - ); - } - - @Test - public void frame30_switchTest() { - compareSrc(30, "trace(\"switchTest\");\r\n" - + "var i = 5;\r\n" - + "switch(i)\r\n" - + "{\r\n" - + "case 0:\r\n" - + "case 1:\r\n" - + "trace(\"one\");\r\n" - + "break;\r\n" - + "case 2:\r\n" - + "trace(\"two\");\r\n" - + "case 3:\r\n" - + "trace(\"three\");\r\n" - + "break;\r\n" - + "case 4:\r\n" - + "trace(\"four\");\r\n" - + "break;\r\n" - + "default:\r\n" - + "trace(\"default clause\");\r\n" - + "}\r\n" - + "trace(\"scriptend\");\r\n" - ); - } - - @Test - public void frame31_strictEqualsTest() { - compareSrc(31, "trace(\"strictEqualsTest\");\r\n" - + "var i = 5;\r\n" - + "if(i === 5)\r\n" - + "{\r\n" - + "trace(\"equals strict\");\r\n" - + "}\r\n" - + "if(!(i === 5))\r\n" - + "{\r\n" - + "trace(\"not equals strict\");\r\n" - + "}\r\n" - ); - } - - @Test - public void frame32_switchForTest() { - compareSrc(32, "trace(\"switchForTest\");\r\n" - + "var i = 0;\r\n" - + "for(;i < 10;i++)\r\n" - + "{\r\n" - + "switch(i)\r\n" - + "{\r\n" - + "case 0:\r\n" - + "trace(\"zero\");\r\n" - + "continue;\r\n" - + "case 5:\r\n" - + "trace(\"five\");\r\n" - + "break;\r\n" - + "case 10:\r\n" - + "trace(\"ten\");\r\n" - + "break;\r\n" - + "case 1:\r\n" - + "if(i == 7)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "trace(\"one\");\r\n" - + "default:\r\n" - + "trace(\"def\");\r\n" - + "}\r\n" - + "trace(\"before loop end\");\r\n" - + "}\r\n" - ); - } - - @Test - public void frame33_functionTest() { - compareSrc(33, "function hello(what, second)\r\n" - + "{\r\n" - + "trace(\"hello \" + what + \"! \" + second);\r\n" - + "}\r\n" - + "trace(\"functionTest\");\r\n" - + "hello(\"friend\",7);\r\n" - ); - } - - @Test - public void frame34_multipleConditionsTest() { - compareSrc(34, "trace(\"multipleConditionsTest\");\r\n" - + "var k = 5;\r\n" - + "if(k == 7 && k == 8)\r\n" - + "{\r\n" - + "trace(\"first\");\r\n" - + "}\r\n" - + "if(k == 9)\r\n" - + "{\r\n" - + "trace(\"second\");\r\n" - + "}\r\n" - + "trace(\"finish\");\r\n" - ); - } - - @Test - public void frame35_multipleConditions2Test() { - compareSrc(35, "trace(\"multipleConditions2Test\");\r\n" - + "var k = 5;\r\n" - + "if(k == 7 && k == 8)\r\n" - + "{\r\n" - + "trace(\"first\");\r\n" - + "}\r\n" - + "if(k == 9 || k == 6)\r\n" - + "{\r\n" - + "trace(\"second\");\r\n" - + "}\r\n" - + "trace(\"finish\");\r\n" - ); - } - - @Test - public void frame36_chainedAssignmentsTest() { - compareSrc(36, "trace(\"chainedAssignmentsTest\");\r\n" - + "var a = 7;\r\n" - + "var b = 8;\r\n" - + "var c = 9;\r\n" - + "var d = c = b = a = 10;\r\n" - + "trace(d);\r\n" - ); - } - - @Test - public void frame37_objectsTest() { - compareSrc(37, "trace(\"objectsTest\");\r\n" - + "var flashBox = new Box(box1);\r\n" - + "_root.onEnterFrame = function()\r\n" - + "{\r\n" - + "flashBox.moveUp();\r\n" - + "};\r\n" - + "var ship = new Ship(200);\r\n" - + "var enemy = new Enemy(56);\r\n" - + "ship.moveDown(0.5);\r\n" - + "ship.moveUp(0.2);\r\n" - + "enemy.moveRight(230);\r\n" - + "enemy.moveLeft(100);\r\n" - + "var mt = new com.jpexs.MyTest();\r\n" - + "mt.test();\r\n" - + "var c = new Cox(box1);\r\n" - ); - } - - @Test - public void frame38_doWhile2Test() { - compareSrc(38, "trace(\"doWhile2Test\");\r\n" - + "var k = 5;\r\n" - + "do\r\n" - + "{\r\n" - + "k++;\r\n" - + "if(k == 7)\r\n" - + "{\r\n" - + "k = 5 * k;\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "k = 5 + k;\r\n" - + "}\r\n" - + "}\r\n" - + "while(k < 9);\r\n" - ); - } - - @Test - public void frame39_whileAndTest() { - compareSrc(39, "trace(\"whileAndTest\");\r\n" - + "var a = 5;\r\n" - + "var b = 10;\r\n" - + "while(a < 10 && b > 1)\r\n" - + "{\r\n" - + "a++;\r\n" - + "b--;\r\n" - + "}\r\n" - + "a = 7;\r\n" - + "b = 9;\r\n" - ); - } - - @Test - public void frame40_forInTest() { - compareSrc(40, "function testForIn()\r\n" - + "{\r\n" - + "var _loc1_ = [];\r\n" - + "for(var _loc2_ in _loc1_)\r\n" - + "{\r\n" - + "if(_loc2_ > 3)\r\n" - + "{\r\n" - + "if(_loc2_ == 5)\r\n" - + "{\r\n" - + "return 7;\r\n" - + "}\r\n" - + "return 8;\r\n" - + "}\r\n" - + "}\r\n" - + "}\r\n" - + "trace(\"forInTest\");\r\n" - + "trace(testForIn());\r\n" - + "var arr = [];\r\n" - + "for(var a in arr)\r\n" - + "{\r\n" - + "trace(a);\r\n" - + "}\r\n" - ); - } - - @Test - public void frame41_tryTest() { - compareSrc(41, "trace(\"tryTest\");\r\n" - + "var k = 5;\r\n" - + "try\r\n" - + "{\r\n" - + "k = Infinity;\r\n" - + "}\r\n" - + "catch(e)\r\n" - + "{\r\n" - + "trace(\"bug \" + e);\r\n" - + "}\r\n" - + "finally\r\n" - + "{\r\n" - + "trace(\"huu\");\r\n" - + "}\r\n" - + "trace(\"next\");\r\n" - + "try\r\n" - + "{\r\n" - + "k = 6;\r\n" - + "}\r\n" - + "catch(e)\r\n" - + "{\r\n" - + "trace(\"bug2 \" + e);\r\n" - + "}\r\n" - + "trace(\"next2\");\r\n" - + "var k = 5;\r\n" - + "try\r\n" - + "{\r\n" - + "k = Infinity;\r\n" - + "}\r\n" - + "finally\r\n" - + "{\r\n" - + "trace(\"final\");\r\n" - + "}\r\n" - + "trace(\"end\");\r\n" - ); - } - - @Test - public void frame42_indicesTest() { - compareSrc(42, "trace(\"indicesTest\");\r\n" - + "var k = [1,2,3];\r\n" - + "var b = k[1];\r\n" - + "trace(b);\r\n" - ); - } - - @Test - public void frame43_incDecTest() { - compareSrc(43, "function tst()\r\n" - + "{\r\n" - + "return 1;\r\n" - + "}\r\n" - + "trace(\"incDecTest\");\r\n" - + "var i = 5;\r\n" - + "var b = i++;\r\n" - + "var c = --i + 5;\r\n" - + "trace(\"a:\" + a + \" b:\" + b + \" c:\" + c);\r\n" - + "var arr = [1,2,3];\r\n" - + "arr[tst()]++;\r\n" - + "var d = arr[tst()];\r\n" - + "trace(d);\r\n" - ); - } - - @Test - public void frame44_chainedAssignments2Test() { - compareSrc(44, "trace(\"chainedAssignments2Test\");\r\n" - + "var a = 5;\r\n" - + "var b = 6;\r\n" - + "var c = 7;\r\n" - + "var d = c = b = a = 4;\r\n" - + "if((d = c = b = a = 7) > 2)\r\n" - + "{\r\n" - + "trace(d);\r\n" - + "}\r\n" - + "trace(d + 1);\r\n" - + "var i = 0;\r\n" - + "while(i < 5)\r\n" - + "{\r\n" - + "if(i == 7)\r\n" - + "{\r\n" - + "}\r\n" - + "i++;\r\n" - + "}\r\n" - ); - } - - @Test - public void frame45_function2Test() { - compareSrc(45, "function a()\r\n" - + "{\r\n" - + "trace(\"hi\");\r\n" - + "var _loc1_ = 5;\r\n" - + "if(_loc1_ == 7)\r\n" - + "{\r\n" - + "return undefined;\r\n" - + "}\r\n" - + "_loc1_ = _loc1_ * 9;\r\n" - + "trace(_loc1_);\r\n" - + "}\r\n" - + "trace(\"function2Test\");\r\n" - ); - } - - @Test - public void frame46_tryFunctionTest() { - compareSrc(46, "function testtry()\r\n" - + "{\r\n" - + "var _loc1_ = 5;\r\n" - + "try\r\n" - + "{\r\n" - + "if(_loc1_ == 3)\r\n" - + "{\r\n" - + "return undefined;\r\n" - + "}\r\n" - + "if(_loc1_ == 4)\r\n" - + "{\r\n" - + "throw new Error();\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "_loc1_ = 7;\r\n" - + "}\r\n" - + "}\r\n" - + "catch(e)\r\n" - + "{\r\n" - + "trace(\"error\");\r\n" - + "}\r\n" - + "finally\r\n" - + "{\r\n" - + "trace(\"finally\");\r\n" - + "}\r\n" - + "}\r\n" - + "trace(\"tryFunctionTest\");\r\n" - ); - } - - @Test - public void frame47_ternarTest() { - compareSrc(47, "trace(\"ternarTest\");\r\n" - + "var a = 5;\r\n" - + "var b = a != 4?3:2;\r\n" - + "trace(b);\r\n" - ); - } - - @Test - public void frame48_forInInTest() { - compareSrc(48, "function tst()\r\n" - + "{\r\n" - + "var _loc2_ = [];\r\n" - + "_loc2_[0] = [];\r\n" - + "for(var _loc3_ in _loc2_)\r\n" - + "{\r\n" - + "for(var _loc1_ in _loc3_)\r\n" - + "{\r\n" - + "if(_loc1_ == 5)\r\n" - + "{\r\n" - + "return 5;\r\n" - + "}\r\n" - + "}\r\n" - + "if(_loc3_ == 8)\r\n" - + "{\r\n" - + "return 3;\r\n" - + "}\r\n" - + "}\r\n" - + "return 8;\r\n" - + "}\r\n" - + "trace(\"forInInTest\");\r\n" - + "tst();\r\n" - ); - } - - @Test - public void frame49_registersFuncTest() { - compareSrc(49, "function tst(px)\r\n" - + "{\r\n" - + "var _loc1_ = 57;\r\n" - + "_loc1_ = _loc1_ * 27;\r\n" - + "}\r\n" - + "trace(\"registersFuncTest\");\r\n" - + "tst(5);\r\n" - + "var s = String(5);\r\n" - ); - } - - @Test - public void frame50_ifFrameLoadedTest() { - compareSrc(50, "trace(\"ifFrameLoadedTest\");\r\n" - + "ifFrameLoaded(9)\r\n" - + "{\r\n" - + "trace(\"loaded\");\r\n" - + "}\r\n" - ); - } - - @Test - public void frame51_function3Test() { - compareSrc(51, "function tst()\r\n" - + "{\r\n" - + "var _loc1_ = 5;\r\n" - + "c = _loc1_ = 8;\r\n" - + "trace(\"hi\");\r\n" - + "trace(_loc1_);\r\n" - + "if((e = d = f = c = 9) > 5)\r\n" - + "{\r\n" - + "trace(\"dd\");\r\n" - + "}\r\n" - + "}\r\n" - + "trace(\"function3Test\");\r\n" - + "var c = 7;\r\n" - + "var d = 7;\r\n" - + "var e = 8;\r\n" - + "tst();\r\n" - ); - } - - @Test - public void frame52_commaOperatorTest() { - compareSrc(52, "trace(\"commaOperatorTest\");\r\n" - + "var a = 0;\r\n" - + "var b = 0;\r\n" - + "var c = 0;\r\n" - + "while(true)\r\n" - + "{\r\n" - + "a++;\r\n" - + "b = b + 2;\r\n" - + "if(c < 10)\r\n" - + "{\r\n" - + "trace(c);\r\n" - + "c++;\r\n" - + "continue;\r\n" - + "}\r\n" - + "break;\r\n" - + "}\r\n" - + "trace(\"konec\");\r\n" - ); - } - - @Test - public void frame53_commaOperator2Test() { - compareSrc(53, "trace(\"commaOperator2Test\");\r\n" - + "var k = 8;\r\n" - + "do\r\n" - + "{\r\n" - + "if(k == 9)\r\n" - + "{\r\n" - + "trace(\"h\");\r\n" - + "if(k == 9)\r\n" - + "{\r\n" - + "trace(\"f\");\r\n" - + "continue;\r\n" - + "}\r\n" - + "trace(\"b\");\r\n" - + "}\r\n" - + "trace(\"gg\");\r\n" - + "}\r\n" - + "while(k++, k < 10);\r\n" - + "trace(\"ss\");\r\n" - ); - } - - @Test - public void frame54_function4Test() { - compareSrc(54, "function tst()\r\n" - + "{\r\n" - + "var _loc1_ = 5;\r\n" - + "while(_loc1_ < 10)\r\n" - + "{\r\n" - + "if(_loc1_ == 5)\r\n" - + "{\r\n" - + "if(_loc1_ == 6)\r\n" - + "{\r\n" - + "return true;\r\n" - + "}\r\n" - + "_loc1_ = _loc1_ + 1;\r\n" - + "continue;\r\n" - + "}\r\n" - + "return false;\r\n" - + "}\r\n" - + "}\r\n" - + "trace(\"function4Test\");\r\n" - + "tst();\r\n" - ); - } - - @Test - public void frame55_pushTest() { - compareSrc(55, "trace(\"pushTest\");\r\n" - ); - } - - @Test - public void frame56_commaOperator3Test() { - compareSrc(56, "trace(\"commaOperator3Test\");\r\n" - + "var k = 1;\r\n" - + "while(true)\r\n" - + "{\r\n" - + "k++;\r\n" - + "if(k < 10)\r\n" - + "{\r\n" - + "k = k * 5;\r\n" - + "trace(k);\r\n" - + "continue;\r\n" - + "}\r\n" - + "break;\r\n" - + "}\r\n" - + "trace(\"end\");\r\n" - ); - } - - @Test - public void frame57_commaOperator4Test() { - compareSrc(57, "trace(\"commaOperator4Test\");\r\n" - + "var k = 0;\r\n" - + "do\r\n" - + "{\r\n" - + "trace(k);\r\n" - + "if(k == 8)\r\n" - + "{\r\n" - + "trace(\"a\");\r\n" - + "if(k == 9)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "trace(\"d\");\r\n" - + "trace(\"b\");\r\n" - + "}\r\n" - + "k++;\r\n" - + "}\r\n" - + "while(k = k + 5, k < 20);\r\n" - + "trace(\"end\");\r\n" - ); - } - - @Test - public void frame58_globalFunctionsTest() { - compareSrc(58, "function tst(p1)\r\n" - + "{\r\n" - + "trace(\"test\");\r\n" - + "}\r\n" - + "trace(\"globalFunctionsTest\");\r\n" - + "var k = Array(1,2,3);\r\n" - + "var a = 1;\r\n" - + "var b = Boolean(a);\r\n" - + "call(5);\r\n" - + "var c = \"A\";\r\n" - + "clearInterval(5);\r\n" - + "clearTimeout(4);\r\n" - + "var mc;\r\n" - + "duplicateMovieClip(mc,\"copy\",16389);\r\n" - + "a = escape(\"how\");\r\n" - + "var f = a;\r\n" - + "fscommand(\"alert(\\\"hi\\\");\");\r\n" - + "a = mc._alpha;\r\n" - + "a = getTimer();\r\n" - + "getURL(\"http://localhost/\",\"wnd\",\"POST\");\r\n" - + "a = getVersion();\r\n" - + "gotoAndPlay(5);\r\n" - + "gotoAndStop(8);\r\n" - + "ifFrameLoaded(4)\r\n" - + "{\r\n" - + "trace(\"loaded\");\r\n" - + "}\r\n" - + "a = int(f);\r\n" - + "a = isFinite(f);\r\n" - + "a = isNaN(f);\r\n" - + "a = length(f);\r\n" - + "loadMovie(\"http://localhost/test.swf\",a,\"GET\");\r\n" - + "loadMovieNum(\"http://localhost/test.swf\",5,\"GET\");\r\n" - + "loadVariables(\"http://localhost/vars.txt\",a,\"GET\");\r\n" - + "loadVariablesNum(\"http://localhost/vars.txt\",4,\"GET\");\r\n" - + "a = mbchr(f);\r\n" - + "a = mblength(f);\r\n" - + "a = mbord(f);\r\n" - + "a = mbsubstring(\"aaaa\",5,4);\r\n" - + "MMExecute(\"destroyPC\");\r\n" - + "nextFrame();\r\n" - + "gotoAndStop(1);\r\n" - + "a = Number(f);\r\n" - + "a = Object(f);\r\n" - + "a = ord(f);\r\n" - + "a = parseFloat(f);\r\n" - + "a = parseInt(f,16);\r\n" - + "play();\r\n" - + "prevFrame();\r\n" - + "gotoAndStop(1);\r\n" - + "print(mc,\"bframe\");\r\n" - + "printAsBitmap(mc,\"bframe\");\r\n" - + "printAsBitmapNum(5,\"bframe\");\r\n" - + "printNum(4,\"bframe\");\r\n" - + "a = random(10);\r\n" - + "removeMovieClip(mc);\r\n" - + "setInterval(tst,5,f);\r\n" - + "mc._alpha = 25;\r\n" - + "setTimeout(tst,5,f);\r\n" - + "showRedrawRegions(false,0);\r\n" - + "startDrag(mc,1,5,5,6,6);\r\n" - + "stop();\r\n" - + "stopAllSounds();\r\n" - + "stopDrag();\r\n" - + "a = String(f);\r\n" - + "a = \"aa\";\r\n" - + "targetPath(f);\r\n" - + "tellTarget(mc)\r\n" - + "{\r\n" - + "trace(\"told\");\r\n" - + "}\r\n" - + "toggleHighQuality();\r\n" - + "a = unescape(f);\r\n" - + "unloadMovie(mc);\r\n" - + "unloadMovieNum(4);\r\n" - + "updateAfterEvent();\r\n" - ); - } - - @Test - public void frame59_unaryOpTest() { - compareSrc(59, "trace(\"unaryOpTest\");\r\n" - + "var a = 5;\r\n" - + "var c = ~a;\r\n" - + "var d = ~(a + c);\r\n" - + "var e = - c;\r\n" - ); - } - -} +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.helpers.CodeFormatting; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.Tag; +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author JPEXS + */ +public class ActionScript2Test extends ActionStript2TestBase { + + @BeforeClass + public void init() throws IOException, InterruptedException { + Main.initLogging(false); + Configuration.autoDeobfuscate.set(false); + Configuration.decompile.set(true); + Configuration.registerNameFormat.set("_loc%d_"); + swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as2/as2.swf")), false); + } + + private void compareSrc(int frame, String expectedResult) { + DoActionTag doa = getFrameSource(frame); + assertNotNull(doa); + HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); + try { + Action.actionsToSource(doa, doa.getActions(), "", writer); + } catch (InterruptedException ex) { + fail(); + } + String actualResult = cleanPCode(writer.toString()); + expectedResult = cleanPCode(expectedResult); + assertEquals(actualResult, expectedResult); + + } + + private DoActionTag getFrameSource(int frame) { + int f = 0; + DoActionTag lastDoa = null; + + for (Tag t : swf.tags) { + if (t instanceof DoActionTag) { + lastDoa = (DoActionTag) t; + } + if (t instanceof ShowFrameTag) { + f++; + if (f == frame) { + return lastDoa; + } + lastDoa = null; + } + } + return null; + } + + @Test + public void frame23_Test() { + compareSrc(23, "stop();\r\n" + ); + } + + @Test + public void frame24_unicodeTest() { + compareSrc(24, "trace(\"unicodeTest\");\r\n" + + "var k = \"היפופוטמי, או א\";\r\n" + + "trace(k);\r\n" + ); + } + + @Test + public void frame25_ifWithElseTest() { + compareSrc(25, "trace(\"ifWithElseTest\");\r\n" + + "var i = 5;\r\n" + + "if(i == 258)\r\n" + + "{\r\n" + + "trace(\"onTrue\");\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"onFalse\" + i);\r\n" + + "}\r\n" + ); + } + + @Test + public void frame26_forTest() { + compareSrc(26, "trace(\"forTest\");\r\n" + + "var i = 0;\r\n" + + "while(i < 10)\r\n" + + "{\r\n" + + "trace(\"hello:\" + i);\r\n" + + "i++;\r\n" + + "}\r\n" + ); + } + + @Test + public void frame27_whileTest() { + compareSrc(27, "trace(\"whileTest\");\r\n" + + "var i = 0;\r\n" + + "while(i < 10)\r\n" + + "{\r\n" + + "trace(\"hello:\" + i);\r\n" + + "i++;\r\n" + + "}\r\n" + ); + } + + @Test + public void frame28_forWithContinueTest() { + compareSrc(28, "trace(\"forWithContinueTest\");\r\n" + + "var i = 0;\r\n" + + "for(;i < 10;i++)\r\n" + + "{\r\n" + + "trace(\"hello:\" + i);\r\n" + + "if(i == 5)\r\n" + + "{\r\n" + + "trace(\"i==5\");\r\n" + + "if(i == 7)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"hawk\");\r\n" + + "}\r\n" + + "trace(\"end of the loop\");\r\n" + + "}\r\n" + ); + } + + @Test + public void frame29_doWhileTest() { + compareSrc(29, "trace(\"doWhileTest\");\r\n" + + "var i = 0;\r\n" + + "do\r\n" + + "{\r\n" + + "trace(\"i=\" + i);\r\n" + + "i++;\r\n" + + "}\r\n" + + "while(i < 10);\r\n" + + "trace(\"end\");\r\n" + ); + } + + @Test + public void frame30_switchTest() { + compareSrc(30, "trace(\"switchTest\");\r\n" + + "var i = 5;\r\n" + + "switch(i)\r\n" + + "{\r\n" + + "case 0:\r\n" + + "case 1:\r\n" + + "trace(\"one\");\r\n" + + "break;\r\n" + + "case 2:\r\n" + + "trace(\"two\");\r\n" + + "case 3:\r\n" + + "trace(\"three\");\r\n" + + "break;\r\n" + + "case 4:\r\n" + + "trace(\"four\");\r\n" + + "break;\r\n" + + "default:\r\n" + + "trace(\"default clause\");\r\n" + + "}\r\n" + + "trace(\"scriptend\");\r\n" + ); + } + + @Test + public void frame31_strictEqualsTest() { + compareSrc(31, "trace(\"strictEqualsTest\");\r\n" + + "var i = 5;\r\n" + + "if(i === 5)\r\n" + + "{\r\n" + + "trace(\"equals strict\");\r\n" + + "}\r\n" + + "if(!(i === 5))\r\n" + + "{\r\n" + + "trace(\"not equals strict\");\r\n" + + "}\r\n" + ); + } + + @Test + public void frame32_switchForTest() { + compareSrc(32, "trace(\"switchForTest\");\r\n" + + "var i = 0;\r\n" + + "for(;i < 10;i++)\r\n" + + "{\r\n" + + "switch(i)\r\n" + + "{\r\n" + + "case 0:\r\n" + + "trace(\"zero\");\r\n" + + "continue;\r\n" + + "case 5:\r\n" + + "trace(\"five\");\r\n" + + "break;\r\n" + + "case 10:\r\n" + + "trace(\"ten\");\r\n" + + "break;\r\n" + + "case 1:\r\n" + + "if(i == 7)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"one\");\r\n" + + "default:\r\n" + + "trace(\"def\");\r\n" + + "}\r\n" + + "trace(\"before loop end\");\r\n" + + "}\r\n" + ); + } + + @Test + public void frame33_functionTest() { + compareSrc(33, "function hello(what, second)\r\n" + + "{\r\n" + + "trace(\"hello \" + what + \"! \" + second);\r\n" + + "}\r\n" + + "trace(\"functionTest\");\r\n" + + "hello(\"friend\",7);\r\n" + ); + } + + @Test + public void frame34_multipleConditionsTest() { + compareSrc(34, "trace(\"multipleConditionsTest\");\r\n" + + "var k = 5;\r\n" + + "if(k == 7 && k == 8)\r\n" + + "{\r\n" + + "trace(\"first\");\r\n" + + "}\r\n" + + "if(k == 9)\r\n" + + "{\r\n" + + "trace(\"second\");\r\n" + + "}\r\n" + + "trace(\"finish\");\r\n" + ); + } + + @Test + public void frame35_multipleConditions2Test() { + compareSrc(35, "trace(\"multipleConditions2Test\");\r\n" + + "var k = 5;\r\n" + + "if(k == 7 && k == 8)\r\n" + + "{\r\n" + + "trace(\"first\");\r\n" + + "}\r\n" + + "if(k == 9 || k == 6)\r\n" + + "{\r\n" + + "trace(\"second\");\r\n" + + "}\r\n" + + "trace(\"finish\");\r\n" + ); + } + + @Test + public void frame36_chainedAssignmentsTest() { + compareSrc(36, "trace(\"chainedAssignmentsTest\");\r\n" + + "var a = 7;\r\n" + + "var b = 8;\r\n" + + "var c = 9;\r\n" + + "var d = c = b = a = 10;\r\n" + + "trace(d);\r\n" + ); + } + + @Test + public void frame37_objectsTest() { + compareSrc(37, "trace(\"objectsTest\");\r\n" + + "var flashBox = new Box(box1);\r\n" + + "_root.onEnterFrame = function()\r\n" + + "{\r\n" + + "flashBox.moveUp();\r\n" + + "};\r\n" + + "var ship = new Ship(200);\r\n" + + "var enemy = new Enemy(56);\r\n" + + "ship.moveDown(0.5);\r\n" + + "ship.moveUp(0.2);\r\n" + + "enemy.moveRight(230);\r\n" + + "enemy.moveLeft(100);\r\n" + + "var mt = new com.jpexs.MyTest();\r\n" + + "mt.test();\r\n" + + "var c = new Cox(box1);\r\n" + ); + } + + @Test + public void frame38_doWhile2Test() { + compareSrc(38, "trace(\"doWhile2Test\");\r\n" + + "var k = 5;\r\n" + + "do\r\n" + + "{\r\n" + + "k++;\r\n" + + "if(k == 7)\r\n" + + "{\r\n" + + "k = 5 * k;\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "k = 5 + k;\r\n" + + "}\r\n" + + "}\r\n" + + "while(k < 9);\r\n" + ); + } + + @Test + public void frame39_whileAndTest() { + compareSrc(39, "trace(\"whileAndTest\");\r\n" + + "var a = 5;\r\n" + + "var b = 10;\r\n" + + "while(a < 10 && b > 1)\r\n" + + "{\r\n" + + "a++;\r\n" + + "b--;\r\n" + + "}\r\n" + + "a = 7;\r\n" + + "b = 9;\r\n" + ); + } + + @Test + public void frame40_forInTest() { + compareSrc(40, "function testForIn()\r\n" + + "{\r\n" + + "var _loc1_ = [];\r\n" + + "for(var _loc2_ in _loc1_)\r\n" + + "{\r\n" + + "if(_loc2_ > 3)\r\n" + + "{\r\n" + + "if(_loc2_ == 5)\r\n" + + "{\r\n" + + "return 7;\r\n" + + "}\r\n" + + "return 8;\r\n" + + "}\r\n" + + "}\r\n" + + "}\r\n" + + "trace(\"forInTest\");\r\n" + + "trace(testForIn());\r\n" + + "var arr = [];\r\n" + + "for(var a in arr)\r\n" + + "{\r\n" + + "trace(a);\r\n" + + "}\r\n" + ); + } + + @Test + public void frame41_tryTest() { + compareSrc(41, "trace(\"tryTest\");\r\n" + + "var k = 5;\r\n" + + "try\r\n" + + "{\r\n" + + "k = Infinity;\r\n" + + "}\r\n" + + "catch(e)\r\n" + + "{\r\n" + + "trace(\"bug \" + e);\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"huu\");\r\n" + + "}\r\n" + + "trace(\"next\");\r\n" + + "try\r\n" + + "{\r\n" + + "k = 6;\r\n" + + "}\r\n" + + "catch(e)\r\n" + + "{\r\n" + + "trace(\"bug2 \" + e);\r\n" + + "}\r\n" + + "trace(\"next2\");\r\n" + + "var k = 5;\r\n" + + "try\r\n" + + "{\r\n" + + "k = Infinity;\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"final\");\r\n" + + "}\r\n" + + "trace(\"end\");\r\n" + ); + } + + @Test + public void frame42_indicesTest() { + compareSrc(42, "trace(\"indicesTest\");\r\n" + + "var k = [1,2,3];\r\n" + + "var b = k[1];\r\n" + + "trace(b);\r\n" + ); + } + + @Test + public void frame43_incDecTest() { + compareSrc(43, "function tst()\r\n" + + "{\r\n" + + "return 1;\r\n" + + "}\r\n" + + "trace(\"incDecTest\");\r\n" + + "var i = 5;\r\n" + + "var b = i++;\r\n" + + "var c = --i + 5;\r\n" + + "trace(\"a:\" + a + \" b:\" + b + \" c:\" + c);\r\n" + + "var arr = [1,2,3];\r\n" + + "arr[tst()]++;\r\n" + + "var d = arr[tst()];\r\n" + + "trace(d);\r\n" + ); + } + + @Test + public void frame44_chainedAssignments2Test() { + compareSrc(44, "trace(\"chainedAssignments2Test\");\r\n" + + "var a = 5;\r\n" + + "var b = 6;\r\n" + + "var c = 7;\r\n" + + "var d = c = b = a = 4;\r\n" + + "if((d = c = b = a = 7) > 2)\r\n" + + "{\r\n" + + "trace(d);\r\n" + + "}\r\n" + + "trace(d + 1);\r\n" + + "var i = 0;\r\n" + + "while(i < 5)\r\n" + + "{\r\n" + + "if(i == 7)\r\n" + + "{\r\n" + + "}\r\n" + + "i++;\r\n" + + "}\r\n" + ); + } + + @Test + public void frame45_function2Test() { + compareSrc(45, "function a()\r\n" + + "{\r\n" + + "trace(\"hi\");\r\n" + + "var _loc1_ = 5;\r\n" + + "if(_loc1_ == 7)\r\n" + + "{\r\n" + + "return undefined;\r\n" + + "}\r\n" + + "_loc1_ = _loc1_ * 9;\r\n" + + "trace(_loc1_);\r\n" + + "}\r\n" + + "trace(\"function2Test\");\r\n" + ); + } + + @Test + public void frame46_tryFunctionTest() { + compareSrc(46, "function testtry()\r\n" + + "{\r\n" + + "var _loc1_ = 5;\r\n" + + "try\r\n" + + "{\r\n" + + "if(_loc1_ == 3)\r\n" + + "{\r\n" + + "return undefined;\r\n" + + "}\r\n" + + "if(_loc1_ == 4)\r\n" + + "{\r\n" + + "throw new Error();\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "_loc1_ = 7;\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e)\r\n" + + "{\r\n" + + "trace(\"error\");\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"finally\");\r\n" + + "}\r\n" + + "}\r\n" + + "trace(\"tryFunctionTest\");\r\n" + ); + } + + @Test + public void frame47_ternarTest() { + compareSrc(47, "trace(\"ternarTest\");\r\n" + + "var a = 5;\r\n" + + "var b = a != 4?3:2;\r\n" + + "trace(b);\r\n" + ); + } + + @Test + public void frame48_forInInTest() { + compareSrc(48, "function tst()\r\n" + + "{\r\n" + + "var _loc2_ = [];\r\n" + + "_loc2_[0] = [];\r\n" + + "for(var _loc3_ in _loc2_)\r\n" + + "{\r\n" + + "for(var _loc1_ in _loc3_)\r\n" + + "{\r\n" + + "if(_loc1_ == 5)\r\n" + + "{\r\n" + + "return 5;\r\n" + + "}\r\n" + + "}\r\n" + + "if(_loc3_ == 8)\r\n" + + "{\r\n" + + "return 3;\r\n" + + "}\r\n" + + "}\r\n" + + "return 8;\r\n" + + "}\r\n" + + "trace(\"forInInTest\");\r\n" + + "tst();\r\n" + ); + } + + @Test + public void frame49_registersFuncTest() { + compareSrc(49, "function tst(px)\r\n" + + "{\r\n" + + "var _loc1_ = 57;\r\n" + + "_loc1_ = _loc1_ * 27;\r\n" + + "}\r\n" + + "trace(\"registersFuncTest\");\r\n" + + "tst(5);\r\n" + + "var s = String(5);\r\n" + ); + } + + @Test + public void frame50_ifFrameLoadedTest() { + compareSrc(50, "trace(\"ifFrameLoadedTest\");\r\n" + + "ifFrameLoaded(9)\r\n" + + "{\r\n" + + "trace(\"loaded\");\r\n" + + "}\r\n" + ); + } + + @Test + public void frame51_function3Test() { + compareSrc(51, "function tst()\r\n" + + "{\r\n" + + "var _loc1_ = 5;\r\n" + + "c = _loc1_ = 8;\r\n" + + "trace(\"hi\");\r\n" + + "trace(_loc1_);\r\n" + + "if((e = d = f = c = 9) > 5)\r\n" + + "{\r\n" + + "trace(\"dd\");\r\n" + + "}\r\n" + + "}\r\n" + + "trace(\"function3Test\");\r\n" + + "var c = 7;\r\n" + + "var d = 7;\r\n" + + "var e = 8;\r\n" + + "tst();\r\n" + ); + } + + @Test + public void frame52_commaOperatorTest() { + compareSrc(52, "trace(\"commaOperatorTest\");\r\n" + + "var a = 0;\r\n" + + "var b = 0;\r\n" + + "var c = 0;\r\n" + + "while(true)\r\n" + + "{\r\n" + + "a++;\r\n" + + "b = b + 2;\r\n" + + "if(c < 10)\r\n" + + "{\r\n" + + "trace(c);\r\n" + + "c++;\r\n" + + "continue;\r\n" + + "}\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"konec\");\r\n" + ); + } + + @Test + public void frame53_commaOperator2Test() { + compareSrc(53, "trace(\"commaOperator2Test\");\r\n" + + "var k = 8;\r\n" + + "do\r\n" + + "{\r\n" + + "if(k == 9)\r\n" + + "{\r\n" + + "trace(\"h\");\r\n" + + "if(k == 9)\r\n" + + "{\r\n" + + "trace(\"f\");\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"b\");\r\n" + + "}\r\n" + + "trace(\"gg\");\r\n" + + "}\r\n" + + "while(k++, k < 10);\r\n" + + "trace(\"ss\");\r\n" + ); + } + + @Test + public void frame54_function4Test() { + compareSrc(54, "function tst()\r\n" + + "{\r\n" + + "var _loc1_ = 5;\r\n" + + "while(_loc1_ < 10)\r\n" + + "{\r\n" + + "if(_loc1_ == 5)\r\n" + + "{\r\n" + + "if(_loc1_ == 6)\r\n" + + "{\r\n" + + "return true;\r\n" + + "}\r\n" + + "_loc1_ = _loc1_ + 1;\r\n" + + "continue;\r\n" + + "}\r\n" + + "return false;\r\n" + + "}\r\n" + + "}\r\n" + + "trace(\"function4Test\");\r\n" + + "tst();\r\n" + ); + } + + @Test + public void frame55_pushTest() { + compareSrc(55, "trace(\"pushTest\");\r\n" + ); + } + + @Test + public void frame56_commaOperator3Test() { + compareSrc(56, "trace(\"commaOperator3Test\");\r\n" + + "var k = 1;\r\n" + + "while(true)\r\n" + + "{\r\n" + + "k++;\r\n" + + "if(k < 10)\r\n" + + "{\r\n" + + "k = k * 5;\r\n" + + "trace(k);\r\n" + + "continue;\r\n" + + "}\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"end\");\r\n" + ); + } + + @Test + public void frame57_commaOperator4Test() { + compareSrc(57, "trace(\"commaOperator4Test\");\r\n" + + "var k = 0;\r\n" + + "do\r\n" + + "{\r\n" + + "trace(k);\r\n" + + "if(k == 8)\r\n" + + "{\r\n" + + "trace(\"a\");\r\n" + + "if(k == 9)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"d\");\r\n" + + "trace(\"b\");\r\n" + + "}\r\n" + + "k++;\r\n" + + "}\r\n" + + "while(k = k + 5, k < 20);\r\n" + + "trace(\"end\");\r\n" + ); + } + + @Test + public void frame58_globalFunctionsTest() { + compareSrc(58, "function tst(p1)\r\n" + + "{\r\n" + + "trace(\"test\");\r\n" + + "}\r\n" + + "trace(\"globalFunctionsTest\");\r\n" + + "var k = Array(1,2,3);\r\n" + + "var a = 1;\r\n" + + "var b = Boolean(a);\r\n" + + "call(5);\r\n" + + "var c = \"A\";\r\n" + + "clearInterval(5);\r\n" + + "clearTimeout(4);\r\n" + + "var mc;\r\n" + + "duplicateMovieClip(mc,\"copy\",16389);\r\n" + + "a = escape(\"how\");\r\n" + + "var f = a;\r\n" + + "fscommand(\"alert(\\\"hi\\\");\");\r\n" + + "a = mc._alpha;\r\n" + + "a = getTimer();\r\n" + + "getURL(\"http://localhost/\",\"wnd\",\"POST\");\r\n" + + "a = getVersion();\r\n" + + "gotoAndPlay(5);\r\n" + + "gotoAndStop(8);\r\n" + + "ifFrameLoaded(4)\r\n" + + "{\r\n" + + "trace(\"loaded\");\r\n" + + "}\r\n" + + "a = int(f);\r\n" + + "a = isFinite(f);\r\n" + + "a = isNaN(f);\r\n" + + "a = length(f);\r\n" + + "loadMovie(\"http://localhost/test.swf\",a,\"GET\");\r\n" + + "loadMovieNum(\"http://localhost/test.swf\",5,\"GET\");\r\n" + + "loadVariables(\"http://localhost/vars.txt\",a,\"GET\");\r\n" + + "loadVariablesNum(\"http://localhost/vars.txt\",4,\"GET\");\r\n" + + "a = mbchr(f);\r\n" + + "a = mblength(f);\r\n" + + "a = mbord(f);\r\n" + + "a = mbsubstring(\"aaaa\",5,4);\r\n" + + "MMExecute(\"destroyPC\");\r\n" + + "nextFrame();\r\n" + + "gotoAndStop(1);\r\n" + + "a = Number(f);\r\n" + + "a = Object(f);\r\n" + + "a = ord(f);\r\n" + + "a = parseFloat(f);\r\n" + + "a = parseInt(f,16);\r\n" + + "play();\r\n" + + "prevFrame();\r\n" + + "gotoAndStop(1);\r\n" + + "print(mc,\"bframe\");\r\n" + + "printAsBitmap(mc,\"bframe\");\r\n" + + "printAsBitmapNum(5,\"bframe\");\r\n" + + "printNum(4,\"bframe\");\r\n" + + "a = random(10);\r\n" + + "removeMovieClip(mc);\r\n" + + "setInterval(tst,5,f);\r\n" + + "mc._alpha = 25;\r\n" + + "setTimeout(tst,5,f);\r\n" + + "showRedrawRegions(false,0);\r\n" + + "startDrag(mc,1,5,5,6,6);\r\n" + + "stop();\r\n" + + "stopAllSounds();\r\n" + + "stopDrag();\r\n" + + "a = String(f);\r\n" + + "a = \"aa\";\r\n" + + "targetPath(f);\r\n" + + "tellTarget(mc)\r\n" + + "{\r\n" + + "trace(\"told\");\r\n" + + "}\r\n" + + "toggleHighQuality();\r\n" + + "a = unescape(f);\r\n" + + "unloadMovie(mc);\r\n" + + "unloadMovieNum(4);\r\n" + + "updateAfterEvent();\r\n" + ); + } + + @Test + public void frame59_unaryOpTest() { + compareSrc(59, "trace(\"unaryOpTest\");\r\n" + + "var a = 5;\r\n" + + "var c = ~a;\r\n" + + "var d = ~(a + c);\r\n" + + "var e = - c;\r\n" + ); + } + +} diff --git a/test/com/jpexs/decompiler/flash/ActionScript3Test.java b/test/com/jpexs/decompiler/flash/ActionScript3Test.java index 8d7e22af7..708307aea 100644 --- a/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -1,913 +1,913 @@ -/* - * Copyright (C) 2010-2014 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.helpers.CodeFormatting; -import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.DoABCDefineTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Stack; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -/** - * - * @author JPEXS - */ -public class ActionScript3Test { - - private SWF swf; - private int clsIndex; - private ABC abc; - - @BeforeClass - public void init() throws IOException, InterruptedException { - Main.initLogging(false); - swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); - DoABCDefineTag tag = null; - for (Tag t : swf.tags) { - if (t instanceof DoABCDefineTag) { - tag = (DoABCDefineTag) t; - break; - } - } - assertNotNull(tag); - clsIndex = tag.getABC().findClassByName("classes.Test"); - assertTrue(clsIndex > -1); - this.abc = tag.getABC(); - Configuration.autoDeobfuscate.set(false); - Configuration.decompile.set(true); - Configuration.registerNameFormat.set("_loc%d_"); - } - - private void decompileMethod(String methodName, String expectedResult, boolean isStatic) { - int bodyIndex = abc.findMethodBodyByName(clsIndex, methodName); - assertTrue(bodyIndex > -1); - HilightedTextWriter writer = null; - try { - abc.bodies.get(bodyIndex).convert(methodName, ScriptExportMode.AS, isStatic, -1/*FIX?*/, clsIndex, abc, null, abc.constants, abc.method_info, new Stack(), false, new NulWriter(), new ArrayList(), abc.instance_info.get(clsIndex).instance_traits, true); - writer = new HilightedTextWriter(new CodeFormatting(), false); - abc.bodies.get(bodyIndex).toString(methodName, ScriptExportMode.AS, isStatic, -1/*FIX?*/, clsIndex, abc, null, abc.constants, abc.method_info, new Stack(), false, writer, new ArrayList(), abc.instance_info.get(clsIndex).instance_traits); - } catch (InterruptedException ex) { - fail(); - } - String actualResult = writer.toString().replaceAll("[ \r\n]", ""); - expectedResult = expectedResult.replaceAll("[ \r\n]", ""); - assertEquals(actualResult, expectedResult); - } - - @Test - public void testHello() { - decompileMethod("testHello", "trace(\"hello\");\r\n", false); - } - - @Test - public void testIncDec() { - decompileMethod("testIncDec", "var a:* = 5;\r\n" - + "var b:* = 0;\r\n" - + "trace(\"++var\");\r\n" - + "b = ++a;\r\n" - + "trace(\"var++\");\r\n" - + "b = a++;\r\n" - + "trace(\"--var\");\r\n" - + "b = --a;\r\n" - + "trace(\"var--\");\r\n" - + "b = a--;\r\n" - + "var c:* = [1,2,3,4,5];\r\n" - + "trace(\"++arr\");\r\n" - + "b = ++c[2];\r\n" - + "trace(\"arr++\");\r\n" - + "b = c[2]++;\r\n" - + "trace(\"--arr\");\r\n" - + "b = --c[2];\r\n" - + "trace(\"arr--\");\r\n" - + "b = c[2]--;\r\n" - + "var d:* = new TestClass1();\r\n" - + "trace(\"++property\");\r\n" - + "trace(++d.attrib);\r\n" - + "trace(\"property++\");\r\n" - + "trace(d.attrib++);\r\n" - + "trace(\"--property\");\r\n" - + "trace(--d.attrib);\r\n" - + "trace(\"property--\");\r\n" - + "trace(d.attrib--);\r\n" - + "trace(\"arr[e++]\");\r\n" - + "var chars:Array = new Array(36);\r\n" - + "var index:uint = 0;\r\n" - + "chars[index++] = 5;\r\n" - + "trace(\"arr[++e]\");\r\n" - + "chars[++index] = 5;\r\n", false); - } - - @Test - public void testDoWhile() { - decompileMethod("testDoWhile", "var a:* = 8;\r\n" - + "do\r\n" - + "{\r\n" - + "trace(\"a=\" + a);\r\n" - + "a++;\r\n" - + "}\r\n" - + "while(a < 20);\r\n" - + "\r\n", false); - } - - @Test - public void testInnerTry() { - decompileMethod("testInnerTry", "try\r\n" - + "{\r\n" - + "try\r\n" - + "{\r\n" - + "trace(\"try body 1\");\r\n" - + "}\r\n" - + "catch(e:DefinitionError)\r\n" - + "{\r\n" - + "trace(\"catched DefinitionError\");\r\n" - + "}\r\n" - + "trace(\"after try 1\");\r\n" - + "}\r\n" - + "catch(e:Error)\r\n" - + "{\r\n" - + "trace(\"catched Error\");\r\n" - + "}\r\n" - + "finally\r\n" - + "{\r\n" - + "trace(\"finally block\");\r\n" - + "}\r\n", false); - } - - @Test - public void testWhileContinue() { - decompileMethod("testWhileContinue", "var a:* = 5;\r\n" - + "while(true)\r\n" - + "{\r\n" - + "if(a == 9)\r\n" - + "{\r\n" - + "if(a == 8)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "if(a == 9)\r\n" - + "{\r\n" - + "break;\r\n" - + "}\r\n" - + "trace(\"hello 1\");\r\n" - + "}\r\n" - + "trace(\"hello2\");\r\n" - + "}\r\n", false); - } - - @Test - public void testPrecedence() { - decompileMethod("testPrecedence", "var a:* = 0;\r\n" - + "a = (5 + 6) * 7;\r\n" - + "a = 5 * (2 + 3);\r\n" - + "a = 5 + 6 * 7;\r\n" - + "a = 5 * 2 + 2;\r\n" - + "a = 5 * (25 % 3);\r\n" - + "a = 5 % (24 * 307);\r\n" - + "a = 1 / (2 / 3);\r\n" - + "a = 1 / (2 * 3);\r\n" - + "a = 1 * 2 * 3;\r\n" - + "a = 1 * 2 / 3;\r\n" - + "trace(\"a=\" + a);\r\n", false); - } - - @Test - public void testStrings() { - decompileMethod("testStrings", "trace(\"hello\");\r\n" - + "trace(\"quotes:\\\"hello!\\\"\");\r\n" - + "trace(\"backslash: \\\\ \");\r\n" - + "trace(\"single quotes: \\'hello!\\'\");\r\n" - + "trace(\"new line \\r\\n hello!\");\r\n", false); - } - - @Test - public void testContinueLevels() { - decompileMethod("testContinueLevels", "var b:* = undefined;\r\n" - + "var c:* = undefined;\r\n" - + "var d:* = undefined;\r\n" - + "var e:* = undefined;\r\n" - + "var a:* = 5;\r\n" - + "loop3:\r\n" - + "switch(a)\r\n" - + "{\r\n" - + "case 57 * a:\r\n" - + "trace(\"fiftyseven multiply a\");\r\n" - + "b = 0;\r\n" - + "while(b < 50)\r\n" - + "{\r\n" - + "if(b == 10)\r\n" - + "{\r\n" - + "break;\r\n" - + "}\r\n" - + "if(b == 15)\r\n" - + "{\r\n" - + "break loop3;\r\n" - + "}\r\n" - + "b = b + 1;\r\n" - + "}\r\n" - + "break;\r\n" - + "case 13:\r\n" - + "trace(\"thirteen\");\r\n" - + "case 14:\r\n" - + "trace(\"fourteen\");\r\n" - + "break;\r\n" - + "case 89:\r\n" - + "trace(\"eightynine\");\r\n" - + "break;\r\n" - + "default:\r\n" - + "trace(\"default clause\");\r\n" - + "}\r\n" - + "c = 0;\r\n" - + "loop1:\r\n" - + "for(;c < 8;c = c + 1)\r\n" - + "{\r\n" - + "d = 0;\r\n" - + "while(d < 25)\r\n" - + "{\r\n" - + "e = 0;\r\n" - + "if(e < 50)\r\n" - + "{\r\n" - + "if(e == 9)\r\n" - + "{\r\n" - + "break;\r\n" - + "}\r\n" - + "if(e == 20)\r\n" - + "{\r\n" - + "continue loop1;\r\n" - + "}\r\n" - + "if(e != 8)\r\n" - + "{\r\n" - + "break loop1;\r\n" - + "}\r\n" - + "}\r\n" - + "d++;\r\n" - + "}\r\n" - + "trace(\"hello\");\r\n" - + "}\r\n", false); - } - - @Test - public void testSwitchDefault() { - decompileMethod("testSwitchDefault", "var a:* = 5;\r\n" - + "switch(a)\r\n" - + "{\r\n" - + "case 57 * a:\r\n" - + "trace(\"fiftyseven multiply a\");\r\n" - + "break;\r\n" - + "case 13:\r\n" - + "trace(\"thirteen\");\r\n" - + "case 14:\r\n" - + "trace(\"fourteen\");\r\n" - + "break;\r\n" - + "case 89:\r\n" - + "trace(\"eightynine\");\r\n" - + "break;\r\n" - + "default:\r\n" - + "trace(\"default clause\");\r\n" - + "}\r\n", false); - } - - @Test - public void testMultipleCondition() { - decompileMethod("testMultipleCondition", "var a:* = 5;\r\n" - + "var b:* = 8;\r\n" - + "var c:* = 9;\r\n" - + "if((a <= 4 || b <= 8) && c == 7)\r\n" - + "{\r\n" - + "trace(\"onTrue\");\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "trace(\"onFalse\");\r\n" - + "}\r\n", false); - } - - @Test - public void testForBreak() { - decompileMethod("testForBreak", "var a:* = 0;\r\n" - + "while(a < 10)\r\n" - + "{\r\n" - + "if(a == 5)\r\n" - + "{\r\n" - + "break;\r\n" - + "}\r\n" - + "trace(\"hello:\" + a);\r\n" - + "a++;\r\n" - + "}\r\n", false); - } - - @Test - public void testIf() { - decompileMethod("testIf", "var a:* = 5;\r\n" - + "if(a == 7)\r\n" - + "{\r\n" - + "trace(\"onTrue\");\r\n" - + "}\r\n", false); - } - - @Test - public void testIfElse() { - decompileMethod("testIfElse", "var a:* = 5;\r\n" - + "if(a == 7)\r\n" - + "{\r\n" - + "trace(\"onTrue\");\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "trace(\"onFalse\");\r\n" - + "}\r\n", false); - } - - @Test - public void testFor() { - decompileMethod("testFor", "var a:* = 0;\r\n" - + "while(a < 10)\r\n" - + "{\r\n" - + "trace(\"a=\" + a);\r\n" - + "a++;\r\n" - + "}\r\n", false); - } - - @Test - public void testForContinue() { - decompileMethod("testForContinue", "var a:* = 0;\r\n" - + "for(;a < 10;a = a + 1)\r\n" - + "{\r\n" - + "if(a == 9)\r\n" - + "{\r\n" - + "if(a == 5)\r\n" - + "{\r\n" - + "trace(\"part1\");\r\n" - + "continue;\r\n" - + "}\r\n" - + "trace(\"a=\" + a);\r\n" - + "if(a == 7)\r\n" - + "{\r\n" - + "trace(\"part2\");\r\n" - + "continue;\r\n" - + "}\r\n" - + "trace(\"part3\");\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "trace(\"part4\");\r\n" - + "}\r\n" - + "trace(\"part5\");\r\n" - + "}\r\n", false); - } - - @Test - public void testTry() { - decompileMethod("testTry", "var i:int = 0;\r\n" - + "i = 7;\r\n" - + "try\r\n" - + "{\r\n" - + "trace(\"try body\");\r\n" - + "}\r\n" - + "catch(e:DefinitionError)\r\n" - + "{\r\n" - + "trace(\"catched DefinitionError\");\r\n" - + "}\r\n" - + "catch(e:Error)\r\n" - + "{\r\n" - + "trace(\"Error message:\" + e.message);\r\n" - + "trace(\"Stacktrace:\" + e.getStackTrace());\r\n" - + "}\r\n" - + "finally\r\n" - + "{\r\n" - + "trace(\"Finally part\");\r\n" - + "}\r\n" - + "trace(\"end\");\r\n", false); - } - - @Test - public void testSwitch() { - decompileMethod("testSwitch", "var a:* = 5;\r\n" - + "switch(a)\r\n" - + "{\r\n" - + "case 57 * a:\r\n" - + "trace(\"fiftyseven multiply a\");\r\n" - + "break;\r\n" - + "case 13:\r\n" - + "trace(\"thirteen\");\r\n" - + "case 14:\r\n" - + "trace(\"fourteen\");\r\n" - + "break;\r\n" - + "case 89:\r\n" - + "trace(\"eightynine\");\r\n" - + "break;\r\n" - + "}\r\n", false); - } - - @Test - public void testTernarOperator() { - decompileMethod("testTernarOperator", "var a:* = 5;\r\n" - + "var b:* = 4;\r\n" - + "var c:* = 4;\r\n" - + "var d:* = 78;\r\n" - + "var e:* = a == b?c == d?1:7:3;\r\n" - + "trace(\"e=\" + e);\r\n", false); - } - - @Test - public void testInnerIf() { - decompileMethod("testInnerIf", "var a:* = 5;\r\n" - + "var b:* = 4;\r\n" - + "if(a == 5)\r\n" - + "{\r\n" - + "if(b == 6)\r\n" - + "{\r\n" - + "trace(\"b==6\");\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "trace(\"b!=6\");\r\n" - + "}\r\n" - + "}\r\n" - + "else if(b == 7)\r\n" - + "{\r\n" - + "trace(\"b==7\");\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "trace(\"b!=7\");\r\n" - + "}\r\n" - + "\r\n" - + "trace(\"end\");\r\n", false); - } - - @Test - public void testVector() { - decompileMethod("testVector", "var v:Vector. = new Vector.();\r\n" - + "v.push(\"hello\");\r\n" - + "v[0] = \"hi\";\r\n" - + "v[5 * 8 - 39] = \"hi2\";\r\n" - + "trace(v[0]);\r\n", false); - } - - @Test - public void testProperty() { - decompileMethod("testProperty", "var d:* = new TestClass1();\r\n" - + "var k:* = 7 + 8;\r\n" - + "if(k == 15)\r\n" - + "{\r\n" - + "d.method(d.attrib * 5);\r\n" - + "}\r\n", false); - } - - @Test - public void testRest() { - decompileMethod("testRest", "trace(\"firstRest:\" + restval[0]);\r\n" - + "return firstp;\r\n", false); - } - - @Test - public void testParamNames() { - decompileMethod("testParamNames", "return firstp + secondp + thirdp;\r\n", false); - } - - @Test - public void testForEach() { - decompileMethod("testForEach", "var list:Array = null;\r\n" - + "var item:* = undefined;\r\n" - + "list = new Array();\r\n" - + "list[0] = \"first\";\r\n" - + "list[1] = \"second\";\r\n" - + "list[2] = \"third\";\r\n" - + "for each(item in list)\r\n" - + "{\r\n" - + "trace(\"item #\" + item);\r\n" - + "}\r\n", false); - } - - @Test - public void testForEachObjectArray() { - decompileMethod("testForEachObjectArray", "var list:Array = null;\r\n" - + "var test:Array = null;\r\n" - + "list = new Array();\r\n" - + "list[0] = \"first\";\r\n" - + "list[1] = \"second\";\r\n" - + "list[2] = \"third\";\r\n" - + "test = new Array();\r\n" - + "test[0] = 0;\r\n" - + "for each(test[0] in list)\r\n" - + "{\r\n" - + "trace(\"item #\" + test[0]);\r\n" - + "}\r\n", false); - } - - @Test - public void testForEachObjectAttribute() { - decompileMethod("testForEachObjectAttribute", "var list:Array = null;\r\n" - + "list = new Array();\r\n" - + "list[0] = \"first\";\r\n" - + "list[1] = \"second\";\r\n" - + "list[2] = \"third\";\r\n" - + "for each(this.testPriv in list)\r\n" - + "{\r\n" - + "trace(\"item #\" + this.testPriv);\r\n" - + "}\r\n", false); - } - - @Test - public void testParamsCount() { - decompileMethod("testParamsCount", "return firstp;\r\n", false); - } - - @Test - public void testInlineFunctions() { - decompileMethod("testInlineFunctions", "var first:String = null;\r\n" - + "first = \"value1\";\r\n" - + "var traceParameter:Function = function(aParam:String):String\r\n" - + "{\r\n" - + "var second:String = null;\r\n" - + "second = \"value2\";\r\n" - + "second = second + \"cc\";\r\n" - + "var traceParam2:Function = function(bParam:String):String\r\n" - + "{\r\n" - + "trace(bParam + \",\" + aParam);\r\n" - + "return first + second + aParam + bParam;\r\n" - + "};\r\n" - + "trace(second);\r\n" - + "traceParam2(aParam);\r\n" - + "return first;\r\n" - + "};\r\n" - + "traceParameter(\"hello\");\r\n", false); - } - - @Test - public void testMissingDefault() { - decompileMethod("testMissingDefault", "var jj:* = 1;\r\n" - + "switch(jj)\r\n" - + "{\r\n" - + "case 1:\r\n" - + "jj = 1;\r\n" - + "break;\r\n" - + "case 2:\r\n" - + "jj = 2;\r\n" - + "break;\r\n" - + "default:\r\n" - + "jj = 3;\r\n" - + "}\r\n", false); - } - - @Test - public void testChainedAssignments() { - decompileMethod("testChainedAssignments", "var a:* = 0;\r\n" - + "var b:* = 0;\r\n" - + "var c:* = 0;\r\n" - + "var d:* = 0;\r\n" - + "d = c = b = a = 5;\r\n" - + "var e:TestClass2 = TestClass2.createMe(\"test\");\r\n" - + "e.attrib1 = e.attrib2 = e.attrib3 = this.getCounter();\r\n" - + "this.traceIt(e.toString());\r\n", false); - } - - @Test - public void testFinallyZeroJump() { - decompileMethod("testFinallyZeroJump", "var str:String = param1;\r\n" - + "try\r\n" - + "{\r\n" - + "}\r\n" - + "catch(e:Error)\r\n" - + "{\r\n" - + "trace(\"error is :\" + e.message);\r\n" - + "}\r\n" - + "finally\r\n" - + "{\r\n" - + "trace(\"hi \");\r\n" - + "if(5 == 4)\r\n" - + "{\r\n" - + "return str;\r\n" - + "}\r\n" - + "return \"hu\" + str;\r\n" - + "}\r\n", false); - } - - @Test - public void testInnerFunctions() { - decompileMethod("testInnerFunctions", "var s:int = 0;\r\n" - + "var innerFunc:Function = function(b:String):*\r\n" - + "{\r\n" - + "trace(b);\r\n" - + "};\r\n" - + "var k:int = 5;\r\n" - + "if(k == 6)\r\n" - + "{\r\n" - + "s = 8;\r\n" - + "}\r\n" - + "innerFunc(a);\r\n", false); - } - - @Test - public void testDeclarations() { - decompileMethod("testDeclarations", "var vall:* = undefined;\r\n" - + "var vstr:String = null;\r\n" - + "var vint:* = 0;\r\n" - + "var vuint:uint = 0;\r\n" - + "var vclass:TestClass1 = null;\r\n" - + "var vnumber:* = NaN;\r\n" - + "var vobject:Object = null;\r\n" - + "vall = 6;\r\n" - + "vstr = \"hello\";\r\n" - + "vuint = 7;\r\n" - + "vint = -4;\r\n" - + "vclass = new TestClass1();\r\n" - + "vnumber = 0.5;\r\n" - + "vnumber = 6;\r\n" - + "vobject = vclass;\r\n", false); - } - - @Test - public void testForIn() { - decompileMethod("testForIn", "var dic:Dictionary = null;\r\n" - + "var item:Object = null;\r\n" - + "for(item in dic)\r\n" - + "{\r\n" - + "trace(item);\r\n" - + "}\r\n" - + "for each(item in dic)\r\n" - + "{\r\n" - + "trace(item);\r\n" - + "}\r\n", false); - } - - @Test - public void testNames() { - decompileMethod("testNames", "var ns:* = this.getNamespace();\r\n" - + "var name:* = this.getName();\r\n" - + "var a:* = ns::unnamespacedFunc();\r\n" - + "var b:* = ns::[name];\r\n" - + "trace(b.c);\r\n" - + "var c:* = myInternal::neco;\r\n", false); - } - - @Test - public void testComplexExpressions() { - decompileMethod("testComplexExpressions", "var i:* = 0;\r\n" - + "var j:* = 0;\r\n" - + "j = i = i + (i = i + i++);\r\n", false); - } - - @Test - public void testExpressions() { - decompileMethod("testExpressions", "var arr:Array = null;\r\n" - + "var i:* = 5;\r\n" - + "var j:* = 5;\r\n" - + "if((i = i = i / 2) == 1 || i == 2)\r\n" - + "{\r\n" - + "arguments.concat(i);\r\n" - + "}\r\n" - + "else if(i == 0)\r\n" - + "{\r\n" - + "i = j++;\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "arr[0]();\r\n" - + "}\r\n" - + "\r\n" - + "return i == 0;\r\n", false); - } - - @Test - public void testArguments() { - decompileMethod("testArguments", "return arguments[0];\r\n", false); - } - - @Test - public void testLogicalComputing() { - decompileMethod("testLogicalComputing", "var b:* = false;\r\n" - + "var i:* = 5;\r\n" - + "var j:* = 7;\r\n" - + "if(i > j)\r\n" - + "{\r\n" - + "j = 9;\r\n" - + "b = true;\r\n" - + "}\r\n" - + "b = (i == 0 || i == 1) && j == 0;\r\n", false); - } - - @Test - public void testInc2() { - decompileMethod("testInc2", "var a:* = [1];\r\n" - + "a[this.getInt()]++;\r\n" - + "var d:* = a[this.getInt()]++;\r\n" - + "var e:* = ++a[this.getInt()];\r\n" - + "var b:* = 1;\r\n" - + "b++;\r\n" - + "var c:* = 1;\r\n" - + "b = c++;\r\n", false); - } - - @Test - public void testDecl2() { - decompileMethod("testDecl2", "var k:* = 0;\r\n" - + "var i:* = 5;\r\n" - + "i = i + 7;\r\n" - + "if(i == 5)\r\n" - + "{\r\n" - + "if(i < 8)\r\n" - + "{\r\n" - + "k = 6;\r\n" - + "}\r\n" - + "}\r\n" - + "k = 7;\r\n", false); - } - - @Test - public void testChain2() { - decompileMethod("testChain2", "var g:Array = null;\r\n" - + "var h:* = false;\r\n" - + "var extraLine:* = false;\r\n" - + "var r:* = 7;\r\n" - + "var t:* = 0;\r\n" - + "t = this.getInt();\r\n" - + "if(t + 1 < g.length)\r\n" - + "{\r\n" - + "t++;\r\n" - + "h = true;\r\n" - + "}\r\n" - + "if(t >= 0)\r\n" - + "{\r\n" - + "trace(\"ch\");\r\n" - + "}\r\n", false); - } - - @Test - public void testDoWhile2() { - decompileMethod("testDoWhile2", "var k:* = 5;\r\n" - + "do\r\n" - + "{\r\n" - + "k++;\r\n" - + "if(k == 7)\r\n" - + "{\r\n" - + "k = 5 * k;\r\n" - + "}\r\n" - + "else\r\n" - + "{\r\n" - + "k = 5 - k;\r\n" - + "}\r\n" - + "k--;\r\n" - + "}\r\n" - + "while(k < 9);\r\n" - + "\r\n" - + "return 2;\r\n", false); - } - - @Test - public void testWhileAnd() { - decompileMethod("testWhileAnd", "var a:* = 5;\r\n" - + "var b:* = 10;\r\n" - + "while(a < 10 && b > 1)\r\n" - + "{\r\n" - + "a++;\r\n" - + "b--;\r\n" - + "}\r\n" - + "a = 7;\r\n" - + "b = 9;\r\n", false); - } - - @Test - public void testNamedAnonFunctions() { - decompileMethod("testNamedAnonFunctions", "var test:* = new function testFunc(param1:*, param2:int, param3:Array):Boolean\r\n" - + "{\r\n" - + "return (param1 as TestClass2).attrib1 == 5;\r\n" - + "};\r\n", false); - } - - @Test - public void testStringConcat() { - decompileMethod("testStringConcat", "var k:* = 8;\r\n" - + "this.traceIt(\"hello\" + 5 * 6);\r\n" - + "this.traceIt(\"hello\" + (k - 1));\r\n" - + "this.traceIt(\"hello\" + 5 + 6);\r\n", false); - } - - @Test - public void testWhileTry() { - decompileMethod("testWhileTry", "while(true)\r\n" - + "{\r\n" - + "try\r\n" - + "{\r\n" - + "while(true)\r\n" - + "{\r\n" - + "trace(\"a\");\r\n" - + "}\r\n" - + "}\r\n" - + "catch(e:EOFError)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "catch(e:Error)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "}\r\n", false); - } - - @Test - public void testWhileTry2() { - decompileMethod("testWhileTry2", "var j:* = undefined;\r\n" - + "var i:* = 0;\r\n" - + "for(;i < 100;i++)\r\n" - + "{\r\n" - + "try\r\n" - + "{\r\n" - + "j = 0;\r\n" - + "while(j < 20)\r\n" - + "{\r\n" - + "trace(\"a\");\r\n" - + "j++;\r\n" - + "}\r\n" - + "}\r\n" - + "catch(e:EOFError)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "catch(e:Error)\r\n" - + "{\r\n" - + "continue;\r\n" - + "}\r\n" - + "trace(\"after_try\");\r\n" - + "}\r\n" - + "trace(\"end\");\r\n", false); - } - - @Test - public void testTryReturn() { - decompileMethod("testTryReturn", "var i:int = 0;\r\n" - + "var b:Boolean = false;\r\n" - + "try\r\n" - + "{\r\n" - + "i = 0;\r\n" - + "b = true;\r\n" - + "if(i > 0)\r\n" - + "{\r\n" - + "while(this.testDoWhile2())\r\n" - + "{\r\n" - + "if(b)\r\n" - + "{\r\n" - + "return 5;\r\n" - + "}\r\n" - + "}\r\n" - + "}\r\n" - + "i++;\r\n" - + "return 2;\r\n" - + "}\r\n" - + "catch(e:Error)\r\n" - + "{\r\n" - + "}\r\n" - + "return 4;\r\n", false); - } - - @Test - public void testVector2() { - decompileMethod("testVector2", "var a:Vector.> = new Vector.>();\r\n" - + "var b:Vector. = new [10,20,30];\r\n", false); - } - - @Test - public void testOptionalParameters() { - String methodName = "testOptionalParameters"; - int methodInfo = abc.findMethodInfoByName(clsIndex, methodName); - int bodyIndex = abc.findMethodBodyByName(clsIndex, methodName); - assertTrue(methodInfo > -1); - assertTrue(bodyIndex > -1); - HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); - abc.method_info.get(methodInfo).getParamStr(writer, abc.constants, abc.bodies.get(bodyIndex), abc, new ArrayList()); - String actualResult = writer.toString().replaceAll("[ \r\n]", ""); - String expectedResult = "p1:Event=null,p2:Number=1,p3:Number=-1,p4:Number=-1.1,p5:Number=-1.1,p6:String=\"a\""; - expectedResult = expectedResult.replaceAll("[ \r\n]", ""); - assertEquals(actualResult, expectedResult); - } -} +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.helpers.CodeFormatting; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.DoABCDefineTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Stack; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author JPEXS + */ +public class ActionScript3Test extends ActionScriptTestBase { + + private SWF swf; + private int clsIndex; + private ABC abc; + + @BeforeClass + public void init() throws IOException, InterruptedException { + Main.initLogging(false); + swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); + DoABCDefineTag tag = null; + for (Tag t : swf.tags) { + if (t instanceof DoABCDefineTag) { + tag = (DoABCDefineTag) t; + break; + } + } + assertNotNull(tag); + clsIndex = tag.getABC().findClassByName("classes.Test"); + assertTrue(clsIndex > -1); + this.abc = tag.getABC(); + Configuration.autoDeobfuscate.set(false); + Configuration.decompile.set(true); + Configuration.registerNameFormat.set("_loc%d_"); + } + + private void decompileMethod(String methodName, String expectedResult, boolean isStatic) { + int bodyIndex = abc.findMethodBodyByName(clsIndex, methodName); + assertTrue(bodyIndex > -1); + HilightedTextWriter writer = null; + try { + abc.bodies.get(bodyIndex).convert(methodName, ScriptExportMode.AS, isStatic, -1/*FIX?*/, clsIndex, abc, null, abc.constants, abc.method_info, new Stack(), false, new NulWriter(), new ArrayList(), abc.instance_info.get(clsIndex).instance_traits, true); + writer = new HilightedTextWriter(new CodeFormatting(), false); + abc.bodies.get(bodyIndex).toString(methodName, ScriptExportMode.AS, isStatic, -1/*FIX?*/, clsIndex, abc, null, abc.constants, abc.method_info, new Stack(), false, writer, new ArrayList(), abc.instance_info.get(clsIndex).instance_traits); + } catch (InterruptedException ex) { + fail(); + } + String actualResult = cleanPCode(writer.toString()); + expectedResult = cleanPCode(expectedResult); + assertEquals(actualResult, expectedResult); + } + + @Test + public void testHello() { + decompileMethod("testHello", "trace(\"hello\");\r\n", false); + } + + @Test + public void testIncDec() { + decompileMethod("testIncDec", "var a:* = 5;\r\n" + + "var b:* = 0;\r\n" + + "trace(\"++var\");\r\n" + + "b = ++a;\r\n" + + "trace(\"var++\");\r\n" + + "b = a++;\r\n" + + "trace(\"--var\");\r\n" + + "b = --a;\r\n" + + "trace(\"var--\");\r\n" + + "b = a--;\r\n" + + "var c:* = [1,2,3,4,5];\r\n" + + "trace(\"++arr\");\r\n" + + "b = ++c[2];\r\n" + + "trace(\"arr++\");\r\n" + + "b = c[2]++;\r\n" + + "trace(\"--arr\");\r\n" + + "b = --c[2];\r\n" + + "trace(\"arr--\");\r\n" + + "b = c[2]--;\r\n" + + "var d:* = new TestClass1();\r\n" + + "trace(\"++property\");\r\n" + + "trace(++d.attrib);\r\n" + + "trace(\"property++\");\r\n" + + "trace(d.attrib++);\r\n" + + "trace(\"--property\");\r\n" + + "trace(--d.attrib);\r\n" + + "trace(\"property--\");\r\n" + + "trace(d.attrib--);\r\n" + + "trace(\"arr[e++]\");\r\n" + + "var chars:Array = new Array(36);\r\n" + + "var index:uint = 0;\r\n" + + "chars[index++] = 5;\r\n" + + "trace(\"arr[++e]\");\r\n" + + "chars[++index] = 5;\r\n", false); + } + + @Test + public void testDoWhile() { + decompileMethod("testDoWhile", "var a:* = 8;\r\n" + + "do\r\n" + + "{\r\n" + + "trace(\"a=\" + a);\r\n" + + "a++;\r\n" + + "}\r\n" + + "while(a < 20);\r\n" + + "\r\n", false); + } + + @Test + public void testInnerTry() { + decompileMethod("testInnerTry", "try\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"try body 1\");\r\n" + + "}\r\n" + + "catch(e:DefinitionError)\r\n" + + "{\r\n" + + "trace(\"catched DefinitionError\");\r\n" + + "}\r\n" + + "trace(\"after try 1\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"catched Error\");\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"finally block\");\r\n" + + "}\r\n", false); + } + + @Test + public void testWhileContinue() { + decompileMethod("testWhileContinue", "var a:* = 5;\r\n" + + "while(true)\r\n" + + "{\r\n" + + "if(a == 9)\r\n" + + "{\r\n" + + "if(a == 8)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "if(a == 9)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"hello 1\");\r\n" + + "}\r\n" + + "trace(\"hello2\");\r\n" + + "}\r\n", false); + } + + @Test + public void testPrecedence() { + decompileMethod("testPrecedence", "var a:* = 0;\r\n" + + "a = (5 + 6) * 7;\r\n" + + "a = 5 * (2 + 3);\r\n" + + "a = 5 + 6 * 7;\r\n" + + "a = 5 * 2 + 2;\r\n" + + "a = 5 * (25 % 3);\r\n" + + "a = 5 % (24 * 307);\r\n" + + "a = 1 / (2 / 3);\r\n" + + "a = 1 / (2 * 3);\r\n" + + "a = 1 * 2 * 3;\r\n" + + "a = 1 * 2 / 3;\r\n" + + "trace(\"a=\" + a);\r\n", false); + } + + @Test + public void testStrings() { + decompileMethod("testStrings", "trace(\"hello\");\r\n" + + "trace(\"quotes:\\\"hello!\\\"\");\r\n" + + "trace(\"backslash: \\\\ \");\r\n" + + "trace(\"single quotes: \\'hello!\\'\");\r\n" + + "trace(\"new line \\r\\n hello!\");\r\n", false); + } + + @Test + public void testContinueLevels() { + decompileMethod("testContinueLevels", "var b:* = undefined;\r\n" + + "var c:* = undefined;\r\n" + + "var d:* = undefined;\r\n" + + "var e:* = undefined;\r\n" + + "var a:* = 5;\r\n" + + "loop3:\r\n" + + "switch(a)\r\n" + + "{\r\n" + + "case 57 * a:\r\n" + + "trace(\"fiftyseven multiply a\");\r\n" + + "b = 0;\r\n" + + "while(b < 50)\r\n" + + "{\r\n" + + "if(b == 10)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "if(b == 15)\r\n" + + "{\r\n" + + "break loop3;\r\n" + + "}\r\n" + + "b = b + 1;\r\n" + + "}\r\n" + + "break;\r\n" + + "case 13:\r\n" + + "trace(\"thirteen\");\r\n" + + "case 14:\r\n" + + "trace(\"fourteen\");\r\n" + + "break;\r\n" + + "case 89:\r\n" + + "trace(\"eightynine\");\r\n" + + "break;\r\n" + + "default:\r\n" + + "trace(\"default clause\");\r\n" + + "}\r\n" + + "c = 0;\r\n" + + "loop1:\r\n" + + "for(;c < 8;c = c + 1)\r\n" + + "{\r\n" + + "d = 0;\r\n" + + "while(d < 25)\r\n" + + "{\r\n" + + "e = 0;\r\n" + + "if(e < 50)\r\n" + + "{\r\n" + + "if(e == 9)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "if(e == 20)\r\n" + + "{\r\n" + + "continue loop1;\r\n" + + "}\r\n" + + "if(e != 8)\r\n" + + "{\r\n" + + "break loop1;\r\n" + + "}\r\n" + + "}\r\n" + + "d++;\r\n" + + "}\r\n" + + "trace(\"hello\");\r\n" + + "}\r\n", false); + } + + @Test + public void testSwitchDefault() { + decompileMethod("testSwitchDefault", "var a:* = 5;\r\n" + + "switch(a)\r\n" + + "{\r\n" + + "case 57 * a:\r\n" + + "trace(\"fiftyseven multiply a\");\r\n" + + "break;\r\n" + + "case 13:\r\n" + + "trace(\"thirteen\");\r\n" + + "case 14:\r\n" + + "trace(\"fourteen\");\r\n" + + "break;\r\n" + + "case 89:\r\n" + + "trace(\"eightynine\");\r\n" + + "break;\r\n" + + "default:\r\n" + + "trace(\"default clause\");\r\n" + + "}\r\n", false); + } + + @Test + public void testMultipleCondition() { + decompileMethod("testMultipleCondition", "var a:* = 5;\r\n" + + "var b:* = 8;\r\n" + + "var c:* = 9;\r\n" + + "if((a <= 4 || b <= 8) && c == 7)\r\n" + + "{\r\n" + + "trace(\"onTrue\");\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"onFalse\");\r\n" + + "}\r\n", false); + } + + @Test + public void testForBreak() { + decompileMethod("testForBreak", "var a:* = 0;\r\n" + + "while(a < 10)\r\n" + + "{\r\n" + + "if(a == 5)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"hello:\" + a);\r\n" + + "a++;\r\n" + + "}\r\n", false); + } + + @Test + public void testIf() { + decompileMethod("testIf", "var a:* = 5;\r\n" + + "if(a == 7)\r\n" + + "{\r\n" + + "trace(\"onTrue\");\r\n" + + "}\r\n", false); + } + + @Test + public void testIfElse() { + decompileMethod("testIfElse", "var a:* = 5;\r\n" + + "if(a == 7)\r\n" + + "{\r\n" + + "trace(\"onTrue\");\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"onFalse\");\r\n" + + "}\r\n", false); + } + + @Test + public void testFor() { + decompileMethod("testFor", "var a:* = 0;\r\n" + + "while(a < 10)\r\n" + + "{\r\n" + + "trace(\"a=\" + a);\r\n" + + "a++;\r\n" + + "}\r\n", false); + } + + @Test + public void testForContinue() { + decompileMethod("testForContinue", "var a:* = 0;\r\n" + + "for(;a < 10;a = a + 1)\r\n" + + "{\r\n" + + "if(a == 9)\r\n" + + "{\r\n" + + "if(a == 5)\r\n" + + "{\r\n" + + "trace(\"part1\");\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"a=\" + a);\r\n" + + "if(a == 7)\r\n" + + "{\r\n" + + "trace(\"part2\");\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"part3\");\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"part4\");\r\n" + + "}\r\n" + + "trace(\"part5\");\r\n" + + "}\r\n", false); + } + + @Test + public void testTry() { + decompileMethod("testTry", "var i:int = 0;\r\n" + + "i = 7;\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"try body\");\r\n" + + "}\r\n" + + "catch(e:DefinitionError)\r\n" + + "{\r\n" + + "trace(\"catched DefinitionError\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"Error message:\" + e.message);\r\n" + + "trace(\"Stacktrace:\" + e.getStackTrace());\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"Finally part\");\r\n" + + "}\r\n" + + "trace(\"end\");\r\n", false); + } + + @Test + public void testSwitch() { + decompileMethod("testSwitch", "var a:* = 5;\r\n" + + "switch(a)\r\n" + + "{\r\n" + + "case 57 * a:\r\n" + + "trace(\"fiftyseven multiply a\");\r\n" + + "break;\r\n" + + "case 13:\r\n" + + "trace(\"thirteen\");\r\n" + + "case 14:\r\n" + + "trace(\"fourteen\");\r\n" + + "break;\r\n" + + "case 89:\r\n" + + "trace(\"eightynine\");\r\n" + + "break;\r\n" + + "}\r\n", false); + } + + @Test + public void testTernarOperator() { + decompileMethod("testTernarOperator", "var a:* = 5;\r\n" + + "var b:* = 4;\r\n" + + "var c:* = 4;\r\n" + + "var d:* = 78;\r\n" + + "var e:* = a == b?c == d?1:7:3;\r\n" + + "trace(\"e=\" + e);\r\n", false); + } + + @Test + public void testInnerIf() { + decompileMethod("testInnerIf", "var a:* = 5;\r\n" + + "var b:* = 4;\r\n" + + "if(a == 5)\r\n" + + "{\r\n" + + "if(b == 6)\r\n" + + "{\r\n" + + "trace(\"b==6\");\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"b!=6\");\r\n" + + "}\r\n" + + "}\r\n" + + "else if(b == 7)\r\n" + + "{\r\n" + + "trace(\"b==7\");\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"b!=7\");\r\n" + + "}\r\n" + + "\r\n" + + "trace(\"end\");\r\n", false); + } + + @Test + public void testVector() { + decompileMethod("testVector", "var v:Vector. = new Vector.();\r\n" + + "v.push(\"hello\");\r\n" + + "v[0] = \"hi\";\r\n" + + "v[5 * 8 - 39] = \"hi2\";\r\n" + + "trace(v[0]);\r\n", false); + } + + @Test + public void testProperty() { + decompileMethod("testProperty", "var d:* = new TestClass1();\r\n" + + "var k:* = 7 + 8;\r\n" + + "if(k == 15)\r\n" + + "{\r\n" + + "d.method(d.attrib * 5);\r\n" + + "}\r\n", false); + } + + @Test + public void testRest() { + decompileMethod("testRest", "trace(\"firstRest:\" + restval[0]);\r\n" + + "return firstp;\r\n", false); + } + + @Test + public void testParamNames() { + decompileMethod("testParamNames", "return firstp + secondp + thirdp;\r\n", false); + } + + @Test + public void testForEach() { + decompileMethod("testForEach", "var list:Array = null;\r\n" + + "var item:* = undefined;\r\n" + + "list = new Array();\r\n" + + "list[0] = \"first\";\r\n" + + "list[1] = \"second\";\r\n" + + "list[2] = \"third\";\r\n" + + "for each(item in list)\r\n" + + "{\r\n" + + "trace(\"item #\" + item);\r\n" + + "}\r\n", false); + } + + @Test + public void testForEachObjectArray() { + decompileMethod("testForEachObjectArray", "var list:Array = null;\r\n" + + "var test:Array = null;\r\n" + + "list = new Array();\r\n" + + "list[0] = \"first\";\r\n" + + "list[1] = \"second\";\r\n" + + "list[2] = \"third\";\r\n" + + "test = new Array();\r\n" + + "test[0] = 0;\r\n" + + "for each(test[0] in list)\r\n" + + "{\r\n" + + "trace(\"item #\" + test[0]);\r\n" + + "}\r\n", false); + } + + @Test + public void testForEachObjectAttribute() { + decompileMethod("testForEachObjectAttribute", "var list:Array = null;\r\n" + + "list = new Array();\r\n" + + "list[0] = \"first\";\r\n" + + "list[1] = \"second\";\r\n" + + "list[2] = \"third\";\r\n" + + "for each(this.testPriv in list)\r\n" + + "{\r\n" + + "trace(\"item #\" + this.testPriv);\r\n" + + "}\r\n", false); + } + + @Test + public void testParamsCount() { + decompileMethod("testParamsCount", "return firstp;\r\n", false); + } + + @Test + public void testInlineFunctions() { + decompileMethod("testInlineFunctions", "var first:String = null;\r\n" + + "first = \"value1\";\r\n" + + "var traceParameter:Function = function(aParam:String):String\r\n" + + "{\r\n" + + "var second:String = null;\r\n" + + "second = \"value2\";\r\n" + + "second = second + \"cc\";\r\n" + + "var traceParam2:Function = function(bParam:String):String\r\n" + + "{\r\n" + + "trace(bParam + \",\" + aParam);\r\n" + + "return first + second + aParam + bParam;\r\n" + + "};\r\n" + + "trace(second);\r\n" + + "traceParam2(aParam);\r\n" + + "return first;\r\n" + + "};\r\n" + + "traceParameter(\"hello\");\r\n", false); + } + + @Test + public void testMissingDefault() { + decompileMethod("testMissingDefault", "var jj:* = 1;\r\n" + + "switch(jj)\r\n" + + "{\r\n" + + "case 1:\r\n" + + "jj = 1;\r\n" + + "break;\r\n" + + "case 2:\r\n" + + "jj = 2;\r\n" + + "break;\r\n" + + "default:\r\n" + + "jj = 3;\r\n" + + "}\r\n", false); + } + + @Test + public void testChainedAssignments() { + decompileMethod("testChainedAssignments", "var a:* = 0;\r\n" + + "var b:* = 0;\r\n" + + "var c:* = 0;\r\n" + + "var d:* = 0;\r\n" + + "d = c = b = a = 5;\r\n" + + "var e:TestClass2 = TestClass2.createMe(\"test\");\r\n" + + "e.attrib1 = e.attrib2 = e.attrib3 = this.getCounter();\r\n" + + "this.traceIt(e.toString());\r\n", false); + } + + @Test + public void testFinallyZeroJump() { + decompileMethod("testFinallyZeroJump", "var str:String = param1;\r\n" + + "try\r\n" + + "{\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"error is :\" + e.message);\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"hi \");\r\n" + + "if(5 == 4)\r\n" + + "{\r\n" + + "return str;\r\n" + + "}\r\n" + + "return \"hu\" + str;\r\n" + + "}\r\n", false); + } + + @Test + public void testInnerFunctions() { + decompileMethod("testInnerFunctions", "var s:int = 0;\r\n" + + "var innerFunc:Function = function(b:String):*\r\n" + + "{\r\n" + + "trace(b);\r\n" + + "};\r\n" + + "var k:int = 5;\r\n" + + "if(k == 6)\r\n" + + "{\r\n" + + "s = 8;\r\n" + + "}\r\n" + + "innerFunc(a);\r\n", false); + } + + @Test + public void testDeclarations() { + decompileMethod("testDeclarations", "var vall:* = undefined;\r\n" + + "var vstr:String = null;\r\n" + + "var vint:* = 0;\r\n" + + "var vuint:uint = 0;\r\n" + + "var vclass:TestClass1 = null;\r\n" + + "var vnumber:* = NaN;\r\n" + + "var vobject:Object = null;\r\n" + + "vall = 6;\r\n" + + "vstr = \"hello\";\r\n" + + "vuint = 7;\r\n" + + "vint = -4;\r\n" + + "vclass = new TestClass1();\r\n" + + "vnumber = 0.5;\r\n" + + "vnumber = 6;\r\n" + + "vobject = vclass;\r\n", false); + } + + @Test + public void testForIn() { + decompileMethod("testForIn", "var dic:Dictionary = null;\r\n" + + "var item:Object = null;\r\n" + + "for(item in dic)\r\n" + + "{\r\n" + + "trace(item);\r\n" + + "}\r\n" + + "for each(item in dic)\r\n" + + "{\r\n" + + "trace(item);\r\n" + + "}\r\n", false); + } + + @Test + public void testNames() { + decompileMethod("testNames", "var ns:* = this.getNamespace();\r\n" + + "var name:* = this.getName();\r\n" + + "var a:* = ns::unnamespacedFunc();\r\n" + + "var b:* = ns::[name];\r\n" + + "trace(b.c);\r\n" + + "var c:* = myInternal::neco;\r\n", false); + } + + @Test + public void testComplexExpressions() { + decompileMethod("testComplexExpressions", "var i:* = 0;\r\n" + + "var j:* = 0;\r\n" + + "j = i = i + (i = i + i++);\r\n", false); + } + + @Test + public void testExpressions() { + decompileMethod("testExpressions", "var arr:Array = null;\r\n" + + "var i:* = 5;\r\n" + + "var j:* = 5;\r\n" + + "if((i = i = i / 2) == 1 || i == 2)\r\n" + + "{\r\n" + + "arguments.concat(i);\r\n" + + "}\r\n" + + "else if(i == 0)\r\n" + + "{\r\n" + + "i = j++;\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "arr[0]();\r\n" + + "}\r\n" + + "\r\n" + + "return i == 0;\r\n", false); + } + + @Test + public void testArguments() { + decompileMethod("testArguments", "return arguments[0];\r\n", false); + } + + @Test + public void testLogicalComputing() { + decompileMethod("testLogicalComputing", "var b:* = false;\r\n" + + "var i:* = 5;\r\n" + + "var j:* = 7;\r\n" + + "if(i > j)\r\n" + + "{\r\n" + + "j = 9;\r\n" + + "b = true;\r\n" + + "}\r\n" + + "b = (i == 0 || i == 1) && j == 0;\r\n", false); + } + + @Test + public void testInc2() { + decompileMethod("testInc2", "var a:* = [1];\r\n" + + "a[this.getInt()]++;\r\n" + + "var d:* = a[this.getInt()]++;\r\n" + + "var e:* = ++a[this.getInt()];\r\n" + + "var b:* = 1;\r\n" + + "b++;\r\n" + + "var c:* = 1;\r\n" + + "b = c++;\r\n", false); + } + + @Test + public void testDecl2() { + decompileMethod("testDecl2", "var k:* = 0;\r\n" + + "var i:* = 5;\r\n" + + "i = i + 7;\r\n" + + "if(i == 5)\r\n" + + "{\r\n" + + "if(i < 8)\r\n" + + "{\r\n" + + "k = 6;\r\n" + + "}\r\n" + + "}\r\n" + + "k = 7;\r\n", false); + } + + @Test + public void testChain2() { + decompileMethod("testChain2", "var g:Array = null;\r\n" + + "var h:* = false;\r\n" + + "var extraLine:* = false;\r\n" + + "var r:* = 7;\r\n" + + "var t:* = 0;\r\n" + + "t = this.getInt();\r\n" + + "if(t + 1 < g.length)\r\n" + + "{\r\n" + + "t++;\r\n" + + "h = true;\r\n" + + "}\r\n" + + "if(t >= 0)\r\n" + + "{\r\n" + + "trace(\"ch\");\r\n" + + "}\r\n", false); + } + + @Test + public void testDoWhile2() { + decompileMethod("testDoWhile2", "var k:* = 5;\r\n" + + "do\r\n" + + "{\r\n" + + "k++;\r\n" + + "if(k == 7)\r\n" + + "{\r\n" + + "k = 5 * k;\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "k = 5 - k;\r\n" + + "}\r\n" + + "k--;\r\n" + + "}\r\n" + + "while(k < 9);\r\n" + + "\r\n" + + "return 2;\r\n", false); + } + + @Test + public void testWhileAnd() { + decompileMethod("testWhileAnd", "var a:* = 5;\r\n" + + "var b:* = 10;\r\n" + + "while(a < 10 && b > 1)\r\n" + + "{\r\n" + + "a++;\r\n" + + "b--;\r\n" + + "}\r\n" + + "a = 7;\r\n" + + "b = 9;\r\n", false); + } + + @Test + public void testNamedAnonFunctions() { + decompileMethod("testNamedAnonFunctions", "var test:* = new function testFunc(param1:*, param2:int, param3:Array):Boolean\r\n" + + "{\r\n" + + "return (param1 as TestClass2).attrib1 == 5;\r\n" + + "};\r\n", false); + } + + @Test + public void testStringConcat() { + decompileMethod("testStringConcat", "var k:* = 8;\r\n" + + "this.traceIt(\"hello\" + 5 * 6);\r\n" + + "this.traceIt(\"hello\" + (k - 1));\r\n" + + "this.traceIt(\"hello\" + 5 + 6);\r\n", false); + } + + @Test + public void testWhileTry() { + decompileMethod("testWhileTry", "while(true)\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "while(true)\r\n" + + "{\r\n" + + "trace(\"a\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "}\r\n", false); + } + + @Test + public void testWhileTry2() { + decompileMethod("testWhileTry2", "var j:* = undefined;\r\n" + + "var i:* = 0;\r\n" + + "for(;i < 100;i++)\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "j = 0;\r\n" + + "while(j < 20)\r\n" + + "{\r\n" + + "trace(\"a\");\r\n" + + "j++;\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"after_try\");\r\n" + + "}\r\n" + + "trace(\"end\");\r\n", false); + } + + @Test + public void testTryReturn() { + decompileMethod("testTryReturn", "var i:int = 0;\r\n" + + "var b:Boolean = false;\r\n" + + "try\r\n" + + "{\r\n" + + "i = 0;\r\n" + + "b = true;\r\n" + + "if(i > 0)\r\n" + + "{\r\n" + + "while(this.testDoWhile2())\r\n" + + "{\r\n" + + "if(b)\r\n" + + "{\r\n" + + "return 5;\r\n" + + "}\r\n" + + "}\r\n" + + "}\r\n" + + "i++;\r\n" + + "return 2;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "}\r\n" + + "return 4;\r\n", false); + } + + @Test + public void testVector2() { + decompileMethod("testVector2", "var a:Vector.> = new Vector.>();\r\n" + + "var b:Vector. = new [10,20,30];\r\n", false); + } + + @Test + public void testOptionalParameters() { + String methodName = "testOptionalParameters"; + int methodInfo = abc.findMethodInfoByName(clsIndex, methodName); + int bodyIndex = abc.findMethodBodyByName(clsIndex, methodName); + assertTrue(methodInfo > -1); + assertTrue(bodyIndex > -1); + HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); + abc.method_info.get(methodInfo).getParamStr(writer, abc.constants, abc.bodies.get(bodyIndex), abc, new ArrayList()); + String actualResult = writer.toString().replaceAll("[ \r\n]", ""); + String expectedResult = "p1:Event=null,p2:Number=1,p3:Number=-1,p4:Number=-1.1,p5:Number=-1.1,p6:String=\"a\""; + expectedResult = expectedResult.replaceAll("[ \r\n]", ""); + assertEquals(actualResult, expectedResult); + } +} diff --git a/test/com/jpexs/decompiler/flash/ActionScriptTestBase.java b/test/com/jpexs/decompiler/flash/ActionScriptTestBase.java new file mode 100644 index 000000000..9f7b2b09e --- /dev/null +++ b/test/com/jpexs/decompiler/flash/ActionScriptTestBase.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash; + +/** + * + * @author JPEXS + */ +public class ActionScriptTestBase { + + protected String cleanPCode(String pCode) { + pCode = pCode.replaceAll(" *[\r\n]+ *", "\n").trim(); + pCode = pCode.replaceAll(" +", " ").trim(); + return pCode; + } +} diff --git a/test/com/jpexs/decompiler/flash/ActionStript2TestBase.java b/test/com/jpexs/decompiler/flash/ActionStript2TestBase.java index 4f78ad14f..6bbcc220c 100644 --- a/test/com/jpexs/decompiler/flash/ActionStript2TestBase.java +++ b/test/com/jpexs/decompiler/flash/ActionStript2TestBase.java @@ -1,39 +1,38 @@ -/* - * Copyright (C) 2010-2014 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash; - -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.Tag; - -/** - * - * @author JPEXS - */ -public class ActionStript2TestBase { - - protected SWF swf; - - protected DoActionTag getFirstActionTag() { - for (Tag t : swf.tags) { - if (t instanceof DoActionTag) { - return (DoActionTag) t; - } - } - return null; - } - -} +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.Tag; + +/** + * + * @author JPEXS + */ +public class ActionStript2TestBase extends ActionScriptTestBase { + + protected SWF swf; + + protected DoActionTag getFirstActionTag() { + for (Tag t : swf.tags) { + if (t instanceof DoActionTag) { + return (DoActionTag) t; + } + } + return null; + } +}