/* * 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; 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.model.ConstantPool; import com.jpexs.decompiler.flash.action.special.ActionDeobfuscateJump; import com.jpexs.decompiler.flash.action.special.ActionEnd; 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.flash.action.swf5.ActionConstantPool; import com.jpexs.decompiler.flash.configuration.Configuration; 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.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; /** * Class for reading data from SWF file * * @author JPEXS */ public class ActionListReader { 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 * @return List of actions * @throws IOException * @throws java.lang.InterruptedException * @throws java.util.concurrent.TimeoutException */ public static ActionList readActionListTimeout(final List<DisassemblyListener> listeners, final SWFInputStream sis, final int version, final int ip, final int endIp, final String path, final int deobfuscationMode) throws IOException, InterruptedException, TimeoutException { try { ActionList actions = CancellableWorker.call(new Callable<ActionList>() { @Override public ActionList call() throws IOException, InterruptedException { return readActionList(listeners, sis, version, ip, endIp, path, deobfuscationMode); } }, Configuration.decompilationTimeoutSingleMethod.get(), TimeUnit.SECONDS); return actions; } catch (ExecutionException ex) { Throwable cause = ex.getCause(); if (cause instanceof InterruptedException) { throw (InterruptedException) cause; } else if (cause instanceof InterruptedException) { throw (IOException) cause; } else { logger.log(Level.SEVERE, null, ex); } } return new ActionList(); } /** * 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 * @return List of actions * @throws IOException * @throws java.lang.InterruptedException */ public static ActionList readActionList(List<DisassemblyListener> listeners, SWFInputStream sis, int version, int ip, int endIp, String path, int deobfuscationMode) throws IOException, InterruptedException { // Map of the actions. Use TreeMap to sort the keys in ascending order // actionMap and nextOffsets should contain exaclty the same keys Map<Long, Action> actionMap = new TreeMap<>(); Map<Long, Long> nextOffsets = new HashMap<>(); Action entryAction = readActionListAtPos(listeners, null, sis, actionMap, nextOffsets, ip, 0, endIp, path, false, new ArrayList<>()); if (actionMap.isEmpty()) { return new ActionList(); } List<Long> addresses = new ArrayList<>(actionMap.keySet()); // add end action Action lastAction = actionMap.get(addresses.get(addresses.size() - 1)); long endAddress; if (!(lastAction instanceof ActionEnd)) { Action aEnd = new ActionEnd(); aEnd.setAddress(nextOffsets.get(lastAction.getAddress())); endAddress = aEnd.getAddress(); actionMap.put(aEnd.getAddress(), aEnd); nextOffsets.put(endAddress, endAddress + 1); } ActionList actions = fixActionList(new ActionList(actionMap.values()), nextOffsets); // jump to the entry action when it is diffrent from the first action in the map if (entryAction != actions.get(0)) { ActionJump jump = new ActionDeobfuscateJump(0); actions.addAction(0, jump); jump.setJumpOffset((int) (entryAction.getAddress() - jump.getTotalActionLength())); } if (SWFDecompilerPlugin.fireActionListParsed(actions, sis.getSwf())) { actions = fixActionList(actions, null); } if (deobfuscationMode == 1) { try { try (Statistics s = new Statistics("ActionDeobfuscator")) { new ActionDeobfuscator().actionListParsed(actions, sis.getSwf()); } } catch (ThreadDeath | InterruptedException ex) { throw ex; } catch (Throwable ex) { // keep orignal (not deobfuscated) actions logger.log(Level.SEVERE, "Deobfuscation failed in: " + path, ex); } } return actions; } public static ActionList fixActionList(ActionList actions, Map<Long, Long> nextOffsets) { Map<Action, List<Action>> containerLastActions = new HashMap<>(); getContainerLastActions(actions, containerLastActions); ActionList ret = new ActionList(); ret.fileData = actions.fileData; if (nextOffsets != null) { int index = 0; while (index != -1 && index < actions.size()) { Action action = actions.get(index); ret.add(action); index++; if (index < actions.size()) { long nextAddress = nextOffsets.get(action.getAddress()); if (actions.get(index).getAddress() != nextAddress) { if (!action.isExit() && !(action instanceof ActionJump)) { ActionJump jump = new ActionDeobfuscateJump(0); jump.setAddress(action.getAddress()); int size = jump.getTotalActionLength(); jump.setJumpOffset((int) (nextAddress - action.getAddress() - size)); ret.add(jump); } } } } } else { ret.addAll(actions); } // Map for storing the targers of the "jump" actions // "jump" action can be ActionIf, ActionJump and any ActionStore Map<Action, Action> jumps = new HashMap<>(); getJumps(ret, jumps); updateActionLengths(ret); updateAddresses(ret, 0); long endAddress = ret.get(ret.size() - 1).getAddress(); updateJumps(ret, jumps, containerLastActions, endAddress); updateActionStores(ret, jumps); updateContainerSizes(ret, containerLastActions); return ret; } public static List<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<>(); Map<Long, Long> nextOffsets = new HashMap<>(); readActionListAtPos(new ArrayList<>(), null, sis, actionMap, nextOffsets, startIp, startIp, endIp + 1, "", false, new ArrayList<>()); return new ArrayList<>(actionMap.values()); } private static 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 Map<Long, Action> actionListToMap(List<Action> actions) { Map<Long, Action> map = new HashMap<>(actions.size()); for (Action a : actions) { long address = a.getAddress(); // There are multiple actions in the same address (2nd action is a jump for obfuscated code) // So this check is required if (!map.containsKey(address)) { map.put(a.getAddress(), a); } } return map; } private static void getJumps(List<Action> actions, Map<Action, Action> jumps) { Map<Long, Action> actionMap = actionListToMap(actions); 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 = actionMap.get(address); if (targetAction == null) { break; } } jumps.put(a, targetAction); } if (target >= 0) { Action targetAction = actionMap.get(target); jumps.put(a, targetAction); } } } public static 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 static void getContainerLastActions(ActionList actions, Map<Action, List<Action>> lastActions) { for (Action a : actions) { if (a instanceof GraphSourceItemContainer) { lastActions.put(a, getContainerLastActions(actions, a)); } } } private static long updateAddresses(List<Action> actions, long address) { for (int i = 0; i < actions.size(); i++) { Action a = actions.get(i); a.setAddress(address); int length = a.getTotalActionLength(); if ((i != actions.size() - 1) && (a instanceof ActionEnd)) { // placeholder for jump action length = new ActionDeobfuscateJump(0).getTotalActionLength(); } address += length; } return address; } private static void updateActionLengths(List<Action> actions) { for (int i = 0; i < actions.size(); i++) { actions.get(i).updateLength(); } } 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++) { Action a = actions.get(i); 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 = actionMap.get(address); if (a1 == null || a1 == nextActionAfterStore) { break; } store.add(a1); } aStore.setStore(store); } } } 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); 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; } } } } private static void replaceJumpTargets(Map<Action, Action> jumps, Action oldTarget, Action newTarget) { for (Action a : jumps.keySet()) { if (jumps.get(a) == oldTarget) { jumps.put(a, newTarget); } } } private static void replaceContainerLastActions(Map<Action, List<Action>> containerLastActions, Action oldTarget, Action newTarget) { for (Action a : containerLastActions.keySet()) { List<Action> targets = containerLastActions.get(a); for (int i = 0; i < targets.size(); i++) { if (targets.get(i) == oldTarget) { targets.set(i, newTarget); } } } } private static void updateJumps(List<Action> actions, Map<Action, Action> jumps, Map<Action, List<Action>> containerLastActions, long endAddress) { if (actions.isEmpty()) { return; } for (int i = 0; i < actions.size(); i++) { Action a = actions.get(i); if ((i != actions.size() - 1) && (a instanceof ActionEnd)) { ActionJump aJump = new ActionDeobfuscateJump(0); aJump.setJumpOffset((int) (endAddress - a.getAddress() - aJump.getTotalActionLength())); aJump.setAddress(a.getAddress()); replaceJumpTargets(jumps, a, aJump); replaceContainerLastActions(containerLastActions, a, aJump); a = aJump; actions.set(i, a); } else 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); } } } /** * 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 * * @param actions * @param index * @param removeWhenLast * @return */ public static boolean removeAction(ActionList actions, int index, boolean removeWhenLast) { if (index < 0 || actions.size() <= index) { return false; } long startIp = actions.get(0).getAddress(); Action lastAction = actions.get(actions.size() - 1); long endAddress = lastAction.getAddress() + lastAction.getTotalActionLength(); Map<Action, List<Action>> containerLastActions = new HashMap<>(); getContainerLastActions(actions, containerLastActions); Map<Action, Action> jumps = new HashMap<>(); getJumps(actions, jumps); Action prevAction = index > 0 ? actions.get(index - 1) : null; Action nextAction = index + 1 < actions.size() ? actions.get(index + 1) : null; Action actionToRemove = actions.get(index); for (Action a : containerLastActions.keySet()) { List<Action> lastActions = containerLastActions.get(a); for (int i = 0; i < lastActions.size(); i++) { if (lastActions.get(i) == actionToRemove) { if (!removeWhenLast) { return false; } lastActions.set(i, prevAction); } } } for (Action a : jumps.keySet()) { Action targetAction = jumps.get(a); if (targetAction == actionToRemove) { jumps.put(a, nextAction); } } if (containerLastActions.containsKey(actionToRemove)) { containerLastActions.remove(actionToRemove); } if (jumps.containsKey(actionToRemove)) { jumps.remove(actionToRemove); } actions.remove(index); updateActionLengths(actions); updateAddresses(actions, startIp); updateJumps(actions, jumps, containerLastActions, endAddress); updateActionStores(actions, jumps); updateContainerSizes(actions, containerLastActions); return true; } /** * 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 * * @param actions * @param actionsToRemove * @param removeWhenLast * @return */ public static boolean removeActions(ActionList actions, List<Action> actionsToRemove, boolean removeWhenLast) { long startIp = actions.get(0).getAddress(); Action lastAction = actions.get(actions.size() - 1); long endAddress = lastAction.getAddress() + lastAction.getTotalActionLength(); Map<Action, List<Action>> containerLastActions = new HashMap<>(); getContainerLastActions(actions, containerLastActions); Map<Action, Action> jumps = new HashMap<>(); getJumps(actions, jumps); for (Action actionToRemove : actionsToRemove) { int index = actions.getIndexByAction(actionToRemove); Action prevAction = index > 0 ? actions.get(index - 1) : null; Action nextAction = index + 1 < actions.size() ? actions.get(index + 1) : null; for (Action a : containerLastActions.keySet()) { List<Action> lastActions = containerLastActions.get(a); for (int i = 0; i < lastActions.size(); i++) { if (lastActions.get(i) == actionToRemove) { if (!removeWhenLast) { return false; } lastActions.set(i, prevAction); } } } for (Action a : jumps.keySet()) { Action targetAction = jumps.get(a); if (targetAction == actionToRemove) { jumps.put(a, nextAction); } } if (containerLastActions.containsKey(actionToRemove)) { containerLastActions.remove(actionToRemove); } if (jumps.containsKey(actionToRemove)) { jumps.remove(actionToRemove); } actions.remove(index); } updateActionLengths(actions); updateAddresses(actions, startIp); updateJumps(actions, jumps, containerLastActions, endAddress); updateActionStores(actions, jumps); updateContainerSizes(actions, containerLastActions); return true; } /** * Adds an action to the action list to the specified location, and updates * all references * * @param actions * @param index * @param action * @param addToContainer * @param replaceJump * @return */ public static boolean addAction(ActionList actions, int index, Action action, boolean addToContainer, boolean replaceJump) { if (index < 0 || actions.size() < index) { return false; } long startIp = actions.get(0).getAddress(); Action lastAction = actions.get(actions.size() - 1); if (!(lastAction instanceof ActionEnd)) { Action aEnd = new ActionEnd(); aEnd.setAddress(lastAction.getAddress() + lastAction.getTotalActionLength()); actions.add(aEnd); lastAction = aEnd; } long endAddress = lastAction.getAddress(); Map<Action, List<Action>> containerLastActions = new HashMap<>(); getContainerLastActions(actions, containerLastActions); Map<Action, Action> jumps = new HashMap<>(); List<Action> tempActions = new ArrayList<>(actions); tempActions.add(action); getJumps(tempActions, jumps); Action prevAction = actions.get(index); if (addToContainer) { for (Action a : containerLastActions.keySet()) { List<Action> lastActions = containerLastActions.get(a); for (int i = 0; i < lastActions.size(); i++) { if (lastActions.get(i) == prevAction) { lastActions.set(i, action); } } } } if (replaceJump) { for (Action a : jumps.keySet()) { Action targetAction = jumps.get(a); if (targetAction == prevAction) { jumps.put(a, action); } } } actions.add(index, action); updateActionLengths(actions); updateAddresses(actions, startIp); updateJumps(actions, jumps, containerLastActions, endAddress); updateActionStores(actions, jumps); updateContainerSizes(actions, containerLastActions); return true; } /** * Adds an action to the action list to the specified location, and updates * all references * * @param actions * @param index * @param newActions * @return */ public static boolean addActions(ActionList actions, int index, List<Action> newActions) { if (index < 0 || actions.size() < index) { return false; } long startIp = actions.get(0).getAddress(); Action lastAction = actions.get(actions.size() - 1); if (!(lastAction instanceof ActionEnd)) { Action aEnd = new ActionEnd(); aEnd.setAddress(lastAction.getAddress() + lastAction.getTotalActionLength()); actions.add(aEnd); lastAction = aEnd; } long endAddress = lastAction.getAddress(); Map<Action, List<Action>> containerLastActions = new HashMap<>(); getContainerLastActions(actions, containerLastActions); Map<Action, Action> jumps = new HashMap<>(); List<Action> tempActions = new ArrayList<>(actions); tempActions.addAll(newActions); getJumps(tempActions, jumps); actions.addAll(index, newActions); updateActionLengths(actions); updateAddresses(actions, startIp); updateJumps(actions, jumps, containerLastActions, endAddress); updateActionStores(actions, jumps); updateContainerSizes(actions, containerLastActions); return true; } 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) throws IOException { Action entryAction = null; if (visitedContainers.contains(ip)) { return null; } visitedContainers.add(ip); Queue<Long> jumpQueue = new LinkedList<>(); jumpQueue.add(ip); while (!jumpQueue.isEmpty()) { ip = jumpQueue.remove(); if (ip < startIp) { continue; } while (endIp == -1 || endIp > ip) { sis.seek((int) ip); Action a; if ((a = sis.readAction()) == null) { break; } a.fileOffset = ip; int actionLengthWithHeader = a.getTotalActionLength(); // unknown action, replace with jump if (a instanceof ActionUnknown && a.getActionCode() >= 0x80) { ActionJump aJump = new ActionDeobfuscateJump(0); int jumpLength = aJump.getTotalActionLength(); aJump.setAddress(a.getAddress()); //FIXME! This offset can be larger than SI16 value! aJump.setJumpOffset(actionLengthWithHeader - jumpLength); a = aJump; actionLengthWithHeader = a.getTotalActionLength(); } if (entryAction == null) { entryAction = a; } Action existingAction = actions.get(ip); if (existingAction != null) { break; } actions.put(ip, a); nextOffsets.put(ip, ip + actionLengthWithHeader); long pos = sis.getPos(); long length = pos + sis.available(); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).progressReading(pos, length); } a.setAddress(ip); if (a instanceof ActionPush && cpool != null) { ((ActionPush) a).constantPool = cpool.constants; } else if (a instanceof ActionConstantPool) { cpool = new ConstantPool(((ActionConstantPool) a).constantPool); } else if (a instanceof ActionIf) { ActionIf aIf = (ActionIf) a; long nIp = ip + actionLengthWithHeader + aIf.getJumpOffset(); if (nIp >= 0) { jumpQueue.add(nIp); } } else if (a instanceof ActionJump) { ActionJump aJump = (ActionJump) a; long nIp = ip + actionLengthWithHeader + aJump.getJumpOffset(); if (nIp >= 0) { jumpQueue.add(nIp); } break; } else if (a instanceof GraphSourceItemContainer) { GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; String cntName = cnt.getName(); String newPath = path + (cntName == null ? "" : "/" + cntName); for (long size : cnt.getContainerSizes()) { if (size != 0) { long ip2 = ip + actionLengthWithHeader; long endIp2 = ip + actionLengthWithHeader + size; readActionListAtPos(listeners, cpool, sis, actions, nextOffsets, ip2, startIp, endIp2, newPath, indeterminate, visitedContainers); actionLengthWithHeader += size; } } } ip += actionLengthWithHeader; if (a.isExit()) { break; } } } return entryAction; } public static boolean fixConstantPools(List<DisassemblyListener> listeners, ActionList actions) { Action lastAction = actions.get(actions.size() - 1); int endIp = (int) lastAction.getAddress(); List<Action> actionMap = new ArrayList<>(endIp); for (int i = 0; i <= endIp; i++) { actionMap.add(null); } for (Action a : actions) { actionMap.set((int) a.getAddress(), a); } try { int startIp = (int) actions.get(0).getAddress(); return fixConstantPools(listeners, new ConstantPool(), actionMap, new TreeMap<>(), startIp, startIp, endIp, null, true, new ArrayList<>()); } catch (IOException ex) { // ignore } return false; } 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 { if (visitedContainers.contains(ip)) { return false; } visitedContainers.add(ip); Queue<Integer> jumpQueue = new LinkedList<>(); jumpQueue.add(ip); boolean ret = false; while (!jumpQueue.isEmpty()) { ip = jumpQueue.remove(); if (ip < startIp) { continue; } while (endIp == -1 || endIp > ip) { Action a; if ((a = actions.get(ip)) == null) { break; } int actionLengthWithHeader = a.getTotalActionLength(); Action existingAction = actionMap.get(ip); if (existingAction != null) { break; } actionMap.put(ip, a); if (listeners != null) { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).progressReading(ip, actions.size()); } } if (a.getAddress() != ip) { a.setAddress(ip); ret = true; } if (a instanceof ActionPush && cpool != null) { ActionPush push = (ActionPush) a; if (push.constantPool != cpool.constants) { push.constantPool = cpool.constants.isEmpty() ? null : cpool.constants; if (push.constantPool != null) { ret = true; } } } else if (a instanceof ActionConstantPool) { cpool = new ConstantPool(((ActionConstantPool) a).constantPool); } else if (a instanceof ActionIf) { ActionIf aIf = (ActionIf) a; int nIp = ip + actionLengthWithHeader + aIf.getJumpOffset(); if (nIp >= 0) { jumpQueue.add(nIp); } } else if (a instanceof ActionJump) { ActionJump aJump = (ActionJump) a; int nIp = ip + actionLengthWithHeader + aJump.getJumpOffset(); if (nIp >= 0) { jumpQueue.add(nIp); } break; } else if (a instanceof GraphSourceItemContainer) { GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; String cntName = cnt.getName(); String newPath = path + (cntName == null ? "" : "/" + cntName); for (long size : cnt.getContainerSizes()) { if (size != 0) { int ip2 = ip + actionLengthWithHeader; int endIp2 = ip + actionLengthWithHeader + (int) size; ret |= fixConstantPools(listeners, cpool, actions, actionMap, ip2, startIp, endIp2, newPath, indeterminate, visitedContainers); actionLengthWithHeader += size; } } } ip += actionLengthWithHeader; if (a.isExit()) { break; } } } return ret; } }