More documentation.

This commit is contained in:
Jindra Petřík
2024-08-08 19:27:14 +02:00
parent 68954e714d
commit b57e38e387
286 changed files with 11752 additions and 3576 deletions
@@ -16,24 +16,10 @@
*/
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.AppResources;
import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.*;
import com.jpexs.decompiler.flash.action.as2.Trait;
import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscator;
import com.jpexs.decompiler.flash.action.model.ActionItem;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.FunctionActionItem;
import com.jpexs.decompiler.flash.action.model.GetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.GetPropertyActionItem;
import com.jpexs.decompiler.flash.action.model.GetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.SetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.SetPropertyActionItem;
import com.jpexs.decompiler.flash.action.model.SetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem;
import com.jpexs.decompiler.flash.action.model.*;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.ASMParsedSymbol;
import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer;
@@ -41,12 +27,7 @@ import com.jpexs.decompiler.flash.action.parser.script.VariableActionItem;
import com.jpexs.decompiler.flash.action.special.ActionEnd;
import com.jpexs.decompiler.flash.action.special.ActionStore;
import com.jpexs.decompiler.flash.action.swf3.ActionSetTarget;
import com.jpexs.decompiler.flash.action.swf4.ActionEquals;
import com.jpexs.decompiler.flash.action.swf4.ActionIf;
import com.jpexs.decompiler.flash.action.swf4.ActionNot;
import com.jpexs.decompiler.flash.action.swf4.ActionPush;
import com.jpexs.decompiler.flash.action.swf4.ActionSetTarget2;
import com.jpexs.decompiler.flash.action.swf4.RegisterNumber;
import com.jpexs.decompiler.flash.action.swf4.*;
import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool;
import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction;
import com.jpexs.decompiler.flash.action.swf5.ActionEquals2;
@@ -63,17 +44,7 @@ import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphPartChangeException;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SecondPassData;
import com.jpexs.decompiler.graph.SecondPassException;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.decompiler.graph.model.CommentItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.ScriptEndItem;
@@ -81,16 +52,10 @@ import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -99,16 +64,25 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Represents one ACTIONRECORD, also has some static method to work with Actions
* Represents one ACTIONRECORD, also has some static method to work with Actions.
*
* @author JPEXS
*/
public abstract class Action implements GraphSourceItem {
/**
* When to inform listeners about progress
*/
private static final int INFORM_LISTENER_RESOLUTION = 1000;
/**
* Whether this action is ignored
*/
private boolean ignored = false;
/**
* File offset of this action
*/
public long fileOffset = -1;
/**
@@ -121,51 +95,57 @@ public abstract class Action implements GraphSourceItem {
*/
protected int actionLength;
/**
* Address of this action
*/
private long address;
/**
* Virtual address (address before deobfuscation)
*/
private long virtualAddress = -1;
/**
* Charset - SWFs version 5 and lower do not use UTF-8
*/
private String charset;
@Override
public long getLineOffset() {
return fileOffset;
}
public String getCharset() {
return charset;
}
/**
* Names of ActionScript properties
*/
public static final String[] propertyNames = new String[]{
"_X",
"_Y",
"_xscale",
"_yscale",
"_currentframe",
"_totalframes",
"_alpha",
"_visible",
"_width",
"_height",
"_rotation",
"_target",
"_framesloaded",
"_name",
"_droptarget",
"_url",
"_highquality",
"_focusrect",
"_soundbuftime",
"_quality",
"_xmouse",
"_ymouse"
"_X",
"_Y",
"_xscale",
"_yscale",
"_currentframe",
"_totalframes",
"_alpha",
"_visible",
"_width",
"_height",
"_rotation",
"_target",
"_framesloaded",
"_name",
"_droptarget",
"_url",
"_highquality",
"_focusrect",
"_soundbuftime",
"_quality",
"_xmouse",
"_ymouse"
};
/**
* Property names list
*/
public static final List<String> propertyNamesList = Arrays.asList(propertyNames);
/**
* Property names list in lower case
*/
public static final List<String> propertyNamesListLowerCase = new ArrayList<>();
{
@@ -174,10 +154,32 @@ public abstract class Action implements GraphSourceItem {
}
}
/**
* Logger
*/
private static final Logger logger = Logger.getLogger(Action.class.getName());
/**
* Constructor
* Gets the line offset.
* @return Line offset
*/
@Override
public long getLineOffset() {
return fileOffset;
}
/**
* Gets charset
* @return Charset
*/
public String getCharset() {
return charset;
}
/**
* Constructs new Action
*
* @param actionCode Action type identifier
* @param actionLength Length of action data
@@ -188,13 +190,15 @@ public abstract class Action implements GraphSourceItem {
this.charset = charset;
}
/**
* Constructs new Action
*/
public Action() {
}
/**
* Returns address of this action
*
* @return address of this action
* Gets the address.
* @return Address
*/
@Override
public long getAddress() {
@@ -202,9 +206,9 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Return code of this action
* Gets code of this action.
*
* @return code of this action
* @return Code of this action
*/
public int getActionCode() {
return actionCode;
@@ -212,15 +216,15 @@ public abstract class Action implements GraphSourceItem {
/**
* Gets all addresses which are referenced from this action and/or
* subactions
* subactions.
*
* @param refs list of addresses
* @param refs List of addresses
*/
public void getRef(Set<Long> refs) {
}
/**
* Gets all addresses which are referenced from the list of actions
* Gets all addresses which are referenced from the list of actions.
*
* @param list List of actions
* @return List of addresses
@@ -233,12 +237,16 @@ public abstract class Action implements GraphSourceItem {
return ret;
}
/**
* Gets total length of this action.
* @return Total length of this action
*/
public int getTotalActionLength() {
return actionLength + 1 + (actionCode >= 0x80 ? 2 : 0);
}
/**
* Sets address of this instruction
* Sets address of this instruction.
*
* @param address Address
*/
@@ -247,7 +255,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Returns a string representation of the object
* Returns a string representation of the object.
*
* @return a string representation of the object.
*/
@@ -257,7 +265,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Reads String from FlasmLexer
* Reads String from FlasmLexer.
*
* @param lex FlasmLexer
* @return String value
@@ -273,7 +281,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Reads Block startServer from FlasmLexer
* Reads Block startServer from FlasmLexer.
*
* @param lex FlasmLexer
* @throws IOException
@@ -287,7 +295,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Reads Identifier from FlasmLexer
* Reads Identifier from FlasmLexer.
*
* @param lex FlasmLexer
* @return Identifier name
@@ -303,7 +311,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Reads long value from FlasmLexer
* Reads long value from FlasmLexer.
*
* @param lex FlasmLexer
* @return long value
@@ -319,7 +327,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Reads boolean value from FlasmLexer
* Reads boolean value from FlasmLexer.
*
* @param lex FlasmLexer
* @return boolean value
@@ -334,6 +342,13 @@ public abstract class Action implements GraphSourceItem {
return (Boolean) symb.value;
}
/**
* Reads optional comma from FlasmLexer.
*
* @param lex FlasmLexer
* @throws IOException
* @throws ActionParseException
*/
protected void lexOptionalComma(FlasmLexer lex) throws IOException, ActionParseException {
ASMParsedSymbol symb = lex.lex();
if (symb.type != ASMParsedSymbol.TYPE_COMMA) {
@@ -342,7 +357,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Gets action converted to bytes
* Gets action converted to bytes.
*
* @param version SWF version
* @return Array of bytes
@@ -359,11 +374,16 @@ public abstract class Action implements GraphSourceItem {
return surroundWithAction(baos.toByteArray(), version);
}
/**
* Gets content bytes of action.
* @param sos SWFOutputStream
* @throws IOException
*/
protected void getContentBytes(SWFOutputStream sos) throws IOException {
}
/**
* Gets the length of action converted to bytes
* Gets the length of action converted to bytes.
*
* @return Length
*/
@@ -372,12 +392,16 @@ public abstract class Action implements GraphSourceItem {
return getContentBytesLength() + (actionCode >= 0x80 ? 3 : 1);
}
/**
* Gets the length of content bytes of action.
* @return Length
*/
protected int getContentBytesLength() {
return 0;
}
/**
* Updates the action length to the length calculated from action bytes
* Updates the action length to the length calculated from action bytes.
*/
public void updateLength() {
int length = getBytesLength();
@@ -385,7 +409,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Surrounds byte array with Action header
* Surrounds byte array with Action header.
*
* @param data Byte array
* @param version SWF version
@@ -407,16 +431,20 @@ public abstract class Action implements GraphSourceItem {
return baos2.toByteArray();
}
/**
* Gets file offset.
* @return File offset
*/
@Override
public long getFileOffset() {
return fileOffset;
}
/**
* Converts list of Actions to bytes
* Converts list of Actions to bytes.
*
* @param list List of actions
* @param addZero Whether or not to add 0 UI8 value to the end
* @param addZero Whether to add 0 UI8 value to the end
* @param version SWF version
* @return Array of bytes
*/
@@ -437,11 +465,25 @@ public abstract class Action implements GraphSourceItem {
return baos.toByteArray();
}
/**
* Converts list of Actions to bytes.
* @param list List of actions
* @param addZero Whether to add 0 UI8 value to the end
* @param version SWF version
* @return ByteArrayRange
*/
public static ByteArrayRange actionsToByteArrayRange(List<Action> list, boolean addZero, int version) {
byte[] bytes = Action.actionsToBytes(list, addZero, version);
return new ByteArrayRange(bytes);
}
/**
* Sets constant pool of actions.
* @param src ASMSource
* @param constantPools List of constant pools
* @param tryInline Whether to try to inline constant pools
* @throws ConstantPoolTooBigException
*/
public static void setConstantPools(ASMSource src, List<List<String>> constantPools, boolean tryInline) throws ConstantPoolTooBigException {
try {
ActionList actions = src.getActions();
@@ -485,7 +527,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Set addresses of actions in the list
* Sets addresses of actions in the list.
*
* @param list List of actions
* @param baseAddress Address of first action in the list
@@ -506,7 +548,7 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Converts list of actions to ASM source
* Converts list of actions to ASM source.
*
* @param listeners
* @param address
@@ -725,6 +767,16 @@ public abstract class Action implements GraphSourceItem {
return writer;
}
/**
* Converts constant pool actions to String.
* @param listeners List of listeners
* @param address Address
* @param list List of actions
* @param version SWF version
* @param exportMode Export mode
* @param writer GraphTextWriter
* @return GraphTextWriter
*/
public static GraphTextWriter constantPoolActionsToString(List<DisassemblyListener> listeners, long address, ActionList list, int version, ScriptExportMode exportMode, GraphTextWriter writer) {
int poolIdx = 0;
writer.appendNoHilight(Helper.constants).newLine();
@@ -752,9 +804,9 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Convert action to ASM source
* Converts action to ASM source.
*
* @param container
* @param container Container
* @param knownAddreses List of important offsets to mark as labels
* @param exportMode PCode or hex?
* @return String of P-code source
@@ -763,20 +815,37 @@ public abstract class Action implements GraphSourceItem {
return toString();
}
/**
* Executes the action.
* @param lda Local data area
* @return Whether the action was executed successfully
*/
public abstract boolean execute(LocalDataArea lda);
/**
* Gets the number of stack items that are popped by this item.
* @param localData Local data
* @param stack Stack
* @return Number of stack items that are popped by this item
*/
@Override
public int getStackPopCount(BaseLocalData localData, TranslateStack stack) {
return 0;
}
/**
* Gets the number of stack items that are pushed by this item.
* @param localData Local data
* @param stack Stack
* @return Number of stack items that are pushed by this item
*/
@Override
public int getStackPushCount(BaseLocalData localData, TranslateStack stack) {
return 0;
}
/**
* Pops long value off the stack
* Pops long value off the stack.
*
* @param stack Stack
* @return long value
@@ -791,19 +860,13 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Converts action index to address in the specified list of actions
* Converts action index to address in the specified list of actions.
*
* @param actions List of actions
* @param ip Action index
* @return address
*/
public static long ip2adr(List<Action> actions, int ip) {
/* List<Action> actions=new ArrayList<Action>();
for(GraphSourceItem s:sources){
if(s instanceof Action){
actions.add((Action)s);
}
}*/
if (ip >= actions.size()) {
if (actions.isEmpty()) {
return 0;
@@ -817,11 +880,11 @@ public abstract class Action implements GraphSourceItem {
}
/**
* Converts address to action index in the specified list of actions
* Converts address to action index in the specified list of actions.
*
* @param actions List of actions
* @param addr Address
* @return action index
* @return Action index
*/
public static int adr2ip(List<Action> actions, long addr) {
for (int ip = 0; ip < actions.size(); ip++) {
@@ -838,20 +901,31 @@ public abstract class Action implements GraphSourceItem {
return -1;
}
/**
* Converts actions to ActionScript source code - decompiles.
* @param uninitializedClassTraits Uninitialized class traits
* @param asm ASM source
* @param actions List of actions
* @param path Path
* @param writer Writer
* @param charset Charset
* @return Writer
* @throws InterruptedException
*/
public static GraphTextWriter actionsToSource(Map<String, Map<String, Trait>> uninitializedClassTraits, final ASMSource asm, final List<Action> actions, final String path, GraphTextWriter writer, String charset) throws InterruptedException {
return Action.actionsToSource(uninitializedClassTraits, asm, actions, path, writer, charset, new ArrayList<>());
}
/**
* Converts list of actions to ActionScript source code
* Converts list of actions to ActionScript source code.
*
* @param asm
* @param asm ASM source
* @param actions List of actions
* @param path
* @param writer
* @param charset
* @param treeOperations
* @return
* @param path Path
* @param writer Writer
* @param charset Charset
* @param treeOperations List of tree operations
* @return Writer
* @throws java.lang.InterruptedException
*/
public static GraphTextWriter actionsToSource(Map<String, Map<String, Trait>> uninitializedClassTraits, final ASMSource asm, final List<Action> actions, final String path, GraphTextWriter writer, String charset, List<ActionTreeOperation> treeOperations) throws InterruptedException {
@@ -916,20 +990,33 @@ public abstract class Action implements GraphSourceItem {
return writer;
}
/**
* Converts list of actions to List of treeItems
* @param uninitializedClassTraits Uninitialized class traits
* @param insideDoInitAction Inside DoInitAction?
* @param insideFunction Inside function?
* @param actions List of actions
* @param version SWF version
* @param staticOperation Unused
* @param path Path
* @param charset Charset
* @return List of treeItems
* @throws InterruptedException
*/
public static List<GraphTargetItem> actionsToTree(Map<String, Map<String, Trait>> uninitializedClassTraits, boolean insideDoInitAction, boolean insideFunction, List<Action> actions, int version, int staticOperation, String path, String charset) throws InterruptedException {
return actionsToTree(uninitializedClassTraits, insideDoInitAction, insideFunction, new HashMap<>(), new HashMap<>(), new HashMap<>(), actions, version, staticOperation, path, charset);
}
/**
* Converts list of actions to List of treeItems
* Converts list of actions to List of treeItems.
*
* @param regNames Register names
* @param variables
* @param functions
* @param variables Variables
* @param functions Functions
* @param actions List of actions
* @param version SWF version
* @param staticOperation
* @param path
* @param staticOperation Unused
* @param path Path
* @return List of treeItems
* @throws java.lang.InterruptedException
*/
@@ -950,9 +1037,9 @@ public abstract class Action implements GraphSourceItem {
/**
* Translates this function to stack and output.
*
* @param uninitializedClassTraits
* @param secondPassData
* @param insideDoInitAction
* @param uninitializedClassTraits Uninitialized class traits
* @param secondPassData Second pass data
* @param insideDoInitAction Inside DoInitAction?
* @param lineStartIns Line start instruction
* @param stack Stack
* @param output Output
@@ -966,42 +1053,83 @@ public abstract class Action implements GraphSourceItem {
public void translate(Map<String, Map<String, Trait>> uninitializedClassTraits, SecondPassData secondPassData, boolean insideDoInitAction, GraphSourceItem lineStartIns, TranslateStack stack, List<GraphTargetItem> output, HashMap<Integer, String> regNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, int staticOperation, String path) throws InterruptedException {
}
/**
* Translate the item to target items.
* @param localData Local data
* @param stack Stack
* @param output Output list
* @param staticOperation Unused
* @param path Path
* @throws InterruptedException
*/
@Override
public void translate(BaseLocalData localData, TranslateStack stack, List<GraphTargetItem> output, int staticOperation, String path) throws InterruptedException {
ActionLocalData aLocalData = (ActionLocalData) localData;
translate(aLocalData.uninitializedClassTraits, aLocalData.secondPassData, aLocalData.insideDoInitAction, aLocalData.lineStartAction, stack, output, aLocalData.regNames, aLocalData.variables, aLocalData.functions, staticOperation, path);
}
/**
* Checks whether this item is a jump.
* @return True if this item is a jump, false otherwise
*/
@Override
public boolean isJump() {
return false;
}
/**
* Checks whether this item is a branch.
* @return True if this item is a branch, false otherwise
*/
@Override
public boolean isBranch() {
return false;
}
/**
* Checks whether this item is an exit (throw, return, etc.).
* @return True if this item is an exit, false otherwise
*/
@Override
public boolean isExit() {
return false;
}
/**
* Gets branches
* @param code Code
* @return List of IPs to branch to
*/
@Override
public List<Integer> getBranches(GraphSource code) {
return new ArrayList<>();
}
/**
* Checks whether this item is ignored.
* @return True if this item is ignored, false otherwise
*/
@Override
public boolean isIgnored() {
return ignored;
}
/**
* Sets whether this item is ignored.
* @param ignored True if this item is ignored, false otherwise
* @param pos Sub position
*/
@Override
public void setIgnored(boolean ignored, int pos) {
this.ignored = ignored;
}
/**
* Prepares variables for the given container.
* @param cnt Container
* @param variables Variables - map of variable names to variable items
* @return
*/
private static HashMap<String, GraphTargetItem> prepareVariables(GraphSourceItemContainer cnt, HashMap<String, GraphTargetItem> variables) {
HashMap<String, GraphTargetItem> variables2 = new LinkedHashMap<>(variables);
if (cnt instanceof ActionDefineFunction || cnt instanceof ActionDefineFunction2) {
@@ -1014,12 +1142,34 @@ public abstract class Action implements GraphSourceItem {
return variables2;
}
public static List<GraphTargetItem> actionsPartToTree(ActionGraph graph, Set<GraphPart> switchParts, SecondPassData secondPassData, boolean insideDoInitAction, Reference<GraphSourceItem> fi, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, TranslateStack stack, List<Action> actions, int start, int end, int version, int staticOperation, String path, String charset) throws InterruptedException, GraphPartChangeException {
/**
* Converts list of actions to tree.
* @param graph ActionGraph
* @param switchParts Switch parts
* @param secondPassData Second pass data
* @param insideDoInitAction Inside DoInitAction?
* @param lineStartActionRef Line start action reference
* @param registerNames Register names
* @param variables Variables
* @param functions Functions
* @param stack Stack
* @param actions List of actions
* @param start Start
* @param end End
* @param version SWF version
* @param staticOperation Static operation
* @param path Path
* @param charset Charset
* @return List of tree items
* @throws InterruptedException
* @throws GraphPartChangeException
*/
public static List<GraphTargetItem> actionsPartToTree(ActionGraph graph, Set<GraphPart> switchParts, SecondPassData secondPassData, boolean insideDoInitAction, Reference<GraphSourceItem> lineStartActionRef, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, TranslateStack stack, List<Action> actions, int start, int end, int version, int staticOperation, String path, String charset) throws InterruptedException, GraphPartChangeException {
if (start < actions.size() && (end > 0) && (start > 0)) {
logger.log(Level.FINE, "Entering {0}-{1}{2}", new Object[]{start, end, actions.size() > 0 ? (" (" + actions.get(start).toString() + " - " + actions.get(end == actions.size() ? end - 1 : end) + ")") : ""});
}
ActionLocalData localData = new ActionLocalData(switchParts, secondPassData, insideDoInitAction, registerNames, variables, functions, graph.getUninitializedClassTraits());
localData.lineStartAction = fi.getVal();
localData.lineStartAction = lineStartActionRef.getVal();
List<GraphTargetItem> output = new ArrayList<>();
int ip = start;
boolean isWhile = false;
@@ -1057,7 +1207,7 @@ public abstract class Action implements GraphSourceItem {
if (isStackEmpty) {
localData.lineStartAction = action;
fi.setVal(action);
lineStartActionRef.setVal(action);
}
if (action instanceof GraphSourceItemContainer) {
GraphSourceItemContainer cnt = (GraphSourceItemContainer) action;
@@ -1153,6 +1303,11 @@ public abstract class Action implements GraphSourceItem {
return output;
}
/**
* Gets item without global prefix.
* @param ti Target item
* @return Target item without global prefix
*/
public static GraphTargetItem getWithoutGlobal(GraphTargetItem ti) {
GraphTargetItem t = ti;
if (!(t instanceof GetMemberActionItem)) {
@@ -1181,11 +1336,20 @@ public abstract class Action implements GraphSourceItem {
return ti;
}
/**
* Checks whether the loops are ignored.
* @return True if the loops are ignored, false otherwise
*/
@Override
public boolean ignoredLoops() {
return false;
}
/**
* Sets constant pool to actions.
* @param actions List of actions
* @param cpool Constant pool
*/
public static void setConstantPool(List<? extends GraphSourceItem> actions, ConstantPool cpool) {
for (GraphSourceItem a : actions) {
if (a instanceof ActionPush) {
@@ -1206,11 +1370,24 @@ public abstract class Action implements GraphSourceItem {
}
}
/**
* Get ASM source with replaced Actions.
* @param container Container
* @param knownAddreses Known addresses
* @param exportMode Export mode
* @param writer Writer
* @return Writer
*/
public GraphTextWriter getASMSourceReplaced(ActionList container, Set<Long> knownAddreses, ScriptExportMode exportMode, GraphTextWriter writer) {
writer.appendNoHilight(getASMSource(container, knownAddreses, exportMode));
return writer;
}
/**
* Converts ECMA object to float point.
* @param o Object
* @return Float point
*/
public static double toFloatPoint(Object o) {
if (o instanceof Double) {
return (Double) o;
@@ -1240,6 +1417,14 @@ public abstract class Action implements GraphSourceItem {
return 0;
}
/**
* Converts Get to Set.
* (variables, members, properties)
* @param get Get item
* @param value Value
* @param variables Variables
* @return Set item
*/
public static GraphTargetItem gettoset(GraphTargetItem get, GraphTargetItem value, List<VariableActionItem> variables) {
GraphTargetItem ret = get;
boolean boxed = false;
@@ -1269,26 +1454,51 @@ public abstract class Action implements GraphSourceItem {
return ret;
}
/**
* Checks whether this item is a DeobfuscatePop instruction.
* It is a special instruction for deobfuscation.
* @return True if this item is a DeobfuscatePop instruction, false otherwise
*/
@Override
public boolean isDeobfuscatePop() {
return false;
}
/**
* Gets the line in the high level source code.
* @return Line
*/
@Override
public int getLine() {
return 0;
}
/**
* Gets the high level source code file name.
* @return File name
*/
@Override
public String getFile() {
return null;
}
/**
* Gets virtual address. A virtual address can be used for storing original
* address before applying deobfuscation.
*
* @return Virtual address
*/
@Override
public long getVirtualAddress() {
return virtualAddress;
}
/**
* Sets virtual address. A virtual address can be used for storing original
* address before applying deobfuscation.
*
* @param virtualAddress Virtual address
*/
@Override
public void setVirtualAddress(long virtualAddress) {
this.virtualAddress = virtualAddress;
@@ -26,12 +26,8 @@ import com.jpexs.decompiler.flash.action.swf5.ActionReturn;
import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister;
import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.*;
/**
* Cleaner for ActionDefineFunction push registers. From Flash 7 onwards,
@@ -49,11 +45,22 @@ import java.util.TreeSet;
*/
public class ActionDefineFunctionPushRegistersCleaner extends SWFDecompilerAdapter {
/**
* Called when an action list is parsed
* @param actions Action list
* @param swf SWF object
* @throws InterruptedException
*/
@Override
public void actionListParsed(ActionList actions, SWF swf) throws InterruptedException {
cleanActionDefineFunctions(actions);
}
/**
* Cleans ActionDefineFunctions
*
* @param actions Action list
*/
private void cleanActionDefineFunctions(ActionList actions) {
for (int i = actions.size() - 1; i >= 0; i--) {
Action action = actions.get(i);
@@ -71,11 +78,11 @@ public class ActionDefineFunctionPushRegistersCleaner extends SWFDecompilerAdapt
}
/**
*
* @param code
* Cleans push registers from ActionDefineFunction
* @param code Action list
* @param startIndex Index of first Action in DefineFunction body
* @param count Count of actions in DefineFunction
* @return
* @return True if registers were cleaned, false otherwise
*/
private boolean cleanPushRegisters(ActionList code, int startIndex, int count) {
if (count == 0) {
@@ -21,79 +21,60 @@ import com.jpexs.decompiler.flash.FinalProcessLocalData;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.as2.ActionScript2ClassDetector;
import com.jpexs.decompiler.flash.action.as2.Trait;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.EnumerateActionItem;
import com.jpexs.decompiler.flash.action.model.EnumeratedValueActionItem;
import com.jpexs.decompiler.flash.action.model.FunctionActionItem;
import com.jpexs.decompiler.flash.action.model.GetPropertyActionItem;
import com.jpexs.decompiler.flash.action.model.SetTarget2ActionItem;
import com.jpexs.decompiler.flash.action.model.SetTargetActionItem;
import com.jpexs.decompiler.flash.action.model.SetTypeActionItem;
import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem;
import com.jpexs.decompiler.flash.action.model.TemporaryRegister;
import com.jpexs.decompiler.flash.action.model.TemporaryRegisterMark;
import com.jpexs.decompiler.flash.action.model.*;
import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem;
import com.jpexs.decompiler.flash.action.model.operations.EqActionItem;
import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem;
import com.jpexs.decompiler.flash.action.model.operations.StrictEqActionItem;
import com.jpexs.decompiler.flash.action.model.operations.StrictNeqActionItem;
import com.jpexs.decompiler.flash.action.swf4.ActionEquals;
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.RegisterNumber;
import com.jpexs.decompiler.flash.action.swf4.*;
import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction;
import com.jpexs.decompiler.flash.action.swf5.ActionEquals2;
import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals;
import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2;
import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor;
import com.jpexs.decompiler.graph.Block;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphPartChangeException;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.Loop;
import com.jpexs.decompiler.graph.SecondPassData;
import com.jpexs.decompiler.graph.StopPartKind;
import com.jpexs.decompiler.graph.ThrowState;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.model.BinaryOpItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.GotoItem;
import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.PopItem;
import com.jpexs.decompiler.graph.model.PushItem;
import com.jpexs.decompiler.graph.model.ScriptEndItem;
import com.jpexs.decompiler.graph.model.SwitchItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.decompiler.graph.*;
import com.jpexs.decompiler.graph.model.*;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
*
* ActionScript 1/2 graph
* @author JPEXS
*/
public class ActionGraph extends Graph {
/**
* Inside DoInitAction
*/
private boolean insideDoInitAction;
/**
* Inside function
*/
private boolean insideFunction;
/**
* Uninitialized class traits - maps class name to map of trait name to trait
*/
private Map<String, Map<String, Trait>> uninitializedClassTraits;
/**
* Constructs ActionGraph
* @param uninitializedClassTraits Uninitialized class traits
* @param path Path
* @param insideDoInitAction Inside DoInitAction
* @param insideFunction Inside function
* @param code Code
* @param registerNames Register names
* @param variables Variables
* @param functions Functions
* @param version Version
* @param charset Charset
*/
public ActionGraph(Map<String, Map<String, Trait>> uninitializedClassTraits, String path, boolean insideDoInitAction, boolean insideFunction, List<Action> code, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, int version, String charset) {
super(new ActionGraphSource(path, insideDoInitAction, code, version, registerNames, variables, functions, charset), new ArrayList<>());
this.uninitializedClassTraits = uninitializedClassTraits;
@@ -101,15 +82,28 @@ public class ActionGraph extends Graph {
this.insideFunction = insideFunction;
}
/**
* Get uninitialized class traits
* @return Uninitialized class traits - maps class name to map of trait name to trait
*/
public Map<String, Map<String, Trait>> getUninitializedClassTraits() {
return uninitializedClassTraits;
}
/**
* Gets the graphSource
* @return GraphSource
*/
@Override
public ActionGraphSource getGraphCode() {
return (ActionGraphSource) code;
}
/**
* Gets sub-graphs
* @return
*/
@Override
public LinkedHashMap<String, Graph> getSubGraphs() {
LinkedHashMap<String, Graph> subgraphs = new LinkedHashMap<>();
@@ -141,15 +135,40 @@ public class ActionGraph extends Graph {
return subgraphs;
}
/**
* Checks whether is inside DoInitAction.
* @return True if is inside DoInitAction, false otherwise
*/
public boolean isInsideDoInitAction() {
return insideDoInitAction;
}
/**
* Method called after populating all parts.
* @param allParts All parts
*/
@Override
protected void afterPopupateAllParts(Set<GraphPart> allParts) {
}
/**
* Translates via Graph - decompiles.
* @param uninitializedClassTraits Uninitialized class traits
* @param secondPassData Second pass data
* @param insideDoInitAction Inside DoInitAction
* @param insideFunction Inside function
* @param registerNames Register names
* @param variables Variables
* @param functions Functions
* @param code Code
* @param version Version
* @param staticOperation Unused
* @param path Path
* @param charset Charset
* @return List of graph target items
* @throws InterruptedException
*/
public static List<GraphTargetItem> translateViaGraph(Map<String, Map<String, Trait>> uninitializedClassTraits, SecondPassData secondPassData, boolean insideDoInitAction, boolean insideFunction, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, List<Action> code, int version, int staticOperation, String path, String charset) throws InterruptedException {
ActionGraph g = new ActionGraph(uninitializedClassTraits, path, insideDoInitAction, insideFunction, code, registerNames, variables, functions, version, charset);
ActionLocalData localData = new ActionLocalData(secondPassData, insideDoInitAction, registerNames, uninitializedClassTraits);
@@ -157,6 +176,13 @@ public class ActionGraph extends Graph {
return g.translate(localData, staticOperation, path);
}
/**
* Final process stack.
* Override this method to provide custom behavior.
* @param stack Translate stack
* @param output Output
* @param path Path
*/
@Override
public void finalProcessStack(TranslateStack stack, List<GraphTargetItem> output, String path) {
if (stack.size() > 0) {
@@ -172,6 +198,13 @@ public class ActionGraph extends Graph {
}
}
/**
* Checks whether a part can be a break candidate.
* @param localData Local data
* @param part Part
* @param throwStates List of throw states
* @return True if part can be a break candidate
*/
@Override
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part, List<ThrowState> throwStates) {
if (part.refs.size() <= 1) {
@@ -190,6 +223,14 @@ public class ActionGraph extends Graph {
return !isSwitch;
}
/**
* Final process.
* @param list List of GraphTargetItems
* @param level Level
* @param localData Local data
* @param path Path
* @throws InterruptedException
*/
@Override
protected void finalProcess(List<GraphTargetItem> list, int level, FinalProcessLocalData localData, String path) throws InterruptedException {
@@ -464,6 +505,12 @@ public class ActionGraph extends Graph {
}
/**
* Moves all stack items to commands.
* (If it's not a branch stack resistant or other special case)
* @param commands Commands
* @param stack Stack
*/
public void makeAllCommands(List<GraphTargetItem> commands, TranslateStack stack) {
GraphTargetItem enumerate = null;
if (!commands.isEmpty() && (commands.get(commands.size() - 1) instanceof EnumerateActionItem)) {
@@ -509,6 +556,14 @@ public class ActionGraph extends Graph {
}
}
/**
* Translates the graph - decompiles.
* @param localData Local data
* @param staticOperation Unused
* @param path Path
* @return List of GraphTargetItems
* @throws InterruptedException
*/
@Override
public List<GraphTargetItem> translate(BaseLocalData localData, int staticOperation, String path) throws InterruptedException {
List<GraphTargetItem> ret = super.translate(localData, staticOperation, path);
@@ -536,6 +591,10 @@ public class ActionGraph extends Graph {
}
*/
/**
* Makes define registers up.
* @param list List of GraphTargetItems
*/
private void makeDefineRegistersUp(List<GraphTargetItem> list) {
for (int i = 0; i < list.size(); i++) {
@@ -581,6 +640,12 @@ public class ActionGraph extends Graph {
}
}
/**
* Finds part.
* @param ip IP
* @param allParts All parts
* @return GraphPart
*/
private GraphPart findPart(int ip, Set<GraphPart> allParts) {
for (GraphPart p : allParts) {
if (p.containsIP(ip)) {
@@ -590,6 +655,30 @@ public class ActionGraph extends Graph {
return null;
}
/**
* Check before decompiling next section.
* @param currentRet Current return
* @param foundGotos Found gotos
* @param partCodes Part codes
* @param partCodePos Part code position
* @param visited Visited
* @param code Code
* @param localData Local data
* @param allParts All parts
* @param stack Stack
* @param parent Parent part
* @param part Part
* @param stopPart Stop part
* @param stopPartKind Stop part kind
* @param loops Loops
* @param throwStates Throw states
* @param output Output
* @param currentLoop Current loop
* @param staticOperation Unused
* @param path Path
* @return List of GraphTargetItems to replace current output and stop further processing. Null to continue.
* @throws InterruptedException
*/
@Override
protected List<GraphTargetItem> check(List<GraphTargetItem> currentRet, List<GotoItem> foundGotos, Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos, Set<GraphPart> visited, GraphSource code, BaseLocalData localData, Set<GraphPart> allParts, TranslateStack stack, GraphPart parent, GraphPart part, List<GraphPart> stopPart, List<StopPartKind> stopPartKind, List<Loop> loops, List<ThrowState> throwStates, List<GraphTargetItem> output, Loop currentLoop, int staticOperation, String path) throws InterruptedException {
if (!output.isEmpty()) {
@@ -695,6 +784,11 @@ public class ActionGraph extends Graph {
return ret;
}
/**
* Checks IP and allows to modify it.
* @param ip Current IP
* @return New IP
*/
@Override
protected int checkIp(int ip) {
int oldIp = ip;
@@ -723,6 +817,12 @@ public class ActionGraph extends Graph {
return ip;
}
/**
* Prepares second pass data.
* Can return null when no second pass will happen.
* @param list List of GraphTargetItems
* @return Second pass data or null
*/
@Override
public SecondPassData prepareSecondPass(List<GraphTargetItem> list) {
ActionSecondPassData spd = new ActionSecondPassData();
@@ -735,6 +835,14 @@ public class ActionGraph extends Graph {
return spd;
}
/**
* Checks second pass switches.
* @param processedIfs Processed ifs
* @param list List of GraphTargetItems
* @param allSwitchParts All switch parts
* @param allSwitchOnFalseParts All switch on false parts
* @param allSwitchExpressions All switch expressions
*/
private void checkSecondPassSwitches(Set<GraphPart> processedIfs, List<GraphTargetItem> list, List<List<GraphPart>> allSwitchParts, List<List<GraphPart>> allSwitchOnFalseParts, List<List<GraphTargetItem>> allSwitchExpressions) {
for (GraphTargetItem item : list) {
if (item instanceof IfItem) {
@@ -18,15 +18,10 @@ package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphPartChangeException;
import com.jpexs.decompiler.graph.GraphSource;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.*;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -35,31 +30,75 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* ActionScript 1/2 graph source
* @author JPEXS
*/
public class ActionGraphSource extends GraphSource {
/**
* Actions
*/
private final ActionList actions;
/**
* SWF version
*/
public int version;
/**
* Register names - map of register number to register name
*/
private final HashMap<Integer, String> registerNames;
/**
* Variables - map of variable name to variable item
*/
private final HashMap<String, GraphTargetItem> variables;
/**
* Functions - map of function name to function item
*/
private final HashMap<String, GraphTargetItem> functions;
/**
* Is inside doInitAction
*/
private final boolean insideDoInitAction;
/**
* Path
*/
private final String path;
/**
* Charset - SWFs version 5 and lower do not use UTF-8 charset
*/
private String charset;
/**
* Position cache
*/
private List<Long> posCache = null;
/**
* Gets actions
* @return Actions
*/
public List<Action> getActions() {
return actions;
}
/**
* Constructs new ActionGraphSource
* @param path Path
* @param insideDoInitAction Is inside doInitAction
* @param actions Actions
* @param version SWF version
* @param registerNames Register names
* @param variables Variables
* @param functions Functions
* @param charset Charset
*/
public ActionGraphSource(String path, boolean insideDoInitAction, List<Action> actions, int version, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, String charset) {
this.actions = actions instanceof ActionList ? (ActionList) actions : new ActionList(actions, charset);
this.version = version;
@@ -71,15 +110,28 @@ public class ActionGraphSource extends GraphSource {
this.charset = charset;
}
/**
* Gets charset
* @return Charset
*/
public String getCharset() {
return charset;
}
/**
* Gets the important addresses
* @return Set of important addresses
*/
@Override
public Set<Long> getImportantAddresses() {
return Action.getActionsAllRefs(actions);
}
/**
* Converts instruction at the specified position to string
* @param pos Position of the instruction
* @return Instruction as string
*/
@Override
public String insToString(int pos) {
if (pos < actions.size()) {
@@ -88,25 +140,57 @@ public class ActionGraphSource extends GraphSource {
return "";
}
/**
* Gets the size of the graph source
* @return The size of the graph source
*/
@Override
public int size() {
return actions.size();
}
/**
* Gets the graph source item at the specified position
* @param pos Position of the graph source item
* @return The graph source item at the specified position
*/
@Override
public GraphSourceItem get(int pos) {
return actions.get(pos);
}
/**
* Sets the graph source item at the specified position
* @param pos Position of the graph source item
* @param t The graph source item
*/
public void set(int pos, Action t) {
actions.set(pos, t);
}
/**
* Checks if the graph source is empty
* @return True if the graph source is empty, false otherwise
*/
@Override
public boolean isEmpty() {
return actions.isEmpty();
}
/**
* Translates the part of the graph source
* @param graph Graph
* @param part Graph part
* @param localData Local data
* @param stack Translate stack
* @param start Start position
* @param end End position
* @param staticOperation Unused
* @param path Path
* @return List of graph target items
* @throws InterruptedException
* @throws GraphPartChangeException
*/
@Override
public List<GraphTargetItem> translatePart(Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException {
Reference<GraphSourceItem> fi = new Reference<>(localData.lineStartInstruction);
@@ -116,8 +200,10 @@ public class ActionGraphSource extends GraphSource {
return r;
}
private List<Long> posCache = null;
/**
* Rebuilds the position cache
*/
private void rebuildCache() {
posCache = new ArrayList<>();
for (int i = 0; i < size(); i++) {
@@ -125,6 +211,11 @@ public class ActionGraphSource extends GraphSource {
}
}
/**
* Converts position to address
* @param pos Position
* @return Address
*/
@Override
public long pos2adr(int pos) {
GraphSourceItem si = actions.get(pos);
@@ -134,6 +225,11 @@ public class ActionGraphSource extends GraphSource {
return 0;
}
/**
* Converts address to position
* @param adr Address
* @return Position
*/
@Override
public int adr2pos(long adr) {
if (posCache == null) {
@@ -154,6 +250,12 @@ public class ActionGraphSource extends GraphSource {
return ret;
}
/**
* Converts address to position
* @param adr Address
* @param nearest Nearest
* @return Position
*/
@Override
public int adr2pos(long adr, boolean nearest) {
if (posCache == null) {
@@ -178,6 +280,10 @@ public class ActionGraphSource extends GraphSource {
return ret;
}
/**
* Gets variables
* @return Map of variable name to variable item
*/
public HashMap<String, GraphTargetItem> getVariables() {
return variables;
}
@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.helpers.CodeFormatting;
import com.jpexs.decompiler.flash.helpers.FileTextWriter;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -41,43 +42,82 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* List of actions.
* @author JPEXS
*/
public class ActionList extends ArrayList<Action> {
/**
* Deobfuscation mode
*/
public int deobfuscationMode;
/**
* File data
*/
public byte[] fileData;
/**
* Charset - SWF version 5 or lower does not have UTF-8 charset
*/
private String charset;
/**
* Constructs a new action list with the specified charset.
* @param charset Charset
*/
public ActionList(String charset) {
this.charset = charset;
}
/**
* Gets the charset.
* @return Charset
*/
public String getCharset() {
return charset;
}
/**
* Constructs a new action list with the specified actions and charset.
* @param actions Actions
* @param charset Charset
*/
public ActionList(Collection<Action> actions, String charset) {
super(actions);
this.charset = charset;
}
/**
* Sets actions.
* @param list Actions
*/
public void setActions(List<Action> list) {
clear();
addAll(list);
}
/**
* Removes actions.
* @param actionsToRemove Actions to remove
*/
public void removeActions(List<Action> actionsToRemove) {
ActionListReader.removeActions(this, actionsToRemove, true);
}
/**
* Removes action.
* @param index Index
*/
public void removeAction(int index) {
ActionListReader.removeAction(this, index, true);
}
/**
* Removes action.
* @param index Index
* @param count Count
*/
public void removeAction(int index, int count) {
if (size() <= index + count - 1) {
// Can't remove count elements, only size - index is available
@@ -89,22 +129,45 @@ public class ActionList extends ArrayList<Action> {
}
}
/**
* Adds action.
* @param index Index
* @param action Action
*/
public void addAction(int index, Action action) {
ActionListReader.addAction(this, index, action, false, false);
}
/**
* Adds actions.
* @param index Index
* @param actions Actions
*/
public void addActions(int index, List<Action> actions) {
ActionListReader.addActions(this, index, actions);
}
/**
* Fixes action list.
*/
public void fixActionList() {
ActionListReader.fixActionList(this, null);
}
/**
* Gets container last actions.
* @param action Action
* @return Container last actions
*/
public List<Action> getContainerLastActions(Action action) {
return ActionListReader.getContainerLastActions(this, action);
}
/**
* Gets references for action.
* @param target Target
* @return References
*/
public Iterator<Action> getReferencesFor(final Action target) {
return new Iterator<Action>() {
private final Iterator<Action> iterator = ActionList.this.iterator();
@@ -167,6 +230,10 @@ public class ActionList extends ArrayList<Action> {
};
}
/**
* Gets constant pools.
* @return Constant pools
*/
public Iterable<ActionConstantPool> getConstantPools() {
return () -> new Iterator<ActionConstantPool>() {
private final Iterator<Action> iterator = ActionList.this.iterator();
@@ -203,6 +270,10 @@ public class ActionList extends ArrayList<Action> {
};
}
/**
* Gets pushes.
* @return Pushes
*/
public Iterable<ActionPush> getPushes() {
return () -> new Iterator<ActionPush>() {
private final Iterator<Action> iterator = ActionList.this.iterator();
@@ -239,6 +310,11 @@ public class ActionList extends ArrayList<Action> {
};
}
/**
* Gets constant pool index reference count.
* @param index Index
* @return Constant pool index reference count
*/
public int getConstantPoolIndexReferenceCount(int index) {
int count = 0;
for (Action action : this) {
@@ -258,6 +334,11 @@ public class ActionList extends ArrayList<Action> {
return count;
}
/**
* Gets inline constant pool string.
* @param index Index
* @param str String
*/
public void inlineConstantPoolString(int index, String str) {
for (ActionPush push : getPushes()) {
for (int i = 0; i < push.values.size(); i++) {
@@ -272,6 +353,9 @@ public class ActionList extends ArrayList<Action> {
}
}
/**
* Removes non-referenced constant pool items.
*/
public void removeNonReferencedConstantPoolItems() {
int maxSize = 0;
for (ActionConstantPool constantPool : getConstantPools()) {
@@ -321,6 +405,9 @@ public class ActionList extends ArrayList<Action> {
}
}
/**
* Removes nops.
*/
public void removeNops() {
for (int i = 0; i < size(); i++) {
if (get(i) instanceof ActionNop) {
@@ -329,15 +416,30 @@ public class ActionList extends ArrayList<Action> {
}
}
/**
* Gets action by address.
* @param address Address
* @return Action
*/
public Action getByAddress(long address) {
int idx = getIndexByAddress(address);
return idx == -1 ? null : get(idx);
}
/**
* Gets index by action.
* @param action Action
* @return Index
*/
public int getIndexByAction(Action action) {
return getIndexByAddress(action.getAddress());
}
/**
* Gets index by address.
* @param address Address
* @return Index
*/
public int getIndexByAddress(long address) {
int min = 0;
int max = size() - 1;
@@ -357,6 +459,11 @@ public class ActionList extends ArrayList<Action> {
return -1;
}
/**
* Gets container.
* @param idx Index
* @return Container
*/
public GraphSourceItemContainer getContainer(int idx) {
Action action = get(idx);
int i = idx - 1;
@@ -376,6 +483,11 @@ public class ActionList extends ArrayList<Action> {
return null;
}
/**
* Gets container end index.
* @param idx Index
* @return Container end index
*/
public int getContainerEndIndex(int idx) {
Action action = get(idx);
int i = idx - 1;
@@ -395,6 +507,10 @@ public class ActionList extends ArrayList<Action> {
return -1;
}
/**
* Gets unreachable actions.
* @return Unreachable actions
*/
public List<Action> getUnreachableActions() {
int[] isReachable = getUnreachableActionsMap(-1, 0);
List<Action> unreachableActions = new ArrayList<>();
@@ -411,6 +527,12 @@ public class ActionList extends ArrayList<Action> {
return unreachableActions;
}
/**
* Gets unreachable actions.
* @param jumpIndex Jump index
* @param jumpTargetIndex Jump target index
* @return Unreachable actions
*/
public List<Action> getUnreachableActions(int jumpIndex, int jumpTargetIndex) {
int[] isReachable = getUnreachableActionsMap(jumpIndex, jumpTargetIndex);
isReachable[jumpIndex] = 0;
@@ -428,6 +550,12 @@ public class ActionList extends ArrayList<Action> {
return unreachableActions;
}
/**
* Gets unreachable actions map.
* @param jumpIndex Jump index
* @param jumpTargetIndex Jump target index
* @return Unreachable actions array - ip to reachable
*/
private int[] getUnreachableActionsMap(int jumpIndex, int jumpTargetIndex) {
int size = size();
@@ -498,6 +626,9 @@ public class ActionList extends ArrayList<Action> {
return isReachable;
}
/**
* Combines pushes.
*/
public void combinePushes() {
for (int i = 0; i < size() - 1; i++) {
Action action = get(i);
@@ -522,6 +653,9 @@ public class ActionList extends ArrayList<Action> {
}
}
/**
* Expands pushes.
*/
public void expandPushes() {
for (int i = 0; i < size(); i++) {
Action action = get(i);
@@ -543,6 +677,10 @@ public class ActionList extends ArrayList<Action> {
}
}
/**
* Saves to file.
* @param fileName File name
*/
public void saveToFile(String fileName) {
File file = new File(fileName);
try (FileTextWriter writer = new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(file))) {
@@ -552,6 +690,10 @@ public class ActionList extends ArrayList<Action> {
}
}
/**
* Converts to string.
* @return String
*/
@Override
public String toString() {
HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false);
@@ -559,15 +701,4 @@ public class ActionList extends ArrayList<Action> {
writer.finishHilights();
return writer.toString();
}
/*public String toSource() {
try {
HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false);
actionsToSource(null, this, "", writer, charset);
return writer.toString();
} catch (InterruptedException ex) {
Logger.getLogger(ActionList.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}*/
}
@@ -33,14 +33,9 @@ import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.stat.Statistics;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.TreeMap;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -49,25 +44,28 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Class for reading data from SWF file
* Class for reading Action data from SWF file.
*
* @author JPEXS
*/
public class ActionListReader {
/**
* Logger
*/
private static final Logger logger = Logger.getLogger(ActionListReader.class.getName());
/**
* Reads list of actions from the stream. Reading ends with
* ActionEndFlag(=0) or end of the stream.
*
* @param listeners
* @param sis
* @param version
* @param ip
* @param endIp
* @param path
* @param deobfuscationMode
* @param listeners List of listeners
* @param sis SWF input stream
* @param version SWF version
* @param ip Start IP
* @param endIp End IP
* @param path Path
* @param deobfuscationMode Deobfuscation mode
* @return List of actions
* @throws IOException
* @throws java.lang.InterruptedException
@@ -101,13 +99,13 @@ public class ActionListReader {
* Reads list of actions from the stream. Reading ends with
* ActionEndFlag(=0) or end of the stream.
*
* @param listeners
* @param sis
* @param version
* @param ip
* @param endIp
* @param path
* @param deobfuscationMode
* @param listeners List of listeners
* @param sis SWF input stream
* @param version SWF version
* @param ip Start IP
* @param endIp End IP
* @param path Path
* @param deobfuscationMode Deobfuscation mode
* @return List of actions
* @throws IOException
* @throws java.lang.InterruptedException
@@ -183,6 +181,12 @@ public class ActionListReader {
return actions;
}
/**
* Fixes action list.
* @param actions Action list
* @param nextOffsets Next offsets
* @return Fixed action list
*/
public static ActionList fixActionList(ActionList actions, Map<Long, Long> nextOffsets) {
Map<Action, List<Action>> containerLastActions = new HashMap<>();
getContainerLastActions(actions, containerLastActions);
@@ -229,6 +233,15 @@ public class ActionListReader {
return ret;
}
/**
* Gets original actions.
* @param sis SWF input stream
* @param startIp Start IP
* @param endIp End IP
* @return List of actions
* @throws IOException
* @throws InterruptedException
*/
public static List<Action> getOriginalActions(SWFInputStream sis, int startIp, int endIp) throws IOException, InterruptedException {
// Map of the actions. Use TreeMap to sort the keys in ascending order
Map<Long, Action> actionMap = new TreeMap<>();
@@ -241,6 +254,13 @@ public class ActionListReader {
return new ArrayList<>(actionMap.values());
}
/**
* Gets near address.
* @param actions Action list
* @param address Address
* @param next Next
* @return Near address
*/
private static long getNearAddress(ActionList actions, long address, boolean next) {
int min = 0;
int max = actions.size() - 1;
@@ -262,6 +282,11 @@ public class ActionListReader {
: (max >= 0 ? actions.get(max).getAddress() : -1);
}
/**
* Converts list of actions to map of address to action.
* @param actions List of actions
* @return Map of address to action
*/
private static Map<Long, Action> actionListToMap(List<Action> actions) {
Map<Long, Action> map = new HashMap<>(actions.size());
for (Action a : actions) {
@@ -275,6 +300,11 @@ public class ActionListReader {
return map;
}
/**
* Gets jumps.
* @param actions List of actions
* @param jumps Jumps - map of jumpAction to targetAction
*/
private static void getJumps(List<Action> actions, Map<Action, Action> jumps) {
Map<Long, Action> actionMap = actionListToMap(actions);
for (Action a : actions) {
@@ -304,6 +334,12 @@ public class ActionListReader {
}
}
/**
* Gets container last actions.
* @param actions List of actions
* @param action Action
* @return List of last actions
*/
public static List<Action> getContainerLastActions(ActionList actions, Action action) {
GraphSourceItemContainer container = (GraphSourceItemContainer) action;
List<Long> sizes = container.getContainerSizes();
@@ -321,6 +357,11 @@ public class ActionListReader {
return lasts;
}
/**
* Gets container last actions.
* @param actions List of actions
* @param lastActions Last actions
*/
private static void getContainerLastActions(ActionList actions, Map<Action, List<Action>> lastActions) {
for (Action a : actions) {
if (a instanceof GraphSourceItemContainer) {
@@ -329,6 +370,13 @@ public class ActionListReader {
}
}
/**
* Updates addresses.
* @param actions List of actions
* @param address Address
* @param charset Charset
* @return End address
*/
private static long updateAddresses(List<Action> actions, long address, String charset) {
for (int i = 0; i < actions.size(); i++) {
Action a = actions.get(i);
@@ -343,12 +391,21 @@ public class ActionListReader {
return address;
}
/**
* Updates action lengths.
* @param actions List of actions
*/
private static void updateActionLengths(List<Action> actions) {
for (int i = 0; i < actions.size(); i++) {
actions.get(i).updateLength();
}
}
/**
* Updates action stores.
* @param actions List of actions
* @param jumps Jumps - map of jumpAction to targetAction
*/
private static void updateActionStores(List<Action> actions, Map<Action, Action> jumps) {
Map<Long, Action> actionMap = actionListToMap(actions);
for (int i = 0; i < actions.size(); i++) {
@@ -371,6 +428,11 @@ public class ActionListReader {
}
}
/**
* Updates container sizes.
* @param actions List of actions
* @param containerLastActions Container last actions
*/
private static void updateContainerSizes(List<Action> actions, Map<Action, List<Action>> containerLastActions) {
for (int i = 0; i < actions.size(); i++) {
Action a = actions.get(i);
@@ -388,6 +450,12 @@ public class ActionListReader {
}
}
/**
* Replaces jump targets.
* @param jumps Jumps - map of jumpAction to targetAction
* @param oldTarget Old target
* @param newTarget New target
*/
private static void replaceJumpTargets(Map<Action, Action> jumps, Action oldTarget, Action newTarget) {
for (Action a : jumps.keySet()) {
if (jumps.get(a) == oldTarget) {
@@ -396,6 +464,12 @@ public class ActionListReader {
}
}
/**
* Replaces container last actions.
* @param containerLastActions Container last actions
* @param oldTarget Old target
* @param newTarget New target
*/
private static void replaceContainerLastActions(Map<Action, List<Action>> containerLastActions, Action oldTarget, Action newTarget) {
for (Action a : containerLastActions.keySet()) {
List<Action> targets = containerLastActions.get(a);
@@ -407,6 +481,14 @@ public class ActionListReader {
}
}
/**
* Updates jumps.
* @param actions List of actions
* @param jumps Jumps - map of jumpAction to targetAction
* @param containerLastActions Container last actions
* @param endAddress End address
* @param charset Charset
*/
private static void updateJumps(List<Action> actions, Map<Action, Action> jumps, Map<Action, List<Action>> containerLastActions, long endAddress, String charset) {
if (actions.isEmpty()) {
return;
@@ -449,12 +531,12 @@ public class ActionListReader {
/**
* 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
* container.
*
* @param actions
* @param index
* @param removeWhenLast
* @return
* @param actions List of actions
* @param index Index
* @param removeWhenLast Remove when last
* @return True if action was removed, false otherwise
*/
public static boolean removeAction(ActionList actions, int index, boolean removeWhenLast) {
@@ -513,12 +595,12 @@ public class ActionListReader {
/**
* Removes multiple actions from the action list, and updates all references
* This method will keep the inner actions of the container when you remove
* the container
* the container.
*
* @param actions
* @param actionsToRemove
* @param removeWhenLast
* @return
* @param actions List of actions
* @param actionsToRemove Actions to remove
* @param removeWhenLast Remove when last
* @return True if actions were removed, false otherwise
*/
public static boolean removeActions(ActionList actions, List<Action> actionsToRemove, boolean removeWhenLast) {
@@ -575,14 +657,14 @@ public class ActionListReader {
/**
* Adds an action to the action list to the specified location, and updates
* all references
* all references.
*
* @param actions
* @param index
* @param action
* @param addToContainer
* @param replaceJump
* @return
* @param actions List of actions
* @param index Index
* @param action Action
* @param addToContainer Add to container
* @param replaceJump Replace jump
* @return True if action was added, false otherwise
*/
public static boolean addAction(ActionList actions, int index, Action action,
boolean addToContainer, boolean replaceJump) {
@@ -644,12 +726,12 @@ public class ActionListReader {
/**
* Adds an action to the action list to the specified location, and updates
* all references
* all references.
*
* @param actions
* @param index
* @param newActions
* @return
* @param actions List of actions
* @param index Index
* @param newActions New actions
* @return True if actions were added, false otherwise
*/
public static boolean addActions(ActionList actions, int index, List<Action> newActions) {
@@ -687,6 +769,23 @@ public class ActionListReader {
return true;
}
/**
* Reads action list at the specified position.
* @param listeners List of listeners
* @param cpool Constant pool
* @param sis SWF input stream
* @param actions Actions
* @param nextOffsets Next offsets
* @param ip IP
* @param startIp Start IP
* @param endIp End IP
* @param path Path
* @param indeterminate Indeterminate
* @param visitedContainers Visited containers
* @param charset Charset
* @return Action
* @throws IOException
*/
private static Action readActionListAtPos(List<DisassemblyListener> listeners, ConstantPool cpool,
SWFInputStream sis, Map<Long, Action> actions, Map<Long, Long> nextOffsets,
long ip, long startIp, long endIp, String path, boolean indeterminate, List<Long> visitedContainers, String charset) throws IOException {
@@ -792,6 +891,12 @@ public class ActionListReader {
return entryAction;
}
/**
* Fixes constant pools.
* @param listeners List of listeners
* @param actions Actions
* @return True if constant pools were fixed, false otherwise
*/
public static boolean fixConstantPools(List<DisassemblyListener> listeners, ActionList actions) {
Action lastAction = actions.get(actions.size() - 1);
int endIp = (int) lastAction.getAddress();
@@ -813,6 +918,21 @@ public class ActionListReader {
return false;
}
/**
* Fixes constant pools.
* @param listeners List of listeners
* @param cpool Constant pool
* @param actions Actions
* @param actionMap Action map
* @param ip IP
* @param startIp Start IP
* @param endIp End IP
* @param path Path
* @param indeterminate Indeterminate
* @param visitedContainers Visited containers
* @return True if constant pools were fixed, false otherwise
* @throws IOException
*/
private static boolean fixConstantPools(List<DisassemblyListener> listeners, ConstantPool cpool,
List<Action> actions, Map<Integer, Action> actionMap,
int ip, int startIp, int endIp, String path, boolean indeterminate, List<Integer> visitedContainers) throws IOException {
@@ -22,28 +22,53 @@ import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SecondPassData;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
*
* Local data for ActionScript decompilation.
* @author JPEXS
*/
public class ActionLocalData extends BaseLocalData {
/**
* Uninitialized class traits - map of class name to map of trait name to trait
*/
public final Map<String, Map<String, Trait>> uninitializedClassTraits;
/**
* Register names - map of register number to register name
*/
public final HashMap<Integer, String> regNames;
/**
* Variables - map of variable name to variable target item
*/
public final HashMap<String, GraphTargetItem> variables;
/**
* Functions - map of function name to function target item
*/
public final HashMap<String, GraphTargetItem> functions;
/**
* Line start action
*/
public GraphSourceItem lineStartAction;
/**
* Is inside doInitAction
*/
public boolean insideDoInitAction;
/**
* Constructs new ActionLocalData
* @param secondPassData Second pass data
* @param insideDoInitAction Is inside doInitAction
* @param uninitializedClassTraits Uninitialized class traits
*/
public ActionLocalData(SecondPassData secondPassData, boolean insideDoInitAction, Map<String, Map<String, Trait>> uninitializedClassTraits) {
this.secondPassData = secondPassData;
regNames = new HashMap<>();
@@ -53,6 +78,13 @@ public class ActionLocalData extends BaseLocalData {
this.uninitializedClassTraits = uninitializedClassTraits;
}
/**
* Constructs new ActionLocalData
* @param secondPassData Second pass data
* @param insideDoInitAction Is inside doInitAction
* @param regNames Register names
* @param uninitializedClassTraits Uninitialized class traits
*/
public ActionLocalData(SecondPassData secondPassData, boolean insideDoInitAction, HashMap<Integer, String> regNames, Map<String, Map<String, Trait>> uninitializedClassTraits) {
this.regNames = regNames;
this.secondPassData = secondPassData;
@@ -62,6 +94,16 @@ public class ActionLocalData extends BaseLocalData {
this.uninitializedClassTraits = uninitializedClassTraits;
}
/**
* Constructs new ActionLocalData
* @param switchParts Switch parts
* @param secondPassData Second pass data
* @param insideDoInitAction Is inside doInitAction
* @param regNames Register names
* @param variables Variables
* @param functions Functions
* @param uninitializedClassTraits Uninitialized class traits
*/
public ActionLocalData(Set<GraphPart> switchParts, SecondPassData secondPassData, boolean insideDoInitAction, HashMap<Integer, String> regNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, Map<String, Map<String, Trait>> uninitializedClassTraits) {
this.allSwitchParts = switchParts;
this.regNames = regNames;
@@ -17,18 +17,27 @@
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.ecma.Undefined;
import java.util.ArrayList;
import java.util.List;
/**
*
* Represents ActionScript array.
* @author JPEXS
*/
public class ActionScriptArray extends ActionScriptObject {
/**
* Array values
*/
protected List<Object> values = new ArrayList<>();
//TODO: implement some methods?
/**
* Enumerates all members of this object
* @return
*/
@Override
public List<String> enumerate() {
List<String> ret = super.enumerate();
@@ -38,6 +47,11 @@ public class ActionScriptArray extends ActionScriptObject {
return ret;
}
/**
* Gets member of this object
* @param path Member path
* @return Member value
*/
@Override
public Object getMember(String path) {
if (path.matches("[1-9][0-9]*|0")) {
@@ -46,6 +60,11 @@ public class ActionScriptArray extends ActionScriptObject {
return super.getMember(path);
}
/**
* Sets member of this object
* @param path Member path
* @param value Value to set
*/
@Override
public void setMember(String path, Object value) {
if (path.matches("[1-9][0-9]*|0")) {
@@ -55,6 +74,11 @@ public class ActionScriptArray extends ActionScriptObject {
super.setMember(path, value);
}
/**
* Sets value at index
* @param index Index
* @param value Value
*/
public void setValueAtIndex(int index, Object value) {
if (index < 0) {
return;
@@ -71,6 +95,9 @@ public class ActionScriptArray extends ActionScriptObject {
trim();
}
/**
* Trims array by removing trailing undefined values
*/
public void trim() {
for (int i = values.size() - 1; i >= 0; i--) {
if (values.get(i) == Undefined.INSTANCE) {
@@ -81,6 +108,11 @@ public class ActionScriptArray extends ActionScriptObject {
}
}
/**
* Gets value at index
* @param index Index
* @return Value
*/
public Object getValueAtIndex(int index) {
if (index < 0) {
return Undefined.INSTANCE; //throw error?
@@ -20,29 +20,60 @@ import java.util.List;
import java.util.Map;
/**
*
* Represents an ActionScript function.
* @author JPEXS
*/
public class ActionScriptFunction extends ActionScriptObject {
/**
* Offset of the function
*/
protected long functionOffset;
/**
* Length of the function
*/
protected long functionLength;
/**
* Name of the function
*/
protected String functionName;
/**
* Names of the parameters
*/
protected List<String> paramNames;
/**
* Names of the registers - map of register index to register name
*/
protected Map<Integer, String> funcRegNames;
/**
* Gets function name
* @return Function name
*/
public String getFunctionName() {
return functionName;
}
/**
* Gets register names
* @return Register names
*/
public Map<Integer, String> getFuncRegNames() {
return funcRegNames;
}
/**
* Constucts a new ActionScriptFunction
* @param functionOffset Offset of the function
* @param functionLength Length of the function
* @param functionName Name of the function
* @param paramNames Names of the parameters
* @param funcRegNames Names of the registers
*/
public ActionScriptFunction(long functionOffset, long functionLength, String functionName, List<String> paramNames, Map<Integer, String> funcRegNames) {
this.functionOffset = functionOffset;
this.functionLength = functionLength;
@@ -51,10 +82,18 @@ public class ActionScriptFunction extends ActionScriptObject {
this.funcRegNames = funcRegNames;
}
/**
* Gets function length
* @return Function length
*/
public long getFunctionLength() {
return functionLength;
}
/**
* Gets function offset
* @return Function offset
*/
public long getFunctionOffset() {
return functionOffset;
}
@@ -17,25 +17,41 @@
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.ecma.Undefined;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* Represents an ActionScript object.
* @author JPEXS
*/
public class ActionScriptObject implements Cloneable {
/**
* Properties of the object
*/
protected Map<Integer, Object> properties = new HashMap<>();
/**
* Members of the object
*/
protected Map<String, Object> members = new HashMap<>();
/**
* Object that this object extends
*/
protected Object extendsObj;
/**
* Objects that this object implements
*/
protected List<Object> implementsObjs = new ArrayList<>();
/**
* Clears all members of the object
*/
public void clearMembers() {
for (Object o : members.values()) {
if (o instanceof ActionScriptObject) {
@@ -45,31 +61,57 @@ public class ActionScriptObject implements Cloneable {
members.clear();
}
/**
* Clears all properties of the object
*/
public void clearProperties() {
properties.clear();
}
/**
* Clears all members and properties of the object
*/
public void clear() {
clearMembers();
clearProperties();
}
/**
* Gets implements objects
* @return Implements objects
*/
public List<Object> getImplementsObjs() {
return implementsObjs;
}
/**
* Sets implements objects
* @param implementsObjs Implements objects
*/
public void setImplementsObjs(List<Object> implementsObjs) {
this.implementsObjs = implementsObjs;
}
/**
* Sets extends object
* @param extendsObj Extends object
*/
public void setExtendsObj(Object extendsObj) {
this.extendsObj = extendsObj;
}
/**
* Gets extends object
* @return Extends object
*/
public Object getExtendsObj() {
return extendsObj;
}
/**
* Removes a member from the object
* @param path Path to the member
*/
public void removeMember(String path) {
String[] pathParts;
if (path.startsWith("/")) {
@@ -91,14 +133,28 @@ public class ActionScriptObject implements Cloneable {
}
}
/**
* Enumerates all members of the object
* @return List of member names
*/
public List<String> enumerate() {
return new ArrayList<>(members.keySet());
}
/**
* Sets a property of the object
* @param index Index of the property
* @param value Value of the property
*/
public void setProperty(int index, Object value) {
properties.put(index, value);
}
/**
* Gets a property of the object
* @param index Index of the property
* @return Value of the property
*/
public Object getProperty(int index) {
if (!properties.containsKey(index)) {
return Undefined.INSTANCE;
@@ -106,6 +162,11 @@ public class ActionScriptObject implements Cloneable {
return properties.get(index);
}
/**
* Sets a member of the object
* @param path Path to the member
* @param value Value of the member
*/
public void setMember(String path, Object value) {
String[] pathParts;
if (path.startsWith("/")) {
@@ -127,6 +188,10 @@ public class ActionScriptObject implements Cloneable {
}
}
/**
* Clones the object
* @return Cloned object
*/
@Override
public Object clone() {
try {
@@ -137,6 +202,11 @@ public class ActionScriptObject implements Cloneable {
return null;
}
/**
* Gets path to a member
* @param obj Member
* @return Path to the member
*/
public String getMemberPath(Object obj) {
if (obj == this) {
return "";
@@ -157,10 +227,20 @@ public class ActionScriptObject implements Cloneable {
return null;
}
/**
* Gets a member of the object
* @param name Name of the member
* @return Member
*/
protected Object getThisMember(String name) {
return members.get(name);
}
/**
* Gets a member of the object
* @param path Path to the member
* @return Member
*/
public Object getMember(String path) {
String[] pathParts;
if (path.startsWith("/")) {
@@ -17,17 +17,32 @@
package com.jpexs.decompiler.flash.action;
/**
*
* Represents a with block in ActionScript.
* @author JPEXS
*/
public class ActionScriptWith {
/**
* Object that is used in the with block.
*/
protected ActionScriptObject obj;
/**
* Start address of the with block.
*/
protected long startAddr;
/**
* Length of the with block.
*/
protected long length;
/**
* Constructs a new ActionScriptWith object.
* @param obj Object that is used in the with block.
* @param startAddr Start address of the with block.
* @param length Length of the with block.
*/
public ActionScriptWith(ActionScriptObject obj, long startAddr, long length) {
this.obj = obj;
this.startAddr = startAddr;
@@ -19,16 +19,26 @@ package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.graph.GraphPart;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SecondPassData;
import java.util.ArrayList;
import java.util.List;
/**
*
* Second pass data for ActionScript 1/2 decompiler.
* @author JPEXS
*/
public class ActionSecondPassData extends SecondPassData {
/**
* List of parts for each switch statement
*/
List<List<GraphPart>> switchParts = new ArrayList<>();
/**
* List of onFalse parts for each switch statement
*/
List<List<GraphPart>> switchOnFalseParts = new ArrayList<>();
/**
* List of case expressions for each switch statement
*/
List<List<GraphTargetItem>> switchCaseExpressions = new ArrayList<>();
}
@@ -17,13 +17,18 @@
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.graph.GraphTargetItem;
import java.util.List;
/**
*
* Operation on action tree.
* @author JPEXS
*/
public interface ActionTreeOperation {
/**
* Run operation on action tree.
* @param tree Action tree
*/
public void run(List<GraphTargetItem> tree);
}
@@ -17,15 +17,26 @@
package com.jpexs.decompiler.flash.action;
/**
*
* Constant pool too big exception.
* @author JPEXS
*/
public class ConstantPoolTooBigException extends Exception {
/**
* Index of new item
*/
public int index;
/**
* Size of constant pool
*/
public int size;
/**
* Constructs a new ConstantPoolTooBigException with the specified index and size.
* @param index Index of new item
* @param size Size of constant pool
*/
public ConstantPoolTooBigException(int index, int size) {
super("Constant pool too big. index=" + index + ", size=" + size);
this.index = index;
@@ -19,13 +19,20 @@ package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.timeline.DepthState;
/**
*
* Adapter for DepthState object.
* @author JPEXS
*/
public class DepthStateObjectAdapter extends ActionScriptObject {
/**
* DepthState object
*/
protected DepthState ds;
/**
* Constructs a new DepthStateObjectAdapter with the given DepthState object.
* @param ds DepthState object
*/
public DepthStateObjectAdapter(DepthState ds) {
this.ds = ds;
}
@@ -17,46 +17,84 @@
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.ecma.Undefined;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* Display object class.
* @author JPEXS
*/
public class DisplayObject extends ActionScriptObject {
/**
* Display list.
*/
protected Map<Integer, Object> displayList = new HashMap<>();
/**
* Total frames.
*/
protected int totalFrames = 1;
/**
* Current frame.
*/
protected int currentFrame = 1;
/**
* Paused.
*/
protected boolean paused = false;
/**
* Dragging.
*/
protected boolean dragging = false;
/**
* Starts drag.
*/
public void startDrag() {
dragging = true;
}
/**
* Stops drag.
*/
public void stopDrag() {
dragging = false;
}
/**
* Gets current frame.
* @return Current frame.
*/
public int getCurrentFrame() {
return currentFrame;
}
/**
* Gets total frames.
* @return Total frames.
*/
public int getTotalFrames() {
return totalFrames;
}
/**
* Goes to frame.
* @param label Label.
*/
public void gotoLabel(String label) {
//TODO
}
/**
* Goes to frame.
* @param frame Frame.
*/
public void gotoFrame(int frame) {
if (frame < 1) {
frame = 1;
@@ -67,31 +105,65 @@ public class DisplayObject extends ActionScriptObject {
this.currentFrame = frame;
}
/**
* Pauses.
*/
public void pause() {
paused = true;
}
/**
* Plays.
*/
public void play() {
paused = false;
}
/**
* Calls function.
* @param functionAddress Function address.
* @param functionLength Function length.
* @param args Arguments.
* @param regNames Register names.
* @param thisObj This object.
* @return Result.
*/
public Object callFunction(long functionAddress, long functionLength, List<Object> args, Map<Integer, String> regNames, Object thisObj) {
//TODO
return Undefined.INSTANCE;
}
/**
* Calls frame.
* @param frame Frame.
*/
public void callFrame(int frame) {
//TODO
}
/**
* Adds to display list.
* @param depth Depth.
* @param obj Object.
*/
public void addToDisplayList(int depth, Object obj) {
displayList.put(depth, obj);
}
/**
* Removes from display list.
* @param depth Depth.
* @return
*/
public Object removeFromDisplayList(int depth) {
return displayList.remove(depth);
}
/**
* Gets from display list.
* @param depth Depth.
* @return
*/
public Object getFromDisplayList(int depth) {
return displayList.get(depth);
}
@@ -18,50 +18,94 @@ package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.ecma.EcmaScript;
import com.jpexs.decompiler.flash.ecma.Undefined;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.*;
/**
*
* Local data area for ActionScript execution.
* @author JPEXS
*/
public class LocalDataArea {
/**
* Constant pool
*/
public List<String> constantPool;
/**
* Stack
*/
public Stack<Object> stack = new Stack<>();
/**
* Functions
*/
public List<ActionScriptFunction> functions = new ArrayList<>();
/**
* Local variables
*/
public Map<String, Object> localVariables = new HashMap<>();
/**
* Withs
*/
public List<ActionScriptWith> withs = new ArrayList<>();
/**
* Local registers - map of register index to value
*/
public Map<Integer, Object> localRegisters = new HashMap<>();
/**
* Target object
*/
public Object target;
/**
* Stage
*/
public Stage stage;
/**
* Jump
*/
public Long jump;
/**
* Return value
*/
public Object returnValue;
/**
* Execution exception
*/
public String executionException;
/**
* Check stack size
*/
public boolean checkStackSize = true;
/**
* Undefined count
*/
public int undefinedCount = 0;
/**
* Constructs a new local data area.
* @param stage Stage
*/
public LocalDataArea(Stage stage) {
this.stage = stage;
this.target = this.stage;
}
/**
* Constructs a new local data area.
* @param stage Stage
* @param preserveVariableOrder Preserve variable order
*/
public LocalDataArea(Stage stage, boolean preserveVariableOrder) {
this.stage = stage;
target = this.stage;
@@ -70,6 +114,10 @@ public class LocalDataArea {
}
}
/**
* Checks if the stack is empty.
* @return True if the stack is empty, otherwise false
*/
public boolean stackIsEmpty() {
if (!checkStackSize) {
return false;
@@ -77,6 +125,11 @@ public class LocalDataArea {
return stack.isEmpty();
}
/**
* Checks if the stack has a minimum size.
* @param count Count
* @return True if the stack has a minimum size, otherwise false
*/
public boolean stackHasMinSize(int count) {
if (!checkStackSize) {
return true;
@@ -84,6 +137,9 @@ public class LocalDataArea {
return stack.size() >= count;
}
/**
* Clears the local data area.
*/
public void clear() {
constantPool = null;
stack.clear();
@@ -99,10 +155,19 @@ public class LocalDataArea {
undefinedCount = 0;
}
/**
* Pushes a value onto the stack.
* @param val Value
* @return Value
*/
public synchronized Object push(Object val) {
return stack.push(val);
}
/**
* Peeks at the top of the stack.
* @return Value
*/
public synchronized Object peek() {
if (!checkStackSize && stack.isEmpty()) {
undefinedCount++;
@@ -112,6 +177,10 @@ public class LocalDataArea {
return stack.peek();
}
/**
* Pops a value from the stack.
* @return Value
*/
public synchronized Object pop() {
boolean isEmpty = stack.isEmpty();
if (!checkStackSize && stack.isEmpty()) {
@@ -121,10 +190,18 @@ public class LocalDataArea {
return stack.pop();
}
/**
* Pops a value from the stack as a number.
* @return Value
*/
public synchronized Double popAsNumber() {
return EcmaScript.toNumberAs2(pop());
}
/**
* Pops a value from the stack as a string.
* @return Value
*/
public synchronized String popAsString() {
return EcmaScript.toString(pop());
}
@@ -20,23 +20,40 @@ import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import java.util.ArrayList;
import java.util.List;
/**
*
* Stage object for ActionScript execution.
* @author JPEXS
*/
public class Stage extends DisplayObject {
/**
* Start time of the movie
*/
protected long startTime;
/**
* Timelined object
*/
protected Timelined timelined;
/**
* Timeline
*/
protected Timeline timeline;
/**
* Current frame
*/
protected Frame frame;
/**
* Constructs a new Stage object.
* @param timelined Timelined object
*/
public Stage(Timelined timelined) {
startTime = System.currentTimeMillis();
this.timelined = timelined;
@@ -44,6 +61,10 @@ public class Stage extends DisplayObject {
this.frame = timelined != null && this.timeline.getFrameCount() > 0 ? this.timeline.getFrame(0) : null;
}
/**
* Enumerates the instance names.
* @return List of instance names
*/
@Override
public List<String> enumerate() {
List<String> ret = new ArrayList<>();
@@ -57,6 +78,11 @@ public class Stage extends DisplayObject {
return ret;
}
/**
* Gets the member with the given name.
* @param name Name of the member
* @return Member object
*/
@Override
protected Object getThisMember(String name) {
if (frame != null) {
@@ -69,10 +95,18 @@ public class Stage extends DisplayObject {
return null;
}
/**
* Gets the current frame.
* @return Current frame
*/
public long getTime() {
return System.currentTimeMillis() - startTime;
}
/**
* Gets total frames.
* @return Total frames
*/
@Override
public int getTotalFrames() {
if (timeline == null) {
@@ -81,6 +115,10 @@ public class Stage extends DisplayObject {
return timeline.getFrameCount();
}
/**
* Goto frame.
* @param frameNum Frame.
*/
@Override
public void gotoFrame(int frameNum) {
super.gotoFrame(frameNum);
@@ -89,6 +127,10 @@ public class Stage extends DisplayObject {
}
}
/**
* Goto label.
* @param label Label
*/
@Override
public void gotoLabel(String label) {
if (timeline == null) {
@@ -100,18 +142,33 @@ public class Stage extends DisplayObject {
}
}
/**
* Stops sounds.
*/
public void stopSounds() {
}
/**
* Toggle quality.
*/
public void toggleQuality() {
}
/**
* Gets the URL.
* @param url URL
* @param target Target
*/
public void getURL(String url, String target) {
}
/**
* Traces the given values.
* @param val Values
*/
public void trace(Object... val) {
}
@@ -20,10 +20,16 @@ import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.graph.TranslateStack;
/**
*
* Store type action interface.
* @author JPEXS
*/
public interface StoreTypeAction {
/**
* Gets variable name.
* @param stack Stack
* @param cpool Constant pool
* @return Variable name
*/
public String getVariableName(TranslateStack stack, ConstantPool cpool);
}
@@ -17,13 +17,20 @@
package com.jpexs.decompiler.flash.action;
/**
*
* Unknown action exception.
* @author JPEXS
*/
public class UnknownActionException extends RuntimeException {
/**
* OpCode.
*/
public int opCode;
/**
* Constructs a new unknown action exception.
* @param opCode OpCode
*/
public UnknownActionException(int opCode) {
super("Unknown opCode: 0x" + Integer.toHexString(opCode));
this.opCode = opCode;
@@ -17,11 +17,12 @@
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.action.model.ActionItem;
import java.util.List;
import java.util.Stack;
/**
* Raised when actual address has been referenced with an unknown jump
* Raised when actual address has been referenced with an unknown jump.
*
* @author JPEXS
*/
@@ -43,7 +44,7 @@ public class UnknownJumpException extends RuntimeException {
public List<ActionItem> output;
/**
* Constructor
* Constructs a new unknown jump exception.
*
* @param stack Actual stack
* @param addr Actual address
@@ -56,9 +57,9 @@ public class UnknownJumpException extends RuntimeException {
}
/**
* Returns a string representation of the object
* Returns a string representation of the object.
*
* @return a string representation of the object.
* @return A string representation of the object
*/
@Override
public String toString() {
@@ -17,22 +17,7 @@
package com.jpexs.decompiler.flash.action.as2;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.action.model.CallFunctionActionItem;
import com.jpexs.decompiler.flash.action.model.CallMethodActionItem;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.ExtendsActionItem;
import com.jpexs.decompiler.flash.action.model.FunctionActionItem;
import com.jpexs.decompiler.flash.action.model.GetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.GetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.ImplementsOpActionItem;
import com.jpexs.decompiler.flash.action.model.NewMethodActionItem;
import com.jpexs.decompiler.flash.action.model.NewObjectActionItem;
import com.jpexs.decompiler.flash.action.model.ReturnActionItem;
import com.jpexs.decompiler.flash.action.model.SetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.SetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem;
import com.jpexs.decompiler.flash.action.model.TemporaryRegister;
import com.jpexs.decompiler.flash.action.model.TemporaryRegisterMark;
import com.jpexs.decompiler.flash.action.model.*;
import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem;
import com.jpexs.decompiler.flash.action.swf4.RegisterNumber;
@@ -41,41 +26,48 @@ import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.model.CommaExpressionItem;
import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.NotItem;
import com.jpexs.decompiler.graph.model.PopItem;
import com.jpexs.decompiler.graph.model.PushItem;
import com.jpexs.decompiler.graph.model.ScriptEndItem;
import com.jpexs.decompiler.graph.model.TernarOpItem;
import com.jpexs.decompiler.graph.model.*;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
*
* Detects AS2 classes inside DoInitAction tags
* @author JPEXS
*/
public class ActionScript2ClassDetector {
/**
* Logger
*/
private static final Logger logger = Logger.getLogger(ActionScript2ClassDetector.class.getName());
/**
* Assert exception
*/
private class AssertException extends Exception {
/**
* Condition
*/
private final String condition;
/**
* Constructs a new AssertException
* @param condition Condition
*/
public AssertException(String condition) {
super(condition);
this.condition = condition;
}
/**
* Gets condition
* @return Condition
*/
public String getCondition() {
return condition;
}
@@ -84,12 +76,12 @@ public class ActionScript2ClassDetector {
/**
* Checks whether an item is direct submember of path. a.b.c.d is submember
* of a.b.c, x.y.z is not submember of x,
* of a.b.c, x.y.z is not submember of x.
*
* @param item
* @param objectPath
* @param item Item
* @param objectPath Path
* @param newPathItem New submember name
* @return
* @return True if item is submember of path
*/
private boolean isMemberOfPath(GraphTargetItem item, List<String> objectPath, Reference<String> newPathItem) {
List<String> path = getMembersPath(item);
@@ -109,9 +101,9 @@ public class ActionScript2ClassDetector {
}
/**
* Gets path of variable and its getMembers: a.b.c.d => [a,b,c,d]
* Gets path of variable and its getMembers: a.b.c.d => [a,b,c,d].
*
* @param item
* @param item Item
* @return List of path or null if not members path
*/
private List<String> getMembersPath(GraphTargetItem item) {
@@ -144,6 +136,12 @@ public class ActionScript2ClassDetector {
return ret;
}
/**
* Converts SetMemberActionItem to GetMemberActionItem,
* SetVariableActionItem to GetVariableActionItem.
* @param item Item
* @return Converted item
*/
private GraphTargetItem setMemberToGetMember(GraphTargetItem item) {
if (item instanceof SetMemberActionItem) {
return new GetMemberActionItem(null, null, ((SetMemberActionItem) item).object, ((SetMemberActionItem) item).objectName);
@@ -153,6 +151,13 @@ public class ActionScript2ClassDetector {
return null;
}
/**
* Converts NewMethodActionItem or NewObjectActionItem
* to GetMemberActionItem or GetVariableActionItem.
* @param nobj Item
* @return Converted item
* @throws AssertException
*/
private GraphTargetItem newToGetMember(GraphTargetItem nobj) throws AssertException {
if (nobj instanceof NewMethodActionItem) {
NewMethodActionItem nm = (NewMethodActionItem) nobj;
@@ -164,6 +169,11 @@ public class ActionScript2ClassDetector {
throw new AssertException("NewMethod or NewObject expected");
}
/**
* Gets path of setmembers: a.b.c.d => [a,b,c,d].
* @param item Item
* @return List of path or null if not members path
*/
private List<String> getSetMembersPath(GraphTargetItem item) {
if (item instanceof SetVariableActionItem) {
SetVariableActionItem sv = (SetVariableActionItem) item;
@@ -199,10 +209,10 @@ public class ActionScript2ClassDetector {
}
/**
* Get register id or -1 if not found
* Get register id or -1 if not found.
*
* @param item
* @return
* @param item Item
* @return Register id
*/
private int getAsRegisterNum(GraphTargetItem item, String assertName) throws AssertException {
if (item instanceof DirectValueActionItem) {
@@ -219,28 +229,10 @@ public class ActionScript2ClassDetector {
throw new AssertException("not a register - " + assertName);
}
/*private boolean isInstanceRegister(GraphTargetItem item, Reference<Integer> instanceReg, Reference<Integer> classReg,Reference<GraphTargetItem> extracted, String assertName) throws AssertException {
if (item instanceof DirectValueActionItem) {
DirectValueActionItem dv = (DirectValueActionItem) item;
if (dv.value instanceof RegisterNumber) {
RegisterNumber rn = (RegisterNumber) dv.value;
if (rn.number == instanceReg.getVal()) {
return true;
} else if (rn.number == classReg.getVal()) {
return false;
}else{
throw new AssertException(assertName + " - unknown register");
}
}
}
if (item instanceof TemporaryRegister) {
TemporaryRegister tr = (TemporaryRegister) item;
if (!"prototype".equals(getAsString(gm.memberName, "memberName"))) {
throw new AssertException("memberName not \"prototype\"");
}
return tr.getRegId();
}
}
/**
* Gets item without global prefix.
* @param ti Item
* @return Item without global prefix
*/
private static GraphTargetItem getWithoutGlobal(GraphTargetItem ti) {
GraphTargetItem t = ti;
@@ -270,6 +262,13 @@ public class ActionScript2ClassDetector {
return ti;
}
/**
* Converts item to string.
* @param item Item
* @param itemName Item name for exception
* @return String
* @throws AssertException
*/
private String getAsString(GraphTargetItem item, String itemName) throws AssertException {
if (!(item instanceof DirectValueActionItem)) {
throw new AssertException(itemName + " not DirectValue");
@@ -281,6 +280,19 @@ public class ActionScript2ClassDetector {
return mnDv.getAsString();
}
/**
* Detects classes in AS2 script.
* @param uninitializedClassTraits Uninitialized class traits
* @param parts Parts
* @param variables Variables
* @param partsPos Parts position
* @param commandsStartPos Commands start position
* @param commandsEndPos Commands end position
* @param commands Commands
* @param classNamePath Class name path
* @param scriptPath Script path
* @return True if class was detected
*/
private boolean checkClassContent(Map<String, Map<String, Trait>> uninitializedClassTraits, List<GraphTargetItem> parts, HashMap<String, GraphTargetItem> variables, int partsPos, int commandsStartPos, int commandsEndPos, List<GraphTargetItem> commands, List<String> classNamePath, String scriptPath) {
try {
@@ -787,7 +799,12 @@ public class ActionScript2ClassDetector {
return false;
}
//in some weird cases, ifs are detected as ternars, this method expands ternars to ifs
/**
* In some weird cases, ifs are detected as ternars,
* this method expands ternars to ifs.
* @param commands Commands
*/
private void expandTernars(List<GraphTargetItem> commands) {
for (int i = 0; i < commands.size(); i++) {
if (commands.get(i) instanceof TernarOpItem) {
@@ -811,6 +828,15 @@ public class ActionScript2ClassDetector {
}
}
/**
* Checks if variants.
* @param uninitializedClassTraits Uninitialized class traits
* @param commands Commands
* @param variables Variables
* @param pos Position
* @param scriptPath Script path
* @return True if some of the variants was detected
*/
private boolean checkIfVariants(Map<String, Map<String, Trait>> uninitializedClassTraits, List<GraphTargetItem> commands, HashMap<String, GraphTargetItem> variables, int pos, String scriptPath) {
expandTernars(commands);
@@ -950,6 +976,13 @@ public class ActionScript2ClassDetector {
return false;
}
/**
* Checks class.
* @param uninitializedClassTraits Uninitialized class traits
* @param commands Commands
* @param variables Variables
* @param scriptPath Script path
*/
public void checkClass(Map<String, Map<String, Trait>> uninitializedClassTraits, List<GraphTargetItem> commands, HashMap<String, GraphTargetItem> variables, String scriptPath) {
List<GraphTargetItem> localCommands = new ArrayList<>(commands);
boolean changed = false;
@@ -16,17 +16,8 @@
*/
package com.jpexs.decompiler.flash.action.as2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -37,11 +28,23 @@ import java.util.logging.Logger;
*/
public class ActionScript2Classes {
/**
* Map of class name to map of trait name to trait
*/
private static final Map<String, Map<String, Trait>> classToTraits = new HashMap<>();
/**
* Map of class name to list of parent class names
*/
private static final Map<String, List<String>> classInheritance = new HashMap<>();
/**
* Whether the classes are already initialized
*/
private static boolean inited = false;
/**
* Initialize the classes
*/
private static synchronized void initClasses() {
if (inited) {
return;
@@ -100,6 +103,13 @@ public class ActionScript2Classes {
inited = true;
}
/**
* Check if trait exists in class.
* @param className Class name
* @param name Trait name
* @param withInheritance Whether to check also parent classes
* @return True if trait exists
*/
public static boolean traitExists(String className, String name, boolean withInheritance) {
if (!classToTraits.containsKey(className)) {
return false;
@@ -124,11 +134,11 @@ public class ActionScript2Classes {
}
/**
* Get class traits, null when class not exists (or is not built-in)
* Get class traits, null when class not exists (or is not built-in).
*
* @param className
* @param withInheritance
* @return
* @param className Class name
* @param withInheritance Whether to include parent classes
* @return Map of trait name to trait
*/
public static Map<String, Trait> getClassTraits(String className, boolean withInheritance) {
initClasses();
@@ -157,16 +167,29 @@ public class ActionScript2Classes {
return result;
}
/**
* Get map of class name to map of trait name to trait.
* @return Map of class name to map of trait name to trait
*/
public static Map<String, Map<String, Trait>> getClassToTraits() {
initClasses();
return classToTraits;
}
/**
* Get map of class name to list of parent class names.
* @return Map of class name to list of parent class names
*/
public static Map<String, List<String>> getClassInheritance() {
initClasses();
return classInheritance;
}
/**
* Sample test
* @param args
*/
public static void main(String[] args) {
Map<String, Trait> traits = getClassTraits("flash.filters.BevelFilter", true);
if (traits != null) {
@@ -17,16 +17,38 @@
package com.jpexs.decompiler.flash.action.as2;
/**
*
* Represents a method trait in ActionScript 2.
* @author JPEXS
*/
public class Method implements Trait {
/**
* Whether the method is static
*/
private final boolean isStatic;
/**
* Name of the method
*/
private final String name;
/**
* Return type of the method
*/
private final String returnType;
/**
* Name of the class the method is in
*/
private final String className;
/**
* Constructs a new method trait.
* @param isStatic Whether the method is static
* @param name Name of the method
* @param returnType Return type of the method
* @param className Name of the class the method is in
*/
public Method(boolean isStatic, String name, String returnType, String className) {
this.isStatic = isStatic;
this.name = name;
@@ -34,35 +56,63 @@ public class Method implements Trait {
this.className = className;
}
/**
* Gets the name of the class the method is in.
* @return Class name
*/
@Override
public String getClassName() {
return className;
}
/**
* Gets whether the method is static.
* @return Whether the method is static
*/
@Override
public boolean isStatic() {
return isStatic;
}
/**
* Gets the name of the method.
* @return Name
*/
@Override
public String getName() {
return name;
}
/**
* Gets the return type of the method.
* @return Return type
*/
public String getReturnType() {
return returnType;
}
/**
* Converts the method to a string.
* @return String representation
*/
@Override
public String toString() {
return (isStatic ? "static " : "") + "function " + name + ": " + returnType;
}
/**
* Gets the call type of the method.
* @return Call type
*/
@Override
public String getCallType() {
return returnType;
}
/**
* Gets the type of the trait.
* @return Trait type
*/
@Override
public String getType() {
return "Function";
@@ -17,18 +17,38 @@
package com.jpexs.decompiler.flash.action.as2;
/**
*
* Represents a trait in ActionScript 2.
* @author JPEXS
*/
public interface Trait {
/**
* Whether the trait is static
* @return Whether the trait is static
*/
public boolean isStatic();
/**
* Gets the name of the trait
* @return Name of the trait
*/
public String getName();
/**
* Gets the type of the trait
* @return Type of the trait
*/
public String getType();
/**
* Gets the call type of the trait
* @return Call type of the trait
*/
public String getCallType();
/**
* Gets the class name of the trait
* @return Class name of the trait
*/
public String getClassName();
}
@@ -17,15 +17,7 @@
package com.jpexs.decompiler.flash.action.as2;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.action.model.CallMethodActionItem;
import com.jpexs.decompiler.flash.action.model.DeleteActionItem;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.FunctionActionItem;
import com.jpexs.decompiler.flash.action.model.GetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.GetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.NewMethodActionItem;
import com.jpexs.decompiler.flash.action.model.SetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.SetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.*;
import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem;
import com.jpexs.decompiler.flash.action.swf4.RegisterNumber;
@@ -34,22 +26,19 @@ import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor;
import com.jpexs.decompiler.graph.GraphTargetItem;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
*
* Uninitialized class fields detector for ActionScript 2.
* @author JPEXS
*/
public class UninitializedClassFieldsDetector {
/**
* Gets path of variable and its getMembers: a.b.c.d => [a,b,c,d]
* Gets path of variable and its getMembers: a.b.c.d => [a,b,c,d].
*
* @param item
* @param item Item to get path from
* @return List of path or null if not members path
*/
private List<String> getMembersPath(GraphTargetItem item) {
@@ -92,6 +81,11 @@ public class UninitializedClassFieldsDetector {
return ret;
}
/**
* Gets full path of item: a.b.c.d => [a,b,c,d].
* @param item Item to get path from
* @return List of path or null if not members path
*/
private List<String> getFullPath(GraphTargetItem item) {
if (item instanceof GetMemberActionItem) {
return getMembersPath(item);
@@ -149,6 +143,14 @@ public class UninitializedClassFieldsDetector {
return path;
}
/**
* Checks whether the class contains a trait.
* @param classTraits Class traits
* @param classInheritance Class inheritance
* @param className Class name
* @param name Trait name
* @return Whether the class contains the trait
*/
private boolean containsTrait(Map<String, Map<String, Trait>> classTraits, Map<String, List<String>> classInheritance, String className, String name) {
if (!classTraits.containsKey(className)) {
return false;
@@ -164,6 +166,11 @@ public class UninitializedClassFieldsDetector {
return false;
}
/**
* Calculates uninitialized class traits.
* @param swf SWF
* @return Map of class name to map of trait name to trait
*/
public Map<String, Map<String, Trait>> calculateAs2UninitializedClassTraits(SWF swf) {
if (swf.isAS3()) {
return new HashMap<>();
@@ -17,16 +17,38 @@
package com.jpexs.decompiler.flash.action.as2;
/**
*
* Represents a variable trait in ActionScript 2.
* @author JPEXS
*/
public class Variable implements Trait {
/**
* Whether the variable is static
*/
private final boolean isStatic;
/**
* Name of the variable
*/
private final String name;
/**
* Type of the variable
*/
private final String type;
/**
* Name of the class the variable is in
*/
private final String className;
/**
* Constructs a new variable trait.
* @param isStatic Whether the variable is static
* @param name Name of the variable
* @param type Type of the variable
* @param className Name of the class the variable is in
*/
public Variable(boolean isStatic, String name, String type, String className) {
this.isStatic = isStatic;
this.name = name;
@@ -34,31 +56,55 @@ public class Variable implements Trait {
this.className = className;
}
/**
* Checks whether the variable is static
* @return Whether the variable is static
*/
@Override
public boolean isStatic() {
return isStatic;
}
/**
* Gets the name of the variable
* @return Name of the variable
*/
@Override
public String getName() {
return name;
}
/**
* Gets the type of the variable
* @return Type of the variable
*/
@Override
public String getType() {
return type;
}
/**
* Converts the variable to a string
* @return String representation of the variable
*/
@Override
public String toString() {
return (isStatic ? "static " : "") + "var " + name + ": " + getType();
}
/**
* Gets the call type of the variable
* @return Call type of the variable
*/
@Override
public String getCallType() {
return null;
}
/**
* Gets the class name of the variable
* @return Class name of the variable
*/
@Override
public String getClassName() {
return className;
@@ -0,0 +1,4 @@
/**
* ActionScript 2.0 decompilation.
*/
package com.jpexs.decompiler.flash.action.as2;
@@ -0,0 +1,4 @@
/**
* Deobfuscating ActionScript 1/2 code.
*/
package com.jpexs.decompiler.flash.action.deobfuscation;
@@ -0,0 +1,4 @@
/**
* Fast action list.
*/
package com.jpexs.decompiler.flash.action.fastactionlist;
@@ -0,0 +1,4 @@
/**
* Flash Lite Actions.
*/
package com.jpexs.decompiler.flash.action.flashlite;
@@ -19,7 +19,7 @@ package com.jpexs.decompiler.flash.action.model;
import com.jpexs.decompiler.graph.model.CompoundableBinaryOp;
/**
* CompoundableBinaryOp but for AS1/2, which does not have &&, || compound
* CompoundableBinaryOp but for AS1/2, which does not have &amp;&amp;, || compound
* operator.
*
* @author JPEXS
@@ -0,0 +1,4 @@
/**
* High-level ActionScript 1/2 model of various clauses.
*/
package com.jpexs.decompiler.flash.action.model.clauses;
@@ -0,0 +1,4 @@
/**
* High-level ActionScript 1/2 model of operations.
*/
package com.jpexs.decompiler.flash.action.model.operations;
@@ -0,0 +1,4 @@
/**
* ActionScript1/2 high-level model.
*/
package com.jpexs.decompiler.flash.action.model;
@@ -0,0 +1,4 @@
/**
* Parsing, decompiling and execution of ActionScript 1/2 bytecode.
*/
package com.jpexs.decompiler.flash.action;
@@ -0,0 +1,4 @@
/**
* Parsing ActionScript 1/2 scripts and P-code.
*/
package com.jpexs.decompiler.flash.action.parser;
@@ -0,0 +1,4 @@
/**
* Parsing ActionScript 1/2 PCode.
*/
package com.jpexs.decompiler.flash.action.parser.pcode;
@@ -0,0 +1,4 @@
/**
* Parsing ActionScript 1/2 scripts.
*/
package com.jpexs.decompiler.flash.action.parser.script;
@@ -0,0 +1,4 @@
/**
* Special actions.
*/
package com.jpexs.decompiler.flash.action.special;
@@ -0,0 +1,4 @@
/**
* SWF 3 actions.
*/
package com.jpexs.decompiler.flash.action.swf3;
@@ -0,0 +1,4 @@
/**
* SWF 4 actions.
*/
package com.jpexs.decompiler.flash.action.swf4;
@@ -0,0 +1,4 @@
/**
* SWF 5 actions.
*/
package com.jpexs.decompiler.flash.action.swf5;
@@ -0,0 +1,4 @@
/**
* SWF 6 actions.
*/
package com.jpexs.decompiler.flash.action.swf6;
@@ -0,0 +1,4 @@
/**
* SWF 7 actions.
*/
package com.jpexs.decompiler.flash.action.swf7;