/* * Copyright (C) 2010-2023 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.action.ActionList; import com.jpexs.decompiler.flash.cache.ScriptDecompiledListener; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; 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.ImmediateFuture; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author JPEXS */ public class DecompilerPool { private final ThreadPoolExecutor executor; private Map>> openableToFutures = new WeakHashMap<>(); public DecompilerPool() { int threadCount = Configuration.getParallelThreadCount(); executor = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); } public Future submitTask(ASMSource src, ActionList actions, ScriptDecompiledListener listener) { Callable callable = new Callable() { @Override public HighlightedText call() throws Exception { if (listener != null) { listener.onStart(); } HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); writer.startFunction("!script"); src.getActionScriptSource(writer, actions); writer.endFunction(); writer.finishHilights(); HighlightedText result = new HighlightedText(writer); SWF swf = src.getSwf(); if (swf != null) { swf.as2Cache.put(src, result); } if (listener != null) { listener.onComplete(result); } return result; } }; return submit(callable); } public Future submitTask(AbcIndexing abcIndex, ScriptPack pack, ScriptDecompiledListener listener) { Callable callable = new Callable() { @Override public HighlightedText call() throws Exception { if (listener != null) { listener.onStart(); } int scriptIndex = pack.scriptIndex; ScriptInfo script = null; if (scriptIndex > -1) { script = pack.abc.script_info.get(scriptIndex); } boolean parallel = Configuration.parallelSpeedUp.get(); HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); pack.toSource(abcIndex, writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel, false); writer.finishHilights(); HighlightedText result = new HighlightedText(writer); Openable openable = pack.getOpenable(); SWF swf = (openable instanceof SWF) ? (SWF) openable : ((ABC) openable).getSwf(); if (swf != null) { swf.as3Cache.put(pack, result); } if (listener != null) { listener.onComplete(result); } return result; } }; return submit(callable); } private Future submit(Callable callable) { boolean parallel = Configuration.parallelSpeedUp.get(); if (parallel) { Future f = executor.submit(callable); return f; } else { boolean cancelled = false; Throwable throwable = null; HighlightedText result = null; try { result = callable.call(); } catch (InterruptedException ex) { cancelled = true; } catch (Exception ex) { throwable = ex; } return new ImmediateFuture<>(result, throwable, cancelled); } } public String getStat() { return "core: " + executor.getCorePoolSize() + " size: " + executor.getPoolSize() + " largest: " + executor.getLargestPoolSize() + " max: " + executor.getMaximumPoolSize() + " active: " + executor.getActiveCount() + " count: " + executor.getTaskCount() + " completed: " + executor.getCompletedTaskCount(); } public HighlightedText decompile(ASMSource src, ActionList actions) throws InterruptedException { Future future = submitTask(src, actions, null); SWF swf = src.getSwf(); if (!openableToFutures.containsKey(swf)) { openableToFutures.put(swf, new ArrayList<>()); } openableToFutures.get(swf).add(future); try { return future.get(); } catch (InterruptedException ex) { future.cancel(true); throw ex; } catch (ExecutionException ex) { Logger.getLogger(DecompilerPool.class.getName()).log(Level.SEVERE, null, ex); } finally { List> futures = openableToFutures.get(swf); if (futures != null) { futures.remove(future); } } return null; } public HighlightedText decompile(AbcIndexing abcIndex, ScriptPack pack) throws InterruptedException { Future future = submitTask(abcIndex, pack, null); Openable openable = pack.getOpenable(); if (!openableToFutures.containsKey(openable)) { openableToFutures.put(openable, new ArrayList<>()); } openableToFutures.get(openable).add(future); try { return future.get(); } catch (InterruptedException ex) { future.cancel(true); throw ex; } catch (ExecutionException ex) { Logger.getLogger(DecompilerPool.class.getName()).log(Level.SEVERE, null, ex); } finally { List> futures = openableToFutures.get(openable); if (futures != null) { futures.remove(future); } } return null; } public void shutdown() throws InterruptedException { executor.shutdown(); executor.awaitTermination(100, TimeUnit.SECONDS); } public void destroySwf(SWF swf) { List> futures = openableToFutures.get(swf); if (futures != null) { for (Future future : futures) { if (future != null) { future.cancel(true); } } } } }