diff --git a/CHANGELOG.md b/CHANGELOG.md index bfadb9e12..1bfc5b316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ All notable changes to this project will be documented in this file. - #1622 Slow scrolling (search results, advanced settings and others) - #1626 AS3 decompilation - unpopped obfuscated function - #1624 Saving last searches saves only first results +- #1627 Previously decompiled scripts not cached +- SWF is not garbage collected on close in some situations +- AS1/2 script search does not show all results ## [14.0.1] - 2021-02-26 ### Added diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index f9380afce..439c4f4c3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -36,6 +36,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; @@ -348,6 +349,12 @@ public final class SWF implements SWFContainerItem, Timelined { @Internal public final AS3Cache as3Cache = new AS3Cache(); + @Internal + private Map asmsCacheExportFilenames; + + @Internal + private Map asmsCache; + private static final DecompilerPool decompilerPool = new DecompilerPool(); public static final String AS2_PKG_PREFIX = "__Packages."; @@ -375,6 +382,7 @@ public final class SWF implements SWFContainerItem, Timelined { DefineSpriteTag spriteTag = (DefineSpriteTag) tag; for (Tag tag1 : spriteTag.getTags()) { tag1.setSwf(null); + tag1.setTimelined(null); } for (int i = spriteTag.getTags().size() - 1; i >= 0; i--) { @@ -390,10 +398,15 @@ public final class SWF implements SWFContainerItem, Timelined { } tag.setSwf(null); + tag.setTimelined(null); } tags.clear(); if (abcList != null) { + + for (ABCContainerTag c : abcList) { + c.getABC().free(); + } abcList.clear(); } @@ -401,8 +414,7 @@ public final class SWF implements SWFContainerItem, Timelined { swfList.swfs.clear(); } - as2Cache.clear(); - as3Cache.clear(); + clearScriptCache(); frameCache.clear(); soundCache.clear(); @@ -1703,11 +1715,25 @@ public final class SWF implements SWFContainerItem, Timelined { } public Map getASMs(boolean exportFileNames, List nodesToExport, boolean exportAll) { + if (exportAll) { + if (exportFileNames && asmsCacheExportFilenames != null) { + return asmsCacheExportFilenames; + } + if (!exportFileNames && asmsCache != null) { + return asmsCache; + } + } Map asmsToExport = new LinkedHashMap<>(); for (TreeItem treeItem : getFirstLevelASMNodes(null)) { getASMs(exportFileNames, treeItem, nodesToExport, exportAll, asmsToExport, File.separator + getASMPath(exportFileNames, treeItem)); } - + if (exportAll) { + if (exportFileNames) { + asmsCacheExportFilenames = asmsToExport; + } else { + asmsCache = asmsToExport; + } + } return asmsToExport; } @@ -2567,6 +2593,13 @@ public final class SWF implements SWFContainerItem, Timelined { public void clearScriptCache() { as2Cache.clear(); as3Cache.clear(); + if (abcList != null) { + for (ABCContainerTag c : abcList) { + c.getABC().clearPacksCache(); + } + } + asmsCache = null; + asmsCacheExportFilenames = null; IdentifiersDeobfuscation.clearCache(); } @@ -3003,15 +3036,15 @@ public final class SWF implements SWFContainerItem, Timelined { timelined.setModified(true); timelined.resetTimeline(); } else // timeline should be always the swf here - if (removeDependencies) { - removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); + if (removeDependencies) { + removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); + timelined.setModified(true); + } else { + boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); + if (modified) { timelined.setModified(true); - } else { - boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); - if (modified) { - timelined.setModified(true); - } } + } } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java index da032de58..d4dba4d6f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -2092,4 +2092,15 @@ public class ABC { } return null; } + + public void clearPacksCache() { + for (ScriptInfo si : script_info) { + si.clearPacksCache(); + } + } + + public void free() { + deobfuscation = null; + abcMethodIndexing = null; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java index 2c8d68eed..413f479e3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.abc.types; import com.jpexs.decompiler.flash.abc.ABC; @@ -57,7 +58,16 @@ public class ScriptInfo { this.traits = traits; } + private List cachedPacks; + + public void clearPacksCache() { + cachedPacks = null; + } + public List getPacks(ABC abc, int scriptIndex, String packagePrefix, List allAbcs) { + if (packagePrefix == null && cachedPacks != null) { + return new ArrayList<>(cachedPacks); + } List ret = new ArrayList<>(); List otherTraits = new ArrayList<>(); @@ -114,6 +124,9 @@ public class ScriptInfo { ret.add(new ScriptPack(cp, abc, allAbcs, scriptIndex, traitIndices)); } } + if (packagePrefix == null) { + cachedPacks = new ArrayList<>(ret); + } return ret; } @@ -134,5 +147,8 @@ public class ScriptInfo { deleted = d; abc.method_info.get(init_index).delete(abc, d); traits.delete(abc, d); + if (d) { + clearPacksCache(); + } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/search/ActionScriptSearch.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/search/ActionScriptSearch.java index 40a3d44ac..5da057768 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/search/ActionScriptSearch.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/search/ActionScriptSearch.java @@ -100,13 +100,21 @@ public class ActionScriptSearch { futures.add(text); } + for (Future future : futures) { + try { + future.get(); + } catch (CancellationException ex) { + throw new InterruptedException(); + } catch (ExecutionException ex) { + Logger.getLogger(ActionScriptSearch.class.getName()).log(Level.SEVERE, null, ex); + } + } } } catch (InterruptedException ex) { for (Future future : futures) { future.cancel(true); } } - return found; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/CancellableWorker.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/CancellableWorker.java index 8b3d47f8d..abbb7835a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/CancellableWorker.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/CancellableWorker.java @@ -43,18 +43,21 @@ public abstract class CancellableWorker implements RunnableFuture { private static List workers = Collections.synchronizedList(new ArrayList()); - private final FutureTask future; + private FutureTask future; private static final Map threadWorkers = Collections.synchronizedMap(new WeakHashMap<>()); private List subWorkers = Collections.synchronizedList(new ArrayList<>()); + private Thread thread; + public CancellableWorker() { super(); Callable callable = new Callable() { @Override public T call() throws Exception { - threadWorkers.put(Thread.currentThread(), CancellableWorker.this); + thread = Thread.currentThread(); + threadWorkers.put(thread, CancellableWorker.this); return doInBackground(); } }; @@ -130,6 +133,9 @@ public abstract class CancellableWorker implements RunnableFuture { } private void workerDone() { + if (thread != null && threadWorkers.get(thread) == this) { + threadWorkers.remove(thread); + } workers.remove(this); done(); } @@ -165,4 +171,11 @@ public abstract class CancellableWorker implements RunnableFuture { } } } + + public void free() { + future = null; + for (CancellableWorker w : subWorkers) { + w.free(); + } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java index fddb8795c..1c9c849e5 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java @@ -143,6 +143,8 @@ public class GenericTagPanel extends JPanel implements ChangeListener { removeButtons.clear(); genericTagPropertiesEditPanel.removeAll(); genericTagPropertiesEditPanel.setSize(0, 0); + tag = null; + editedTag = null; } public void setEditMode(boolean edit, Tag tag) { diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java index e11d9377f..510d38a1c 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java @@ -467,7 +467,11 @@ public class GenericTagTreePanel extends GenericTagPanel { @Override public void clear() { - + tag = null; + editedTag = null; + tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("root"))); + revalidate(); + repaint(); } private static final class TableFieldNodes extends DefaultMutableTreeNode { diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 3ccf2ffe6..f913167cb 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -1185,6 +1185,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { clearImagePanel(); timelined = null; swf = null; + lda = null; fireMediaDisplayStateChanged(); } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index c0bc05ce7..a4203c0a2 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -107,7 +107,6 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; import java.util.logging.Formatter; @@ -922,6 +921,8 @@ public class Main { try { result.add(worker.get()); + worker.free(); + worker = null; } catch (CancellationException ex) { logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", sourceInfo.getFileTitleOrName()); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 095e96a3f..b921317bd 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -989,17 +989,23 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se i--; } } + for (SWF swf : swfsToClose) { + Main.searchResultsStorage.destroySwf(swf); + } swfs.remove(swfList); oldItem = null; clear(); updateUi(); - List swfs2 = new ArrayList<>(swfList); - for (SWF swf : swfs2) { + for (SWF swf : swfsToClose) { swf.clearTagSwfs(); } + refreshTree(); + + mainMenu.updateComponents(null); + previewPanel.clear(); return true; } @@ -1883,7 +1889,6 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (searchDialog.getCurrentScope() == SearchDialog.SCOPE_ALL_FILES) { Set allSwfs = getAllSwfs(); - for (SWF s : allSwfs) { if (s.isAS3()) { scopeAs3.put(s, s.getAS3Packs()); @@ -2791,9 +2796,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se tagTree.updateSwfs(swfs); if (treeItem != null) { - SWF treeItemSwf = treeItem.getSwf().getRootSwf(); - if (this.swfs.contains(treeItemSwf.swfList)) { - setTagTreeSelectedNode(treeItem); + SWF swf = treeItem.getSwf(); + if (swf != null) { + SWF treeItemSwf = swf.getRootSwf(); + if (this.swfs.contains(treeItemSwf.swfList)) { + setTagTreeSelectedNode(treeItem); + } } } diff --git a/src/com/jpexs/decompiler/flash/gui/SearchResultsStorage.java b/src/com/jpexs/decompiler/flash/gui/SearchResultsStorage.java index 52e6a20d9..5fdb32ced 100644 --- a/src/com/jpexs/decompiler/flash/gui/SearchResultsStorage.java +++ b/src/com/jpexs/decompiler/flash/gui/SearchResultsStorage.java @@ -319,4 +319,13 @@ public class SearchResultsStorage { } return ret; } + + public void destroySwf(SWF swf) { + String swfId = getSwfId(swf); + for (int i = 0; i < swfIds.size(); i++) { + if (swfIds.get(i).equals(swfId)) { + unpackedData.remove(i); + } + } + } }