Added: AS2 - Information about need of decompiling all scripts to detect uninitialized class fields

Fixed: #2338 AS decompiling threads got stuck after cancelling / timeout
CancellableWorker refactoring
This commit is contained in:
Jindra Petřík
2024-10-13 20:43:18 +02:00
parent 9793ab0cec
commit a4a9ba4c6b
59 changed files with 495 additions and 204 deletions
@@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.helpers.HighlightedText;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.ImmediateFuture;
import java.util.ArrayList;
import java.util.List;
@@ -79,13 +80,18 @@ public class DecompilerPool {
* @return Future
*/
public Future<HighlightedText> submitTask(ASMSource src, ActionList actions, ScriptDecompiledListener<HighlightedText> listener) {
CancellableWorker w = CancellableWorker.getCurrent();
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
public HighlightedText call() throws Exception {
if (w != null) {
CancellableWorker.assignThreadToWorker(Thread.currentThread(), w);
}
if (listener != null) {
listener.onStart();
}
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
writer.startFunction("!script");
src.getActionScriptSource(writer, actions);
@@ -119,9 +125,13 @@ public class DecompilerPool {
* @return Future
*/
public Future<HighlightedText> submitTask(AbcIndexing abcIndex, ScriptPack pack, ScriptDecompiledListener<HighlightedText> listener) {
CancellableWorker w = CancellableWorker.getCurrent();
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
public HighlightedText call() throws Exception {
if (w != null) {
CancellableWorker.assignThreadToWorker(Thread.currentThread(), w);
}
if (listener != null) {
listener.onStart();
}
@@ -211,9 +221,25 @@ public class DecompilerPool {
openableToFutures.put(swf, new ArrayList<>());
}
openableToFutures.get(swf).add(future);
CancellableWorker w = CancellableWorker.getCurrent();
if (w != null) {
if (w.isCancelled()) {
throw new InterruptedException();
}
w.addCancelListener(new Runnable() {
@Override
public void run() {
w.removeCancelListener(this);
future.cancel(true);
}
});
}
try {
return future.get();
} catch (InterruptedException ex) {
} catch (InterruptedException ex) {
future.cancel(true);
throw ex;
} catch (ExecutionException ex) {
@@ -243,7 +269,7 @@ public class DecompilerPool {
if (!openableToFutures.containsKey(openable)) {
openableToFutures.put(openable, new ArrayList<>());
}
openableToFutures.get(openable).add(future);
openableToFutures.get(openable).add(future);
try {
return future.get();
} catch (InterruptedException ex) {
@@ -173,6 +173,7 @@ import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Cache;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.ImmediateFuture;
import com.jpexs.helpers.NulStream;
@@ -658,6 +659,11 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
*/
private final HashSet<EventListener> listeners = new HashSet<>();
/**
* Lock for characters synchronization
*/
private final Object charactersLock = new Object();
/**
* Sets main GFX exporterinfo tag
*
@@ -945,50 +951,52 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
* @param withImported Include tags imported with importasset/2 tag?
* @return Character id to character map
*/
public synchronized Map<Integer, CharacterTag> getCharacters(boolean withImported) {
if (characters == null || charactersWithImported == null) {
if (destroyed) {
return new HashMap<>();
}
Map<Integer, CharacterTag> chars = new HashMap<>();
Map<Integer, CharacterTag> charsWithImported = new HashMap<>();
Map<Integer, List<CharacterIdTag>> charIdtags = new HashMap<>();
Map<Integer, DefineExternalImage2> eimages = new HashMap<>();
parseCharacters(getTags(), eimages, chars, charIdtags);
charsWithImported.putAll(chars);
for (int importedCharacterId : importedCharacterIds.keySet()) {
int exportedCharacterId = importedCharacterIds.get(importedCharacterId);
SWF importedSwf = importedCharacterSourceSwfs.get(importedCharacterId);
CharacterTag exportedCharacter = importedSwf.getCharacter(exportedCharacterId);
charsWithImported.put(importedCharacterId, exportedCharacter);
charIdtags.put(importedCharacterId, importedSwf.getCharacterIdTags(exportedCharacterId));
//FIXME? eimages
public Map<Integer, CharacterTag> getCharacters(boolean withImported) {
synchronized (charactersLock) {
if (characters == null || charactersWithImported == null) {
if (destroyed) {
return new HashMap<>();
}
Map<Integer, CharacterTag> chars = new HashMap<>();
Map<Integer, CharacterTag> charsWithImported = new HashMap<>();
Map<Integer, List<CharacterIdTag>> charIdtags = new HashMap<>();
Map<Integer, DefineExternalImage2> eimages = new HashMap<>();
parseCharacters(getTags(), eimages, chars, charIdtags);
charsWithImported.putAll(chars);
for (int importedCharacterId : importedCharacterIds.keySet()) {
int exportedCharacterId = importedCharacterIds.get(importedCharacterId);
SWF importedSwf = importedCharacterSourceSwfs.get(importedCharacterId);
CharacterTag exportedCharacter = importedSwf.getCharacter(exportedCharacterId);
charsWithImported.put(importedCharacterId, exportedCharacter);
charIdtags.put(importedCharacterId, importedSwf.getCharacterIdTags(exportedCharacterId));
//FIXME? eimages
charsWithImported.get(importedCharacterId).setImported(true, true);
for (CharacterIdTag chi : charIdtags.get(importedCharacterId)) {
if (chi instanceof Tag) {
((Tag) chi).setImported(true, true);
charsWithImported.get(importedCharacterId).setImported(true, true);
for (CharacterIdTag chi : charIdtags.get(importedCharacterId)) {
if (chi instanceof Tag) {
((Tag) chi).setImported(true, true);
}
}
}
}
Map<CharacterIdTag, Integer> charToId = new IdentityHashMap<>();
for (int id : charsWithImported.keySet()) {
charToId.put(charsWithImported.get(id), id);
}
for (int id : charIdtags.keySet()) {
for (CharacterIdTag ch : charIdtags.get(id)) {
charToId.put(ch, id);
Map<CharacterIdTag, Integer> charToId = new IdentityHashMap<>();
for (int id : charsWithImported.keySet()) {
charToId.put(charsWithImported.get(id), id);
}
for (int id : charIdtags.keySet()) {
for (CharacterIdTag ch : charIdtags.get(id)) {
charToId.put(ch, id);
}
}
characters = Collections.unmodifiableMap(chars);
charactersWithImported = Collections.unmodifiableMap(charsWithImported);
characterToId = Collections.unmodifiableMap(charToId);
characterIdTags = Collections.unmodifiableMap(charIdtags);
externalImages2 = Collections.unmodifiableMap(eimages);
}
characters = Collections.unmodifiableMap(chars);
charactersWithImported = Collections.unmodifiableMap(charsWithImported);
characterToId = Collections.unmodifiableMap(charToId);
characterIdTags = Collections.unmodifiableMap(charIdtags);
externalImages2 = Collections.unmodifiableMap(eimages);
return withImported ? charactersWithImported : characters;
}
return withImported ? charactersWithImported : characters;
}
/**
@@ -1270,11 +1278,11 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
if (fontClass == null) {
return null;
}
CharacterTag t = getCharacterByClass(fontClass);
CharacterTag t = getCharacterByClass(fontClass);
if (t instanceof FontTag) {
return (FontTag) t;
}
return null;
}
@@ -2313,7 +2321,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
ImportTag importTag = (ImportTag) t;
String url = importTag.getUrl();
SWF iSwf;
if (importedSwfs.containsKey(url)) {
iSwf = importedSwfs.get(url);
@@ -5386,7 +5394,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
List<ScriptPack> packs = getAS3Packs();
int i = 0;
for (ScriptPack s : packs) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
i++;
@@ -5419,7 +5427,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
List<ScriptPack> packs = getAS3Packs();
int i = 0;
for (ScriptPack s : packs) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
i++;
@@ -5623,7 +5631,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
List<String> names = new ArrayList<>(asms.keySet());
Collections.sort(names);
for (String name : names) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
informListeners("generate_swd", name);
@@ -5720,7 +5728,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
List<String> names = new ArrayList<>(asms.keySet());
Collections.sort(names);
for (String name : names) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
informListeners("generate_swd", name);
@@ -6169,11 +6177,18 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
/**
* Calculates uninitialized class traits in AS2.
*
* @throws java.lang.InterruptedException On interruption
*/
public void calculateAs2UninitializedClassTraits() {
public void calculateAs2UninitializedClassTraits() throws InterruptedException {
uninitializedAs2ClassTraits = new HashMap<>();
UninitializedClassFieldsDetector detector = new UninitializedClassFieldsDetector();
uninitializedAs2ClassTraits = detector.calculateAs2UninitializedClassTraits(this);
try {
uninitializedAs2ClassTraits = detector.calculateAs2UninitializedClassTraits(this);
} catch (Throwable t) {
uninitializedAs2ClassTraits = null;
throw t;
}
}
/**
@@ -6181,12 +6196,26 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
*
* @return Map of class name to map of trait name to trait
*/
public synchronized Map<String, Map<String, com.jpexs.decompiler.flash.action.as2.Trait>> getUninitializedAs2ClassTraits() {
if (uninitializedAs2ClassTraits == null) {
public synchronized Map<String, Map<String, com.jpexs.decompiler.flash.action.as2.Trait>> getUninitializedAs2ClassTraits() throws InterruptedException {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
if (uninitializedAs2ClassTraits == null) {
calculateAs2UninitializedClassTraits();
}
return uninitializedAs2ClassTraits;
}
public boolean needsCalculatingAS2UninitializeClassTraits(ASMSource src) {
if (!isAS3()) {
if (src instanceof DoInitActionTag) {
if (uninitializedAs2ClassTraits == null) {
return true;
}
}
}
return false;
}
/**
* Gets SWF (self)
@@ -275,6 +275,7 @@ import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.FakeMemoryInputStream;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.ImmediateFuture;
@@ -1416,7 +1417,7 @@ public class SWFInputStream implements AutoCloseable {
* @throws InterruptedException On interrupt
*/
public List<Tag> readTagList(Timelined timelined, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags, boolean lazy) throws IOException, InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -466,7 +466,7 @@ public class ScriptPack extends AS3ClassTreeItem {
writer.suspendMeasure();
int timeout = Configuration.decompilationTimeoutFile.get();
try {
CancellableWorker.call(new Callable<Void>() {
CancellableWorker.call("script.scriptPack.toSource", new Callable<Void>() {
@Override
public Void call() throws Exception {
convert(abcIndex, new NulWriter(), traits, convertData, exportMode, parallel);
@@ -305,6 +305,7 @@ import com.jpexs.decompiler.graph.SimpleValue;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.ScriptEndItem;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.LinkedIdentityHashSet;
import com.jpexs.helpers.Reference;
@@ -3458,7 +3459,7 @@ public class AVM2Code implements Cloneable {
toVisit.add(ip);
toVisitLast.add(lastIp);
while (!toVisit.isEmpty()) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
ip = toVisit.remove();
@@ -40,6 +40,7 @@ import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.NotCompileTimeItem;
import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.helpers.CancellableWorker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -107,7 +108,7 @@ public class AVM2DeobfuscatorGetSet extends SWFDecompilerAdapter {
AVM2LocalData localData = newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex);
int localReservedCount = body.getLocalReservedCount();
for (int i = 0; i < code.code.size(); i++) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -44,6 +44,7 @@ import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashSet;
@@ -134,7 +135,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
Reference<AVM2Instruction> assignmentRef = new Reference<>(null);
while (setReg > -1) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -200,7 +201,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
AVM2Code code = body.getCode();
for (int i = 0; i < code.code.size(); i++) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -271,7 +272,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
toVisitStacks.add(stack);
outer:
while (!toVisit.isEmpty()) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -42,6 +42,7 @@ import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.TranslateStack;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -131,7 +132,7 @@ public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld {
Reference<AVM2Instruction> assignmentRef = new Reference<>(null);
while (setReg > -1) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -290,7 +291,7 @@ public class AVM2DeobfuscatorRegistersOld extends AVM2DeobfuscatorSimpleOld {
toVisitStacks.add(stack);
outer:
while (!toVisit.isEmpty()) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -78,6 +78,7 @@ import com.jpexs.decompiler.flash.ecma.NotCompileTime;
import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.flash.helpers.collections.FixItemCounterStack;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.helpers.CancellableWorker;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -135,7 +136,7 @@ public class AVM2DeobfuscatorSimple extends AVM2DeobfuscatorZeroJumpsNullPushes
int localReservedCount = body.getLocalReservedCount();
for (int i = 0; i < code.code.size(); i++) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -99,6 +99,7 @@ import com.jpexs.decompiler.graph.ScopeStack;
import com.jpexs.decompiler.graph.TranslateException;
import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashMap;
@@ -209,7 +210,7 @@ public class AVM2DeobfuscatorSimpleOld extends AVM2DeobfuscatorZeroJumpsNullPush
int localReservedCount = body.getLocalReservedCount();
Set<Long> importantOffsets = code.getImportantOffsets(body, isStatic);
for (int i = 0; i < code.code.size(); i++) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -37,6 +37,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerAdapter;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Reference;
import java.util.Set;
@@ -83,7 +84,7 @@ public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter {
AVM2Instruction ins = code.code.get(i);
if (ins.definition instanceof JumpIns) {
if (ins.operands[0] == 0) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -142,7 +143,7 @@ public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter {
if (ins2.definition instanceof PopIns
&& !offsets.contains(ins2.getAddress())
&& isSimplePush(ins1.definition)) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -159,7 +160,7 @@ public class AVM2DeobfuscatorZeroJumpsNullPushes extends SWFDecompilerAdapter {
&& !offsets.contains(ins2.getAddress())
&& !offsets.contains(ins1.getAddress())
&& isSimplePush(ins0.definition)) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
code.removeInstruction(i - 2, body);
@@ -42,6 +42,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import java.io.IOException;
import java.io.Reader;
@@ -1605,7 +1606,7 @@ public class ASM3Parser {
}
for (OffsetItem oi : offsetItems) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
if (!labelToOffset.containsKey(oi.label)) {
@@ -119,6 +119,7 @@ import com.jpexs.decompiler.graph.model.SwitchItem;
import com.jpexs.decompiler.graph.model.TernarOpItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import com.jpexs.helpers.utf8.Utf8Helper;
@@ -473,7 +474,7 @@ public class ActionScript3Parser {
}
private ParsedSymbol lex() throws IOException, AVM2ParseException, InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
ParsedSymbol ret = lexer.lex();
@@ -482,7 +482,7 @@ public final class MethodBody implements Cloneable {
}
};
if (firstLevel) {
CancellableWorker.call(callable, timeout, TimeUnit.SECONDS);
CancellableWorker.call("script.methodbody.convert", callable, timeout, TimeUnit.SECONDS);
} else {
callable.call();
}
@@ -986,12 +986,13 @@ public abstract class Action implements GraphSourceItem {
final SWF swf = asm == null ? null : asm.getSwf();
final int version = swf == null ? SWF.DEFAULT_VERSION : swf.version;
try {
tree = CancellableWorker.call(new Callable<List<GraphTargetItem>>() {
tree = CancellableWorker.call("script.actionsToSource", new Callable<List<GraphTargetItem>>() {
@Override
public List<GraphTargetItem> call() throws Exception {
int staticOperation = 0;
boolean insideDoInitAction = (asm instanceof DoInitActionTag);
List<GraphTargetItem> tree = actionsToTree(uninitializedClassTraits, insideDoInitAction, false, new HashMap<>(), new HashMap<>(), new HashMap<>(), actions, version, staticOperation, path, charset);
List<GraphTargetItem>
tree = actionsToTree(uninitializedClassTraits, insideDoInitAction, false, new HashMap<>(), new HashMap<>(), new HashMap<>(), actions, version, staticOperation, path, charset);
SWFDecompilerPlugin.fireActionTreeCreated(tree, swf);
for (ActionTreeOperation treeOperation : treeOperations) {
treeOperation.run(tree);
@@ -84,7 +84,7 @@ public class ActionListReader {
*/
public static ActionList readActionListTimeout(final List<DisassemblyListener> listeners, final SWFInputStream sis, final int version, final int ip, final int endIp, final String path, final int deobfuscationMode) throws IOException, InterruptedException, TimeoutException {
try {
ActionList actions = CancellableWorker.call(new Callable<ActionList>() {
ActionList actions = CancellableWorker.call("script.readActionList", new Callable<ActionList>() {
@Override
public ActionList call() throws IOException, InterruptedException {
@@ -34,6 +34,7 @@ import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.graph.AbstractGraphTargetVisitor;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.helpers.CancellableWorker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -185,8 +186,9 @@ public class UninitializedClassFieldsDetector {
*
* @param swf SWF
* @return Map of class name to map of trait name to trait
* @throws java.lang.InterruptedException On interruption
*/
public Map<String, Map<String, Trait>> calculateAs2UninitializedClassTraits(SWF swf) {
public Map<String, Map<String, Trait>> calculateAs2UninitializedClassTraits(SWF swf) throws InterruptedException {
if (swf.isAS3()) {
return new HashMap<>();
}
@@ -200,6 +202,9 @@ public class UninitializedClassFieldsDetector {
//get all assigned traits and inheritance tree
for (String key : asms.keySet()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
ASMSource asm = asms.get(key);
if (asm instanceof DoInitActionTag) {
DoInitActionTag doi = (DoInitActionTag) asm;
@@ -282,6 +287,9 @@ public class UninitializedClassFieldsDetector {
//Detect this.x assigns
for (String key : asms.keySet()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
ASMSource asm = asms.get(key);
if (asm instanceof DoInitActionTag) {
DoInitActionTag doi = (DoInitActionTag) asm;
@@ -335,6 +343,9 @@ public class UninitializedClassFieldsDetector {
//getting static classname.x assigns
for (String key : asms.keySet()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
ASMSource asm = asms.get(key);
List<GraphTargetItem> tree = asm.getActionsToTree();
for (GraphTargetItem item : tree) {
@@ -83,6 +83,7 @@ import com.jpexs.decompiler.flash.helpers.collections.FixItemCounterStack;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.PushItem;
import com.jpexs.helpers.CancellableWorker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -132,7 +133,7 @@ public class ActionDeobfuscator extends SWFDecompilerAdapter {
}
}
private boolean removeGetTimes(FastActionList actions) {
private boolean removeGetTimes(FastActionList actions) throws InterruptedException {
if (actions.isEmpty()) {
return false;
}
@@ -215,7 +216,7 @@ public class ActionDeobfuscator extends SWFDecompilerAdapter {
FastActionListIterator iterator = actions.iterator();
boolean first = true;
while (iterator.hasNext()) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.action.swf4.ActionIf;
import com.jpexs.decompiler.flash.action.swf4.ActionJump;
import com.jpexs.decompiler.flash.action.swf4.ActionPush;
import com.jpexs.decompiler.graph.GraphSourceItemContainer;
import com.jpexs.helpers.CancellableWorker;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -582,7 +583,7 @@ public class FastActionList implements Collection<ActionItem> {
/**
* Removes the unreachable actions.
*/
public void removeUnreachableActions() {
public void removeUnreachableActions() throws InterruptedException {
ActionItem item = firstItem;
if (item == null) {
return;
@@ -629,7 +630,7 @@ public class FastActionList implements Collection<ActionItem> {
* @param jumpTarget Jump target
* @return Unreachable action count
*/
public int getUnreachableActionCount(ActionItem jump, ActionItem jumpTarget) {
public int getUnreachableActionCount(ActionItem jump, ActionItem jumpTarget) throws InterruptedException {
ActionItem item = firstItem;
if (item == null) {
return 0;
@@ -688,7 +689,7 @@ public class FastActionList implements Collection<ActionItem> {
* @param jump Jump
* @param jumpTarget Jump target
*/
private void updateReachableFlags(ActionItem jump, ActionItem jumpTarget) {
private void updateReachableFlags(ActionItem jump, ActionItem jumpTarget) throws InterruptedException {
if (firstItem == null) {
return;
}
@@ -699,9 +700,12 @@ public class FastActionList implements Collection<ActionItem> {
ActionItem firstItem2 = firstItem;
boolean modified = true;
while (modified) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
modified = false;
ActionItem item = firstItem2;
do {
do {
ActionItem next = item.next;
//ActionItem alternativeNext = null;
Action action = item.action;
@@ -165,6 +165,7 @@ import com.jpexs.decompiler.graph.model.SwitchItem;
import com.jpexs.decompiler.graph.model.TernarOpItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Reference;
import java.io.IOException;
import java.io.StringReader;
@@ -365,7 +366,7 @@ public class ActionScript2Parser {
}
private ParsedSymbol lex() throws IOException, ActionParseException, InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
ParsedSymbol ret = lexer.lex();
@@ -25,6 +25,7 @@ import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.BinaryDataInterface;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.BufferedOutputStream;
@@ -86,7 +87,7 @@ public class BinaryDataExporter {
*/
public List<File> exportBinaryData(AbortRetryIgnoreHandler handler, String outdir, List<BinaryDataInterface> binaryDatas, BinaryDataExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -133,7 +134,7 @@ public class BinaryDataExporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.exporters.modes.Font4ExportMode;
import com.jpexs.decompiler.flash.exporters.settings.Font4ExportSettings;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.BufferedOutputStream;
@@ -50,7 +51,7 @@ public class Font4Exporter {
public List<File> exportFonts(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final Font4ExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -103,7 +104,7 @@ public class Font4Exporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
@@ -35,6 +35,7 @@ import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import fontastic.FGlyph;
@@ -65,7 +66,7 @@ public class FontExporter {
public List<File> exportFonts(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final FontExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -118,7 +119,7 @@ public class FontExporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
@@ -57,6 +57,7 @@ import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.decompiler.flash.types.filters.GLOWFILTER;
import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER;
import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.SerializableImage;
@@ -200,7 +201,7 @@ public class FrameExporter {
@Override
public boolean hasNext() {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return false;
}
return fframes.size() > pos;
@@ -226,7 +227,7 @@ public class FrameExporter {
int fframe = fframes.get(pos++);
BufferedImage result = SWF.frameToImageGet(tim, fframe, 0, null, 0, tim.displayRect, new Matrix(), null, backgroundColor == null && !usesTransparency ? Color.white : backgroundColor, settings.zoom, true).getBufferedImage();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return null;
}
if (evl != null) {
@@ -239,7 +240,7 @@ public class FrameExporter {
public List<File> exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List<Integer> frames, final FrameExportSettings settings, final EventListener evl) throws IOException, InterruptedException {
final List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -318,7 +319,7 @@ public class FrameExporter {
}, handler).run();
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
@@ -614,7 +615,7 @@ public class FrameExporter {
}
g.dispose();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return;
}
pos++;
@@ -774,7 +775,7 @@ public class FrameExporter {
Stack<Integer> clipDepths = new Stack<>();
for (int frame : frames) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
result.append("\t\tcase ").append(frame).append(":\r\n");
@@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.HasSeparateAlphaChannel;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.awt.Dimension;
@@ -55,7 +56,7 @@ public class ImageExporter {
public List<File> exportImages(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, ImageExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -170,7 +171,7 @@ public class ImageExporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
@@ -42,6 +42,7 @@ import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.SerializableImage;
@@ -72,7 +73,7 @@ public class MorphShapeExporter {
//TODO: implement morphshape export. How to handle 65536 frames?
public List<File> exportMorphShapes(AbortRetryIgnoreHandler handler, final String outdir, ReadOnlyTagList tags, final MorphShapeExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -257,7 +258,7 @@ public class MorphShapeExporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
if (evl != null) {
@@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.flv.VIDEODATA;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.VideoFrameTag;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.BufferedOutputStream;
@@ -59,7 +60,7 @@ public class MovieExporter {
public List<File> exportMovies(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final MovieExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
if (tags.isEmpty()) {
@@ -109,7 +110,7 @@ public class MovieExporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
if (evl != null) {
@@ -40,6 +40,7 @@ import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.SerializableImage;
@@ -69,7 +70,7 @@ public class ShapeExporter {
public List<File> exportShapes(AbortRetryIgnoreHandler handler, final String outdir, final SWF swf, ReadOnlyTagList tags, final ShapeExportSettings settings, EventListener evl, double unzoom) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -180,7 +181,7 @@ public class ShapeExporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
if (evl != null) {
@@ -38,6 +38,7 @@ import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.BufferedOutputStream;
@@ -73,7 +74,7 @@ public class SoundExporter {
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, List<SoundTag> tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -133,7 +134,7 @@ public class SoundExporter {
ret.add(file);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.exporters.settings.SymbolClassExportSettings;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.utf8.Utf8OutputStreamWriter;
@@ -46,7 +47,7 @@ public class SymbolClassExporter {
public List<File> exportNames(AbortRetryIgnoreHandler handler, final String outdir, ReadOnlyTagList tags, SymbolClassExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -79,7 +80,7 @@ public class SymbolClassExporter {
writer.append(sct.tags.get(i) + ";" + sct.names.get(i) + Helper.newLine);
}
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
}
@@ -28,6 +28,7 @@ import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.utf8.Utf8Helper;
@@ -52,7 +53,7 @@ public class TextExporter {
public List<File> exportTexts(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final TextExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -130,7 +131,7 @@ public class TextExporter {
fos.write(Utf8Helper.getBytes(Helper.newLine + Configuration.textExportSingleFileSeparator.get() + Helper.newLine));
}, handler).run();
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
}
@@ -159,7 +160,7 @@ public class TextExporter {
}, handler).run();
ret.add(file);
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
if (evl != null) {
@@ -118,11 +118,11 @@ public class AS2ScriptExporter {
if (!parallel || tasks.size() < 2) {
try {
CancellableWorker.call(new Callable<Void>() {
CancellableWorker.call("as2scriptexport", new Callable<Void>() {
@Override
public Void call() throws Exception {
for (ExportScriptTask task : tasks) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -140,19 +140,23 @@ public class AS2ScriptExporter {
ExecutorService executor = Executors.newFixedThreadPool(Configuration.getParallelThreadCount());
List<Future<File>> futureResults = new ArrayList<>();
for (ExportScriptTask task : tasks) {
Future<File> future = executor.submit(task);
Future<File> future = executor.submit(task);
futureResults.add(future);
}
}
try {
executor.shutdown();
executor.shutdown();
if (!executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS)) {
logger.log(Level.SEVERE, "{0} ActionScript export limit reached", Helper.formatTimeToText(Configuration.exportTimeout.get()));
for (ExportScriptTask task : tasks) {
CancellableWorker.cancelThread(task.thread);
}
}
} catch (InterruptedException ex) {
//ignored
} finally {
executor.shutdownNow();
executor.shutdownNow();
}
for (int f = 0; f < futureResults.size(); f++) {
@@ -163,9 +167,11 @@ public class AS2ScriptExporter {
} catch (InterruptedException ex) {
//ignored
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, "Error during ABC export", ex);
if (!(ex.getCause() instanceof InterruptedException)) {
logger.log(Level.SEVERE, "Error during ActionScript export", ex);
}
}
}
}
}
return ret;
@@ -535,11 +535,11 @@ public class AS3ScriptExporter {
if (!parallel || tasks.size() < 2) {
try {
CancellableWorker.call(new Callable<Void>() {
CancellableWorker.call("as3scriptexport", new Callable<Void>() {
@Override
public Void call() throws Exception {
for (ExportPackTask task : tasks) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -565,6 +565,10 @@ public class AS3ScriptExporter {
executor.shutdown();
if (!executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS)) {
logger.log(Level.SEVERE, "{0} ActionScript export limit reached", Helper.formatTimeToText(Configuration.exportTimeout.get()));
for (ExportPackTask task : tasks) {
CancellableWorker.cancelThread(task.thread);
}
}
} catch (InterruptedException ex) {
//ignored
@@ -587,7 +591,7 @@ public class AS3ScriptExporter {
if (exportSettings.exportEmbedFlaMode || exportSettings.exportEmbed) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
@@ -638,27 +642,27 @@ public class AS3ScriptExporter {
try {
BinaryDataExporter bde = new BinaryDataExporter();
bde.exportBinaryData(handler, ASSETS_DIR, rttl, new BinaryDataExportSettings(BinaryDataExportMode.RAW), evl);
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
ImageExporter ie = new ImageExporter();
ie.exportImages(handler, ASSETS_DIR, rttl, new ImageExportSettings(ImageExportMode.PNG_GIF_JPEG), evl);
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
SoundExporter se = new SoundExporter();
se.exportSounds(handler, ASSETS_DIR, rttl, new SoundExportSettings(SoundExportMode.MP3_WAV, exportSettings.resampleWav), evl);
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
FontExporter fe = new FontExporter();
fe.exportFonts(handler, ASSETS_DIR, rttl, new FontExportSettings(FontExportMode.TTF), evl);
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
Font4Exporter f4e = new Font4Exporter();
f4e.exportFonts(handler, ASSETS_DIR, rttl, new Font4ExportSettings(Font4ExportMode.CFF), evl);
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return ret;
}
if (!assetsTagList.isEmpty()) {
@@ -59,6 +59,8 @@ public class ExportPackTask implements Callable<File> {
EventListener eventListener;
AbcIndexing abcIndex;
Thread thread;
/**
* Constructor.
@@ -73,7 +75,7 @@ public class ExportPackTask implements Callable<File> {
* @param parallel Parallel
* @param evl Event listener
*/
public ExportPackTask(AbcIndexing abcIndex, AbortRetryIgnoreHandler handler, int index, int count, ClassPath path, ScriptPack pack, File file, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) {
public ExportPackTask(AbcIndexing abcIndex, AbortRetryIgnoreHandler handler, int index, int count, ClassPath path, ScriptPack pack, File file, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) {
this.pack = pack;
this.file = file;
this.exportSettings = exportSettings;
@@ -88,6 +90,7 @@ public class ExportPackTask implements Callable<File> {
@Override
public File call() throws IOException, InterruptedException {
this.thread = Thread.currentThread();
RunnableIOExResult<File> rio = new RunnableIOExResult<File>() {
@Override
public void run() throws IOException, InterruptedException {
@@ -64,6 +64,8 @@ public class ExportScriptTask implements Callable<File> {
long stopTime;
EventListener eventListener;
Thread thread;
/**
* Constructor.
@@ -86,9 +88,10 @@ public class ExportScriptTask implements Callable<File> {
this.handler = handler;
this.eventListener = evl;
}
@Override
public File call() throws IOException, InterruptedException {
thread = Thread.currentThread();
String f = Path.combine(directory, name) + exportSettings.getFileExtension();
RunnableIOExResult<File> rio = new RunnableIOExResult<File>() {
@Override
@@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser;
import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.File;
@@ -79,7 +80,7 @@ public class AS2ScriptImporter {
int importCount = 0;
for (String key : asms.keySet()) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
ASMSource asm = asms.get(key);
@@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import java.io.File;
import java.io.IOException;
@@ -75,7 +76,7 @@ public class AS3ScriptImporter {
int importCount = 0;
for (ScriptPack pack : packs) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return importCount;
}
if (!pack.isSimple) {
@@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
@@ -353,7 +354,7 @@ public class ImageImporter extends TagImporter {
}
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
}
@@ -34,6 +34,7 @@ import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.io.ByteArrayInputStream;
@@ -136,7 +137,7 @@ public class MovieImporter {
} catch (IOException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cannot import movie " + characterId + " from file " + sourceFile.getName(), ex);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
}
@@ -36,6 +36,7 @@ import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
@@ -315,7 +316,7 @@ public class ShapeImporter {
} catch (IOException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cannot import shape " + characterId + " from file " + sourceFile.getName(), ex);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
}
@@ -36,6 +36,7 @@ import com.jpexs.decompiler.flash.types.sound.MP3FRAME;
import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
@@ -612,7 +613,7 @@ public class SoundImporter {
} catch (IOException | SoundImportException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cannot import sound " + characterId + " from file " + sourceFile.getName(), ex);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
}
@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
@@ -241,7 +242,7 @@ public class SpriteImporter {
} catch (IOException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cannot import sprite " + characterId + " from file " + sourceFile.getName(), ex);
}
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
break;
}
}
@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.helpers.HighlightedText;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.helpers.CancellableWorker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -231,7 +232,7 @@ public class ActionScriptSearch {
Future<HighlightedText> text = SWF.getCachedFuture(pack, new ScriptDecompiledListener<HighlightedText>() {
@Override
public void onStart() {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
return;
}
if (listener != null) {
@@ -242,7 +243,7 @@ public class ActionScriptSearch {
@Override
public void onComplete(HighlightedText result) {
if (!Thread.currentThread().isInterrupted()) {
if (!CancellableWorker.isInterrupted()) {
if (listener != null) {
listener.onSearch(fpos, fscope.size(), pack.getClassPath().toString());
}
@@ -220,7 +220,7 @@ public class BUTTONCONDACTION implements ASMSource, Serializable, HasSwfAndTag {
actions = getActions();
}
return Action.actionsToSource(swf.getUninitializedAs2ClassTraits(), this, actions, getScriptName(), writer, actions.getCharset());
return Action.actionsToSource(new HashMap<>(), this, actions, getScriptName(), writer, actions.getCharset());
}
@Override
@@ -53,6 +53,7 @@ import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.UniversalLoopItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.decompiler.graph.precontinues.GraphPrecontinueDetector;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Reference;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -1441,7 +1442,7 @@ public class Graph {
//For detection based on debug line information
boolean[] toDelete = new boolean[list.size()];
for (int i = 0; i < list.size(); i++) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -2755,7 +2756,7 @@ public class Graph {
protected final List<GraphTargetItem> printGraph(List<GotoItem> foundGotos, Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos, Set<GraphPart> visited, BaseLocalData localData, TranslateStack stack, Set<GraphPart> allParts, GraphPart parent, GraphPart part, List<GraphPart> stopPart, List<StopPartKind> stopPartKind, List<Loop> loops, List<ThrowState> throwStates, List<GraphTargetItem> ret, int staticOperation, String path, int recursionLevel) throws InterruptedException {
loopPrintGraph:
while (true) {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
if (stopPart == null) {
@@ -3747,7 +3748,7 @@ public class Graph {
* @throws InterruptedException On interrupt
*/
private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startIp, int lastIp, List<GraphPart> allBlocks, HashMap<Integer, List<Integer>> refs, boolean[] visited) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.helpers.CancellableWorker;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
@@ -144,7 +145,7 @@ public class GraphPart implements Serializable {
* @throws InterruptedException On interrupt
*/
private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet<GraphPart> visited, List<Loop> loops, List<ThrowState> throwStates, boolean useThrow) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.helpers.CancellableWorker;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
@@ -95,7 +96,7 @@ public abstract class GraphSource implements Serializable {
* @throws InterruptedException On interrupt
*/
private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs, int endIp) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -41,6 +41,7 @@ import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.NotItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.LinkedIdentityHashSet;
import com.jpexs.helpers.Reference;
import java.io.Serializable;
@@ -388,7 +389,7 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
* @throws InterruptedException On interrupt
*/
public GraphTextWriter toStringSemicoloned(GraphTextWriter writer, LocalData localData) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -478,7 +479,7 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
* @throws InterruptedException On interrupt
*/
public GraphTextWriter toString(GraphTextWriter writer, LocalData localData, String implicitCoerce) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
@@ -31,6 +31,14 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
/*private static final Map<Thread, CancellableWorker> threadWorkers = Collections.synchronizedMap(new WeakHashMap<>());
private static final Map<Thread, CancellableWorker> parentThreadWorkers = Collections.synchronizedMap(new WeakHashMap<>());
private static final Map<Thread, Thread> threadToParentThread = Collections.synchronizedMap(new WeakHashMap<>());
*/
/**
* Cancellable worker.
@@ -46,22 +54,87 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
private FutureTask<T> future;
private static final Map<Thread, CancellableWorker> threadWorkers = Collections.synchronizedMap(new WeakHashMap<>());
private List<CancellableWorker> subWorkers = Collections.synchronizedList(new ArrayList<>());
private static final Map<Thread, CancellableWorker> thread2Worker = Collections.synchronizedMap(new WeakHashMap<>());
private Thread parentThread;
private Thread thread;
private String name;
private CancellableWorker parentWorker;
private boolean canceled = false;
private List<Runnable> cancelListeners = new ArrayList<>();
public Thread getThread() {
return thread;
}
public static void assignThreadToWorker(Thread t, CancellableWorker w) {
if (w == null) {
return;
}
thread2Worker.put(t, w);
}
public static CancellableWorker getCurrent() {
Thread t = Thread.currentThread();
CancellableWorker w = thread2Worker.get(t);
return w;
}
public void addCancelListener(Runnable listener) {
cancelListeners.add(listener);
}
public void removeCancelListener(Runnable listener) {
cancelListeners.remove(listener);
}
public static boolean isInterrupted() {
Thread t = Thread.currentThread();
if (t.isInterrupted()) {
return true;
}
CancellableWorker w = thread2Worker.get(t);
if (w != null) {
while (w != null) {
t = w.thread;
if (t != null && t.isInterrupted()) {
return true;
}
if (w.canceled) {
return true;
}
w = w.parentWorker;
}
}
return false;
}
/**
* Constructor.
* @param name Identifier of action
*/
public CancellableWorker() {
public CancellableWorker(String name) {
super();
this.name = name;
Callable<T> callable = new Callable<T>() {
@Override
public T call() throws Exception {
thread = Thread.currentThread();
threadWorkers.put(thread, CancellableWorker.this);
thread = Thread.currentThread();
thread2Worker.put(thread, CancellableWorker.this);
if (isInterrupted()) {
throw new InterruptedException();
}
return doInBackground();
}
};
@@ -104,24 +177,36 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
*/
@SuppressWarnings("unchecked")
public final void execute() {
Thread t = Thread.currentThread();
if (threadWorkers.containsKey(t)) {
threadWorkers.get(t).subWorkers.add(this);
parentThread = Thread.currentThread();
if (thread2Worker.containsKey(parentThread)) {
CancellableWorker currentWorker = thread2Worker.get(parentThread);
currentWorker.subWorkers.add(this);
parentWorker = currentWorker;
}
onStart();
THREAD_POOL.execute(this);
}
@Override
public final boolean cancel(boolean mayInterruptIfRunning) {
public final boolean cancel(boolean mayInterruptIfRunning) {
canceled = true;
boolean r = future.cancel(mayInterruptIfRunning);
List<CancellableWorker> sw = new ArrayList<>(subWorkers);
for (CancellableWorker w : sw) {
w.cancel(mayInterruptIfRunning);
}
boolean r = future.cancel(mayInterruptIfRunning);
if (r) {
}
if (r) {
List<Runnable> cls = new ArrayList<>(cancelListeners);
for (Runnable listener : cls) {
listener.run();
}
workerCancelled();
}
}
return r;
}
@@ -149,13 +234,20 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
@Override
public final T get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
ExecutionException, TimeoutException {
return future.get(timeout, unit);
}
@Override
public String toString() {
return "[CancellableWorker \"" + name + "\" on thread " + thread +", subworkers: " + subWorkers.size() + ", " + (canceled ? " canceled" : "") + ", " + (parentWorker != null ? "HASPARENT" : "NOPARENT");
}
private void workerDone() {
if (thread != null && threadWorkers.get(thread) == this) {
threadWorkers.remove(thread);
if (thread != null && thread2Worker.get(thread) == this) {
//thread2Worker.remove(thread);
}
workers.remove(this);
done();
@@ -163,6 +255,7 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
/**
* Calls a callable with a timeout.
* @param name Name
* @param c Callable
* @param timeout Timeout
* @param timeUnit Time unit
@@ -172,12 +265,8 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
* @throws ExecutionException On execution error
* @throws TimeoutException On timeout
*/
public static <T> T call(final Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
Thread t = Thread.currentThread();
if (t.isInterrupted()) {
throw new InterruptedException();
}
CancellableWorker<T> worker = new CancellableWorker<T>() {
public static <T> T call(String name, final Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
CancellableWorker<T> worker = new CancellableWorker<T>(name) {
@Override
protected T doInBackground() throws Exception {
@@ -190,7 +279,7 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
} finally {
worker.cancel(true);
}
}
}
/**
* Cancels all background threads.
@@ -206,6 +295,17 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
}
}
}
public static void cancelThread(Thread t) {
List<CancellableWorker> ws = new ArrayList<>(workers);
for (CancellableWorker w : ws) {
if (w != null) {
if (w.parentThread == t) {
w.cancel(true);
}
}
}
}
/**
* Frees the worker.
@@ -216,4 +316,56 @@ public abstract class CancellableWorker<T> implements RunnableFuture<T> {
w.free();
}
}
@SuppressWarnings("unchecked")
private void printTree(List<String> out) {
out.add("worker " + name + " on thread " + thread);
List<CancellableWorker> sws = new ArrayList<>(subWorkers);
for (CancellableWorker sw : sws) {
List<String> subout = new ArrayList<>();
sw.printTree(subout);
for (String s:subout) {
out.add("-" + s);
}
}
}
public static void printAllWorkers() {
List<CancellableWorker> aw = new ArrayList<>(workers);
System.err.println("====ALL WORKERS ====");
for (CancellableWorker w : aw) {
System.err.println("" + w);
}
System.err.println("/====================");
}
public void printTree() {
System.err.println("=======================");
List<String> out = new ArrayList<>();
printTree(out);
for (String s:out) {
System.err.println(s);
}
System.err.println("/=======================");
}
public void startWorkerMonitor() {
Thread monit = new Thread() {
@Override
public void run() {
CancellableWorker w = null;
while (true) {
CancellableWorker.printAllWorkers();
w.printTree();
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
return;
}
}
}
};
monit.start();
}
}