From a8c21cb3b4d700a7ccd52d70a74d4f81864d2830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 7 Jan 2018 18:47:49 +0100 Subject: [PATCH] Fixed removeIncludedActions - do..while changed to while(true) as it was not working when firstItem was excluded and thus removed. Statement "continue" in do..while checks condition immediately and then terminating loop, because firstItem is now the second of the list. Other excluded items then won't be removed. --- .../action/fastactionlist/FastActionList.java | 1521 +++++++++-------- 1 file changed, 762 insertions(+), 759 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java index 655c0b8f1..991aab147 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/fastactionlist/FastActionList.java @@ -1,759 +1,762 @@ -/* - * Copyright (C) 2010-2016 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.special.ActionUnknown; -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 { - - private int size; - - private ActionItem firstItem; - - private final Map actionItemMap; - - private final Set 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); - } - - public final ActionItem insertItemBefore(ActionItem item, Action action) { - ActionItem newItem = new ActionItem(action); - return insertItemBefore(item, newItem); - } - - public final ActionItem insertItemAfter(ActionItem item, Action action) { - ActionItem newItem = new ActionItem(action); - return insertItemAfter(item, newItem); - } - - public final ActionItem insertItemBefore(ActionItem item, ActionItem newItem) { - insertItemAfter(item.prev, newItem); - if (item == firstItem) { - firstItem = newItem; - } - - return newItem; - } - - public final ActionItem insertItemAfter(ActionItem item, ActionItem newItem) { - if (item == null && firstItem == null) { - firstItem = newItem; - newItem.next = newItem; - newItem.prev = newItem; - } else { - if (item == null) { - // insert to the end - item = firstItem.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; - } - - public ActionItem removeItem(ActionItem item) { - ActionItem next = null; - if (item == firstItem) { - if (item.next == item) { - // there is only 1 item - firstItem = null; - } else { - next = item.next; - firstItem = 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); - - item.removeJumpTarget(); - item.removeContainerLastActions(); - - if (item.jumpsHere != null) { - for (ActionItem item1 : new ArrayList<>(item.jumpsHere)) { - item1.setJumpTarget(item.next); - } - } - - if (item.lastActionOf != null) { - for (ActionItem item1 : new ArrayList<>(item.lastActionOf)) { - item1.replaceContainerLastAction(item, item.prev); - } - } - - return next; - } - - public void removeItem(int index, int count) { - FastActionListIterator iterator = new FastActionListIterator(this, index); - for (int i = 0; i < count; i++) { - iterator.next(); - iterator.remove(); - } - } - - public ActionItem get(int index) { - FastActionListIterator iterator = new FastActionListIterator(this, index); - return iterator.next(); - } - - public void replaceJumpTargets(ActionItem target, ActionItem newTarget) { - if (target.jumpsHere != null) { - for (ActionItem item : new ArrayList<>(target.jumpsHere)) { - item.setJumpTarget(newTarget); - } - } - } - - private void getContainerLastActions(ActionList actions, Map actionItemMap) { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - Action action = item.action; - if (action instanceof GraphSourceItemContainer) { - item.setContainerLastActions(getContainerLastActions(actions, action, actionItemMap)); - } - - item = item.next; - } while (item != firstItem); - } - - private List getContainerLastActions(ActionList actions, Action action, Map actionItemMap) { - GraphSourceItemContainer container = (GraphSourceItemContainer) action; - List sizes = container.getContainerSizes(); - long endAddress = action.getAddress() + container.getHeaderSize(); - List 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 actionItemMap) { - ActionItem item = firstItem; - 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.setJumpTarget(actionItemMap.get(targetAction)); - } - if (target >= 0) { - Action targetAction = actions.getByAddress(target); - item.setJumpTarget(actionItemMap.get(targetAction)); - } - - item = item.next; - } while (item != firstItem); - } - - private void updateActionAddressesAndLengths() { - ActionItem item = firstItem; - 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 != firstItem); - } - - private void updateJumps() { - ActionItem item = firstItem; - 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.getJumpTargetAction(); - 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.getJumpTargetAction(); - 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 != firstItem); - } - - private void updateActionStores() { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - Action action = item.action; - if (action instanceof ActionStore) { - ActionStore aStore = (ActionStore) action; - Action nextActionAfterStore = item.getJumpTargetAction(); - ActionItem item1 = item; - List store = new ArrayList<>(); - while (true) { - item1 = item1.next; - if (item1 == firstItem || item1.action == nextActionAfterStore) { - break; - } - - store.add(item1.action); - } - - aStore.setStore(store); - } - - item = item.next; - } while (item != firstItem); - } - - private void updateContainerSizes() { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - Action action = item.action; - if (action instanceof GraphSourceItemContainer) { - GraphSourceItemContainer container = (GraphSourceItemContainer) action; - List lastActions = item.getContainerLastActions(); - 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 != firstItem); - } - - public ActionItem getContainer(ActionItem item) { - while (!(item.action instanceof GraphSourceItemContainer) && item != firstItem) { - item = item.prev; - } - - if (item.action instanceof GraphSourceItemContainer) { - return item; - } - - return null; - } - - public void expandPushes() { - ActionItem item = firstItem; - 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 != firstItem); - } - - public void removeUnknownActions() { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - Action action = item.action; - if (action instanceof ActionUnknown) { - item = removeItem(item); - continue; - } - - item = item.next; - } while (item != firstItem); - } - - public void removeZeroJumps() { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - Action action = item.action; - if (action instanceof ActionJump) { - if (item.getJumpTarget() == item.next && item.getJumpTarget() != firstItem) { - item = removeItem(item); - continue; - } - } - - item = item.next; - } while (item != firstItem); - } - - public void removeUnreachableActions() { - ActionItem item = firstItem; - if (item == null) { - return; - } - - updateReachableFlags(null, null); - - do { - if (item.reachable == 0) { - item = removeItem(item); - continue; - } - - item = item.next; - } while (item != firstItem); - } - - public void removeIncludedActions() { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - if (!item.excluded) { - item = removeItem(item); - continue; - } - - item = item.next; - } while (item != firstItem); - } - - public int getUnreachableActionCount(ActionItem jump, ActionItem jumpTarget) { - ActionItem item = firstItem; - if (item == null) { - return 0; - } - - updateReachableFlags(jump, jumpTarget); - jump.reachable = 0; - - int count = 0; - do { - if (item.reachable == 0) { - count++; - } - - item = item.next; - } while (item != firstItem); - - return count; - } - - private void clearReachableFlags() { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - item.reachable = 0; - item = item.next; - } while (item != firstItem); - } - - public void setExcludedFlags(boolean value) { - ActionItem item = firstItem; - if (item == null) { - return; - } - - do { - item.excluded = value; - item = item.next; - } while (item != firstItem); - } - - private void updateReachableFlags(ActionItem jump, ActionItem jumpTarget) { - if (firstItem == null) { - return; - } - - clearReachableFlags(); - - firstItem.reachable = 1; - ActionItem firstItem2 = firstItem; - boolean modified = true; - while (modified) { - modified = false; - ActionItem item = firstItem2; - do { - ActionItem next = item.next; - //ActionItem alternativeNext = null; - Action action = item.action; - if (item.reachable == 1) { - item.reachable = 2; - modified = true; - - if (item == firstItem2) { - firstItem2 = next; - } - - if (item == jump) { - if (jumpTarget.reachable == 0) { - jumpTarget.reachable = 1; - //alternativeNext = jumpTarget; - } - } else { - - if (!action.isExit() && !(action instanceof ActionJump)) { - if (next.reachable == 0) { - next.reachable = 1; - } - } - - if (action instanceof GraphSourceItemContainer) { - for (ActionItem lastActionItem : item.getContainerLastActions()) { - if (lastActionItem != null && lastActionItem.next != null && lastActionItem.next.reachable == 0) { - lastActionItem.next.reachable = 1; - //alternativeNext = lastActionItem.next; - } - } - } - - ActionItem target = item.getJumpTarget(); - if (target != null) { - if (target.reachable == 0) { - target.reachable = 1; - //alternativeNext = target; - } - } - } - } - - //item = alternativeNext == null || next.reachable == 1 ? next : alternativeNext; - item = next; - } while (item != firstItem); - } - } - - public ActionList updateActions() { - List resultList = new ArrayList<>(size); - ActionItem item = firstItem; - if (item == null) { - return new ActionList(resultList); - } - - do { - resultList.add(item.action); - item = item.next; - } while (item != firstItem); - - ActionList result = new ActionList(resultList); - updateActionAddressesAndLengths(); - updateJumps(); - updateActionStores(); - updateContainerSizes(); - return result; - } - - public ActionItem first() { - return firstItem; - } - - public ActionItem last() { - return firstItem == null ? null : firstItem.prev; - } - - 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(this); - } - - @Override - public Object[] toArray() { - Object[] result = new Object[size]; - - ActionItem item = firstItem; - if (item == null) { - return result; - } - - int i = 0; - do { - result[i] = item.action; - item = item.next; - i++; - } while (item != firstItem); - return null; - } - - @Override - @SuppressWarnings("unchecked") - public T[] toArray(T[] a) { - if (a.length != size) { - a = (T[]) new ActionItem[size]; - } - - ActionItem item = firstItem; - if (item == null) { - return a; - } - - int i = 0; - do { - a[i] = (T) item; - item = item.next; - i++; - } while (item != firstItem); - 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((Action) 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 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 = firstItem; - if (item == null) { - return false; - } - - boolean modified = false; - do { - if (!c.contains(item)) { - item = removeItem(item); - modified = true; - continue; - } - - item = item.next; - } while (item != firstItem); - return modified; - } - - @Override - public void clear() { - firstItem = null; - size = 0; - } -} +/* + * Copyright (C) 2010-2016 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.special.ActionUnknown; +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 { + + private int size; + + private ActionItem firstItem; + + private final Map actionItemMap; + + private final Set 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); + } + + public final ActionItem insertItemBefore(ActionItem item, Action action) { + ActionItem newItem = new ActionItem(action); + return insertItemBefore(item, newItem); + } + + public final ActionItem insertItemAfter(ActionItem item, Action action) { + ActionItem newItem = new ActionItem(action); + return insertItemAfter(item, newItem); + } + + public final ActionItem insertItemBefore(ActionItem item, ActionItem newItem) { + insertItemAfter(item.prev, newItem); + if (item == firstItem) { + firstItem = newItem; + } + + return newItem; + } + + public final ActionItem insertItemAfter(ActionItem item, ActionItem newItem) { + if (item == null && firstItem == null) { + firstItem = newItem; + newItem.next = newItem; + newItem.prev = newItem; + } else { + if (item == null) { + // insert to the end + item = firstItem.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; + } + + public ActionItem removeItem(ActionItem item) { + ActionItem next = null; + if (item == firstItem) { + if (item.next == item) { + // there is only 1 item + firstItem = null; + } else { + next = item.next; + firstItem = 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); + + item.removeJumpTarget(); + item.removeContainerLastActions(); + + if (item.jumpsHere != null) { + for (ActionItem item1 : new ArrayList<>(item.jumpsHere)) { + item1.setJumpTarget(item.next); + } + } + + if (item.lastActionOf != null) { + for (ActionItem item1 : new ArrayList<>(item.lastActionOf)) { + item1.replaceContainerLastAction(item, item.prev); + } + } + + return next; + } + + public void removeItem(int index, int count) { + FastActionListIterator iterator = new FastActionListIterator(this, index); + for (int i = 0; i < count; i++) { + iterator.next(); + iterator.remove(); + } + } + + public ActionItem get(int index) { + FastActionListIterator iterator = new FastActionListIterator(this, index); + return iterator.next(); + } + + public void replaceJumpTargets(ActionItem target, ActionItem newTarget) { + if (target.jumpsHere != null) { + for (ActionItem item : new ArrayList<>(target.jumpsHere)) { + item.setJumpTarget(newTarget); + } + } + } + + private void getContainerLastActions(ActionList actions, Map actionItemMap) { + ActionItem item = firstItem; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof GraphSourceItemContainer) { + item.setContainerLastActions(getContainerLastActions(actions, action, actionItemMap)); + } + + item = item.next; + } while (item != firstItem); + } + + private List getContainerLastActions(ActionList actions, Action action, Map actionItemMap) { + GraphSourceItemContainer container = (GraphSourceItemContainer) action; + List sizes = container.getContainerSizes(); + long endAddress = action.getAddress() + container.getHeaderSize(); + List 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 actionItemMap) { + ActionItem item = firstItem; + 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.setJumpTarget(actionItemMap.get(targetAction)); + } + if (target >= 0) { + Action targetAction = actions.getByAddress(target); + item.setJumpTarget(actionItemMap.get(targetAction)); + } + + item = item.next; + } while (item != firstItem); + } + + private void updateActionAddressesAndLengths() { + ActionItem item = firstItem; + 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 != firstItem); + } + + private void updateJumps() { + ActionItem item = firstItem; + 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.getJumpTargetAction(); + 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.getJumpTargetAction(); + 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 != firstItem); + } + + private void updateActionStores() { + ActionItem item = firstItem; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof ActionStore) { + ActionStore aStore = (ActionStore) action; + Action nextActionAfterStore = item.getJumpTargetAction(); + ActionItem item1 = item; + List store = new ArrayList<>(); + while (true) { + item1 = item1.next; + if (item1 == firstItem || item1.action == nextActionAfterStore) { + break; + } + + store.add(item1.action); + } + + aStore.setStore(store); + } + + item = item.next; + } while (item != firstItem); + } + + private void updateContainerSizes() { + ActionItem item = firstItem; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof GraphSourceItemContainer) { + GraphSourceItemContainer container = (GraphSourceItemContainer) action; + List lastActions = item.getContainerLastActions(); + 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 != firstItem); + } + + public ActionItem getContainer(ActionItem item) { + while (!(item.action instanceof GraphSourceItemContainer) && item != firstItem) { + item = item.prev; + } + + if (item.action instanceof GraphSourceItemContainer) { + return item; + } + + return null; + } + + public void expandPushes() { + ActionItem item = firstItem; + 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 != firstItem); + } + + public void removeUnknownActions() { + ActionItem item = firstItem; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof ActionUnknown) { + item = removeItem(item); + continue; + } + + item = item.next; + } while (item != firstItem); + } + + public void removeZeroJumps() { + ActionItem item = firstItem; + if (item == null) { + return; + } + + do { + Action action = item.action; + if (action instanceof ActionJump) { + if (item.getJumpTarget() == item.next && item.getJumpTarget() != firstItem) { + item = removeItem(item); + continue; + } + } + + item = item.next; + } while (item != firstItem); + } + + public void removeUnreachableActions() { + ActionItem item = firstItem; + if (item == null) { + return; + } + + updateReachableFlags(null, null); + + do { + if (item.reachable == 0) { + item = removeItem(item); + continue; + } + + item = item.next; + } while (item != firstItem); + } + + public void removeIncludedActions() { + ActionItem item = firstItem; + if (item == null) { + return; + } + + while (true) { + if (!item.excluded) { + item = removeItem(item); + continue; + } + + item = item.next; + if (item == firstItem) { + break; + } + } + } + + public int getUnreachableActionCount(ActionItem jump, ActionItem jumpTarget) { + ActionItem item = firstItem; + if (item == null) { + return 0; + } + + updateReachableFlags(jump, jumpTarget); + jump.reachable = 0; + + int count = 0; + do { + if (item.reachable == 0) { + count++; + } + + item = item.next; + } while (item != firstItem); + + return count; + } + + private void clearReachableFlags() { + ActionItem item = firstItem; + if (item == null) { + return; + } + + do { + item.reachable = 0; + item = item.next; + } while (item != firstItem); + } + + public void setExcludedFlags(boolean value) { + ActionItem item = firstItem; + if (item == null) { + return; + } + + do { + item.excluded = value; + item = item.next; + } while (item != firstItem); + } + + private void updateReachableFlags(ActionItem jump, ActionItem jumpTarget) { + if (firstItem == null) { + return; + } + + clearReachableFlags(); + + firstItem.reachable = 1; + ActionItem firstItem2 = firstItem; + boolean modified = true; + while (modified) { + modified = false; + ActionItem item = firstItem2; + do { + ActionItem next = item.next; + //ActionItem alternativeNext = null; + Action action = item.action; + if (item.reachable == 1) { + item.reachable = 2; + modified = true; + + if (item == firstItem2) { + firstItem2 = next; + } + + if (item == jump) { + if (jumpTarget.reachable == 0) { + jumpTarget.reachable = 1; + //alternativeNext = jumpTarget; + } + } else { + + if (!action.isExit() && !(action instanceof ActionJump)) { + if (next.reachable == 0) { + next.reachable = 1; + } + } + + if (action instanceof GraphSourceItemContainer) { + for (ActionItem lastActionItem : item.getContainerLastActions()) { + if (lastActionItem != null && lastActionItem.next != null && lastActionItem.next.reachable == 0) { + lastActionItem.next.reachable = 1; + //alternativeNext = lastActionItem.next; + } + } + } + + ActionItem target = item.getJumpTarget(); + if (target != null) { + if (target.reachable == 0) { + target.reachable = 1; + //alternativeNext = target; + } + } + } + } + + //item = alternativeNext == null || next.reachable == 1 ? next : alternativeNext; + item = next; + } while (item != firstItem); + } + } + + public ActionList updateActions() { + List resultList = new ArrayList<>(size); + ActionItem item = firstItem; + if (item == null) { + return new ActionList(resultList); + } + + do { + resultList.add(item.action); + item = item.next; + } while (item != firstItem); + + ActionList result = new ActionList(resultList); + updateActionAddressesAndLengths(); + updateJumps(); + updateActionStores(); + updateContainerSizes(); + return result; + } + + public ActionItem first() { + return firstItem; + } + + public ActionItem last() { + return firstItem == null ? null : firstItem.prev; + } + + 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(this); + } + + @Override + public Object[] toArray() { + Object[] result = new Object[size]; + + ActionItem item = firstItem; + if (item == null) { + return result; + } + + int i = 0; + do { + result[i] = item.action; + item = item.next; + i++; + } while (item != firstItem); + return null; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a.length != size) { + a = (T[]) new ActionItem[size]; + } + + ActionItem item = firstItem; + if (item == null) { + return a; + } + + int i = 0; + do { + a[i] = (T) item; + item = item.next; + i++; + } while (item != firstItem); + 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((Action) 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 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 = firstItem; + if (item == null) { + return false; + } + + boolean modified = false; + do { + if (!c.contains(item)) { + item = removeItem(item); + modified = true; + continue; + } + + item = item.next; + } while (item != firstItem); + return modified; + } + + @Override + public void clear() { + firstItem = null; + size = 0; + } +}