Skip to content

Commit 88e5f26

Browse files
committed
Create WorkspaceResource stream methods to combine jvm+jvm-versioned classes
1 parent 1b61858 commit 88e5f26

File tree

15 files changed

+145
-40
lines changed

15 files changed

+145
-40
lines changed

recaf-core/src/main/java/software/coley/recaf/services/callgraph/CallGraph.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import java.util.concurrent.CompletableFuture;
3737
import java.util.concurrent.ConcurrentHashMap;
3838
import java.util.concurrent.ExecutorService;
39-
import java.util.stream.Stream;
4039

4140
/**
4241
* Represents method calls as a navigable graph.
@@ -138,11 +137,9 @@ public void initialize() {
138137
// Initialize asynchronously, and mark 'isReady' if completed successfully
139138
CompletableFuture.runAsync(() -> {
140139
for (WorkspaceResource resource : workspace.getAllResources(false)) {
141-
Stream.concat(resource.jvmClassBundleStream(),
142-
resource.getVersionedJvmClassBundles().values().stream()).forEach(bundle -> {
143-
for (JvmClassInfo jvmClass : bundle.values()) {
140+
resource.jvmAllClassBundleStreamRecursive().forEach(bundle -> {
141+
for (JvmClassInfo jvmClass : bundle.values())
144142
visit(jvmClass);
145-
}
146143
});
147144
}
148145
}, threadPool).whenComplete((unused, t) -> {
@@ -377,22 +374,18 @@ public Result<Resolution<JvmClassInfo, MethodMember>> resolve(int opcode, @Nonnu
377374
@Override
378375
public void onAddLibrary(@Nonnull Workspace workspace, @Nonnull WorkspaceResource library) {
379376
// Visit all library classes
380-
Stream.concat(library.jvmClassBundleStream(),
381-
library.getVersionedJvmClassBundles().values().stream()).forEach(bundle -> {
382-
for (JvmClassInfo jvmClass : bundle.values()) {
377+
library.jvmAllClassBundleStreamRecursive().forEach(bundle -> {
378+
for (JvmClassInfo jvmClass : bundle.values())
383379
onNewClass(library, bundle, jvmClass);
384-
}
385380
});
386381
}
387382

388383
@Override
389384
public void onRemoveLibrary(@Nonnull Workspace workspace, @Nonnull WorkspaceResource library) {
390385
// Remove all vertices from library
391-
Stream.concat(library.jvmClassBundleStream(),
392-
library.getVersionedJvmClassBundles().values().stream()).forEach(bundle -> {
393-
for (JvmClassInfo jvmClass : bundle.values()) {
386+
library.jvmAllClassBundleStreamRecursive().forEach(bundle -> {
387+
for (JvmClassInfo jvmClass : bundle.values())
394388
onRemoveClass(library, bundle, jvmClass);
395-
}
396389
});
397390
}
398391

recaf-core/src/main/java/software/coley/recaf/services/compile/VirtualFileManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public Iterable<JavaFileObject> list(@Nonnull Location location, @Nonnull String
5252
name.indexOf('/', formatted.length()) == -1;
5353
}
5454
return () -> new ClassPathIterator(list.iterator(), virtualClasspath.stream()
55-
.flatMap(resource -> resource.jvmClassBundleStream().flatMap(b -> b.entrySet().stream()))
55+
.flatMap(resource -> resource.jvmClassBundleStreamRecursive().flatMap(b -> b.entrySet().stream()))
5656
.filter(entry -> check.test(entry.getKey()))
5757
.<JavaFileObject>map(entry -> new ResourceVirtualJavaFileObject(entry.getKey(),
5858
entry.getValue().getBytecode(), JavaFileObject.Kind.CLASS))

recaf-core/src/main/java/software/coley/recaf/services/decompile/vineflower/ClassSource.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ protected DecompiledOutputSink getSink() {
4040

4141
@Override
4242
public Entries getEntries() {
43-
// TODO: Bug in QF/VF makes it so that 'addLibrary' doesn't yield inner info for a class provided with 'addSource'
43+
// TODO: Bug in Vineflower makes it so that 'addLibrary' doesn't yield inner info for a class provided with 'addSource'
4444
// So for now until this is fixed upstream we will also supply inners here.
45-
// This will make QF/VF decompile each inner class separately as well, but its the best fix for now without
45+
// This will make Vineflower decompile each inner class separately as well, but its the best fix for now without
4646
// too much of a perf hit.
4747
List<Entry> entries = new ArrayList<>();
4848
entries.add(new Entry(targetInfo.getName(), Entry.BASE_VERSION));
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package software.coley.recaf.services.decompile.vineflower;
22

33
import jakarta.annotation.Nonnull;
4+
import org.jetbrains.java.decompiler.main.extern.IContextSource;
45
import software.coley.recaf.info.JvmClassInfo;
56
import software.coley.recaf.workspace.model.Workspace;
6-
import software.coley.recaf.workspace.model.resource.WorkspaceResource;
77

88
import java.util.Collections;
99
import java.util.List;
10-
import java.util.stream.Collectors;
1110

1211
/**
1312
* Full library source for Vineflower.
@@ -16,23 +15,23 @@
1615
* @author therathatter
1716
*/
1817
public class LibrarySource extends BaseSource {
18+
private final List<Entry> entries;
19+
1920
/**
21+
* @param entries
22+
* List of context entries in the given workspace.
2023
* @param workspace
2124
* Workspace to pull class files from.
2225
* @param targetInfo
2326
* Target class to decompile.
2427
*/
25-
protected LibrarySource(@Nonnull Workspace workspace, @Nonnull JvmClassInfo targetInfo) {
28+
protected LibrarySource(@Nonnull List<IContextSource.Entry> entries, @Nonnull Workspace workspace, @Nonnull JvmClassInfo targetInfo) {
2629
super(workspace, targetInfo);
30+
this.entries = entries;
2731
}
2832

2933
@Override
3034
public Entries getEntries() {
31-
List<Entry> entries = workspace.getAllResources(false).stream()
32-
.flatMap(WorkspaceResource::jvmClassBundleStream)
33-
.flatMap(c -> c.keySet().stream())
34-
.map(className -> new Entry(className, Entry.BASE_VERSION))
35-
.collect(Collectors.toList());
3635
return new Entries(entries, Collections.emptyList(), Collections.emptyList());
3736
}
3837
}

recaf-core/src/main/java/software/coley/recaf/services/decompile/vineflower/VineflowerDecompiler.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class VineflowerDecompiler extends AbstractJvmDecompiler {
2222
private final VineflowerConfig config;
2323
private final IFernflowerLogger logger;
2424
private final IResultSaver dummySaver = new DummyResultSaver();
25+
private final WorkspaceEntriesCache workspaceEntriesCache = new WorkspaceEntriesCache();
2526

2627
/**
2728
* New Vineflower decompiler instance.
@@ -45,14 +46,12 @@ public DecompileResult decompileInternal(@Nonnull Workspace workspace, @Nonnull
4546
try {
4647
ClassSource source = new ClassSource(workspace, info);
4748
fernflower.addSource(source);
48-
fernflower.addLibrary(new LibrarySource(workspace, info));
49+
fernflower.addLibrary(new LibrarySource(workspaceEntriesCache.getCachedEntries(workspace), workspace, info));
4950
fernflower.decompileContext();
5051

5152
String decompiled = source.getSink().getDecompiledOutput().get();
52-
53-
if (decompiled == null || decompiled.isEmpty()) {
53+
if (decompiled == null || decompiled.isEmpty())
5454
return new DecompileResult(new IllegalStateException("Missing decompilation output"), config.getHash());
55-
}
5655

5756
return new DecompileResult(decompiled, config.getHash());
5857
} catch (Exception e) {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package software.coley.recaf.services.decompile.vineflower;
2+
3+
import jakarta.annotation.Nonnull;
4+
import org.jetbrains.java.decompiler.main.extern.IContextSource;
5+
import software.coley.recaf.info.JvmClassInfo;
6+
import software.coley.recaf.workspace.model.Workspace;
7+
import software.coley.recaf.workspace.model.WorkspaceModificationListener;
8+
import software.coley.recaf.workspace.model.bundle.JvmClassBundle;
9+
import software.coley.recaf.workspace.model.resource.ResourceJvmClassListener;
10+
import software.coley.recaf.workspace.model.resource.WorkspaceResource;
11+
12+
import java.util.List;
13+
import java.util.stream.Collectors;
14+
15+
/**
16+
* Caches the list of {@link IContextSource.Entry} items in a workspace.
17+
*
18+
* @author Matt Coley
19+
* @see LibrarySource
20+
*/
21+
public class WorkspaceEntriesCache implements WorkspaceModificationListener, ResourceJvmClassListener {
22+
private List<IContextSource.Entry> cache;
23+
private int lastWorkspaceHash;
24+
25+
@Override
26+
public void onAddLibrary(@Nonnull Workspace workspace, @Nonnull WorkspaceResource library) {
27+
resetCache();
28+
}
29+
30+
@Override
31+
public void onRemoveLibrary(@Nonnull Workspace workspace, @Nonnull WorkspaceResource library) {
32+
resetCache();
33+
}
34+
35+
@Override
36+
public void onNewClass(@Nonnull WorkspaceResource resource, @Nonnull JvmClassBundle bundle, @Nonnull JvmClassInfo cls) {
37+
resetCache();
38+
}
39+
40+
@Override
41+
public void onUpdateClass(@Nonnull WorkspaceResource resource, @Nonnull JvmClassBundle bundle, @Nonnull JvmClassInfo oldCls, @Nonnull JvmClassInfo newCls) {
42+
resetCache();
43+
}
44+
45+
@Override
46+
public void onRemoveClass(@Nonnull WorkspaceResource resource, @Nonnull JvmClassBundle bundle, @Nonnull JvmClassInfo cls) {
47+
resetCache();
48+
}
49+
50+
/**
51+
* Clears the cache.
52+
*/
53+
private synchronized void resetCache() {
54+
cache = null;
55+
}
56+
57+
/**
58+
* @param workspace
59+
* Workspace to get cached entries of.
60+
*
61+
* @return List of all distinctly named entries for classes in the workspace.
62+
*/
63+
@Nonnull
64+
public synchronized List<IContextSource.Entry> getCachedEntries(@Nonnull Workspace workspace) {
65+
List<IContextSource.Entry> local = cache;
66+
int workspaceHash = System.identityHashCode(workspace);
67+
if (local == null || workspaceHash != lastWorkspaceHash) {
68+
local = workspace.getAllResources(false).stream()
69+
.flatMap(WorkspaceResource::jvmAllClassBundleStreamRecursive)
70+
.flatMap(c -> c.keySet().stream())
71+
.distinct()
72+
.map(className -> new IContextSource.Entry(className, IContextSource.Entry.BASE_VERSION))
73+
.collect(Collectors.toList());
74+
cache = local;
75+
lastWorkspaceHash = workspaceHash;
76+
}
77+
return local;
78+
}
79+
}

recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingApplier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public MappingResults applyToPrimaryResource(@Nonnull Mappings mappings) {
115115
Mappings finalMappings = mappings;
116116
ExecutorService service = ThreadUtil.phasingService(applierThreadPool);
117117
WorkspaceResource resource = workspace.getPrimaryResource();
118-
Stream.concat(resource.jvmClassBundleStream(), resource.versionedJvmClassBundleStream()).forEach(bundle -> {
118+
resource.jvmAllClassBundleStreamRecursive().forEach(bundle -> {
119119
bundle.forEach(classInfo -> {
120120
service.execute(() -> dumpIntoResults(results, workspace, resource, bundle, classInfo, finalMappings));
121121
});

recaf-core/src/main/java/software/coley/recaf/services/mapping/gen/MappingGenerator.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,9 @@ public Mappings generate(@Nullable Workspace workspace,
7373
if (workspace != null)
7474
mappings.enableClassLookup(workspace);
7575
SortedMap<String, ClassInfo> classMap = new TreeMap<>();
76-
resource.versionedJvmClassBundleStream()
76+
resource.jvmAllClassBundleStreamRecursive()
7777
.flatMap(Bundle::stream)
78-
.forEach(c -> classMap.put(c.getName(), c));
79-
classMap.putAll(resource.getJvmClassBundle());
78+
.forEach(c -> classMap.putIfAbsent(c.getName(), c));
8079

8180
// Pull a class, create mappings for its inheritance family, then remove those classes from the map.
8281
// When the map is empty everything has been run through the mapping generation process.

recaf-core/src/main/java/software/coley/recaf/services/phantom/JPhantomGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public JPhantomGenerator(@Nonnull JPhantomGeneratorConfig config, @Nonnull Works
7676
@Override
7777
public GeneratedPhantomWorkspaceResource createPhantomsForWorkspace(@Nonnull Workspace workspace) throws PhantomGenerationException {
7878
// Extract all JVM classes from workspace
79-
Map<String, JvmClassInfo> classMap = workspace.getPrimaryResource().jvmClassBundleStream()
79+
Map<String, JvmClassInfo> classMap = workspace.getPrimaryResource().jvmAllClassBundleStreamRecursive()
8080
.flatMap(Bundle::stream)
8181
.collect(Collectors.toMap(Info::getName, Function.identity()));
8282

recaf-core/src/main/java/software/coley/recaf/services/search/SearchService.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import java.util.Collections;
4343
import java.util.List;
4444
import java.util.concurrent.ExecutorService;
45-
import java.util.stream.Stream;
4645

4746
/**
4847
* Outline for running various searches.
@@ -207,7 +206,7 @@ private static void searchResource(@Nonnull Results results,
207206

208207
// Visit JVM content
209208
if (jvmClassVisitor != null) {
210-
Stream.concat(resource.jvmClassBundleStream(), resource.versionedJvmClassBundleStream()).forEach(bundle -> {
209+
resource.jvmAllClassBundleStream().forEach(bundle -> {
211210
BundlePathNode bundlePath = resourcePath.child(bundle);
212211
for (JvmClassInfo classInfo : bundle) {
213212
if (feedback.hasRequestedCancellation())

0 commit comments

Comments
 (0)