Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions FernFlower-Patches/0038-Make-decomp-threaded.patch
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ Subject: [PATCH] Make decomp threaded
`-thr AUTO` to auto select, (default)

diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
index 305b06903b36bc4cdb740a8f050543403e6406fe..e734afaf2fb87c2e9d6ede6925f4ca6b3d560773 100644
index 305b06903b36bc4cdb740a8f050543403e6406fe..fc2ed5383e6e7ce5b4121c5bd5e77a6cfe0059ff 100644
--- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
+++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
@@ -37,7 +37,8 @@ public class ClassesProcessor implements CodeConstants {
@@ -32,12 +32,13 @@ import org.jetbrains.java.decompiler.util.TextBuffer;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;

public class ClassesProcessor implements CodeConstants {
public static final int AVERAGE_CLASS_SIZE = 16 * 1024;

private final StructContext context;
- private final Map<String, ClassNode> mapRootClasses = new HashMap<>();
+ //TODO, This is synchronized because LambdaProcessor adds classes to this. Figure out a way to not sync this map.
+ private final Map<String, ClassNode> mapRootClasses = Collections.synchronizedMap(new HashMap<>());
+ private final Map<String, ClassNode> mapRootClasses = new ConcurrentHashMap<>();
private final Set<String> whitelist = new HashSet<>();

private static class Inner {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: zml <zml@stellardrift.ca>
Date: Wed, 23 Mar 2022 21:41:32 -0700
Subject: [PATCH] Cache zip file instances and source class data


diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java
index eefcf79cedb6148f5c02b8077914a467f3d58b64..025a8ac22323407bf6b2a4f3e70a0409abadc7d9 100644
--- a/src/org/jetbrains/java/decompiler/main/Fernflower.java
+++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java
@@ -137,6 +137,7 @@ public class Fernflower implements IDecompiledData {
}

public void clearContext() {
+ structContext.clear();
DecompilerContext.setCurrentContext(null);
}

diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java
index cab48267cfe9a4b6c1f7f63449340cf6d446bd92..5d717145324d1547432ed54d4d6358ba9baf0c1e 100644
--- a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java
+++ b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java
@@ -7,6 +7,7 @@ import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.ZipFileCache;

import java.io.*;
import java.nio.charset.StandardCharsets;
@@ -144,6 +145,7 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
private final Fernflower engine;
private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>();
private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>();
+ private final ZipFileCache openZips = new ZipFileCache();

protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) {
root = destination;
@@ -179,16 +181,15 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {

@Override
public byte[] getBytecode(String externalPath, String internalPath) throws IOException {
- File file = new File(externalPath);
if (internalPath == null) {
+ File file = new File(externalPath);
return InterpreterUtil.getBytes(file);
}
else {
- try (ZipFile archive = new ZipFile(file)) {
- ZipEntry entry = archive.getEntry(internalPath);
- if (entry == null) throw new IOException("Entry not found: " + internalPath);
- return InterpreterUtil.getBytes(archive, entry);
- }
+ final ZipFile archive = this.openZips.get(externalPath);
+ ZipEntry entry = archive.getEntry(internalPath);
+ if (entry == null) throw new IOException("Entry not found: " + internalPath);
+ return InterpreterUtil.getBytes(archive, entry);
}
}

@@ -266,7 +267,8 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
return;
}

- try (ZipFile srcArchive = new ZipFile(new File(source))) {
+ try {
+ ZipFile srcArchive = this.openZips.get(source);
ZipEntry entry = srcArchive.getEntry(entryName);
if (entry != null) {
try (InputStream in = srcArchive.getInputStream(entry)) {
@@ -336,4 +338,9 @@ public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {
DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN);
}
}
+
+ @Override
+ public void close() throws IOException {
+ this.openZips.close();
+ }
}
diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java b/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java
index 164cd142118f37d33375c19b8903d61658d3a07a..cfb332145a5ce8f20882732a76c64c4f7a2f3b58 100644
--- a/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java
+++ b/src/org/jetbrains/java/decompiler/main/decompiler/SingleFileSaver.java
@@ -17,11 +17,13 @@ import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.ZipFileCache;

public class SingleFileSaver implements IResultSaver {
private final File target;
private ZipOutputStream output;
private Set<String> entries = new HashSet<>();
+ private final ZipFileCache openZips = new ZipFileCache();

public SingleFileSaver(File target) {
this.target = target;
@@ -72,7 +74,8 @@ public class SingleFileSaver implements IResultSaver {
if (!checkEntry(entryName))
return;

- try (ZipFile srcArchive = new ZipFile(new File(source))) {
+ try {
+ final ZipFile srcArchive = this.openZips.get(source);
ZipEntry entry = srcArchive.getEntry(entryName);
if (entry != null) {
try (InputStream in = srcArchive.getInputStream(entry)) {
@@ -125,6 +128,11 @@ public class SingleFileSaver implements IResultSaver {
}
}

+ @Override
+ public void close() throws IOException {
+ this.openZips.close();
+ }
+
private boolean checkEntry(String entryName) {
boolean added = entries.add(entryName);
if (!added) {
diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java b/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java
index 81cd12ba302af00d66c3f699804651d940f06a5a..3c0db2d989160f1d05fbb21a2e34ca883f1ea946 100644
--- a/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java
+++ b/src/org/jetbrains/java/decompiler/main/decompiler/ThreadSafeResultSaver.java
@@ -5,13 +5,14 @@ import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
+import org.jetbrains.java.decompiler.util.ZipFileCache;

import java.io.*;
import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
@@ -23,11 +24,11 @@ import java.util.zip.ZipOutputStream;
*/
//TODO, Split off default impl inside ConsoleDecompiler and make this extend that.
public class ThreadSafeResultSaver implements IResultSaver {
-
- private final Map<String, ArchiveContext> archiveContexts = new HashMap<>();
+ private final Map<String, ArchiveContext> archiveContexts = new ConcurrentHashMap<>();
private final File target;
private final boolean archiveMode;//Latch for Archive mode.
private ArchiveContext singeArchiveCtx;
+ private final ZipFileCache sources = new ZipFileCache();

public ThreadSafeResultSaver(File target) {
this.target = target;
@@ -90,7 +91,8 @@ public class ThreadSafeResultSaver implements IResultSaver {
if (!ctx.addEntry(entryName)) {
return;
}
- try (ZipFile srcArchive = new ZipFile(new File(source))) {
+ try {
+ final ZipFile srcArchive = this.sources.get(source);
ZipEntry entry = srcArchive.getEntry(entryName);
if (entry != null) {
try (InputStream in = srcArchive.getInputStream(entry)) {
@@ -194,6 +196,19 @@ public class ThreadSafeResultSaver implements IResultSaver {
}
}

+ @Override
+ public void close() throws IOException {
+ if (!this.archiveContexts.isEmpty()) {
+ for (final Map.Entry<String, ArchiveContext> entry : this.archiveContexts.entrySet()) {
+ DecompilerContext.getLogger().writeMessage("Unclosed archive detected at end of run in " + entry.getKey(), IFernflowerLogger.Severity.ERROR);
+ entry.getValue().stream.close();
+ }
+ this.archiveContexts.clear();
+ }
+
+ this.sources.close();
+ }
+
private String getAbsolutePath(String path) {
return new File(target, path).getAbsolutePath();
}
diff --git a/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java
index 23d9500ce885a6ba36c25be48400df6e4e380a90..1b25473dd69252186118cadc4d45f366ddb36dbd 100644
--- a/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java
+++ b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java
@@ -1,11 +1,12 @@
// 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.
package org.jetbrains.java.decompiler.main.extern;

+import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.jar.Manifest;

-public interface IResultSaver {
+public interface IResultSaver extends AutoCloseable {
long STABLE_ZIP_TIMESTAMP = 0x386D4380; // 01/01/2000 00:00:00 java 8 breaks when using 0.

void saveFolder(String path);
@@ -31,6 +32,9 @@ public interface IResultSaver {

void closeArchive(String path, String archiveName);

+ @Override
+ default void close() throws IOException {}
+
default byte[] getCodeLineData(int[] mappings) {
if (mappings == null || mappings.length == 0) {
return null;
diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java
index 0f3dc610ec134a438e81561ee2b15bb97edc3fb3..c3cdea305c71f1add8da3e7aed5bbfd0f54a78d9 100644
--- a/src/org/jetbrains/java/decompiler/struct/StructContext.java
+++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java
@@ -3,6 +3,7 @@ package org.jetbrains.java.decompiler.struct;

import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
+import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger.Severity;
import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain;
import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor;
@@ -153,7 +154,7 @@ public class StructContext {
StructClass cl = StructClass.create(new DataInputFullStream(bytes), isOwn, loader);
classes.put(cl.qualifiedName, cl);
unit.addClass(cl, name);
- loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name));
+ loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name, bytes));
}
else {
unit.addOtherEntry(file.getAbsolutePath(), name);
@@ -246,4 +247,12 @@ public class StructContext {
List<String> params = this.abstractNames.get(className + ' ' + methodName + ' ' + descriptor);
return params != null && index < params.size() ? params.get(index) : _default;
}
+
+ public void clear() {
+ try {
+ this.saver.close();
+ } catch (final IOException ex) {
+ DecompilerContext.getLogger().writeMessage("Failed to close out result saver", IFernflowerLogger.Severity.ERROR, ex);
+ }
+ }
}
diff --git a/src/org/jetbrains/java/decompiler/util/ZipFileCache.java b/src/org/jetbrains/java/decompiler/util/ZipFileCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6eabd65a54198170927ef523deb63e5f2e7b3c0
--- /dev/null
+++ b/src/org/jetbrains/java/decompiler/util/ZipFileCache.java
@@ -0,0 +1,44 @@
+// Copyright 2022 ForgeFlower contributors and 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.
+package org.jetbrains.java.decompiler.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.ZipFile;
+
+public final class ZipFileCache implements AutoCloseable {
+ private final Map<String, ZipFile> files = new ConcurrentHashMap<>();
+
+ public ZipFile get(final String path) throws IOException {
+ try {
+ return this.files.computeIfAbsent(path, pth -> {
+ try {
+ return new ZipFile(new File(pth));
+ } catch (final IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ });
+ } catch (final UncheckedIOException ex) {
+ throw ex.getCause();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ IOException failure = null;
+ for (final Map.Entry<String, ZipFile> entry : this.files.entrySet()) {
+ try {
+ entry.getValue().close();
+ } catch (final IOException ex) {
+ if (failure == null) {
+ failure = ex;
+ } else {
+ failure.addSuppressed(ex);
+ }
+ }
+ }
+ this.files.clear();
+ }
+}
diff --git a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java
index 7d29c198014cbfee8092fd31461208ce58e7f775..1af0ba49459b15bcc9d6bea2f618cf396a2202b2 100644
--- a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java
+++ b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java
@@ -60,7 +60,7 @@ public class DecompilerTestFixture {
if (tempDir != null && cleanup) {
delete(tempDir);
}
- decompiler.close();
+ decompiler.clear();
}

public File getTestDataDir() {
@@ -149,7 +149,7 @@ public class DecompilerTestFixture {
}
}

- void close() {
+ void clear() {
for (ZipFile file : zipFiles.values()) {
try {
file.close();