faster "removeGetTimes"

This commit is contained in:
honfika@gmail.com
2015-10-07 15:53:36 +02:00
parent 4b98a7fae7
commit e8425fcadd
7 changed files with 1130 additions and 243 deletions

View File

@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.DisassemblyListener;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscator;
import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscatorSimple;
import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscatorSimpleFast;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.special.ActionDeobfuscateJump;
@@ -180,6 +181,9 @@ public class ActionListReader {
}
} else if (deobfuscationMode == 1) {
try {
try (Statistics s = new Statistics("ActionDeobfuscatorSimpleFast")) {
new ActionDeobfuscatorSimpleFast().actionListParsed(actions, sis.getSwf());
}
try (Statistics s = new Statistics("ActionDeobfuscatorSimple")) {
new ActionDeobfuscatorSimple().actionListParsed(actions, sis.getSwf());
}

View File

@@ -1,235 +0,0 @@
/*
* Copyright (C) 2010-2015 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.action;
import com.jpexs.decompiler.flash.action.special.ActionStore;
import com.jpexs.decompiler.flash.action.swf4.ActionIf;
import com.jpexs.decompiler.flash.action.swf4.ActionJump;
import com.jpexs.decompiler.flash.action.swf4.ActionPush;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
*
* @author JPEXS
*/
public class FastActionList {
private LinkedList<Action> actions;
private Map<Action, List<Action>> containerLastActions;
private Map<Action, Action> jumps;
public FastActionList(ActionList actions) {
this.actions = new LinkedList<>(actions);
Map<Action, List<Action>> containerLastActions = new HashMap<>();
getContainerLastActions(this.actions, actions, containerLastActions);
this.containerLastActions = containerLastActions;
Map<Action, Action> jumps = new HashMap<>();
getJumps(actions, jumps);
this.jumps = jumps;
}
private void getContainerLastActions(LinkedList<Action> linkedActions, ActionList actions, Map<Action, List<Action>> lastActions) {
for (Action a : linkedActions) {
if (a instanceof GraphSourceItemContainer) {
lastActions.put(a, getContainerLastActions(actions, a));
}
}
}
private List<Action> getContainerLastActions(ActionList actions, Action action) {
GraphSourceItemContainer container = (GraphSourceItemContainer) action;
List<Long> sizes = container.getContainerSizes();
long endAddress = action.getAddress() + container.getHeaderSize();
List<Action> lasts = new ArrayList<>(sizes.size());
for (long size : sizes) {
endAddress += size;
long lastActionAddress = getNearAddress(actions, endAddress - 1, false);
Action lastAction = null;
if (lastActionAddress != -1) {
lastAction = actions.getByAddress(lastActionAddress);
}
lasts.add(lastAction);
}
return lasts;
}
private long getNearAddress(ActionList actions, long address, boolean next) {
int min = 0;
int max = actions.size() - 1;
while (max >= min) {
int mid = (min + max) / 2;
long midValue = actions.get(mid).getAddress();
if (midValue == address) {
return address;
} else if (midValue < address) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return next
? (min < actions.size() ? actions.get(min).getAddress() : -1)
: (max >= 0 ? actions.get(max).getAddress() : -1);
}
private static void getJumps(ActionList actions, Map<Action, Action> jumps) {
for (Action a : actions) {
long target = -1;
if (a instanceof ActionIf) {
target = ((ActionIf) a).getTargetAddress();
} else if (a instanceof ActionJump) {
target = ((ActionJump) a).getTargetAddress();
} else if (a instanceof ActionStore) {
ActionStore aStore = (ActionStore) a;
int storeSize = aStore.getStoreSize();
// skip storeSize + 1 actions (+1 is the current action)
Action targetAction = a;
for (int i = 0; i <= storeSize; i++) {
long address = targetAction.getAddress() + targetAction.getTotalActionLength();
targetAction = actions.getByAddress(address);
if (targetAction == null) {
break;
}
}
jumps.put(a, targetAction);
}
if (target >= 0) {
Action targetAction = actions.getByAddress(target);
jumps.put(a, targetAction);
}
}
}
private void updateJumps(Map<Action, Action> jumps) {
if (actions.isEmpty()) {
return;
}
long endAddress = actions.getLast().getAddress();
for (Action a : actions) {
if (a instanceof ActionIf) {
ActionIf aIf = (ActionIf) a;
Action target = jumps.get(a);
long offset;
if (target != null) {
offset = target.getAddress() - a.getAddress() - a.getTotalActionLength();
} else {
offset = endAddress - a.getAddress() - a.getTotalActionLength();
}
aIf.setJumpOffset((int) offset);
} else if (a instanceof ActionJump) {
ActionJump aJump = (ActionJump) a;
Action target = jumps.get(a);
long offset;
if (target != null) {
offset = target.getAddress() - a.getAddress() - a.getTotalActionLength();
} else {
offset = endAddress - a.getAddress() - a.getTotalActionLength();
}
aJump.setJumpOffset((int) offset);
}
}
}
private void updateActionStores(ActionList actionList, Map<Action, Action> jumps) {
for (Action a : actions) {
if (a instanceof ActionStore) {
ActionStore aStore = (ActionStore) a;
Action nextActionAfterStore = jumps.get(a);
Action a1 = a;
List<Action> store = new ArrayList<>();
while (true) {
long address = a1.getAddress() + a1.getTotalActionLength();
a1 = actionList.getByAddress(address);
if (a1 == null || a1 == nextActionAfterStore) {
break;
}
store.add(a1);
}
aStore.setStore(store);
}
}
}
private void updateContainerSizes(Map<Action, List<Action>> containerLastActions) {
for (Action a : actions) {
if (a instanceof GraphSourceItemContainer) {
GraphSourceItemContainer container = (GraphSourceItemContainer) a;
List<Action> lastActions = containerLastActions.get(a);
long startAddress = a.getAddress() + container.getHeaderSize();
for (int j = 0; j < lastActions.size(); j++) {
Action lastAction = lastActions.get(j);
int length = (int) (lastAction.getAddress() + lastAction.getTotalActionLength() - startAddress);
container.setContainerSize(j, length);
startAddress += length;
}
}
}
}
public void expandPushes() {
ListIterator<Action> iterator = actions.listIterator();
while (iterator.hasNext()) {
Action action = iterator.next();
if (action instanceof ActionPush) {
ActionPush push = (ActionPush) action;
if (push.values.size() > 1) {
for (int i = 1; i < push.values.size(); i++) {
Object value = push.values.get(i);
ActionPush newPush = new ActionPush(value);
newPush.constantPool = push.constantPool;
iterator.add(newPush);
}
Object obj = push.values.get(0);
push.values.clear();
push.values.add(obj);
}
}
}
}
private void updateActionAddressesAndLengths() {
long address = actions.get(0).getAddress();
for (Action action : actions) {
action.setAddress(address);
action.updateLength();
address += action.getTotalActionLength();
}
}
public ActionList toActionList() {
ActionList result = new ActionList(actions);
updateActionAddressesAndLengths();
updateJumps(jumps);
updateActionStores(result, jumps);
updateContainerSizes(containerLastActions);
return result;
}
}

View File

@@ -22,7 +22,6 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.ActionLocalData;
import com.jpexs.decompiler.flash.action.FastActionList;
import com.jpexs.decompiler.flash.action.swf4.ActionAdd;
import com.jpexs.decompiler.flash.action.swf4.ActionAnd;
import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar;
@@ -84,11 +83,7 @@ public class ActionDeobfuscatorSimple implements SWFDecompilerListener {
@Override
public void actionListParsed(ActionList actions, SWF swf) throws InterruptedException {
FastActionList fastActions = new FastActionList(actions);
fastActions.expandPushes();
actions.setActions(fastActions.toActionList());
removeGetTimes(actions);
//removeGetTimes(actions);
removeObfuscationIfs(actions);
}
@@ -370,7 +365,7 @@ public class ActionDeobfuscatorSimple implements SWFDecompilerListener {
if (action instanceof ActionJump) {
ActionJump jump = (ActionJump) action;
long address = jump.getAddress() + jump.getTotalActionLength() + jump.getJumpOffset();
long address = jump.getTargetAddress();
idx = actions.getIndexByAddress(address);
if (idx == -1) {
throw new TranslateException("Jump target not found: " + address);
@@ -384,7 +379,7 @@ public class ActionDeobfuscatorSimple implements SWFDecompilerListener {
}
if (EcmaScript.toBoolean(stack.pop().getResult())) {
long address = aif.getAddress() + aif.getTotalActionLength() + aif.getJumpOffset();
long address = aif.getTargetAddress();
idx = actions.getIndexByAddress(address);
if (idx == -1) {
throw new TranslateException("If target not found: " + address);

View File

@@ -0,0 +1,389 @@
/*
* Copyright (C) 2010-2015 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.deobfuscation;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.ActionLocalData;
import com.jpexs.decompiler.flash.action.fastactionlist.ActionItem;
import com.jpexs.decompiler.flash.action.fastactionlist.FastActionList;
import com.jpexs.decompiler.flash.action.fastactionlist.FastActionListIterator;
import com.jpexs.decompiler.flash.action.swf4.ActionAdd;
import com.jpexs.decompiler.flash.action.swf4.ActionAnd;
import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar;
import com.jpexs.decompiler.flash.action.swf4.ActionCharToAscii;
import com.jpexs.decompiler.flash.action.swf4.ActionDivide;
import com.jpexs.decompiler.flash.action.swf4.ActionEquals;
import com.jpexs.decompiler.flash.action.swf4.ActionGetTime;
import com.jpexs.decompiler.flash.action.swf4.ActionIf;
import com.jpexs.decompiler.flash.action.swf4.ActionJump;
import com.jpexs.decompiler.flash.action.swf4.ActionLess;
import com.jpexs.decompiler.flash.action.swf4.ActionMBAsciiToChar;
import com.jpexs.decompiler.flash.action.swf4.ActionMBStringLength;
import com.jpexs.decompiler.flash.action.swf4.ActionMultiply;
import com.jpexs.decompiler.flash.action.swf4.ActionNot;
import com.jpexs.decompiler.flash.action.swf4.ActionOr;
import com.jpexs.decompiler.flash.action.swf4.ActionPush;
import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd;
import com.jpexs.decompiler.flash.action.swf4.ActionStringEquals;
import com.jpexs.decompiler.flash.action.swf4.ActionStringLength;
import com.jpexs.decompiler.flash.action.swf4.ActionStringLess;
import com.jpexs.decompiler.flash.action.swf4.ActionSubtract;
import com.jpexs.decompiler.flash.action.swf4.ActionToInteger;
import com.jpexs.decompiler.flash.action.swf4.ConstantIndex;
import com.jpexs.decompiler.flash.action.swf4.RegisterNumber;
import com.jpexs.decompiler.flash.action.swf5.ActionAdd2;
import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd;
import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift;
import com.jpexs.decompiler.flash.action.swf5.ActionBitOr;
import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift;
import com.jpexs.decompiler.flash.action.swf5.ActionBitURShift;
import com.jpexs.decompiler.flash.action.swf5.ActionBitXor;
import com.jpexs.decompiler.flash.action.swf5.ActionDecrement;
import com.jpexs.decompiler.flash.action.swf5.ActionEquals2;
import com.jpexs.decompiler.flash.action.swf5.ActionIncrement;
import com.jpexs.decompiler.flash.action.swf5.ActionLess2;
import com.jpexs.decompiler.flash.action.swf5.ActionModulo;
import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate;
import com.jpexs.decompiler.flash.action.swf5.ActionToNumber;
import com.jpexs.decompiler.flash.action.swf5.ActionToString;
import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf;
import com.jpexs.decompiler.flash.action.swf6.ActionGreater;
import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater;
import com.jpexs.decompiler.flash.ecma.EcmaScript;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerListener;
import com.jpexs.decompiler.graph.Graph;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.TranslateStack;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class ActionDeobfuscatorSimpleFast implements SWFDecompilerListener {
private final int executionLimit = 30000;
@Override
public void actionListParsed(ActionList actions, SWF swf) throws InterruptedException {
FastActionList fastActions = new FastActionList(actions);
fastActions.expandPushes();
removeGetTimes(fastActions);
//removeObfuscationIfs(fastActions);
actions.setActions(fastActions.toActionList());
}
private boolean removeGetTimes(FastActionList actions) {
if (actions.isEmpty()) {
return false;
}
boolean changed = true;
int getTimeCount = 1;
while (changed && getTimeCount > 0) {
changed = false;
actions.removeUnreachableActions();
actions.removeZeroJumps();
getTimeCount = 0;
// GetTime, If => Jump, assume GetTime > 0
FastActionListIterator iterator = actions.iterator();
while (iterator.hasNext()) {
Action a = iterator.next().action;
ActionItem a2Item = iterator.peek(0);
Action a2 = a2Item.action;
boolean isGetTime = a instanceof ActionGetTime;
if (isGetTime) {
getTimeCount++;
}
if (isGetTime && a2 instanceof ActionIf) {
ActionJump jump = new ActionJump(0);
ActionItem jumpItem = new ActionItem(jump);
jumpItem.jumpTarget = a2Item.jumpTarget;
iterator.remove(); // GetTime
iterator.next();
iterator.remove(); // If
iterator.add(jumpItem); // replace If with Jump
changed = true;
getTimeCount--;
break;
}
}
if (!changed && getTimeCount > 0) {
// GetTime, Increment If => Jump
iterator = actions.iterator();
while (iterator.hasNext()) {
Action a = iterator.next().action;
Action a1 = iterator.peek(0).action;
ActionItem a2Item = iterator.peek(1);
Action a2 = a2Item.action;
if (a instanceof ActionGetTime && a1 instanceof ActionIncrement && a2 instanceof ActionIf) {
ActionJump jump = new ActionJump(0);
ActionItem jumpItem = new ActionItem(jump);
jumpItem.jumpTarget = a2Item.jumpTarget;
iterator.remove(); // GetTime
iterator.next();
iterator.remove(); // Increment
iterator.next();
iterator.remove(); // If
iterator.add(jumpItem); // replace If with Jump
changed = true;
break;
}
}
}
}
return false;
}
// private boolean removeObfuscationIfs(FastActionList actions) throws InterruptedException {
// if (actions.isEmpty()) {
// return false;
// }
//
// actions.removeUnreachableActions();
// actions.removeZeroJumps();
//
// FastActionListIterator iterator = actions.iterator();
// while (iterator.hasNext()) {
// ActionItem actionItem = iterator.next();
// ExecutionResult result = new ExecutionResult();
// executeActions(actions, i, actions.size() - 1, result);
//
// if (result.idx != -1) {
// int newIstructionCount = 1 /*jump */ + result.stack.size();
// List<Action> unreachable = actions.getUnreachableActions(i, result.idx);
// int unreachableCount = unreachable.size();
//
// if (newIstructionCount < unreachableCount) {
// Action target = actions.get(result.idx);
// Action prevAction = actions.get(i);
//
// if (result.stack.isEmpty() && prevAction instanceof ActionJump) {
// ActionJump jump = (ActionJump) prevAction;
// jump.setJumpOffset((int) (target.getAddress() - jump.getAddress() - jump.getTotalActionLength()));
// } else {
// if (!result.stack.isEmpty()) {
// ActionPush push = new ActionPush(0);
// push.values.clear();
// for (GraphTargetItem graphTargetItem : result.stack) {
// push.values.add(graphTargetItem.getResult());
// }
// push.setAddress(prevAction.getAddress());
// actions.addAction(i++, push);
// prevAction = push;
// }
//
// ActionJump jump = new ActionJump(0);
// jump.setAddress(prevAction.getAddress());
// jump.setJumpOffset((int) (target.getAddress() - jump.getAddress() - jump.getTotalActionLength()));
// actions.addAction(i++, jump);
// }
//
// Action nextAction = actions.size() > i ? actions.get(i) : null;
//
// actions.removeUnreachableActions();
// actions.removeZeroJumps();
//
// if (nextAction != null) {
// int nextIdx = actions.indexOf(nextAction);
// if (nextIdx < i) {
// i = nextIdx;
// }
// }
// }
// }
// }
//
// return false;
// }
protected boolean isFakeName(String name) {
for (char ch : name.toCharArray()) {
if (ch > 31) {
return false;
}
}
return true;
}
private void executeActions(ActionList actions, int idx, int endIdx, ExecutionResult result) throws InterruptedException {
List<GraphTargetItem> output = new ArrayList<>();
ActionLocalData localData = new ActionLocalData();
FixItemCounterTranslateStack stack = new FixItemCounterTranslateStack("");
int instructionsProcessed = 0;
while (true) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
if (idx > endIdx) {
break;
}
if (instructionsProcessed > executionLimit) {
break;
}
Action action = actions.get(idx);
/*System.out.print(action.getASMSource(actions, new ArrayList<Long>(), ScriptExportMode.PCODE));
for (int j = 0; j < stack.size(); j++) {
System.out.print(" '" + stack.get(j).getResult() + "'");
}
System.out.println();*/
// do not throw EmptyStackException, much faster
int requiredStackSize = action.getStackPopCount(localData, stack);
if (stack.size() < requiredStackSize) {
return;
}
action.translate(localData, stack, output, Graph.SOP_USE_STATIC, "");
if (!(action instanceof ActionPush
|| action instanceof ActionPushDuplicate
//|| action instanceof ActionPop
|| action instanceof ActionAsciiToChar
|| action instanceof ActionCharToAscii
|| action instanceof ActionDecrement
|| action instanceof ActionIncrement
|| action instanceof ActionNot
|| action instanceof ActionToInteger
|| action instanceof ActionToNumber
|| action instanceof ActionToString
|| action instanceof ActionTypeOf
|| action instanceof ActionStringLength
|| action instanceof ActionMBAsciiToChar
|| action instanceof ActionMBStringLength
|| action instanceof ActionAnd
|| action instanceof ActionAdd
|| action instanceof ActionAdd2
|| action instanceof ActionBitAnd
|| action instanceof ActionBitLShift
|| action instanceof ActionBitOr
|| action instanceof ActionBitRShift
|| action instanceof ActionBitURShift
|| action instanceof ActionBitXor
|| action instanceof ActionDivide
|| action instanceof ActionEquals
|| action instanceof ActionEquals2
|| action instanceof ActionGreater
|| action instanceof ActionLess
|| action instanceof ActionLess2 // todo: fix (tz.swf/frame_6/DoAction: _loc3_.icon.gotoAndStop((Number(item.cost) || 0) >= 0?1:2)
|| action instanceof ActionModulo
|| action instanceof ActionMultiply
|| action instanceof ActionOr
|| action instanceof ActionStringAdd
|| action instanceof ActionStringEquals
|| action instanceof ActionStringGreater
|| action instanceof ActionStringLess
|| action instanceof ActionSubtract
|| action instanceof ActionIf
|| action instanceof ActionJump)) {
break;
}
if (action instanceof ActionPush) {
ActionPush push = (ActionPush) action;
boolean ok = true;
instructionsProcessed += push.values.size() - 1;
for (Object value : push.values) {
if (value instanceof ConstantIndex || value instanceof RegisterNumber) {
ok = false;
break;
}
}
if (!ok) {
break;
}
}
idx++;
if (action instanceof ActionJump) {
ActionJump jump = (ActionJump) action;
long address = jump.getTargetAddress();
idx = actions.getIndexByAddress(address);
if (idx == -1) {
throw new TranslateException("Jump target not found: " + address);
}
}
if (action instanceof ActionIf) {
ActionIf aif = (ActionIf) action;
if (stack.isEmpty()) {
return;
}
if (EcmaScript.toBoolean(stack.pop().getResult())) {
long address = aif.getTargetAddress();
idx = actions.getIndexByAddress(address);
if (idx == -1) {
throw new TranslateException("If target not found: " + address);
}
}
}
instructionsProcessed++;
if (stack.allItemsFixed() && !(action instanceof ActionPush)) {
result.idx = idx == actions.size() ? idx - 1 : idx;
result.instructionsProcessed = instructionsProcessed;
result.stack.clear();
result.stack.addAll(stack);
}
}
}
@Override
public byte[] proxyFileCatched(byte[] data) {
return null;
}
@Override
public void swfParsed(SWF swf) {
}
@Override
public void abcParsed(ABC abc, SWF swf) {
}
@Override
public void methodBodyParsed(MethodBody body, SWF swf) {
}
class ExecutionResult {
public int idx = -1;
public int instructionsProcessed = -1;
public TranslateStack stack = new TranslateStack("?");
public Object resultValue;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2010-2015 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.fastactionlist;
import com.jpexs.decompiler.flash.action.Action;
import java.util.List;
/**
*
* @author JPEXS
*/
public class ActionItem {
public Action action;
public ActionItem prev;
public ActionItem next;
public ActionItem jumpTarget;
public List<ActionItem> containerLastActions;
// 1 means reachable, 2 means reachable and processed
int reachable;
public ActionItem(Action action) {
this.action = action;
}
}

View File

@@ -0,0 +1,610 @@
/*
* Copyright (C) 2010-2015 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.fastactionlist;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.action.special.ActionStore;
import com.jpexs.decompiler.flash.action.swf4.ActionIf;
import com.jpexs.decompiler.flash.action.swf4.ActionJump;
import com.jpexs.decompiler.flash.action.swf4.ActionPush;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @author JPEXS
*/
public class FastActionList implements Collection<ActionItem> {
private int size;
private ActionItem firstAction;
private final Map<Action, ActionItem> actionItemMap;
private final Set<ActionItem> actionItemSet;
public FastActionList(ActionList actions) {
actionItemMap = new HashMap<>(actions.size());
actionItemSet = new HashSet<>(actions.size());
for (Action action : actions) {
insertItemAfter(null, action);
}
size = actions.size();
getContainerLastActions(actions, actionItemMap);
getJumps(actions, actionItemMap);
}
final ActionItem insertItemAfter(ActionItem item, Action action) {
ActionItem newItem = new ActionItem(action);
return insertItemAfter(item, newItem);
}
final ActionItem insertItemAfter(ActionItem item, ActionItem newItem) {
if (item == null && firstAction == null) {
firstAction = newItem;
newItem.next = newItem;
newItem.prev = newItem;
} else {
if (item == null) {
// insert to the end
item = firstAction.prev;
}
ActionItem oldNext = item.next;
newItem.prev = item;
newItem.next = oldNext;
item.next = newItem;
oldNext.prev = newItem;
}
size++;
actionItemMap.put(newItem.action, newItem);
actionItemSet.add(newItem);
return newItem;
}
ActionItem removeItem(ActionItem item) {
ActionItem next = null;
if (item == firstAction) {
if (item.next == item) {
// there is only 1 item
firstAction = null;
} else {
next = item.next;
firstAction = next;
next.prev = item.prev;
item.prev.next = next;
}
} else {
next = item.next;
item.prev.next = next;
next.prev = item.prev;
}
size--;
actionItemMap.remove(item.action);
actionItemSet.remove(item);
return next;
}
private void getContainerLastActions(ActionList actions, Map<Action, ActionItem> actionItemMap) {
ActionItem item = firstAction;
if (item == null) {
return;
}
do {
Action action = item.action;
if (action instanceof GraphSourceItemContainer) {
item.containerLastActions = getContainerLastActions(actions, action, actionItemMap);
}
item = item.next;
} while (item != firstAction);
}
private List<ActionItem> getContainerLastActions(ActionList actions, Action action, Map<Action, ActionItem> actionItemMap) {
GraphSourceItemContainer container = (GraphSourceItemContainer) action;
List<Long> sizes = container.getContainerSizes();
long endAddress = action.getAddress() + container.getHeaderSize();
List<ActionItem> lasts = new ArrayList<>(sizes.size());
for (long size : sizes) {
endAddress += size;
long lastActionAddress = getNearAddress(actions, endAddress - 1, false);
Action lastAction = null;
if (lastActionAddress != -1) {
lastAction = actions.getByAddress(lastActionAddress);
}
if (lastAction != null) {
lasts.add(actionItemMap.get(lastAction));
} else {
lasts.add(null);
}
}
return lasts;
}
private long getNearAddress(ActionList actions, long address, boolean next) {
int min = 0;
int max = actions.size() - 1;
while (max >= min) {
int mid = (min + max) / 2;
long midValue = actions.get(mid).getAddress();
if (midValue == address) {
return address;
} else if (midValue < address) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return next
? (min < actions.size() ? actions.get(min).getAddress() : -1)
: (max >= 0 ? actions.get(max).getAddress() : -1);
}
private void getJumps(ActionList actions, Map<Action, ActionItem> actionItemMap) {
ActionItem item = firstAction;
if (item == null) {
return;
}
do {
Action action = item.action;
long target = -1;
if (action instanceof ActionIf) {
target = ((ActionIf) action).getTargetAddress();
} else if (action instanceof ActionJump) {
target = ((ActionJump) action).getTargetAddress();
} else if (action instanceof ActionStore) {
ActionStore aStore = (ActionStore) action;
int storeSize = aStore.getStoreSize();
// skip storeSize + 1 actions (+1 is the current action)
Action targetAction = action;
for (int i = 0; i <= storeSize; i++) {
long address = targetAction.getAddress() + targetAction.getTotalActionLength();
targetAction = actions.getByAddress(address);
if (targetAction == null) {
break;
}
}
item.jumpTarget = actionItemMap.get(targetAction);
}
if (target >= 0) {
Action targetAction = actions.getByAddress(target);
item.jumpTarget = actionItemMap.get(targetAction);
}
item = item.next;
} while (item != firstAction);
}
private void updateActionAddressesAndLengths() {
ActionItem item = firstAction;
if (item == null) {
return;
}
long address = item.action.getAddress();
do {
Action action = item.action;
action.setAddress(address);
action.updateLength();
address += action.getTotalActionLength();
item = item.next;
} while (item != firstAction);
}
private void updateJumps() {
ActionItem item = firstAction;
if (item == null) {
return;
}
long endAddress = item.prev.action.getAddress();
do {
Action action = item.action;
if (action instanceof ActionIf) {
ActionIf aIf = (ActionIf) action;
Action target = item.jumpTarget == null ? null : item.jumpTarget.action;
long offset;
if (target != null) {
offset = target.getAddress() - action.getAddress() - action.getTotalActionLength();
} else {
offset = endAddress - action.getAddress() - action.getTotalActionLength();
}
aIf.setJumpOffset((int) offset);
} else if (action instanceof ActionJump) {
ActionJump aJump = (ActionJump) action;
Action target = item.jumpTarget == null ? null : item.jumpTarget.action;
long offset;
if (target != null) {
offset = target.getAddress() - action.getAddress() - action.getTotalActionLength();
} else {
offset = endAddress - action.getAddress() - action.getTotalActionLength();
}
aJump.setJumpOffset((int) offset);
}
item = item.next;
} while (item != firstAction);
}
private void updateActionStores(ActionList actionList) {
ActionItem item = firstAction;
if (item == null) {
return;
}
do {
Action action = item.action;
if (action instanceof ActionStore) {
ActionStore aStore = (ActionStore) action;
Action nextActionAfterStore = item.jumpTarget == null ? null : item.jumpTarget.action;
Action a1 = action;
List<Action> store = new ArrayList<>();
while (true) {
long address = a1.getAddress() + a1.getTotalActionLength();
a1 = actionList.getByAddress(address);
if (a1 == null || a1 == nextActionAfterStore) {
break;
}
store.add(a1);
}
aStore.setStore(store);
}
item = item.next;
} while (item != firstAction);
}
private void updateContainerSizes() {
ActionItem item = firstAction;
if (item == null) {
return;
}
do {
Action action = item.action;
if (action instanceof GraphSourceItemContainer) {
GraphSourceItemContainer container = (GraphSourceItemContainer) action;
List<ActionItem> lastActions = item.containerLastActions;
long startAddress = action.getAddress() + container.getHeaderSize();
for (int j = 0; j < lastActions.size(); j++) {
Action lastAction = lastActions.get(j).action;
int length = (int) (lastAction.getAddress() + lastAction.getTotalActionLength() - startAddress);
container.setContainerSize(j, length);
startAddress += length;
}
}
item = item.next;
} while (item != firstAction);
}
public void expandPushes() {
ActionItem item = firstAction;
if (item == null) {
return;
}
do {
Action action = item.action;
if (action instanceof ActionPush) {
ActionPush push = (ActionPush) action;
if (push.values.size() > 1) {
for (int i = 1; i < push.values.size(); i++) {
Object value = push.values.get(i);
ActionPush newPush = new ActionPush(value);
newPush.constantPool = push.constantPool;
insertItemAfter(item, newPush);
item = item.next;
}
Object obj = push.values.get(0);
push.values.clear();
push.values.add(obj);
}
}
item = item.next;
} while (item != firstAction);
}
public void removeZeroJumps() {
ActionItem item = firstAction;
if (item == null) {
return;
}
do {
Action action = item.action;
if (action instanceof ActionJump) {
if (item.jumpTarget == item.next && item.jumpTarget != firstAction) {
item = removeItem(item);
continue;
}
}
item = item.next;
} while (item != firstAction);
}
public void removeUnreachableActions() {
ActionItem item = firstAction;
if (item == null) {
return;
}
updateReachableFlags(null, null);
do {
if (item.reachable == 0) {
item = removeItem(item);
continue;
}
item = item.next;
} while (item != firstAction);
}
private void clearReachableFlags() {
ActionItem item = firstAction;
if (item == null) {
return;
}
do {
item.reachable = 0;
item = item.next;
} while (item != firstAction);
}
private void updateReachableFlags(ActionItem jump, ActionItem jumpTarget) {
if (firstAction == null) {
return;
}
clearReachableFlags();
firstAction.reachable = 1;
boolean modified = true;
while (modified) {
modified = false;
int i = 0;
FastActionListIterator iterator = iterator();
while (iterator.hasNext()) {
ActionItem item = iterator.next();
Action action = item.action;
if (item.reachable == 1) {
item.reachable = 2;
modified = true;
if (item == jump) {
if (jumpTarget.reachable == 0) {
jumpTarget.reachable = 1;
}
continue;
}
if (!action.isExit() && !(action instanceof ActionJump) && item.next != null) {
if (item.next.reachable == 0) {
item.next.reachable = 1;
}
}
ActionItem target = item.jumpTarget;
if (target != null) {
if (target.reachable == 0) {
target.reachable = 1;
}
}
if (action instanceof GraphSourceItemContainer) {
for (ActionItem lastActionItem : item.containerLastActions) {
if (lastActionItem != null && lastActionItem.next != null && lastActionItem.next.reachable == 0) {
lastActionItem.next.reachable = 1;
}
}
}
}
}
}
}
public ActionList updateActions() {
List<Action> resultList = new ArrayList<>(size);
ActionItem item = firstAction;
if (item == null) {
return new ActionList(resultList);
}
do {
resultList.add(item.action);
item = item.next;
} while (item != firstAction);
ActionList result = new ActionList(resultList);
updateActionAddressesAndLengths();
updateJumps();
updateActionStores(result);
updateContainerSizes();
return result;
}
public ActionList toActionList() {
return updateActions();
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean contains(Object o) {
if (o instanceof ActionItem) {
return actionItemSet.contains(o);
} else if (o instanceof Action) {
return actionItemMap.containsKey((Action) o);
}
return false;
}
@Override
public FastActionListIterator iterator() {
return new FastActionListIterator(firstAction, this);
}
@Override
public Object[] toArray() {
Object[] result = new Object[size];
ActionItem item = firstAction;
if (item == null) {
return result;
}
int i = 0;
do {
result[i] = item.action;
item = item.next;
i++;
} while (item != firstAction);
return null;
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length != size) {
a = (T[]) new ActionItem[size];
}
ActionItem item = firstAction;
if (item == null) {
return a;
}
int i = 0;
do {
a[i] = (T) item;
item = item.next;
i++;
} while (item != firstAction);
return null;
}
@Override
public boolean add(ActionItem e) {
insertItemAfter(null, e);
return true;
}
@Override
public boolean remove(Object o) {
ActionItem item = null;
if (o instanceof ActionItem) {
item = (ActionItem) o;
} else if (o instanceof Action) {
item = actionItemMap.get(o);
}
if (item == null) {
return false;
}
removeItem(item);
return true;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object c1 : c) {
if (!contains(c1)) {
return false;
}
}
return true;
}
@Override
public boolean addAll(Collection<? extends ActionItem> c) {
for (ActionItem c1 : c) {
insertItemAfter(null, c1);
}
return true;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean result = false;
for (Object c1 : c) {
result |= remove(c1);
}
return result;
}
@Override
public boolean retainAll(Collection<?> c) {
ActionItem item = firstAction;
if (item == null) {
return false;
}
boolean modified = false;
do {
if (!c.contains(item)) {
item = removeItem(item);
modified = true;
continue;
}
item = item.next;
} while (item != firstAction);
return modified;
}
@Override
public void clear() {
firstAction = null;
size = 0;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2010-2015 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.fastactionlist;
import com.jpexs.decompiler.flash.action.Action;
import java.util.Iterator;
/**
*
* @author JPEXS
*/
public class FastActionListIterator implements Iterator<ActionItem> {
private ActionItem item;
private final FastActionList list;
private int index;
FastActionListIterator(ActionItem firstAction, FastActionList list) {
item = firstAction;
this.list = list;
}
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public ActionItem next() {
ActionItem result = item;
index++;
item = item.next;
return result;
}
@Override
public void remove() {
item = list.removeItem(item.prev);
index--;
}
public void add(Action action) {
item = list.insertItemAfter(item.prev, action).next;
index++;
}
public void add(ActionItem actionItem) {
item = list.insertItemAfter(item.prev, actionItem).next;
index++;
}
public ActionItem peek(int index) {
ActionItem item = this.item;
for (int i = 0; i < index; i++) {
if (item == null) {
break;
}
item = item.next;
}
return item;
}
}