diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index 021acc1de..e4bf18af7 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionDeobfuscation; import com.jpexs.decompiler.flash.action.ActionGraphSource; import com.jpexs.decompiler.flash.action.model.ConstantPool; import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; @@ -101,6 +102,7 @@ import com.jpexs.decompiler.graph.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.helpers.Cache; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import com.jpexs.helpers.ProgressListener; import com.jpexs.helpers.utf8.Utf8Helper; @@ -129,7 +131,6 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.Stack; import java.util.concurrent.Callable; @@ -142,7 +143,6 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Pattern; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; import javax.imageio.ImageIO; @@ -298,7 +298,7 @@ public final class SWF { } } - public SWF(InputStream is, boolean parallelRead) throws IOException { + public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { this(is, null, parallelRead); } @@ -310,7 +310,7 @@ public final class SWF { * @param parallelRead Use parallel threads? * @throws IOException */ - public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException { + public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { this(is, listener, parallelRead, false); } @@ -323,7 +323,7 @@ public final class SWF { * @param checkOnly Check only file validity * @throws IOException */ - public SWF(InputStream is, ProgressListener listener, boolean parallelRead, boolean checkOnly) throws IOException { + public SWF(InputStream is, ProgressListener listener, boolean parallelRead, boolean checkOnly) throws IOException, InterruptedException { byte[] hdr = new byte[3]; is.read(hdr); String shdr = new String(hdr, Utf8Helper.charset); @@ -663,7 +663,7 @@ public final class SWF { long time = stopTime - startTime; informListeners("exported", "Exported script " + currentIndex + "/" + count + " " + path + ", " + Helper.formatTimeSec(time)); } - return null; + return rio.result; } } @@ -696,7 +696,7 @@ public final class SWF { if (!parallel || packs.size() < 2) { try { - Helper.timedCall(new Callable() { + CancellableWorker.call(new Callable() { @Override public Void call() throws Exception { for (MyEntry item : packs) { @@ -712,26 +712,32 @@ public final class SWF { Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, "Error during ABC export", ex); } } else { - ExecutorService executor = Executors.newFixedThreadPool(20); + ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get()); List> futureResults = new ArrayList<>(); for (MyEntry item : packs) { Future future = executor.submit(new ExportPackTask(handler, cnt, packs.size(), item.key, item.value, outdir, abcTags, exportMode, parallel)); futureResults.add(future); } - for (int f = 0; f < futureResults.size(); f++) { - try { - ret.add(futureResults.get(f).get()); - } catch (InterruptedException | ExecutionException ex) { - Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during ABC export", ex); + try { + executor.shutdown(); + if (!executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS)) { + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached"); } + } catch (InterruptedException ex) { + } finally { + executor.shutdownNow(); } - try { - executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS); - executor.shutdownNow(); - } catch (InterruptedException ex) { - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached", ex); + for (int f = 0; f < futureResults.size(); f++) { + try { + if (futureResults.get(f).isDone()) { + ret.add(futureResults.get(f).get()); + } + } catch (InterruptedException ex) { + } catch (ExecutionException ex) { + Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during ABC export", ex); + } } } @@ -1482,133 +1488,12 @@ public final class SWF { public void exportBinaryData(AbortRetryIgnoreHandler handler, String outdir) throws IOException { exportBinaryData(handler, outdir, tags); } - public static final String[] reservedWords = { - "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", - "extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof", - "interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public", - "return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", - "with", "dynamic", "default", "final", "in"}; - - private boolean isReserved(String s) { - for (String rw : reservedWords) { - if (rw.equals(s.trim())) { - return true; - } - } - return false; - } private HashMap deobfuscated = new HashMap<>(); - private Random rnd = new Random(); - private final int DEFAULT_FOO_SIZE = 10; - public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; - public static final String validNextCharacters = validFirstCharacters + "0123456789"; - public static final String fooCharacters = "bcdfghjklmnpqrstvwz"; - public static final String fooJoinCharacters = "aeiouy"; private List> allVariableNames = new ArrayList<>(); - private HashSet allVariableNamesStr = new HashSet<>(); private List allFunctions = new ArrayList<>(); private HashMap allStrings = new HashMap<>(); private HashMap usageTypes = new HashMap<>(); - - private String fooString(String orig, boolean firstUppercase, int rndSize) { - boolean exists; - String ret; - loopfoo: - do { - exists = false; - int len = 3 + rnd.nextInt(rndSize - 3); - ret = ""; - for (int i = 0; i < len; i++) { - String c = ""; - if ((i % 2) == 0) { - c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length())); - } else { - c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length())); - } - if (i == 0 && firstUppercase) { - c = c.toUpperCase(Locale.ENGLISH); - } - ret += c; - } - if (allVariableNamesStr.contains(ret)) { - exists = true; - rndSize += 1; - continue loopfoo; - } - if (isReserved(ret)) { - exists = true; - rndSize += 1; - continue; - } - if (deobfuscated.containsValue(ret)) { - exists = true; - rndSize += 1; - continue; - } - } while (exists); - return ret; - } - - public String deobfuscateName(String s, boolean firstUppercase, String usageType, RenameType renameType, Map selected) { - boolean isValid = true; - if (usageType == null) { - usageType = "name"; - } - - if (selected != null) { - if (selected.containsKey(s)) { - return selected.get(s); - } - } - - if (isReserved(s)) { - isValid = false; - } - - if (isValid) { - for (int i = 0; i < s.length(); i++) { - if (s.charAt(i) > 127) { - isValid = false; - break; - } - } - } - - if (isValid) { - Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$"); - if (!pat.matcher(s).matches()) { - isValid = false; - } - } - if (!isValid) { - if (deobfuscated.containsKey(s)) { - return deobfuscated.get(s); - } else { - Integer cnt = typeCounts.get(usageType); - if (cnt == null) { - cnt = 0; - } - - String ret = null; - if (renameType == RenameType.TYPENUMBER) { - - boolean found; - do { - found = false; - cnt++; - ret = usageType + "_" + cnt; - found = allVariableNamesStr.contains(ret); - } while (found); - typeCounts.put(usageType, cnt); - } else if (renameType == RenameType.RANDOMWORD) { - ret = fooString(s, firstUppercase, DEFAULT_FOO_SIZE); - } - deobfuscated.put(s, ret); - return ret; - } - } - return null; - } + private ActionDeobfuscation deobfuscation = new ActionDeobfuscation(); private static void getVariables(ConstantPool constantPool, List localData, Stack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { boolean debugMode = false; @@ -1638,6 +1523,9 @@ public final class SWF { || (ins instanceof ActionNewObject) || (ins instanceof ActionCallMethod) || (ins instanceof ActionCallFunction)) { + if (stack.isEmpty()) { + break; + } name = stack.peek(); } @@ -1681,12 +1569,18 @@ public final class SWF { r.add(new ArrayList()); r.add(new ArrayList()); r.add(new ArrayList()); - ((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap(), new HashMap(), new HashMap()); + try { + ((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap(), new HashMap(), new HashMap()); + } catch (EmptyStackException ex) { + } //ip++; continue; } if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) { + if (stack.size() < 2) { + break; + } name = stack.get(stack.size() - 2); } @@ -1718,8 +1612,6 @@ public final class SWF { } catch (EmptyStackException ex) { // probably obfucated code, never executed branch break; - } catch (InterruptedException ex) { - Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during getting variables", ex); } if (ins.isExit()) { break; @@ -1742,6 +1634,9 @@ public final class SWF { if (ins.isBranch() || ins.isJump()) { if (ins instanceof ActionIf) { + if (stack.isEmpty()) { + break; + } stack.pop(); } visited.add(ip); @@ -1799,96 +1694,6 @@ public final class SWF { } } - public void deobfuscateInstanceNames(RenameType renameType, List tags, Map selected) { - for (Tag t : tags) { - if (t instanceof DefineSpriteTag) { - deobfuscateInstanceNames(renameType, ((DefineSpriteTag) t).subTags, selected); - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - String name = po.getInstanceName(); - if (name != null) { - String changedName = deobfuscateName(name, false, "instance", renameType, selected); - if (changedName != null) { - po.setInstanceName(changedName); - } - } - String className = po.getClassName(); - if (className != null) { - String changedClassName = deobfuscateNameWithPackage(className, renameType, selected); - if (changedClassName != null) { - po.setClassName(changedClassName); - } - } - } - } - } - - public String deobfuscatePackage(String pkg, RenameType renameType, Map selected) { - if (deobfuscated.containsKey(pkg)) { - return deobfuscated.get(pkg); - } - String[] parts = null; - if (pkg.contains(".")) { - parts = pkg.split("\\."); - } else { - parts = new String[]{pkg}; - } - String ret = ""; - boolean isChanged = false; - for (int p = 0; p < parts.length; p++) { - if (p > 0) { - ret += "."; - } - String partChanged = deobfuscateName(parts[p], false, "package", renameType, selected); - if (partChanged != null) { - ret += partChanged; - isChanged = true; - } else { - ret += parts[p]; - } - } - if (isChanged) { - deobfuscated.put(pkg, ret); - return ret; - } - return null; - } - - public String deobfuscateNameWithPackage(String n, RenameType renameType, Map selected) { - String pkg = null; - String name = ""; - if (n.contains(".")) { - pkg = n.substring(0, n.lastIndexOf('.')); - name = n.substring(n.lastIndexOf('.') + 1); - } else { - name = n; - } - boolean changed = false; - if ((pkg != null) && (!pkg.isEmpty())) { - String changedPkg = deobfuscatePackage(pkg, renameType, selected); - if (changedPkg != null) { - changed = true; - pkg = changedPkg; - } - } - String changedName = deobfuscateName(name, true, "class", renameType, selected); - if (changedName != null) { - changed = true; - name = changedName; - } - if (changed) { - String newClassName = ""; - if (pkg == null) { - newClassName = name; - } else { - newClassName = pkg + "." + name; - } - return newClassName; - } - return null; - } - public int deobfuscateAS3Identifiers(RenameType renameType) { for (Tag tag : tags) { if (tag instanceof ABCContainerTag) { @@ -1904,17 +1709,16 @@ public final class SWF { if (tag instanceof SymbolClassTag) { SymbolClassTag sc = (SymbolClassTag) tag; for (int i = 0; i < sc.classNames.length; i++) { - String newname = deobfuscateNameWithPackage(sc.classNames[i], renameType, deobfuscated); + String newname = deobfuscation.deobfuscateNameWithPackage(sc.classNames[i], deobfuscated, renameType, deobfuscated); if (newname != null) { sc.classNames[i] = newname; } } } } - deobfuscateInstanceNames(renameType, tags, new HashMap()); + deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, new HashMap()); return deobfuscated.size(); } - HashMap typeCounts = new HashMap<>(); public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException { findFileAttributes(); @@ -1956,7 +1760,7 @@ public final class SWF { int fc = 0; for (MyEntry it : allVariableNames) { String name = it.key.toStringNoH(it.value); - allVariableNamesStr.add(name); + deobfuscation.allVariableNamesStr.add(name); } informListeners("rename", "classes"); @@ -1984,7 +1788,12 @@ public final class SWF { } } int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - List dec = Action.actionsToTree(dia.getActions(version), version, staticOperation, ""/*FIXME*/); + List dec; + try { + dec = Action.actionsToTree(dia.getActions(version), version, staticOperation, ""/*FIXME*/); + } catch (EmptyStackException ex) { + continue; + } GraphTargetItem name = null; for (GraphTargetItem it : dec) { if (it instanceof ClassActionItem) { @@ -1999,7 +1808,7 @@ public final class SWF { if (fun.calculatedFunctionName instanceof DirectValueActionItem) { DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName; String fname = dvf.toStringNoH(null); - String changed = deobfuscateName(fname, false, "method", renameType, selected); + String changed = deobfuscation.deobfuscateName(fname, false, "method", deobfuscated, renameType, selected); if (changed != null) { deobfuscated.put(fname, changed); } @@ -2019,7 +1828,7 @@ public final class SWF { if (gti instanceof DirectValueActionItem) { DirectValueActionItem dvf = (DirectValueActionItem) gti; String vname = dvf.toStringNoH(null); - String changed = deobfuscateName(vname, false, "attribute", renameType, selected); + String changed = deobfuscation.deobfuscateName(vname, false, "attribute", deobfuscated, renameType, selected); if (changed != null) { deobfuscated.put(vname, changed); } @@ -2053,7 +1862,7 @@ public final class SWF { if (classNameParts != null) { changedNameStr = classNameParts[classNameParts.length - 1 - pos]; } - String changedNameStr2 = deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", renameType, selected); + String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); if (changedNameStr2 != null) { changedNameStr = changedNameStr2; } @@ -2077,7 +1886,7 @@ public final class SWF { if (classNameParts != null) { changedNameStr = classNameParts[classNameParts.length - 1 - pos]; } - String changedNameStr2 = deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", renameType, selected); + String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); if (changedNameStr2 != null) { changedNameStr = changedNameStr2; } @@ -2098,7 +1907,7 @@ public final class SWF { if (f.functionName.isEmpty()) { //anonymous function, leave as is continue; } - String changed = deobfuscateName(f.functionName, false, "function", renameType, selected); + String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected); if (changed != null) { f.replacedFunctionName = changed; ret++; @@ -2109,7 +1918,7 @@ public final class SWF { if (f.functionName.isEmpty()) { //anonymous function, leave as is continue; } - String changed = deobfuscateName(f.functionName, false, "function", renameType, selected); + String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected); if (changed != null) { f.replacedFunctionName = changed; ret++; @@ -2132,7 +1941,7 @@ public final class SWF { for (MyEntry it : allVariableNames) { vc++; String name = it.key.toStringNoH(it.value); - String changed = deobfuscateName(name, false, usageTypes.get(it.key), renameType, selected); + String changed = deobfuscation.deobfuscateName(name, false, usageTypes.get(it.key), deobfuscated, renameType, selected); if (changed != null) { boolean addNew = false; String h = System.identityHashCode(it.key) + "_" + name; @@ -2169,7 +1978,7 @@ public final class SWF { actionsMap.put(src, Action.removeNops(0, actionsMap.get(src), version, 0, ""/*FIXME path*/)); src.setActions(actionsMap.get(src), version); } - deobfuscateInstanceNames(renameType, tags, selected); + deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, selected); return ret; } diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java index 7851f5308..2affedd3f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -604,7 +604,7 @@ public class SWFInputStream extends InputStream { * @return List of tags * @throws IOException */ - public List readTagList(SWF swf, int level, boolean parallel) throws IOException { + public List readTagList(SWF swf, int level, boolean parallel) throws IOException, InterruptedException { return readTagList(swf, level, parallel, false); } @@ -619,7 +619,7 @@ public class SWFInputStream extends InputStream { * @return List of tags * @throws IOException */ - public List readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags) throws IOException { + public List readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException { return readTagList(swf, level, parallel, skipUnusualTags, true); } @@ -635,11 +635,11 @@ public class SWFInputStream extends InputStream { * @return List of tags * @throws IOException */ - public List readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags) throws IOException { + public List readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags) throws IOException, InterruptedException { ExecutorService executor = null; List> futureResults = new ArrayList<>(); if (parallel) { - executor = Executors.newFixedThreadPool(20); + executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get()); futureResults = new ArrayList<>(); } List tags = new ArrayList<>(); @@ -723,7 +723,9 @@ public class SWFInputStream extends InputStream { for (Future future : futureResults) { try { tags.add(future.get()); - } catch (InterruptedException | ExecutionException e) { + } catch (InterruptedException ex) { + future.cancel(true); + } catch (ExecutionException e) { Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "Error during tag reading", e); } } @@ -733,7 +735,7 @@ public class SWFInputStream extends InputStream { return tags; } - public static Tag resolveTag(SWF swf, Tag tag, int version, int level, boolean parallel, boolean skipUnusualTags) { + public static Tag resolveTag(SWF swf, Tag tag, int version, int level, boolean parallel, boolean skipUnusualTags) throws InterruptedException { Tag ret; byte[] data = tag.getData(version); @@ -1023,7 +1025,7 @@ public class SWFInputStream extends InputStream { * @return Tag or null when End tag * @throws IOException */ - public Tag readTag(SWF swf, int level, long pos, boolean resolve, boolean parallel, boolean skipUnusualTags) throws IOException { + public Tag readTag(SWF swf, int level, long pos, boolean resolve, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException { int tagIDTagLength = readUI16(); int tagID = (tagIDTagLength) >> 6; if (tagID == 0) { diff --git a/trunk/src/com/jpexs/decompiler/flash/TagNode.java b/trunk/src/com/jpexs/decompiler/flash/TagNode.java index 03cdddd66..9f9b308c9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/TagNode.java +++ b/trunk/src/com/jpexs/decompiler/flash/TagNode.java @@ -50,6 +50,7 @@ import com.jpexs.decompiler.flash.tags.base.ContainerItem; import com.jpexs.decompiler.flash.tags.base.Exportable; import com.jpexs.decompiler.graph.ExportMode; import com.jpexs.decompiler.graph.TranslateException; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import java.io.File; import java.io.FileOutputStream; @@ -260,7 +261,7 @@ public class TagNode { public static List exportNodeAS(final List allTags, final AbortRetryIgnoreHandler handler, final List nodeList, final String outdir, final ExportMode exportMode, final EventListener ev) throws IOException { try { - List result = Helper.timedCall(new Callable>() { + List result = CancellableWorker.call(new Callable>() { @Override public List call() throws Exception { diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/ABC.java b/trunk/src/com/jpexs/decompiler/flash/abc/ABC.java index 1ca3bdea4..773c0c982 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.abc; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; @@ -43,13 +44,10 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Pattern; public class ABC { @@ -67,6 +65,7 @@ public class ABC { public static final int MINORwithDECIMAL = 17; protected HashSet listeners = new HashSet<>(); private static final Logger logger = Logger.getLogger(ABC.class.getName()); + private AVM2Deobfuscation deobfuscation; public int addMethodBody(MethodBody body) { bodies = Arrays.copyOf(bodies, bodies.length + 1); @@ -111,7 +110,7 @@ public class ABC { return rem; } - public int removeDeadCode() { + public int removeDeadCode() throws InterruptedException { int rem = 0; for (MethodBody body : bodies) { rem += body.removeDeadCode(constants, null/*FIXME*/, method_info[body.method_info]); @@ -119,7 +118,7 @@ public class ABC { return rem; } - public void restoreControlFlow() { + public void restoreControlFlow() throws InterruptedException { for (MethodBody body : bodies) { body.restoreControlFlow(constants, null/*FIXME*/, method_info[body.method_info]); } @@ -238,14 +237,14 @@ public class ABC { for (int i = 0; i < instance_info.length; i++) { informListeners("deobfuscate", "class " + i + "/" + instance_info.length); if (instance_info[i].name_index != 0) { - constants.getMultiname(instance_info[i].name_index).name_index = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].name_index).name_index, true, renameType); + constants.getMultiname(instance_info[i].name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].name_index).name_index, true, renameType); if (constants.getMultiname(instance_info[i].name_index).namespace_index != 0) { constants.getNamespace(constants.getMultiname(instance_info[i].name_index).namespace_index).name_index = - deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(instance_info[i].name_index).namespace_index).name_index, renameType); + deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(instance_info[i].name_index).namespace_index).name_index, renameType); } } if (instance_info[i].super_index != 0) { - constants.getMultiname(instance_info[i].super_index).name_index = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].super_index).name_index, true, renameType); + constants.getMultiname(instance_info[i].super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].super_index).name_index, true, renameType); } } if (classesOnly) { @@ -253,14 +252,14 @@ public class ABC { } for (int i = 1; i < constants.getMultinameCount(); i++) { informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); - constants.getMultiname(i).name_index = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); + constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); } for (int i = 1; i < constants.getNamespaceCount(); i++) { informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { //only packages continue; } - constants.getNamespace(i).name_index = deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); + constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); } //process reflection using getDefinitionByName too @@ -283,11 +282,11 @@ public class ABC { } if (!pkg.isEmpty()) { int pkgStrIndex = constants.getStringId(pkg, true); - pkgStrIndex = deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); + pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); pkg = constants.getString(pkgStrIndex); } int nameStrIndex = constants.getStringId(name, true); - nameStrIndex = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); + nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); name = constants.getString(nameStrIndex); String fullChanged = ""; if (!pkg.isEmpty()) { @@ -311,6 +310,7 @@ public class ABC { major_version = ais.readU16(); logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); constants = new ConstantPool(); + deobfuscation = new AVM2Deobfuscation(constants); //constant integers int constant_int_pool_count = ais.readU30(); constants.constant_int = new ArrayList<>(constant_int_pool_count); @@ -738,21 +738,11 @@ public class ABC { } } - public String builtInNs(String ns) { - if (ns.equals("http://www.adobe.com/2006/actionscript/flash/proxy")) { - return "flash.utils.flash_proxy"; - } - if (ns.equals("http://adobe.com/AS3/2006/builtin")) { - return "-"; - } - return null; - } - public String nsValueToName(String value) { if (namespaceMap.containsKey(value)) { return namespaceMap.get(value); } else { - String ns = builtInNs(value); + String ns = deobfuscation.builtInNs(value); if (ns == null) { return ""; } else { @@ -792,198 +782,6 @@ public class ABC { output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); } } - public static final String[] reservedWords = { - "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", - "extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof", - "interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public", - "return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", - "with", "dynamic", "default", "final", "in", "static"}; - public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; - public static final String validNextCharacters = validFirstCharacters + "0123456789"; - public static final String validNsCharacters = ".:$"; - public static final String fooCharacters = "bcdfghjklmnpqrstvwz"; - public static final String fooJoinCharacters = "aeiouy"; - private Random rnd = new Random(); - private final int DEFAULT_FOO_SIZE = 10; - private Map usageTypesCount = new HashMap<>(); - - private String fooString(HashMap deobfuscated, String orig, boolean firstUppercase, int rndSize, String usageType, RenameType renameType) { - boolean exists; - String ret; - int pos = 0; - - if (usageType == null) { - usageType = "name"; - } - if (usageTypesCount.containsKey(usageType)) { - pos = usageTypesCount.get(usageType); - } - - loopfoo: - do { - exists = false; - ret = ""; - if (renameType == RenameType.TYPENUMBER) { - pos++; - ret = usageType + "_" + pos; - } else if (renameType == RenameType.RANDOMWORD) { - int len = 3 + rnd.nextInt(rndSize - 3); - - for (int i = 0; i < len; i++) { - String c = ""; - if ((i % 2) == 0) { - c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length())); - } else { - c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length())); - } - if (i == 0 && firstUppercase) { - c = c.toUpperCase(Locale.ENGLISH); - } - ret += c; - } - } - for (int i = 1; i < constants.getStringCount(); i++) { - if (constants.getString(i).equals(ret)) { - exists = true; - rndSize += 1; - continue loopfoo; - } - } - if (isReserved(ret)) { - exists = true; - rndSize += 1; - continue; - } - if (deobfuscated.containsValue(ret)) { - exists = true; - rndSize += 1; - continue; - } - } while (exists); - usageTypesCount.put(usageType, pos); - deobfuscated.put(orig, ret); - return ret; - } - - private boolean isReserved(String s) { - for (String rw : reservedWords) { - if (rw.equals(s.trim())) { - return true; - } - } - return false; - } - - private boolean isValidNSPart(String s) { - boolean isValid = true; - if (isReserved(s)) { - isValid = false; - } - - if (isValid) { - for (int i = 0; i < s.length(); i++) { - if (s.charAt(i) > 127) { - isValid = false; - break; - } - } - } - if (isValid) { - Pattern pat = Pattern.compile("^([" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters + validNsCharacters) + "]*)*$"); - if (!pat.matcher(s).matches()) { - isValid = false; - } - } - return isValid; - } - - public int deobfuscatePackageName(Map stringUsageTypes, Set stringUsages, HashMap namesMap, int strIndex, RenameType renameType) { - if (strIndex <= 0) { - return strIndex; - } - String s = constants.getString(strIndex); - if (builtInNs(s) != null) { - return strIndex; - } - boolean isValid = isValidNSPart(s); - if (!isValid) { - String newName; - if (namesMap.containsKey(s)) { - newName = constants.setString(strIndex, namesMap.get(s)); - } else { - String[] parts = null; - if (s.contains(".")) { - parts = s.split("\\."); - } else { - parts = new String[]{s}; - } - String ret = ""; - for (int p = 0; p < parts.length; p++) { - if (p > 0) { - ret += "."; - } - if (!isValidNSPart(parts[p])) { - ret += fooString(namesMap, parts[p], false, DEFAULT_FOO_SIZE, "package", renameType); - } else { - ret += parts[p]; - } - } - newName = ret; - namesMap.put(s, newName); - } - if (stringUsages.contains(strIndex)) { - strIndex = constants.addString(newName); - } else { - constants.setString(strIndex, newName); - } - - } - return strIndex; - } - - public int deobfuscateName(Map stringUsageTypes, Set stringUsages, Set namespaceUsages, HashMap namesMap, int strIndex, boolean firstUppercase, RenameType renameType) { - if (strIndex <= 0) { - return strIndex; - } - String s = constants.getString(strIndex); - boolean isValid = true; - if (isReserved(s)) { - isValid = false; - } - - if (isValid) { - for (int i = 0; i < s.length(); i++) { - if (s.charAt(i) > 127) { - isValid = false; - break; - } - } - } - - if (isValid) { - Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$"); - if (!pat.matcher(s).matches()) { - isValid = false; - } - } - - if (!isValid) { - String newname; - if (namesMap.containsKey(s)) { - newname = namesMap.get(s); - } else { - newname = fooString(namesMap, constants.getString(strIndex), firstUppercase, DEFAULT_FOO_SIZE, stringUsageTypes.get(strIndex), renameType); - } - if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { //this name is already referenced as String - strIndex = constants.addString(s); //add new index - } - constants.setString(strIndex, newname); - if (!namesMap.containsKey(s)) { - namesMap.put(s, constants.getString(strIndex)); - } - } - return strIndex; - } private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { for (int p = 0; p < method_info[methodInfo].param_types.length; p++) { diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/ScriptPack.java b/trunk/src/com/jpexs/decompiler/flash/abc/ScriptPack.java index 8529cb08d..9501083b9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/ScriptPack.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/ScriptPack.java @@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.graph.ExportMode; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import java.io.File; import java.io.FileOutputStream; @@ -143,7 +144,7 @@ public class ScriptPack { public void toSource(GraphTextWriter writer, final List abcList, final Trait[] traits, final ExportMode exportMode, final boolean parallel) throws InterruptedException { writer.suspendMeasure(); try { - Helper.timedCall(new Callable() { + CancellableWorker.call(new Callable() { @Override public Void call() throws Exception { convert(new NulWriter(), abcList, traits, exportMode, parallel); @@ -159,7 +160,7 @@ public class ScriptPack { writer.appendNoHilight(" */").newLine(); writer.appendNoHilight("throw new IllegalOperationError(\"Not decompiled due to timeout\");").newLine(); return; - } catch (InterruptedException | ExecutionException ex) { + } catch (ExecutionException ex) { writer.continueMeasure(); Logger.getLogger(MethodBody.class.getName()).log(Level.SEVERE, "Decompilation error", ex); writer.appendNoHilight("/*").newLine(); diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 7175299aa..16b008d5a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -1796,7 +1796,10 @@ public class AVM2Code implements Serializable { return stats; } - private void visitCode(int ip, int lastIp, HashMap> refs) { + private void visitCode(int ip, int lastIp, HashMap> refs) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } while (ip < code.size()) { if (!refs.containsKey(ip)) { refs.put(ip, new ArrayList()); @@ -1844,7 +1847,7 @@ public class AVM2Code implements Serializable { }; } - public HashMap> visitCode(MethodBody body) { + public HashMap> visitCode(MethodBody body) throws InterruptedException { HashMap> refs = new HashMap<>(); for (int i = 0; i < code.size(); i++) { refs.put(i, new ArrayList()); @@ -2078,7 +2081,7 @@ public class AVM2Code implements Serializable { } - private void restoreControlFlowPass(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, boolean secondpass) { + private void restoreControlFlowPass(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, boolean secondpass) throws InterruptedException { try { HashMap> refs; int[] visited2 = new int[code.size()]; @@ -2134,7 +2137,7 @@ public class AVM2Code implements Serializable { removeDeadCode(constants, trait, info, body); } - public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) { + public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { restoreControlFlowPass(constants, trait, info, body, false); //restoreControlFlowPass(constants, body, true); } @@ -2146,7 +2149,7 @@ public class AVM2Code implements Serializable { } } }*/ - public void removeIgnored(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) { + public void removeIgnored(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { try { List outputMap = new ArrayList<>(); HilightedTextWriter writer = new HilightedTextWriter(false); @@ -2170,7 +2173,7 @@ public class AVM2Code implements Serializable { invalidateCache(); } - public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) { + public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { HashMap> refs = visitCode(body); int cnt = 0; @@ -2377,7 +2380,7 @@ public class AVM2Code implements Serializable { @SuppressWarnings("unchecked") private static int removeTraps(HashMap> refs, boolean secondPass, boolean indeterminate, List localData, Stack stack, List output, AVM2GraphSource code, int ip, HashMap visited, HashMap> visitedStates, HashMap decisions, String path, int recursionLevel) throws InterruptedException { - if (Thread.interrupted()) { + if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } if (recursionLevel > code.size() + 1) { diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscation.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscation.java new file mode 100644 index 000000000..b4538218c --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Deobfuscation.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.abc.avm2; + +import com.jpexs.decompiler.flash.abc.RenameType; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * + * @author JPEXS + */ +public class AVM2Deobfuscation { + + private static Random rnd = new Random(); + private static final int DEFAULT_FOO_SIZE = 10; + + public static final String[] reservedWords = { + "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", + "extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof", + "interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public", + "return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", + "with", "dynamic", "default", "final", "in", "static"}; + public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + public static final String validNextCharacters = validFirstCharacters + "0123456789"; + public static final String validNsCharacters = ".:$"; + public static final String fooCharacters = "bcdfghjklmnpqrstvwz"; + public static final String fooJoinCharacters = "aeiouy"; + + private ConstantPool constants; + private Map usageTypesCount = new HashMap<>(); + + public AVM2Deobfuscation(ConstantPool constants) { + this.constants = constants; + } + + private static boolean isReserved(String s) { + for (String rw : reservedWords) { + if (rw.equals(s.trim())) { + return true; + } + } + return false; + } + + private boolean isValidNSPart(String s) { + boolean isValid = true; + if (isReserved(s)) { + isValid = false; + } + + if (isValid) { + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) > 127) { + isValid = false; + break; + } + } + } + if (isValid) { + Pattern pat = Pattern.compile("^([" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters + validNsCharacters) + "]*)*$"); + if (!pat.matcher(s).matches()) { + isValid = false; + } + } + return isValid; + } + + public String builtInNs(String ns) { + if (ns.equals("http://www.adobe.com/2006/actionscript/flash/proxy")) { + return "flash.utils.flash_proxy"; + } + if (ns.equals("http://adobe.com/AS3/2006/builtin")) { + return "-"; + } + return null; + } + + private String fooString(HashMap deobfuscated, String orig, boolean firstUppercase, int rndSize, String usageType, RenameType renameType) { + boolean exists; + String ret; + int pos = 0; + + if (usageType == null) { + usageType = "name"; + } + if (usageTypesCount.containsKey(usageType)) { + pos = usageTypesCount.get(usageType); + } + + loopfoo: + do { + exists = false; + ret = ""; + if (renameType == RenameType.TYPENUMBER) { + pos++; + ret = usageType + "_" + pos; + } else if (renameType == RenameType.RANDOMWORD) { + int len = 3 + rnd.nextInt(rndSize - 3); + + for (int i = 0; i < len; i++) { + String c = ""; + if ((i % 2) == 0) { + c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length())); + } else { + c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length())); + } + if (i == 0 && firstUppercase) { + c = c.toUpperCase(Locale.ENGLISH); + } + ret += c; + } + } + for (int i = 1; i < constants.getStringCount(); i++) { + if (constants.getString(i).equals(ret)) { + exists = true; + rndSize += 1; + continue loopfoo; + } + } + if (isReserved(ret)) { + exists = true; + rndSize += 1; + continue; + } + if (deobfuscated.containsValue(ret)) { + exists = true; + rndSize += 1; + continue; + } + } while (exists); + usageTypesCount.put(usageType, pos); + deobfuscated.put(orig, ret); + return ret; + } + + public int deobfuscatePackageName(Map stringUsageTypes, Set stringUsages, HashMap namesMap, int strIndex, RenameType renameType) { + if (strIndex <= 0) { + return strIndex; + } + String s = constants.getString(strIndex); + if (builtInNs(s) != null) { + return strIndex; + } + boolean isValid = isValidNSPart(s); + if (!isValid) { + String newName; + if (namesMap.containsKey(s)) { + newName = constants.setString(strIndex, namesMap.get(s)); + } else { + String[] parts = null; + if (s.contains(".")) { + parts = s.split("\\."); + } else { + parts = new String[]{s}; + } + String ret = ""; + for (int p = 0; p < parts.length; p++) { + if (p > 0) { + ret += "."; + } + if (!isValidNSPart(parts[p])) { + ret += fooString(namesMap, parts[p], false, DEFAULT_FOO_SIZE, "package", renameType); + } else { + ret += parts[p]; + } + } + newName = ret; + namesMap.put(s, newName); + } + if (stringUsages.contains(strIndex)) { + strIndex = constants.addString(newName); + } else { + constants.setString(strIndex, newName); + } + + } + return strIndex; + } + + public int deobfuscateName(Map stringUsageTypes, Set stringUsages, Set namespaceUsages, HashMap namesMap, int strIndex, boolean firstUppercase, RenameType renameType) { + if (strIndex <= 0) { + return strIndex; + } + String s = constants.getString(strIndex); + boolean isValid = true; + if (isReserved(s)) { + isValid = false; + } + + if (isValid) { + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) > 127) { + isValid = false; + break; + } + } + } + + if (isValid) { + Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$"); + if (!pat.matcher(s).matches()) { + isValid = false; + } + } + + if (!isValid) { + String newname; + if (namesMap.containsKey(s)) { + newname = namesMap.get(s); + } else { + newname = fooString(namesMap, constants.getString(strIndex), firstUppercase, DEFAULT_FOO_SIZE, stringUsageTypes.get(strIndex), renameType); + } + if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { //this name is already referenced as String + strIndex = constants.addString(s); //add new index + } + constants.setString(strIndex, newname); + if (!namesMap.containsKey(s)) { + namesMap.put(s, constants.getString(strIndex)); + } + } + return strIndex; + } + +} diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java index 8b852b1a5..29266e682 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java @@ -7,7 +7,6 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSource; -import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import java.util.ArrayList; import java.util.HashMap; diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/ASM3Parser.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/ASM3Parser.java index b68fe787a..90d63b190 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/ASM3Parser.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/ASM3Parser.java @@ -73,7 +73,7 @@ public class ASM3Parser { } } - public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MethodBody body, MethodInfo info) throws IOException, ParseException { + public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException { return parse(reader, constants, trait, null, body, info); } @@ -473,7 +473,7 @@ public class ASM3Parser { return new ValueKind(value_index, value_kind); } - public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, ParseException { + public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException { AVM2Code code = new AVM2Code(); List offsetItems = new ArrayList<>(); @@ -909,6 +909,9 @@ public class ASM3Parser { for (OffsetItem oi : offsetItems) { for (LabelItem li : labelItems) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } if (oi.label.equals(li.label)) { AVM2Instruction ins = code.code.get((int) oi.insPosition); int relOffset; diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/trunk/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index 8d4adc814..58b4c886c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -30,6 +30,7 @@ import com.jpexs.decompiler.graph.ExportMode; import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import java.io.Serializable; import java.util.ArrayList; @@ -76,11 +77,11 @@ public class MethodBody implements Cloneable, Serializable { return s; } - public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info) { + public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException { return code.removeDeadCode(constants, trait, info, this); } - public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info) { + public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException { code.restoreControlFlow(constants, trait, info, this); } @@ -139,7 +140,7 @@ public class MethodBody implements Cloneable, Serializable { } }; if (firstLevel) { - Helper.timedCall(callable, timeout, TimeUnit.SECONDS); + CancellableWorker.call(callable, timeout, TimeUnit.SECONDS); } else { callable.call(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index 36bd0a156..828c0c450 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -437,12 +437,12 @@ public class TraitClass extends Trait implements TraitWithSlot { int bodyIndex = abc.findBodyIndex(abc.class_info[class_info].cinit_index); if (bodyIndex != -1) { if (!classInitializerIsEmpty) { + writer.newLine(); writer.startTrait(abc.class_info[class_info].static_traits.traits.length + abc.instance_info[class_info].instance_traits.traits.length + 1); writer.appendNoHilight("{").newLine(); abc.bodies[bodyIndex].toString(path +/*packageName +*/ "/" + abc.instance_info[class_info].getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info[class_info].static_traits); writer.appendNoHilight("}").newLine(); writer.endTrait(); - writer.newLine(); } } else { //"/*classInitializer*/"; @@ -465,6 +465,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } } + writer.newLine(); writer.startTrait(abc.class_info[class_info].static_traits.traits.length + abc.instance_info[class_info].instance_traits.traits.length); writer.appendNoHilight(modifier); writer.appendNoHilight("function "); @@ -482,7 +483,6 @@ public class TraitClass extends Trait implements TraitWithSlot { } writer.appendNoHilight("}").newLine(); writer.endTrait(); - writer.newLine(); } //static variables,constants & methods diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java index 8070c3ce2..d6e4dbb96 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.abc.types.traits; import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; @@ -121,9 +122,7 @@ public class Traits implements Serializable { public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ExportMode exportMode, boolean makePackages, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { for (int t = 0; t < traits.length; t++) { - if (t > 0) { - writer.newLine(); - } + writer.newLine(); Trait trait = traits[t]; int h = t; if (classIndex != -1) { @@ -157,7 +156,7 @@ public class Traits implements Serializable { task.call(); } } else { - ExecutorService executor = Executors.newFixedThreadPool(20); + ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get()); List> futureResults = null; futureResults = new ArrayList<>(); @@ -170,7 +169,10 @@ public class Traits implements Serializable { for (int f = 0; f < futureResults.size(); f++) { try { futureResults.get(f).get(); - } catch (InterruptedException | ExecutionException ex) { + } catch (InterruptedException ex) { + executor.shutdownNow(); + throw ex; + } catch (ExecutionException ex) { Logger.getLogger(Traits.class.getName()).log(Level.SEVERE, "Error during traits converting", ex); } } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/Action.java b/trunk/src/com/jpexs/decompiler/flash/action/Action.java index 815b2a132..7c1e2f8b1 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/Action.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/Action.java @@ -64,6 +64,7 @@ import com.jpexs.decompiler.graph.model.IfItem; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.decompiler.graph.model.NotItem; import com.jpexs.decompiler.graph.model.ScriptEndItem; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -705,7 +706,7 @@ public class Action implements GraphSourceItem { Throwable convertException = null; int timeout = Configuration.decompilationTimeoutSingleMethod.get(); try { - tree = Helper.timedCall(new Callable>() { + tree = CancellableWorker.call(new Callable>() { @Override public List call() throws Exception { int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; @@ -714,7 +715,7 @@ public class Action implements GraphSourceItem { return tree; } }, timeout, TimeUnit.SECONDS); - } catch (InterruptedException | TimeoutException | ExecutionException | OutOfMemoryError | TranslateException | StackOverflowError ex) { + } catch (TimeoutException | ExecutionException | OutOfMemoryError | TranslateException | StackOverflowError ex) { Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", ex); convertException = ex; if (ex instanceof ExecutionException && ex.getCause() instanceof Exception) { diff --git a/trunk/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java b/trunk/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java new file mode 100644 index 000000000..73446b5f2 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.action; + +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.regex.Pattern; + +/** + * + * @author JPEXS + */ +public class ActionDeobfuscation { + + private Random rnd = new Random(); + private final int DEFAULT_FOO_SIZE = 10; + public HashSet allVariableNamesStr = new HashSet<>(); + private HashMap typeCounts = new HashMap<>(); + + public static final String[] reservedWords = { + "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", + "extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof", + "interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public", + "return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", + "with", "dynamic", "default", "final", "in"}; + public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + public static final String validNextCharacters = validFirstCharacters + "0123456789"; + public static final String fooCharacters = "bcdfghjklmnpqrstvwz"; + public static final String fooJoinCharacters = "aeiouy"; + + private boolean isReserved(String s) { + for (String rw : reservedWords) { + if (rw.equals(s.trim())) { + return true; + } + } + return false; + } + + private String fooString(HashMap deobfuscated, String orig, boolean firstUppercase, int rndSize) { + boolean exists; + String ret; + loopfoo: + do { + exists = false; + int len = 3 + rnd.nextInt(rndSize - 3); + ret = ""; + for (int i = 0; i < len; i++) { + String c = ""; + if ((i % 2) == 0) { + c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length())); + } else { + c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length())); + } + if (i == 0 && firstUppercase) { + c = c.toUpperCase(Locale.ENGLISH); + } + ret += c; + } + if (allVariableNamesStr.contains(ret)) { + exists = true; + rndSize += 1; + continue loopfoo; + } + if (isReserved(ret)) { + exists = true; + rndSize += 1; + continue; + } + if (deobfuscated.containsValue(ret)) { + exists = true; + rndSize += 1; + continue; + } + } while (exists); + return ret; + } + + public void deobfuscateInstanceNames(HashMap namesMap, RenameType renameType, List tags, Map selected) { + for (Tag t : tags) { + if (t instanceof DefineSpriteTag) { + deobfuscateInstanceNames(namesMap, renameType, ((DefineSpriteTag) t).subTags, selected); + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + String name = po.getInstanceName(); + if (name != null) { + String changedName = deobfuscateName(name, false, "instance", namesMap, renameType, selected); + if (changedName != null) { + po.setInstanceName(changedName); + } + } + String className = po.getClassName(); + if (className != null) { + String changedClassName = deobfuscateNameWithPackage(className, namesMap, renameType, selected); + if (changedClassName != null) { + po.setClassName(changedClassName); + } + } + } + } + } + + public String deobfuscatePackage(String pkg, HashMap namesMap, RenameType renameType, Map selected) { + if (namesMap.containsKey(pkg)) { + return namesMap.get(pkg); + } + String[] parts = null; + if (pkg.contains(".")) { + parts = pkg.split("\\."); + } else { + parts = new String[]{pkg}; + } + String ret = ""; + boolean isChanged = false; + for (int p = 0; p < parts.length; p++) { + if (p > 0) { + ret += "."; + } + String partChanged = deobfuscateName(parts[p], false, "package", namesMap, renameType, selected); + if (partChanged != null) { + ret += partChanged; + isChanged = true; + } else { + ret += parts[p]; + } + } + if (isChanged) { + namesMap.put(pkg, ret); + return ret; + } + return null; + } + + public String deobfuscateNameWithPackage(String n, HashMap namesMap, RenameType renameType, Map selected) { + String pkg = null; + String name = ""; + if (n.contains(".")) { + pkg = n.substring(0, n.lastIndexOf('.')); + name = n.substring(n.lastIndexOf('.') + 1); + } else { + name = n; + } + boolean changed = false; + if ((pkg != null) && (!pkg.isEmpty())) { + String changedPkg = deobfuscatePackage(pkg, namesMap, renameType, selected); + if (changedPkg != null) { + changed = true; + pkg = changedPkg; + } + } + String changedName = deobfuscateName(name, true, "class", namesMap, renameType, selected); + if (changedName != null) { + changed = true; + name = changedName; + } + if (changed) { + String newClassName = ""; + if (pkg == null) { + newClassName = name; + } else { + newClassName = pkg + "." + name; + } + return newClassName; + } + return null; + } + + public String deobfuscateName(String s, boolean firstUppercase, String usageType, HashMap namesMap, RenameType renameType, Map selected) { + boolean isValid = true; + if (usageType == null) { + usageType = "name"; + } + + if (selected != null) { + if (selected.containsKey(s)) { + return selected.get(s); + } + } + + if (isReserved(s)) { + isValid = false; + } + + if (isValid) { + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) > 127) { + isValid = false; + break; + } + } + } + + if (isValid) { + Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$"); + if (!pat.matcher(s).matches()) { + isValid = false; + } + } + if (!isValid) { + if (namesMap.containsKey(s)) { + return namesMap.get(s); + } else { + Integer cnt = typeCounts.get(usageType); + if (cnt == null) { + cnt = 0; + } + + String ret = null; + if (renameType == RenameType.TYPENUMBER) { + + boolean found; + do { + found = false; + cnt++; + ret = usageType + "_" + cnt; + found = allVariableNamesStr.contains(ret); + } while (found); + typeCounts.put(usageType, cnt); + } else if (renameType == RenameType.RANDOMWORD) { + ret = fooString(namesMap, s, firstUppercase, DEFAULT_FOO_SIZE); + } + namesMap.put(s, ret); + return ret; + } + } + return null; + } + +} diff --git a/trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java b/trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java index 7baa8a9e7..01f66bf7d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/ActionListReader.java @@ -43,6 +43,7 @@ import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.NotCompileTimeItem; import com.jpexs.decompiler.graph.TranslateException; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import com.jpexs.helpers.MemoryInputStream; import java.io.IOException; @@ -54,6 +55,10 @@ import java.util.Map; import java.util.Queue; import java.util.Scanner; import java.util.Stack; +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; @@ -80,7 +85,41 @@ public class ActionListReader { * @return List of actions * @throws IOException */ - public static List readActionList(List listeners, long containerSWFOffset, MemoryInputStream mis, int version, int ip, int endIp, String path) throws IOException { + public static List readActionListTimeout(final List listeners, final long containerSWFOffset, final MemoryInputStream mis, final int version, final int ip, final int endIp, final String path) throws IOException, InterruptedException, TimeoutException { + try { + return CancellableWorker.call(new Callable>() { + + @Override + public List call() throws IOException, InterruptedException { + return readActionList(listeners, containerSWFOffset, mis, version, ip, endIp, path); + } + }, Configuration.decompilationTimeoutSingleMethod.get(), TimeUnit.SECONDS); + } catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof InterruptedException) { + throw (InterruptedException) cause; + } else if (cause instanceof InterruptedException) { + throw (IOException) cause; + } else { + Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, ex); + } + } + return null; + } + /** + * Reads list of actions from the stream. Reading ends with + * ActionEndFlag(=0) or end of the stream. + * @param listeners + * @param containerSWFOffset + * @param mis + * @param version + * @param ip + * @param endIp + * @param path + * @return List of actions + * @throws IOException + */ + public static List readActionList(List listeners, long containerSWFOffset, MemoryInputStream mis, int version, int ip, int endIp, String path) throws IOException, InterruptedException { boolean deobfuscate = Configuration.autoDeobfuscate.get(); ConstantPool cpool = new ConstantPool(); @@ -151,11 +190,7 @@ public class ActionListReader { if (deobfuscate) { try { - try { - actions = deobfuscateActionList(listeners, containerSWFOffset, actions, version, ip, path); - } catch (InterruptedException ex) { - Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, ex); - } + actions = deobfuscateActionList(listeners, containerSWFOffset, actions, version, ip, path); updateActionLengths(actions, version); removeZeroJumps(actions, version); } catch (TranslateException ex) { @@ -680,6 +715,10 @@ public class ActionListReader { Scanner sc = new Scanner(System.in); loopip: while (((endip == -1) || (endip > ip)) && (a = actions.get(pos)) != null) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + int actionLen = getTotalActionLength(a); pos += actionLen; if (!visited.containsKey(ip)) { diff --git a/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java index d66b0626b..fe5dbaa84 100644 --- a/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -53,6 +53,8 @@ public class Configuration { public static final ConfigurationItem decompile = null; @ConfigurationDefaultBoolean(true) public static final ConfigurationItem parallelSpeedUp = null; + @ConfigurationDefaultInt(20) + public static final ConfigurationItem parallelThreadCount = null; @ConfigurationDefaultBoolean(true) public static final ConfigurationItem autoDeobfuscate = null; @ConfigurationDefaultBoolean(true) diff --git a/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index f74bb0fe7..33dbed5e7 100644 --- a/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -94,6 +94,10 @@ public class CommandLineArgumentParser { System.out.println(" ...error handling mode. \"abort\" stops the exporting, \"retry\" tries the exporting N times, \"ignore\" ignores the current file"); System.out.println(" 11) -timeout N"); System.out.println(" ...decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds"); + System.out.println(" 12) -exportTimeout N"); + System.out.println(" ...total export timeout in seconds"); + System.out.println(" 13) -exportFileTimeout N"); + System.out.println(" ...export timeout for a single AS3 class in seconds"); System.out.println(); System.out.println("Examples:"); System.out.println("java -jar ffdec.jar myfile.swf"); @@ -125,6 +129,9 @@ public class CommandLineArgumentParser { OUTER: while (true) { nextParam = args.remove(); + if (nextParam != null) { + nextParam = nextParam.toLowerCase(); + } switch (nextParam) { case "-config": parseConfig(args); @@ -140,6 +147,12 @@ public class CommandLineArgumentParser { case "-timeout": parseTimeout(args); break; + case "-exporttimeout": + parseExportTimeout(args); + break; + case "-exportfiletimeout": + parseExportFileTimeout(args); + break; case "-affinity": parseAffinity(args); break; @@ -320,6 +333,32 @@ public class CommandLineArgumentParser { } } + private static void parseExportTimeout(Queue args) { + if (args.isEmpty()) { + System.err.println("timeout parameter expected"); + badArguments(); + } + try { + int timeout = Integer.parseInt(args.remove()); + Configuration.exportTimeout.set(timeout); + } catch (NumberFormatException nex) { + System.err.println("Bad timeout value"); + } + } + + private static void parseExportFileTimeout(Queue args) { + if (args.isEmpty()) { + System.err.println("timeout parameter expected"); + badArguments(); + } + try { + int timeout = Integer.parseInt(args.remove()); + Configuration.decompilationTimeoutFile.set(timeout); + } catch (NumberFormatException nex) { + System.err.println("Bad timeout value"); + } + } + private static void parseAffinity(Queue args) { if (Platform.isWindows()) { if (args.isEmpty()) { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/Main.java b/trunk/src/com/jpexs/decompiler/flash/gui/Main.java index 909d611cb..b39aa4077 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/Main.java @@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.console.ContextMenuTools; import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; import com.jpexs.helpers.Cache; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import com.jpexs.helpers.ProgressListener; import com.jpexs.helpers.streams.SeekableInputStream; @@ -151,17 +152,17 @@ public class Main { startWork(name, percent, null); } - public static void startWork(String name, Runnable cancelCallback) { - startWork(name, -1, cancelCallback); + public static void startWork(String name, CancellableWorker worker) { + startWork(name, -1, worker); } - public static void startWork(final String name, final int percent, final Runnable cancelCallback) { + public static void startWork(final String name, final int percent, final CancellableWorker worker) { working = true; View.execInEventDispatchLater(new Runnable() { @Override public void run() { if (mainFrame != null) { - mainFrame.setWorkStatus(name, cancelCallback); + mainFrame.setWorkStatus(name, worker); if (percent == -1) { mainFrame.hidePercent(); } else { @@ -186,12 +187,17 @@ public class Main { public static void stopWork() { working = false; - if (mainFrame != null) { - mainFrame.setWorkStatus("", null); - } - if (loadingDialog != null) { - loadingDialog.setDetail(""); - } + View.execInEventDispatchLater(new Runnable() { + @Override + public void run() { + if (mainFrame != null) { + mainFrame.setWorkStatus("", null); + } + if (loadingDialog != null) { + loadingDialog.setDetail(""); + } + } + }); } public static SWF parseSWF(String file) throws Exception { @@ -302,6 +308,7 @@ public class Main { } public static boolean reloadSWF() { + CancellableWorker.cancelBackgroundThreads(); if (Main.inputStream == null) { mainFrame.setVisible(false); Helper.emptyObject(mainFrame); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index aaec53589..24ea15562 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -101,9 +101,8 @@ import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.TEXTRECORD; import com.jpexs.decompiler.graph.ExportMode; -import com.jpexs.helpers.AsyncResult; import com.jpexs.helpers.Cache; -import com.jpexs.helpers.Callback; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import com.jpexs.process.ProcessTools; import java.awt.BorderLayout; @@ -167,8 +166,7 @@ import java.util.Stack; import java.util.Timer; import java.util.TimerTask; import java.util.TreeSet; -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; +import java.util.concurrent.CancellationException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.Box; @@ -198,7 +196,6 @@ import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.border.BevelBorder; import javax.swing.event.DocumentEvent; @@ -247,7 +244,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T public JLabel statusLabel = new JLabel(""); public JPanel statusPanel = new JPanel(); public JButton cancelButton = new JButton(); - public Runnable cancelButtonPressed; + public CancellableWorker currentWorker; public JProgressBar progressBar = new JProgressBar(0, 100); private DeobfuscationDialog deobfuscationDialog; public JTree tagTree; @@ -310,7 +307,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T public static final String INTERNAL_VIEWER_CARD = "INTERNALVIEWER"; private Map sourceFontsMap = new HashMap<>(); private AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); - private FutureTask setSourceTask; + private CancellableWorker setSourceWorker; public void setPercent(int percent) { progressBar.setValue(percent); @@ -346,15 +343,15 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T statusLabel.setText(s); } - public void setWorkStatus(String s, Runnable cancelCallback) { + public void setWorkStatus(String s, CancellableWorker worker) { if (s.isEmpty()) { loadingPanel.setVisible(false); } else { loadingPanel.setVisible(true); } statusLabel.setText(s); - cancelButtonPressed = cancelCallback; - cancelButton.setVisible(cancelCallback != null); + currentWorker = worker; + cancelButton.setVisible(worker != null); } private String fixCommandTitle(String title) { @@ -564,7 +561,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T RibbonApplicationMenu mainMenu = new RibbonApplicationMenu(); RibbonApplicationMenuEntryPrimary exportFlaMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportfla32"), translate("menu.file.export.fla"), new ActionRedirector(this, "EXPORTFLA"), CommandButtonKind.ACTION_ONLY); - RibbonApplicationMenuEntryPrimary exportAllMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("export32"), translate("menu.file.export.all"), new ActionRedirector(this, "EXPORTSEL"), CommandButtonKind.ACTION_ONLY); + RibbonApplicationMenuEntryPrimary exportAllMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("export32"), translate("menu.file.export.all"), new ActionRedirector(this, "EXPORT"), CommandButtonKind.ACTION_ONLY); RibbonApplicationMenuEntryPrimary exportSelMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportsel32"), translate("menu.file.export.selection"), new ActionRedirector(this, "EXPORTSEL"), CommandButtonKind.ACTION_ONLY); RibbonApplicationMenuEntryPrimary checkUpdatesMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("update32"), translate("menu.help.checkupdates"), new ActionRedirector(this, "CHECKUPDATES"), CommandButtonKind.ACTION_ONLY); RibbonApplicationMenuEntryPrimary aboutMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("about32"), translate("menu.help.about"), new ActionRedirector(this, "ABOUT"), CommandButtonKind.ACTION_ONLY); @@ -1111,8 +1108,8 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T @Override public void actionPerformed(ActionEvent e) { - if (cancelButtonPressed != null) { - cancelButtonPressed.run(); + if (currentWorker != null) { + currentWorker.cancel(true); } } }); @@ -2463,9 +2460,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T final String txt = searchDialog.searchField.getText(); if (!txt.isEmpty()) { if (abcPanel != null) { - (new Thread() { + new CancellableWorker() { @Override - public void run() { + protected Void doInBackground() throws Exception { if (abcPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { View.execInEventDispatch(new Runnable() { @Override @@ -2477,12 +2474,13 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T } else { View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); } + return null; } - }).start(); + }.execute(); } else { - (new Thread() { + new CancellableWorker() { @Override - public void run() { + protected Void doInBackground() { if (actionPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { View.execInEventDispatch(new Runnable() { @Override @@ -2493,8 +2491,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T } else { View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); } + return null; } - }).start(); + }.execute(); } } } @@ -2646,14 +2645,19 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T if (swf.fileAttributes.actionScript3) { final int multiName = abcPanel.decompiledTextArea.getMultinameUnderCursor(); if (multiName > 0) { - (new Thread() { + new CancellableWorker() { @Override - public void run() { + public Void doInBackground() throws Exception { Main.startWork(translate("work.renaming") + "..."); renameMultiname(multiName); + return null; + } + + @Override + protected void done() { Main.stopWork(); } - }).start(); + }.execute(); } else { View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); @@ -2661,18 +2665,23 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T } else { final String identifier = actionPanel.getStringUnderCursor(); if (identifier != null) { - (new Thread() { + new CancellableWorker() { @Override - public void run() { + public Void doInBackground() throws Exception { Main.startWork(translate("work.renaming") + "..."); try { renameIdentifier(identifier); } catch (InterruptedException ex) { Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex); } + return null; + } + + @Override + protected void done() { Main.stopWork(); } - }).start(); + }.execute(); } else { View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); } @@ -2760,9 +2769,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T } } final File selfile = sf; - (new Thread() { + new CancellableWorker() { @Override - public void run() { + protected Void doInBackground() throws Exception { Helper.freeMem(); try { if (compressed) { @@ -2774,9 +2783,14 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); } Helper.freeMem(); + return null; + } + + @Override + protected void done() { Main.stopWork(); } - }).start(); + }.execute(); } break; case "EXPORTSEL": @@ -2798,9 +2812,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T final boolean isMp3OrWav = export.getOption(ExportDialog.OPTION_SOUNDS) == 0; final boolean isFormatted = export.getOption(ExportDialog.OPTION_TEXTS) == 1; final boolean onlySel = e.getActionCommand().endsWith("SEL"); - (new Thread() { + new CancellableWorker() { @Override - public void run() { + public Void doInBackground() throws Exception { try { if (onlySel) { exportSelection(errorHandler, selFile, export); @@ -2817,13 +2831,24 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Error during export", ex); View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage()); } + return null; + } + + @Override + protected void done() { Main.stopWork(); long timeAfter = System.currentTimeMillis(); - long timeMs = timeAfter - timeBefore; + final long timeMs = timeAfter - timeBefore; - setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); + View.execInEventDispatchLater(new Runnable() { + + @Override + public void run() { + setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); + } + }); } - }).start(); + }.execute(); } } @@ -2868,7 +2893,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T Main.startWork(translate("work.restoringControlFlow")); final boolean all = e.getActionCommand().endsWith("ALL"); if ((!all) || confirmExperimental()) { - new SwingWorker() { + new CancellableWorker() { @Override protected Object doInBackground() throws Exception { int cnt = 0; @@ -2883,12 +2908,23 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T } abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc, abcPanel.decompiledTextArea.getCurrentTrait()); } - Main.stopWork(); - View.showMessageDialog(null, "Control flow restored"); - abcPanel.reload(); - doFilter(); return true; } + + @Override + protected void done() { + Main.stopWork(); + View.showMessageDialog(null, translate("work.restoringControlFlow.complete")); + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + abcPanel.reload(); + doFilter(); + } + }); + } }.execute(); } break; @@ -2897,29 +2933,39 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T final RenameType renameType = new RenameDialog().display(); if (renameType != null) { Main.startWork(translate("work.renaming.identifiers") + "..."); - new SwingWorker() { + new CancellableWorker() { @Override - protected Object doInBackground() throws Exception { - try { - int cnt = swf.deobfuscateIdentifiers(renameType); - Main.stopWork(); - View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", "" + cnt)); - swf.assignClassesToSymbols(); - clearCache(); - if (abcPanel != null) { - abcPanel.reload(); + protected Integer doInBackground() throws Exception { + int cnt = swf.deobfuscateIdentifiers(renameType); + return cnt; + } + + @Override + protected void done() { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + try { + int cnt = get(); + Main.stopWork(); + View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", "" + cnt)); + swf.assignClassesToSymbols(); + clearCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + doFilter(); + reload(true); + } catch (Exception ex) { + Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Error during renaming identifiers", ex); + Main.stopWork(); + View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); + } } - doFilter(); - reload(true); - } catch (Exception ex) { - Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Error during renaming identifiers", ex); - Main.stopWork(); - View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); - } - return true; + }); } }.execute(); - } } break; @@ -2931,7 +2977,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T deobfuscationDialog.setVisible(true); if (deobfuscationDialog.ok) { Main.startWork(translate("work.deobfuscating") + "..."); - new SwingWorker() { + new CancellableWorker() { @Override protected Object doInBackground() throws Exception { try { @@ -2964,12 +3010,23 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T } catch (Exception ex) { Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Deobfuscation error", ex); } + return true; + } + + @Override + protected void done() { Main.stopWork(); View.showMessageDialog(null, translate("work.deobfuscating.complete")); - clearCache(); - abcPanel.reload(); - doFilter(); - return true; + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + clearCache(); + abcPanel.reload(); + doFilter(); + } + }); } }.execute(); } @@ -3076,22 +3133,14 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T oldValue = tagObj; if (tagObj instanceof ScriptPack) { final ScriptPack scriptLeaf = (ScriptPack) tagObj; + if (setSourceWorker != null) { + setSourceWorker.cancel(true); + } if (!Main.isWorking()) { - Main.startWork(translate("work.decompiling") + "...", new Runnable() { + CancellableWorker worker = new CancellableWorker() { @Override - public void run() { - if (setSourceTask != null) { - setSourceTask.cancel(true); - } - abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("work.canceled")); - } - }); - - FutureTask task = Helper.callAsync(new Callable() { - - @Override - public Void call() throws Exception { + protected Void doInBackground() throws Exception { int classIndex = -1; for (Trait t : scriptLeaf.abc.script_info[scriptLeaf.scriptIndex].traits.traits) { if (t instanceof TraitClass) { @@ -3108,18 +3157,29 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T abcPanel.decompiledTextArea.setNoTrait(); return null; } - }, new Callback>() { @Override - public void call(AsyncResult result) { - setSourceTask = null; - if (result.error != null) { - abcPanel.decompiledTextArea.setText("//Decompilation error: " + result.error); - } + protected void done() { Main.stopWork(); + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + try { + get(); + } catch (CancellationException ex) { + abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("work.canceled")); + } catch (Exception ex) { + abcPanel.decompiledTextArea.setText("//Decompilation error: " + ex); + } + } + }); } - }); - setSourceTask = task; + }; + worker.execute(); + setSourceWorker = worker; + Main.startWork(translate("work.decompiling") + "...", worker); } showDetail(DETAILCARDAS3NAVIGATOR); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/View.java b/trunk/src/com/jpexs/decompiler/flash/gui/View.java index f4d063b52..c5452489b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/View.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/View.java @@ -220,7 +220,8 @@ public class View { } else { try { SwingUtilities.invokeAndWait(r); - } catch (InterruptedException | InvocationTargetException ex) { + } catch (InterruptedException ex) { + } catch (InvocationTargetException ex) { Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); } } diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 67f6211dc..e770a3bc4 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -53,6 +53,7 @@ import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; import com.jpexs.decompiler.flash.helpers.Freed; import com.jpexs.decompiler.flash.helpers.collections.MyEntry; import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import java.awt.BorderLayout; import java.awt.Component; @@ -64,10 +65,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; -import java.util.concurrent.FutureTask; -import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -131,21 +129,11 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Fr decAdd = ", " + AppStrings.translate("work.decompiling"); } - final AtomicReference> taskRef = new AtomicReference<>(); - Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + item.key.toString() + "... ", new Runnable() { - - @Override - public void run() { - FutureTask task = taskRef.get(); - if (task != null) { - task.cancel(true); - } - } - }); try { - Helper.cancellableCall(new Callable() { + CancellableWorker worker = new CancellableWorker() { + @Override - public Void call() throws Exception { + public Void doInBackground() throws Exception { decompiledTextArea.cacheScriptPack(item.value, list); if (pat.matcher(decompiledTextArea.getCachedText(item.value)).find()) { found.add(item.value); @@ -153,7 +141,10 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Fr } return null; } - }, taskRef); + }; + worker.execute(); + Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + item.key.toString() + "... ", worker); + worker.get(); } catch (InterruptedException ex) { break; } catch (ExecutionException ex) { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java index 746c2ec65..efae8c66b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java @@ -223,6 +223,7 @@ public class ASMSourceEditorPane extends LineMarkedEditorPane implements CaretLi abc.bodies[bodyIndex].code = acode; } } catch (IOException ex) { + } catch (InterruptedException ex) { } catch (ParseException ex) { View.showMessageDialog(this, (ex.text + " on line " + ex.line)); selectLine((int) ex.line); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ClassesListTree.java b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ClassesListTree.java index e1a09fdd1..a58c61b41 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ClassesListTree.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ClassesListTree.java @@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.helpers.collections.MyEntry; import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.helpers.CancellableWorker; import java.util.ArrayList; import java.util.List; import javax.swing.JTree; @@ -127,9 +128,9 @@ public final class ClassesListTree extends JTree implements TreeSelectionListene final ScriptPack scriptLeaf = (ScriptPack) item; if (!Main.isWorking()) { Main.startWork(AppStrings.translate("work.decompiling") + "..."); - (new Thread() { + new CancellableWorker() { @Override - public void run() { + public Void doInBackground() throws Exception { int classIndex = -1; for (Trait t : scriptLeaf.abc.script_info[scriptLeaf.scriptIndex].traits.traits) { if (t instanceof TraitClass) { @@ -144,9 +145,14 @@ public final class ClassesListTree extends JTree implements TreeSelectionListene abcPanel.decompiledTextArea.setClassIndex(classIndex); abcPanel.decompiledTextArea.setNoTrait(); abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); + return null; + } + + @Override + protected void done() { Main.stopWork(); } - }).start(); + }.execute(); } } diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index a2531cc04..46963c89d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -38,8 +38,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.swing.SwingUtilities; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; @@ -488,7 +486,6 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL cacheScriptPack(scriptLeaf, abcList); cd = getCached(scriptLeaf); } catch (InterruptedException ex) { - Logger.getLogger(DecompiledEditorPane.class.getName()).log(Level.SEVERE, null, ex); } if (cd != null) { final String hilightedCode = cd.text; diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index 263bb2444..47e9d2ea6 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -41,9 +41,8 @@ import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.graph.ExportMode; import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.helpers.AsyncResult; import com.jpexs.helpers.Cache; -import com.jpexs.helpers.Callback; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import java.awt.BorderLayout; import java.awt.FlowLayout; @@ -59,8 +58,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; +import java.util.concurrent.CancellationException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -119,7 +117,7 @@ public class ActionPanel extends JPanel implements ActionListener { private boolean searchIgnoreCase; private boolean searchRegexp; private Cache cache = Cache.getInstance(true); - private FutureTask setSourceTask; + private CancellableWorker setSourceWorker; public void clearCache() { cache.clear(); @@ -287,7 +285,11 @@ public class ActionPanel extends JPanel implements ActionListener { DisassemblyListener listener = getDisassemblyListener(); asm.addDisassemblyListener(listener); HilightedTextWriter writer = new HilightedTextWriter(true); - asm.getASMSource(SWF.DEFAULT_VERSION, exportMode, writer, lastCode); + try { + asm.getASMSource(SWF.DEFAULT_VERSION, exportMode, writer, lastCode); + } catch (InterruptedException ex) { + Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); + } asm.removeDisassemblyListener(listener); return new HilightedText(writer); } @@ -339,27 +341,17 @@ public class ActionPanel extends JPanel implements ActionListener { } public void setSource(final ASMSource src, final boolean useCache) { - if (setSourceTask != null) { - setSourceTask.cancel(true); + if (setSourceWorker != null) { + setSourceWorker.cancel(true); } this.src = src; - Main.startWork(AppStrings.translate("work.decompiling") + "...", new Runnable() { - - @Override - public void run() { - if (setSourceTask != null) { - setSourceTask.cancel(true); - } - editor.setText("; " + AppStrings.translate("work.canceled")); - } - }); final ASMSource asm = (ASMSource) src; - FutureTask task = Helper.callAsync(new Callable() { + CancellableWorker worker = new CancellableWorker() { @Override - public Void call() throws Exception { + protected Void doInBackground() throws Exception { editor.setText("; " + AppStrings.translate("work.disassembling") + "..."); if (Configuration.decompile.get()) { decompiledEditor.setText("//" + AppStrings.translate("work.waitingfordissasembly") + "..."); @@ -389,20 +381,30 @@ public class ActionPanel extends JPanel implements ActionListener { setDecompiledEditMode(false); return null; } - }, new Callback>() { @Override - public void call(AsyncResult result) { - setSourceTask = null; - if (result.error != null) { - decompiledEditor.setText("//Decompilation error: " + result.error); - } + protected void done() { + setSourceWorker = null; Main.stopWork(); + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + try { + get(); + } catch (CancellationException ex) { + editor.setText("; " + AppStrings.translate("work.canceled")); + } catch (Exception ex) { + decompiledEditor.setText("//Decompilation error: " + ex); + } + } + }); } - - }); - - setSourceTask = task; + }; + worker.execute(); + setSourceWorker = worker; + Main.startWork(AppStrings.translate("work.decompiling") + "...", worker); } public void hilightOffset(long offset) { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index f3fde224f..b6a9990a2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -377,4 +377,5 @@ menu.advancedsettings.advancedsettings = Advanced Settings menu.recentFiles = Recent files #after version 1.7.4 +work.restoringControlFlow.complete = Control flow restored diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_hu.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_hu.properties index bd642f8b2..bcc08dda1 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_hu.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_hu.properties @@ -375,3 +375,6 @@ work.canceled = Megszak\u00edtva work.restoringControlFlow = Vez\u00e9rl\u00e9si-folyam helyre\u00e1ll\u00edt\u00e1s menu.advancedsettings.advancedsettings = Halad\u00f3 be\u00e1ll\u00edt\u00e1sok menu.recentFiles = El\u0151zm\u00e9nyek + +#after version 1.7.4 +work.restoringControlFlow.complete = Vez\u00e9rl\u00e9si-folyam helyre\u00e1ll\u00edt\u00e1s befejez\u0151d\u00f6tt diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 4c1e0cb12..9de31d2b8 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -139,7 +139,7 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT * @return ASM source */ @Override - public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) { + public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { if (actions == null) { actions = getActions(version); } @@ -163,7 +163,7 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT * @return List of actions */ @Override - public List getActions(int version) { + public List getActions(int version) throws InterruptedException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int prevLength = 0; @@ -176,8 +176,10 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); rri.seek(prevLength); - List list = ActionListReader.readActionList(listeners, getPos() + hdrSize - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); + List list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrSize - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); return list; + } catch (InterruptedException ex) { + throw ex; } catch (Exception ex) { Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); return new ArrayList<>(); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index a1372dc10..cd2e48ae4 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -192,7 +192,7 @@ public class DefineSpriteTag extends CharacterTag implements Container, BoundedT * @param skipUnusualTags * @throws IOException */ - public DefineSpriteTag(SWF swf, byte[] data, int version, int level, long pos, boolean parallel, boolean skipUnusualTags) throws IOException { + public DefineSpriteTag(SWF swf, byte[] data, int version, int level, long pos, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException { super(swf, ID, "DefineSprite", data, pos); SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), version, pos); spriteId = sis.readUI16(); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java index e246cf432..5bd47de6d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DoActionTag.java @@ -76,7 +76,7 @@ public class DoActionTag extends Tag implements ASMSource { * @return ASM source */ @Override - public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) { + public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { if (actions == null) { actions = getActions(version); } @@ -104,7 +104,7 @@ public class DoActionTag extends Tag implements ASMSource { } @Override - public List getActions(int version) { + public List getActions(int version) throws InterruptedException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int prevLength = 0; @@ -119,8 +119,10 @@ public class DoActionTag extends Tag implements ASMSource { baos.write(actionBytes); MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); rri.seek(prevLength); - List list = ActionListReader.readActionList(listeners, getPos() - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); + List list = ActionListReader.readActionListTimeout(listeners, getPos() - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); return list; + } catch (InterruptedException ex) { + throw ex; } catch (Exception ex) { Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); return new ArrayList<>(); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java index a637cfdf5..9fef17bbf 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java @@ -103,7 +103,7 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource { * @return ASM source */ @Override - public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) { + public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { if (actions == null) { actions = getActions(version); } @@ -111,7 +111,7 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource { } @Override - public List getActions(int version) { + public List getActions(int version) throws InterruptedException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int prevLength = 0; @@ -129,8 +129,10 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource { baos.write(actionBytes); MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); rri.seek(prevLength); - List list = ActionListReader.readActionList(listeners, getPos() + 2 - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); + List list = ActionListReader.readActionListTimeout(listeners, getPos() + 2 - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/); return list; + } catch (InterruptedException ex) { + throw ex; } catch (Exception ex) { Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); return new ArrayList<>(); diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java b/trunk/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java index a419e25aa..b6d348e6d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java @@ -36,7 +36,7 @@ public interface ASMSource { * @param exportMode PCode or hex? * @return ASM source */ - public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions); + public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException; /** * Whether or not this object contains ASM source @@ -51,7 +51,7 @@ public interface ASMSource { * @param version Version * @return List of actions */ - public List getActions(int version); + public List getActions(int version) throws InterruptedException; /** * Sets actions associated with this object diff --git a/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java b/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java index 21abcd30f..7965a4d7c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java @@ -148,7 +148,7 @@ public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem { * @return ASM source */ @Override - public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) { + public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { if (actions == null) { actions = getActions(version); } @@ -172,11 +172,13 @@ public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem { * @return List of actions */ @Override - public List getActions(int version) { + public List getActions(int version) throws InterruptedException { try { - List list = ActionListReader.readActionList(listeners, getPos() + 4, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/); + List list = ActionListReader.readActionListTimeout(listeners, getPos() + 4, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/); return list; + } catch (InterruptedException ex) { + throw ex; } catch (Exception ex) { Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex); return new ArrayList<>(); diff --git a/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java index 5de93fe96..24d4a105c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java @@ -153,7 +153,7 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem { * @return ASM source */ @Override - public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) { + public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { if (actions == null) { actions = getActions(version); } @@ -171,10 +171,12 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem { } @Override - public List getActions(int version) { + public List getActions(int version) throws InterruptedException { try { - List list = ActionListReader.readActionList(listeners, getPos() + hdrPos, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/); + List list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrPos, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/); return list; + } catch (InterruptedException ex) { + throw ex; } catch (Exception ex) { Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex); return new ArrayList<>(); diff --git a/trunk/src/com/jpexs/decompiler/graph/Graph.java b/trunk/src/com/jpexs/decompiler/graph/Graph.java index d938af670..b2061abe1 100644 --- a/trunk/src/com/jpexs/decompiler/graph/Graph.java +++ b/trunk/src/com/jpexs/decompiler/graph/Graph.java @@ -103,6 +103,10 @@ public class Graph { } private boolean fixGraphOnce(List localData, GraphPart part, List visited, boolean doChildren) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + if (visited.contains(part)) { return false; } @@ -1358,7 +1362,7 @@ public class Graph { } protected List printGraph(List visited, List localData, Stack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { - if (Thread.interrupted()) { + if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } if (stopPart == null) { @@ -2034,7 +2038,7 @@ public class Graph { protected void checkGraph(List allBlocks) { } - private List makeGraph(GraphSource code, List allBlocks, List alternateEntries) { + private List makeGraph(GraphSource code, List allBlocks, List alternateEntries) throws InterruptedException { HashMap> refs = code.visitCode(alternateEntries); List ret = new ArrayList<>(); boolean[] visited = new boolean[code.size()]; @@ -2052,7 +2056,10 @@ public class Graph { return ip; } - private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startip, int lastIp, List allBlocks, HashMap> refs, boolean[] visited2) { + private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startip, int lastIp, List allBlocks, HashMap> refs, boolean[] visited2) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } int ip = startip; for (GraphPart p : allBlocks) { diff --git a/trunk/src/com/jpexs/decompiler/graph/GraphPart.java b/trunk/src/com/jpexs/decompiler/graph/GraphPart.java index 6f05914ca..a21275278 100644 --- a/trunk/src/com/jpexs/decompiler/graph/GraphPart.java +++ b/trunk/src/com/jpexs/decompiler/graph/GraphPart.java @@ -61,7 +61,7 @@ public class GraphPart implements Serializable { } private boolean leadsTo(List localData, Graph gr, GraphSource code, GraphPart part, List visited, List loops) throws InterruptedException { - if (Thread.interrupted()) { + if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } diff --git a/trunk/src/com/jpexs/decompiler/graph/GraphSource.java b/trunk/src/com/jpexs/decompiler/graph/GraphSource.java index c0682b10a..a6d018dbe 100644 --- a/trunk/src/com/jpexs/decompiler/graph/GraphSource.java +++ b/trunk/src/com/jpexs/decompiler/graph/GraphSource.java @@ -37,7 +37,10 @@ public abstract class GraphSource implements Serializable { public abstract List translatePart(GraphPart part, List localData, Stack stack, int start, int end, int staticOperation, String path) throws InterruptedException; - private void visitCode(int ip, int lastIp, HashMap> refs, int endIp) { + private void visitCode(int ip, int lastIp, HashMap> refs, int endIp) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } boolean debugMode = false; while (((endIp == -1) || (ip < endIp)) && (ip < size())) { refs.get(ip).add(lastIp); @@ -87,7 +90,7 @@ public abstract class GraphSource implements Serializable { }; } - public HashMap> visitCode(List alternateEntries) { + public HashMap> visitCode(List alternateEntries) throws InterruptedException { HashMap> refs = new HashMap<>(); int siz = size(); for (int i = 0; i < siz; i++) { diff --git a/trunk/src/com/jpexs/decompiler/graph/model/CommentItem.java b/trunk/src/com/jpexs/decompiler/graph/model/CommentItem.java index 72bd6c021..afc3c9127 100644 --- a/trunk/src/com/jpexs/decompiler/graph/model/CommentItem.java +++ b/trunk/src/com/jpexs/decompiler/graph/model/CommentItem.java @@ -18,7 +18,6 @@ package com.jpexs.decompiler.graph.model; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.GraphTargetItem; -import static com.jpexs.decompiler.graph.GraphTargetItem.NOPRECEDENCE; /** * diff --git a/trunk/src/com/jpexs/helpers/AsyncResult.java b/trunk/src/com/jpexs/helpers/AsyncResult.java deleted file mode 100644 index baee87ae8..000000000 --- a/trunk/src/com/jpexs/helpers/AsyncResult.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010-2013 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.helpers; - -/** - * Class with helper method - * - * @author JPEXS - */ -public class AsyncResult { - - public T result; - - public Throwable error; - - public AsyncResult(T result, Throwable error) { - this.result = result; - this.error = error; - } -} diff --git a/trunk/src/com/jpexs/helpers/Cache.java b/trunk/src/com/jpexs/helpers/Cache.java index 4ce67f48b..72e564c5a 100644 --- a/trunk/src/com/jpexs/helpers/Cache.java +++ b/trunk/src/com/jpexs/helpers/Cache.java @@ -162,8 +162,7 @@ public class Cache { cacheFiles.put(key, temp); - - } catch (Exception ex) { + } catch (IOException ex) { //ignore } } else if (storageType == STORAGE_MEMORY) { diff --git a/trunk/src/com/jpexs/helpers/CancellableWorker.java b/trunk/src/com/jpexs/helpers/CancellableWorker.java new file mode 100644 index 000000000..4572d5852 --- /dev/null +++ b/trunk/src/com/jpexs/helpers/CancellableWorker.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010-2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.helpers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * + * @author JPEXS + */ +public abstract class CancellableWorker implements RunnableFuture { + + private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(); + private static List workers = Collections.synchronizedList(new ArrayList()); + private final FutureTask future; + + public CancellableWorker() { + super(); + Callable callable = + new Callable() { + @Override + public T call() throws Exception { + return doInBackground(); + } + }; + + future = new FutureTask(callable) { + @Override + protected void done() { + workerDone(); + } + }; + } + + protected abstract T doInBackground() throws Exception; + + @Override + public final void run() { + workers.add(this); + future.run(); + } + + protected void done() { + } + + public final void execute() { + THREAD_POOL.execute(this); + } + + @Override + public final boolean cancel(boolean mayInterruptIfRunning) { + return future.cancel(mayInterruptIfRunning); + } + + @Override + public final boolean isCancelled() { + return future.isCancelled(); + } + + @Override + public final boolean isDone() { + return future.isDone(); + } + + @Override + public final T get() throws InterruptedException, ExecutionException { + return future.get(); + } + + @Override + public final T get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { + return future.get(timeout, unit); + } + + private void workerDone() { + workers.remove(this); + done(); + } + + public static T call(final Callable c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException { + CancellableWorker worker = new CancellableWorker() { + + @Override + protected T doInBackground() throws Exception { + return c.call(); + } + }; + try { + worker.execute(); + return worker.get(timeout, timeUnit); + } finally { + worker.cancel(true); + } + } + + public static void cancelBackgroundThreads() { + List oldWorkers = workers; + workers = new ArrayList<>(); + for (CancellableWorker worker : oldWorkers) { + worker.cancel(true); + } + } +} diff --git a/trunk/src/com/jpexs/helpers/Helper.java b/trunk/src/com/jpexs/helpers/Helper.java index a4ef1c2e3..63cc41530 100644 --- a/trunk/src/com/jpexs/helpers/Helper.java +++ b/trunk/src/com/jpexs/helpers/Helper.java @@ -41,15 +41,6 @@ import java.util.Collection; import java.util.List; import java.util.Scanner; import java.util.Stack; -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -62,8 +53,6 @@ import java.util.regex.Pattern; */ public class Helper { - private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(); - /** * Converts array of int values to string * @@ -625,61 +614,6 @@ public class Helper { return data; } - public static T timedCall(Callable c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException { - FutureTask task = new FutureTask<>(c); - THREAD_POOL.execute(task); - try { - return task.get(timeout, timeUnit); - } finally { - task.cancel(true); - } - } - - public static FutureTask callAsync(final Callable c) { - return callAsync(c, null); - } - - public static FutureTask callAsync(final Callable c, final Callback> callback) { - FutureTask task = new FutureTask<>(new Callable() { - - @Override - public T call() throws Exception { - try { - T t = c.call(); - if (callback != null) { - callback.call(new AsyncResult<>(t, null)); - } - return t; - } catch (Throwable ex) { - if (callback != null) { - callback.call(new AsyncResult(null, ex)); - } - throw ex; - } - } - }); - THREAD_POOL.execute(task); - return task; - } - - public static T cancellableCall(final Callable c, final AtomicReference> cancellation) throws InterruptedException, ExecutionException { - FutureTask task = new FutureTask<>(new Callable() { - - @Override - public T call() throws Exception { - T t = c.call(); - return t; - } - }); - cancellation.set(task); - THREAD_POOL.execute(task); - try { - return task.get(); - } catch (CancellationException ex) { - throw new InterruptedException(); - } - } - public static boolean contains(int[] array, int value) { for (int i : array) { if (i == value) {