|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: zml <zml@stellardrift.ca> |
| 3 | +Date: Wed, 23 Mar 2022 21:41:32 -0700 |
| 4 | +Subject: [PATCH] Cache zip file instances and source class data |
| 5 | + |
| 6 | + |
| 7 | +diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java |
| 8 | +index 22cb517d3ab2901bb7d88e38802eb2825584b78d..c62bf0d68c62e0a982104172ce4f78ae4a6f233b 100644 |
| 9 | +--- a/src/org/jetbrains/java/decompiler/main/Fernflower.java |
| 10 | ++++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java |
| 11 | +@@ -125,6 +125,7 @@ public class Fernflower implements IDecompiledData { |
| 12 | + |
| 13 | + public void clearContext() { |
| 14 | + DecompilerContext.setCurrentContext(null); |
| 15 | ++ structContext.clear(); |
| 16 | + } |
| 17 | + |
| 18 | + @Override |
| 19 | +diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java |
| 20 | +index 3e939e4e5a39fce84d694a02e2e53ec486e50ba5..c8acfeb637ea18fb06840f696c3b444202f02d64 100644 |
| 21 | +--- a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java |
| 22 | ++++ b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java |
| 23 | +@@ -8,6 +8,7 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; |
| 24 | + import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; |
| 25 | + import org.jetbrains.java.decompiler.main.extern.IResultSaver; |
| 26 | + import org.jetbrains.java.decompiler.util.InterpreterUtil; |
| 27 | ++import org.jetbrains.java.decompiler.util.ZipFileCache; |
| 28 | + |
| 29 | + import java.io.*; |
| 30 | + import java.nio.charset.StandardCharsets; |
| 31 | +@@ -148,6 +149,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { |
| 32 | + private final Fernflower engine; |
| 33 | + private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>(); |
| 34 | + private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>(); |
| 35 | ++ private final ZipFileCache openZips = new ZipFileCache(); |
| 36 | + |
| 37 | + protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) { |
| 38 | + root = destination; |
| 39 | +@@ -199,16 +201,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { |
| 40 | + |
| 41 | + @Override |
| 42 | + public byte[] getBytecode(String externalPath, String internalPath) throws IOException { |
| 43 | +- File file = new File(externalPath); |
| 44 | + if (internalPath == null) { |
| 45 | ++ File file = new File(externalPath); |
| 46 | + return InterpreterUtil.getBytes(file); |
| 47 | + } |
| 48 | + else { |
| 49 | +- try (ZipFile archive = new ZipFile(file)) { |
| 50 | +- ZipEntry entry = archive.getEntry(internalPath); |
| 51 | +- if (entry == null) throw new IOException("Entry not found: " + internalPath); |
| 52 | +- return InterpreterUtil.getBytes(archive, entry); |
| 53 | +- } |
| 54 | ++ final ZipFile archive = this.openZips.get(externalPath); |
| 55 | ++ ZipEntry entry = archive.getEntry(internalPath); |
| 56 | ++ if (entry == null) throw new IOException("Entry not found: " + internalPath); |
| 57 | ++ return InterpreterUtil.getBytes(archive, entry); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | +@@ -279,7 +280,8 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { |
| 62 | + return; |
| 63 | + } |
| 64 | + |
| 65 | +- try (ZipFile srcArchive = new ZipFile(new File(source))) { |
| 66 | ++ try { |
| 67 | ++ ZipFile srcArchive = this.openZips.get(source); |
| 68 | + ZipEntry entry = srcArchive.getEntry(entryName); |
| 69 | + if (entry != null) { |
| 70 | + try (InputStream in = srcArchive.getInputStream(entry)) { |
| 71 | +@@ -346,4 +348,9 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { |
| 72 | + DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN); |
| 73 | + } |
| 74 | + } |
| 75 | ++ |
| 76 | ++ @Override |
| 77 | ++ public void close() throws IOException { |
| 78 | ++ this.openZips.close(); |
| 79 | ++ } |
| 80 | + } |
| 81 | +diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java b/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java |
| 82 | +index 8e37643c54c6e961c317d9bc2c1ed5556e0b4179..d12e1a684a58f43194776b61d1238c9c863262b4 100644 |
| 83 | +--- a/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java |
| 84 | ++++ b/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java |
| 85 | +@@ -17,11 +17,13 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; |
| 86 | + import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; |
| 87 | + import org.jetbrains.java.decompiler.main.extern.IResultSaver; |
| 88 | + import org.jetbrains.java.decompiler.util.InterpreterUtil; |
| 89 | ++import org.jetbrains.java.decompiler.util.ZipFileCache; |
| 90 | + |
| 91 | + public class SingleFileSaver implements IResultSaver { |
| 92 | + private final File target; |
| 93 | + private ZipOutputStream output; |
| 94 | + private Set<String> entries = new HashSet<>(); |
| 95 | ++ private final ZipFileCache openZips = new ZipFileCache(); |
| 96 | + |
| 97 | + public SingleFileSaver(File target) { |
| 98 | + this.target = target; |
| 99 | +@@ -65,7 +67,8 @@ public class SingleFileSaver implements IResultSaver { |
| 100 | + if (!checkEntry(entryName)) |
| 101 | + return; |
| 102 | + |
| 103 | +- try (ZipFile srcArchive = new ZipFile(new File(source))) { |
| 104 | ++ try { |
| 105 | ++ final ZipFile srcArchive = this.openZips.get(source); |
| 106 | + ZipEntry entry = srcArchive.getEntry(entryName); |
| 107 | + if (entry != null) { |
| 108 | + try (InputStream in = srcArchive.getInputStream(entry)) { |
| 109 | +@@ -115,6 +118,11 @@ public class SingleFileSaver implements IResultSaver { |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | ++ @Override |
| 114 | ++ public void close() throws IOException { |
| 115 | ++ this.openZips.close(); |
| 116 | ++ } |
| 117 | ++ |
| 118 | + private boolean checkEntry(String entryName) { |
| 119 | + boolean added = entries.add(entryName); |
| 120 | + if (!added) { |
| 121 | +diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java b/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java |
| 122 | +index 585494c81bda9c2bde5ea284fb8851df3c1b072b..565949b9782abfbb9303e86b59cca0986a9149df 100644 |
| 123 | +--- a/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java |
| 124 | ++++ b/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java |
| 125 | +@@ -5,13 +5,14 @@ import org.jetbrains.java.decompiler.main.DecompilerContext; |
| 126 | + import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; |
| 127 | + import org.jetbrains.java.decompiler.main.extern.IResultSaver; |
| 128 | + import org.jetbrains.java.decompiler.util.InterpreterUtil; |
| 129 | ++import org.jetbrains.java.decompiler.util.ZipFileCache; |
| 130 | + |
| 131 | + import java.io.*; |
| 132 | + import java.nio.charset.StandardCharsets; |
| 133 | +-import java.util.HashMap; |
| 134 | + import java.util.HashSet; |
| 135 | + import java.util.Map; |
| 136 | + import java.util.Set; |
| 137 | ++import java.util.concurrent.ConcurrentHashMap; |
| 138 | + import java.util.concurrent.ExecutionException; |
| 139 | + import java.util.concurrent.ExecutorService; |
| 140 | + import java.util.concurrent.Executors; |
| 141 | +@@ -27,11 +28,11 @@ import java.util.zip.ZipOutputStream; |
| 142 | + */ |
| 143 | + //TODO, Split off default impl inside ConsoleDecompiler and make this extend that. |
| 144 | + public class ThreadSafeResultSaver implements IResultSaver { |
| 145 | +- |
| 146 | +- private final Map<String, ArchiveContext> archiveContexts = new HashMap<>(); |
| 147 | ++ private final Map<String, ArchiveContext> archiveContexts = new ConcurrentHashMap<>(); |
| 148 | + private final File target; |
| 149 | + private final boolean archiveMode;//Latch for Archive mode. |
| 150 | + private ArchiveContext singeArchiveCtx; |
| 151 | ++ private final ZipFileCache sources = new ZipFileCache(); |
| 152 | + |
| 153 | + public ThreadSafeResultSaver(File target) { |
| 154 | + this.target = target; |
| 155 | +@@ -88,7 +89,8 @@ public class ThreadSafeResultSaver implements IResultSaver { |
| 156 | + if (!ctx.addEntry(entryName)) { |
| 157 | + return; |
| 158 | + } |
| 159 | +- try (ZipFile srcArchive = new ZipFile(new File(source))) { |
| 160 | ++ try { |
| 161 | ++ final ZipFile srcArchive = this.sources.get(source); |
| 162 | + ZipEntry entry = srcArchive.getEntry(entryName); |
| 163 | + if (entry != null) { |
| 164 | + try (InputStream in = srcArchive.getInputStream(entry)) { |
| 165 | +@@ -204,6 +206,20 @@ public class ThreadSafeResultSaver implements IResultSaver { |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | ++ @Override |
| 170 | ++ public void close() throws IOException { |
| 171 | ++ if (!this.archiveContexts.isEmpty()) { |
| 172 | ++ for (final Map.Entry<String, ArchiveContext> entry : this.archiveContexts.entrySet()) { |
| 173 | ++ DecompilerContext.getLogger().writeMessage("Unclosed archive detected at end of run in " + entry.getKey(), IFernflowerLogger.Severity.ERROR); |
| 174 | ++ entry.getValue().executor.shutdown(); |
| 175 | ++ entry.getValue().stream.close(); |
| 176 | ++ } |
| 177 | ++ this.archiveContexts.clear(); |
| 178 | ++ } |
| 179 | ++ |
| 180 | ++ this.sources.close(); |
| 181 | ++ } |
| 182 | ++ |
| 183 | + private String getAbsolutePath(String path) { |
| 184 | + return new File(target, path).getAbsolutePath(); |
| 185 | + } |
| 186 | +diff --git a/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java |
| 187 | +index e16c60301264feaeaabfaf281495807d888456db..eb2d5a7a09acca7a0fc101c6d31c364867ea2eb8 100644 |
| 188 | +--- a/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java |
| 189 | ++++ b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java |
| 190 | +@@ -1,11 +1,12 @@ |
| 191 | + // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. |
| 192 | + package org.jetbrains.java.decompiler.main.extern; |
| 193 | + |
| 194 | ++import java.io.IOException; |
| 195 | + import java.nio.ByteBuffer; |
| 196 | + import java.nio.ByteOrder; |
| 197 | + import java.util.jar.Manifest; |
| 198 | + |
| 199 | +-public interface IResultSaver { |
| 200 | ++public interface IResultSaver extends AutoCloseable { |
| 201 | + void saveFolder(String path); |
| 202 | + |
| 203 | + void copyFile(String source, String path, String entryName); |
| 204 | +@@ -28,6 +29,9 @@ public interface IResultSaver { |
| 205 | + } |
| 206 | + |
| 207 | + void closeArchive(String path, String archiveName); |
| 208 | ++ |
| 209 | ++ @Override |
| 210 | ++ default void close() throws IOException {} |
| 211 | + |
| 212 | + default byte[] getCodeLineData(int[] mappings) { |
| 213 | + if (mappings == null || mappings.length == 0) { |
| 214 | +diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java |
| 215 | +index 5e119e5030efab2bccc1bd96601075c081056339..920932069cc12c2d55842559ba0060be3f227262 100644 |
| 216 | +--- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java |
| 217 | ++++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java |
| 218 | +@@ -189,12 +189,9 @@ public class ContextUnit { |
| 219 | + } |
| 220 | + |
| 221 | + //Ask the executor to shutdown |
| 222 | +- executor.shutdown(); |
| 223 | + waitForAll(futures); |
| 224 | + futures.clear(); |
| 225 | + |
| 226 | +- executor = Executors.newFixedThreadPool(threads); |
| 227 | +- |
| 228 | + // classes |
| 229 | + for (ClassContext clCtx : toProcess) { |
| 230 | + if (clCtx.shouldContinue) { |
| 231 | +diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java |
| 232 | +index 5aa59c4d71dc73bbc8875879ac4c90ab030d6920..a3cec5d4e0097c95d596c924e49fe21b6423ef20 100644 |
| 233 | +--- a/src/org/jetbrains/java/decompiler/struct/StructContext.java |
| 234 | ++++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java |
| 235 | +@@ -3,6 +3,7 @@ package org.jetbrains.java.decompiler.struct; |
| 236 | + |
| 237 | + import org.jetbrains.java.decompiler.main.DecompilerContext; |
| 238 | + import org.jetbrains.java.decompiler.main.extern.IResultSaver; |
| 239 | ++import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; |
| 240 | + import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger.Severity; |
| 241 | + import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; |
| 242 | + import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; |
| 243 | +@@ -153,7 +154,7 @@ public class StructContext { |
| 244 | + StructClass cl = StructClass.create(new DataInputFullStream(bytes), isOwn, loader); |
| 245 | + classes.put(cl.qualifiedName, cl); |
| 246 | + unit.addClass(cl, name); |
| 247 | +- loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name)); |
| 248 | ++ loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name, bytes)); |
| 249 | + } |
| 250 | + else { |
| 251 | + unit.addOtherEntry(file.getAbsolutePath(), name); |
| 252 | +@@ -246,4 +247,13 @@ public class StructContext { |
| 253 | + List<String> params = this.abstractNames.get(className + ' ' + methodName + ' ' + descriptor); |
| 254 | + return params != null && index < params.size() ? params.get(index) : _default; |
| 255 | + } |
| 256 | ++ |
| 257 | ++ public void clear() { |
| 258 | ++ try { |
| 259 | ++ this.saver.close(); |
| 260 | ++ } catch (final IOException ex) { |
| 261 | ++ DecompilerContext.getLogger().writeMessage("Failed to close out result saver", IFernflowerLogger.Severity.ERROR, ex); |
| 262 | ++ } |
| 263 | ++ } |
| 264 | ++ |
| 265 | + } |
| 266 | +diff --git a/src/org/jetbrains/java/decompiler/util/ZipFileCache.java b/src/org/jetbrains/java/decompiler/util/ZipFileCache.java |
| 267 | +new file mode 100644 |
| 268 | +index 0000000000000000000000000000000000000000..92ebb33e213d4a9da802284f77df25b0cebbfa4f |
| 269 | +--- /dev/null |
| 270 | ++++ b/src/org/jetbrains/java/decompiler/util/ZipFileCache.java |
| 271 | +@@ -0,0 +1,43 @@ |
| 272 | ++package org.jetbrains.java.decompiler.util; |
| 273 | ++ |
| 274 | ++import java.io.File; |
| 275 | ++import java.io.IOException; |
| 276 | ++import java.io.UncheckedIOException; |
| 277 | ++import java.util.Map; |
| 278 | ++import java.util.concurrent.ConcurrentHashMap; |
| 279 | ++import java.util.zip.ZipFile; |
| 280 | ++ |
| 281 | ++public final class ZipFileCache implements AutoCloseable { |
| 282 | ++ private final Map<String, ZipFile> files = new ConcurrentHashMap<>(); |
| 283 | ++ |
| 284 | ++ public ZipFile get(final String path) throws IOException { |
| 285 | ++ try { |
| 286 | ++ return this.files.computeIfAbsent(path, pth -> { |
| 287 | ++ try { |
| 288 | ++ return new ZipFile(new File(pth)); |
| 289 | ++ } catch (final IOException ex) { |
| 290 | ++ throw new UncheckedIOException(ex); |
| 291 | ++ } |
| 292 | ++ }); |
| 293 | ++ } catch (final UncheckedIOException ex) { |
| 294 | ++ throw ex.getCause(); |
| 295 | ++ } |
| 296 | ++ } |
| 297 | ++ |
| 298 | ++ @Override |
| 299 | ++ public void close() throws IOException { |
| 300 | ++ IOException failure = null; |
| 301 | ++ for (final Map.Entry<String, ZipFile> entry : this.files.entrySet()) { |
| 302 | ++ try { |
| 303 | ++ entry.getValue().close(); |
| 304 | ++ } catch (final IOException ex) { |
| 305 | ++ if (failure == null) { |
| 306 | ++ failure = ex; |
| 307 | ++ } else { |
| 308 | ++ failure.addSuppressed(ex); |
| 309 | ++ } |
| 310 | ++ } |
| 311 | ++ } |
| 312 | ++ this.files.clear(); |
| 313 | ++ } |
| 314 | ++} |
| 315 | +diff --git a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java |
| 316 | +index 7d29c198014cbfee8092fd31461208ce58e7f775..9648d9dcb68376e43611ef659572ec361e235c9c 100644 |
| 317 | +--- a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java |
| 318 | ++++ b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java |
| 319 | +@@ -60,7 +60,7 @@ public class DecompilerTestFixture { |
| 320 | + if (tempDir != null && cleanup) { |
| 321 | + delete(tempDir); |
| 322 | + } |
| 323 | +- decompiler.close(); |
| 324 | ++ decompiler.clear(); |
| 325 | + } |
| 326 | + |
| 327 | + public File getTestDataDir() { |
| 328 | +@@ -78,11 +78,11 @@ public class DecompilerTestFixture { |
| 329 | + public ConsoleDecompiler getDecompiler() { |
| 330 | + return decompiler; |
| 331 | + } |
| 332 | +- |
| 333 | ++ |
| 334 | + public void setCleanup(boolean value) { |
| 335 | + this.cleanup = value; |
| 336 | + } |
| 337 | +- |
| 338 | ++ |
| 339 | + public boolean getCleanup() { |
| 340 | + return cleanup; |
| 341 | + } |
| 342 | +@@ -149,7 +149,7 @@ public class DecompilerTestFixture { |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | +- void close() { |
| 347 | ++ void clear() { |
| 348 | + for (ZipFile file : zipFiles.values()) { |
| 349 | + try { |
| 350 | + file.close(); |
0 commit comments