diff options
| author | 2024-04-06 13:05:49 +0200 | |
|---|---|---|
| committer | 2024-04-06 12:05:49 +0100 | |
| commit | 31b6e9e188fb2abbab040ee07f8a8c14cec2571d (patch) | |
| tree | 6c51dc775b9f95cd70fde11a30eb655c8899ae68 | |
| parent | Bump version (diff) | |
| download | enigma-fork-31b6e9e188fb2abbab040ee07f8a8c14cec2571d.tar.gz enigma-fork-31b6e9e188fb2abbab040ee07f8a8c14cec2571d.tar.xz enigma-fork-31b6e9e188fb2abbab040ee07f8a8c14cec2571d.zip | |
Add Vineflower decompiler (#541)
* Add Vineflower integration
* Fix some confusing names
* Rename method to reflect visitor pattern naming schemes
* Remove dead null check
21 files changed, 630 insertions, 20 deletions
| @@ -8,8 +8,9 @@ Enigma is distributed under the [LGPL-3.0](LICENSE). | |||
| 8 | 8 | ||
| 9 | Enigma includes the following open-source libraries: | 9 | Enigma includes the following open-source libraries: |
| 10 | 10 | ||
| 11 | - A [modified version](https://github.com/FabricMC/procyon) of [Procyon](https://bitbucket.org/mstrobel/procyon) (Apache-2.0) | 11 | - [Vineflower](https://github.com/Vineflower/vineflower) (Apache-2.0) |
| 12 | - A [modified version](https://github.com/FabricMC/cfr) of [CFR](https://github.com/leibnitz27/cfr) (MIT) | 12 | - A [modified version](https://github.com/FabricMC/cfr) of [CFR](https://github.com/leibnitz27/cfr) (MIT) |
| 13 | - A [modified version](https://github.com/FabricMC/procyon) of [Procyon](https://bitbucket.org/mstrobel/procyon) (Apache-2.0) | ||
| 13 | - [Guava](https://github.com/google/guava) (Apache-2.0) | 14 | - [Guava](https://github.com/google/guava) (Apache-2.0) |
| 14 | - [SyntaxPane](https://github.com/Sciss/SyntaxPane) (Apache-2.0) | 15 | - [SyntaxPane](https://github.com/Sciss/SyntaxPane) (Apache-2.0) |
| 15 | - [FlatLaf](https://github.com/JFormDesigner/FlatLaf) (Apache-2.0) | 16 | - [FlatLaf](https://github.com/JFormDesigner/FlatLaf) (Apache-2.0) |
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Decompiler.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Decompiler.java index f9b5cbe..41ee254 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Decompiler.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Decompiler.java | |||
| @@ -4,6 +4,7 @@ import cuchaz.enigma.source.DecompilerService; | |||
| 4 | import cuchaz.enigma.source.Decompilers; | 4 | import cuchaz.enigma.source.Decompilers; |
| 5 | 5 | ||
| 6 | public enum Decompiler { | 6 | public enum Decompiler { |
| 7 | VINEFLOWER("Vineflower", Decompilers.VINEFLOWER), | ||
| 7 | CFR("CFR", Decompilers.CFR), | 8 | CFR("CFR", Decompilers.CFR), |
| 8 | PROCYON("Procyon", Decompilers.PROCYON), | 9 | PROCYON("Procyon", Decompilers.PROCYON), |
| 9 | BYTECODE("Bytecode", Decompilers.BYTECODE); | 10 | BYTECODE("Bytecode", Decompilers.BYTECODE); |
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java index 32d6d76..ecd17f2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java | |||
| @@ -105,7 +105,7 @@ public final class UiConfig { | |||
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | public static Decompiler getDecompiler() { | 107 | public static Decompiler getDecompiler() { |
| 108 | return ui.data().section("Decompiler").setIfAbsentEnum(Decompiler::valueOf, "Current", Decompiler.CFR); | 108 | return ui.data().section("Decompiler").setIfAbsentEnum(Decompiler::valueOf, "Current", Decompiler.VINEFLOWER); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | public static void setDecompiler(Decompiler d) { | 111 | public static void setDecompiler(Decompiler d) { |
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java index 0e8f7da..2f7cf19 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java | |||
| @@ -78,7 +78,7 @@ public class Config { | |||
| 78 | 78 | ||
| 79 | public float scaleFactor = 1.0f; | 79 | public float scaleFactor = 1.0f; |
| 80 | 80 | ||
| 81 | public Decompiler decompiler = Decompiler.CFR; | 81 | public Decompiler decompiler = Decompiler.VINEFLOWER; |
| 82 | 82 | ||
| 83 | public Config() { | 83 | public Config() { |
| 84 | gson = new GsonBuilder().registerTypeAdapter(Integer.class, new IntSerializer()).registerTypeAdapter(Integer.class, new IntDeserializer()).registerTypeAdapter(Config.class, (InstanceCreator<Config>) type -> this).setPrettyPrinting().create(); | 84 | gson = new GsonBuilder().registerTypeAdapter(Integer.class, new IntSerializer()).registerTypeAdapter(Integer.class, new IntDeserializer()).registerTypeAdapter(Config.class, (InstanceCreator<Config>) type -> this).setPrettyPrinting().create(); |
diff --git a/enigma/build.gradle b/enigma/build.gradle index ffea473..44f2680 100644 --- a/enigma/build.gradle +++ b/enigma/build.gradle | |||
| @@ -10,6 +10,7 @@ dependencies { | |||
| 10 | 10 | ||
| 11 | implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.6.0' | 11 | implementation 'org.bitbucket.mstrobel:procyon-compilertools:0.6.0' |
| 12 | implementation 'net.fabricmc:cfr:0.2.2' | 12 | implementation 'net.fabricmc:cfr:0.2.2' |
| 13 | implementation 'org.vineflower:vineflower:1.10.0' | ||
| 13 | 14 | ||
| 14 | proGuard 'com.guardsquare:proguard-base:7.4.0-beta02' | 15 | proGuard 'com.guardsquare:proguard-base:7.4.0-beta02' |
| 15 | 16 | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 48d6736..b3a7274 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java | |||
| @@ -274,7 +274,17 @@ public class EnigmaProject { | |||
| 274 | progress.init(classes.size(), I18n.translate("progress.classes.decompiling")); | 274 | progress.init(classes.size(), I18n.translate("progress.classes.decompiling")); |
| 275 | 275 | ||
| 276 | //create a common instance outside the loop as mappings shouldn't be changing while this is happening | 276 | //create a common instance outside the loop as mappings shouldn't be changing while this is happening |
| 277 | Decompiler decompiler = decompilerService.create(compiled::get, new SourceSettings(false, false)); | 277 | Decompiler decompiler = decompilerService.create(new ClassProvider() { |
| 278 | @Override | ||
| 279 | public Collection<String> getClassNames() { | ||
| 280 | return compiled.keySet(); | ||
| 281 | } | ||
| 282 | |||
| 283 | @Override | ||
| 284 | public ClassNode get(String name) { | ||
| 285 | return compiled.get(name); | ||
| 286 | } | ||
| 287 | }, new SourceSettings(false, false)); | ||
| 278 | 288 | ||
| 279 | AtomicInteger count = new AtomicInteger(); | 289 | AtomicInteger count = new AtomicInteger(); |
| 280 | 290 | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java index 45dac2c..10bc436 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java | |||
| @@ -55,8 +55,9 @@ public final class BuiltinPlugin implements EnigmaPlugin { | |||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | private void registerDecompilerServices(EnigmaPluginContext ctx) { | 57 | private void registerDecompilerServices(EnigmaPluginContext ctx) { |
| 58 | ctx.registerService("enigma:procyon", DecompilerService.TYPE, ctx1 -> Decompilers.PROCYON); | 58 | ctx.registerService("enigma:vineflower", DecompilerService.TYPE, ctx1 -> Decompilers.VINEFLOWER); |
| 59 | ctx.registerService("enigma:cfr", DecompilerService.TYPE, ctx1 -> Decompilers.CFR); | 59 | ctx.registerService("enigma:cfr", DecompilerService.TYPE, ctx1 -> Decompilers.CFR); |
| 60 | ctx.registerService("enigma:procyon", DecompilerService.TYPE, ctx1 -> Decompilers.PROCYON); | ||
| 60 | ctx.registerService("enigma:bytecode", DecompilerService.TYPE, ctx1 -> Decompilers.BYTECODE); | 61 | ctx.registerService("enigma:bytecode", DecompilerService.TYPE, ctx1 -> Decompilers.BYTECODE); |
| 61 | } | 62 | } |
| 62 | 63 | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java index eaba6df..28a9392 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | 1 | package cuchaz.enigma.classprovider; |
| 2 | 2 | ||
| 3 | import java.util.Collection; | ||
| 3 | import java.util.Optional; | 4 | import java.util.Optional; |
| 4 | import java.util.concurrent.ExecutionException; | 5 | import java.util.concurrent.ExecutionException; |
| 5 | import java.util.concurrent.TimeUnit; | 6 | import java.util.concurrent.TimeUnit; |
| @@ -22,6 +23,11 @@ public class CachingClassProvider implements ClassProvider { | |||
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | @Override | 25 | @Override |
| 26 | public Collection<String> getClassNames() { | ||
| 27 | return classProvider.getClassNames(); | ||
| 28 | } | ||
| 29 | |||
| 30 | @Override | ||
| 25 | @Nullable | 31 | @Nullable |
| 26 | public ClassNode get(String name) { | 32 | public ClassNode get(String name) { |
| 27 | try { | 33 | try { |
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java index 6eec0f3..069e0a8 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java | |||
| @@ -1,11 +1,18 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | 1 | package cuchaz.enigma.classprovider; |
| 2 | 2 | ||
| 3 | import java.util.Collection; | ||
| 4 | |||
| 3 | import javax.annotation.Nullable; | 5 | import javax.annotation.Nullable; |
| 4 | 6 | ||
| 5 | import org.objectweb.asm.tree.ClassNode; | 7 | import org.objectweb.asm.tree.ClassNode; |
| 6 | 8 | ||
| 7 | public interface ClassProvider { | 9 | public interface ClassProvider { |
| 8 | /** | 10 | /** |
| 11 | * @return Internal names of all contained classes. May be empty if the provider is lazy. | ||
| 12 | */ | ||
| 13 | Collection<String> getClassNames(); | ||
| 14 | |||
| 15 | /** | ||
| 9 | * Gets the {@linkplain ClassNode} for a class. The class provider may return a cached result, | 16 | * Gets the {@linkplain ClassNode} for a class. The class provider may return a cached result, |
| 10 | * so it's important to not mutate it. | 17 | * so it's important to not mutate it. |
| 11 | * | 18 | * |
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java index 224093f..b035cee 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java | |||
| @@ -2,6 +2,8 @@ package cuchaz.enigma.classprovider; | |||
| 2 | 2 | ||
| 3 | import java.io.IOException; | 3 | import java.io.IOException; |
| 4 | import java.io.InputStream; | 4 | import java.io.InputStream; |
| 5 | import java.util.Collection; | ||
| 6 | import java.util.Collections; | ||
| 5 | 7 | ||
| 6 | import javax.annotation.Nullable; | 8 | import javax.annotation.Nullable; |
| 7 | 9 | ||
| @@ -12,6 +14,11 @@ import org.objectweb.asm.tree.ClassNode; | |||
| 12 | * Provides classes by loading them from the classpath. | 14 | * Provides classes by loading them from the classpath. |
| 13 | */ | 15 | */ |
| 14 | public class ClasspathClassProvider implements ClassProvider { | 16 | public class ClasspathClassProvider implements ClassProvider { |
| 17 | @Override | ||
| 18 | public Collection<String> getClassNames() { | ||
| 19 | return Collections.emptyList(); | ||
| 20 | } | ||
| 21 | |||
| 15 | @Nullable | 22 | @Nullable |
| 16 | @Override | 23 | @Override |
| 17 | public ClassNode get(String name) { | 24 | public ClassNode get(String name) { |
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java index 6856540..1b20b8f 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | 1 | package cuchaz.enigma.classprovider; |
| 2 | 2 | ||
| 3 | import java.util.Arrays; | ||
| 4 | import java.util.Collection; | ||
| 5 | import java.util.stream.Collectors; | ||
| 6 | |||
| 3 | import javax.annotation.Nullable; | 7 | import javax.annotation.Nullable; |
| 4 | 8 | ||
| 5 | import org.objectweb.asm.tree.ClassNode; | 9 | import org.objectweb.asm.tree.ClassNode; |
| @@ -16,6 +20,14 @@ public class CombiningClassProvider implements ClassProvider { | |||
| 16 | } | 20 | } |
| 17 | 21 | ||
| 18 | @Override | 22 | @Override |
| 23 | public Collection<String> getClassNames() { | ||
| 24 | return Arrays.stream(classProviders) | ||
| 25 | .map(ClassProvider::getClassNames) | ||
| 26 | .flatMap(Collection::stream) | ||
| 27 | .collect(Collectors.toSet()); | ||
| 28 | } | ||
| 29 | |||
| 30 | @Override | ||
| 19 | @Nullable | 31 | @Nullable |
| 20 | public ClassNode get(String name) { | 32 | public ClassNode get(String name) { |
| 21 | for (ClassProvider cp : classProviders) { | 33 | for (ClassProvider cp : classProviders) { |
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java index 900a0c8..5dec5be 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java | |||
| @@ -41,6 +41,7 @@ public class JarClassProvider implements AutoCloseable, ClassProvider { | |||
| 41 | return classNames.build(); | 41 | return classNames.build(); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | @Override | ||
| 44 | public Set<String> getClassNames() { | 45 | public Set<String> getClassNames() { |
| 45 | return classNames; | 46 | return classNames; |
| 46 | } | 47 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java index 604bf49..543ce48 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | package cuchaz.enigma.classprovider; | 1 | package cuchaz.enigma.classprovider; |
| 2 | 2 | ||
| 3 | import java.util.Collection; | ||
| 4 | |||
| 3 | import javax.annotation.Nullable; | 5 | import javax.annotation.Nullable; |
| 4 | 6 | ||
| 5 | import org.objectweb.asm.ClassVisitor; | 7 | import org.objectweb.asm.ClassVisitor; |
| @@ -39,6 +41,11 @@ public class ObfuscationFixClassProvider implements ClassProvider { | |||
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | @Override | 43 | @Override |
| 44 | public Collection<String> getClassNames() { | ||
| 45 | return classProvider.getClassNames(); | ||
| 46 | } | ||
| 47 | |||
| 48 | @Override | ||
| 42 | @Nullable | 49 | @Nullable |
| 43 | public ClassNode get(String name) { | 50 | public ClassNode get(String name) { |
| 44 | ClassNode node = classProvider.get(name); | 51 | ClassNode node = classProvider.get(name); |
diff --git a/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java b/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java index 0e3244d..1219030 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java +++ b/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java | |||
| @@ -3,9 +3,11 @@ package cuchaz.enigma.source; | |||
| 3 | import cuchaz.enigma.source.bytecode.BytecodeDecompiler; | 3 | import cuchaz.enigma.source.bytecode.BytecodeDecompiler; |
| 4 | import cuchaz.enigma.source.cfr.CfrDecompiler; | 4 | import cuchaz.enigma.source.cfr.CfrDecompiler; |
| 5 | import cuchaz.enigma.source.procyon.ProcyonDecompiler; | 5 | import cuchaz.enigma.source.procyon.ProcyonDecompiler; |
| 6 | import cuchaz.enigma.source.vineflower.VineflowerDecompiler; | ||
| 6 | 7 | ||
| 7 | public class Decompilers { | 8 | public class Decompilers { |
| 8 | public static final DecompilerService PROCYON = ProcyonDecompiler::new; | 9 | public static final DecompilerService VINEFLOWER = VineflowerDecompiler::new; |
| 9 | public static final DecompilerService CFR = CfrDecompiler::new; | 10 | public static final DecompilerService CFR = CfrDecompiler::new; |
| 11 | public static final DecompilerService PROCYON = ProcyonDecompiler::new; | ||
| 10 | public static final DecompilerService BYTECODE = BytecodeDecompiler::new; | 12 | public static final DecompilerService BYTECODE = BytecodeDecompiler::new; |
| 11 | } | 13 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDumper.java index fb5c4a7..950f518 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDumper.java | |||
| @@ -40,7 +40,7 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; | |||
| 40 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | 40 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; |
| 41 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 41 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 42 | 42 | ||
| 43 | public class EnigmaDumper extends StringStreamDumper { | 43 | public class CfrDumper extends StringStreamDumper { |
| 44 | private final StringBuilder sb; | 44 | private final StringBuilder sb; |
| 45 | private final SourceSettings sourceSettings; | 45 | private final SourceSettings sourceSettings; |
| 46 | private final SourceIndex index; | 46 | private final SourceIndex index; |
| @@ -51,11 +51,11 @@ public class EnigmaDumper extends StringStreamDumper { | |||
| 51 | private boolean muteLine = false; | 51 | private boolean muteLine = false; |
| 52 | private MethodEntry contextMethod = null; | 52 | private MethodEntry contextMethod = null; |
| 53 | 53 | ||
| 54 | public EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) { | 54 | public CfrDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) { |
| 55 | this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext()); | 55 | this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext()); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | protected EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) { | 58 | protected CfrDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) { |
| 59 | super((m, e) -> { | 59 | super((m, e) -> { |
| 60 | }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context); | 60 | }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context); |
| 61 | this.sb = sb; | 61 | this.sb = sb; |
| @@ -149,21 +149,15 @@ public class EnigmaDumper extends StringStreamDumper { | |||
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getField().getDescriptor())); | 151 | EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getField().getDescriptor())); |
| 152 | String javadoc = mapping.javadoc(); | ||
| 152 | 153 | ||
| 153 | if (mapping == null) { | 154 | if (javadoc != null) { |
| 154 | continue; | 155 | recordComponentDocs.add(String.format("@param %s %s", mapping.targetName(), javadoc)); |
| 155 | } | ||
| 156 | |||
| 157 | String javaDoc = mapping.javadoc(); | ||
| 158 | |||
| 159 | if (javaDoc != null) { | ||
| 160 | recordComponentDocs.add(String.format("@param %s %s", mapping.targetName(), javaDoc)); | ||
| 161 | } | 156 | } |
| 162 | } | 157 | } |
| 163 | } | 158 | } |
| 164 | 159 | ||
| 165 | EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner)); | 160 | EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner)); |
| 166 | |||
| 167 | String javadoc = null; | 161 | String javadoc = null; |
| 168 | 162 | ||
| 169 | if (mapping != null) { | 163 | if (mapping != null) { |
| @@ -399,7 +393,7 @@ public class EnigmaDumper extends StringStreamDumper { | |||
| 399 | */ | 393 | */ |
| 400 | @Override | 394 | @Override |
| 401 | public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { | 395 | public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { |
| 402 | return new EnigmaDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext); | 396 | return new CfrDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext); |
| 403 | } | 397 | } |
| 404 | 398 | ||
| 405 | @Override | 399 | @Override |
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java index 70b43ac..cf6c52f 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java | |||
| @@ -82,7 +82,7 @@ public class CfrSource implements Source { | |||
| 82 | TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); | 82 | TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); |
| 83 | tree.analyseTop(state, typeUsageCollector); | 83 | tree.analyseTop(state, typeUsageCollector); |
| 84 | 84 | ||
| 85 | EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); | 85 | CfrDumper dumper = new CfrDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); |
| 86 | tree.dump(state.getObfuscationMapping().wrap(dumper)); | 86 | tree.dump(state.getObfuscationMapping().wrap(dumper)); |
| 87 | index = dumper.getIndex(); | 87 | index = dumper.getIndex(); |
| 88 | } | 88 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerContextSource.java b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerContextSource.java new file mode 100644 index 0000000..e0f2b05 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerContextSource.java | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | package cuchaz.enigma.source.vineflower; | ||
| 2 | |||
| 3 | import java.io.ByteArrayInputStream; | ||
| 4 | import java.io.InputStream; | ||
| 5 | import java.util.ArrayList; | ||
| 6 | import java.util.Collections; | ||
| 7 | import java.util.List; | ||
| 8 | |||
| 9 | import org.jetbrains.java.decompiler.main.extern.IContextSource; | ||
| 10 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; | ||
| 11 | import org.objectweb.asm.tree.ClassNode; | ||
| 12 | |||
| 13 | import cuchaz.enigma.classprovider.ClassProvider; | ||
| 14 | import cuchaz.enigma.utils.AsmUtil; | ||
| 15 | |||
| 16 | class VineflowerContextSource implements IContextSource { | ||
| 17 | private final IContextSource classpathSource = new ClasspathSource(); | ||
| 18 | private final ClassProvider classProvider; | ||
| 19 | private final String className; | ||
| 20 | private Entries entries; | ||
| 21 | |||
| 22 | VineflowerContextSource(ClassProvider classProvider, String className) { | ||
| 23 | this.classProvider = classProvider; | ||
| 24 | this.className = className; | ||
| 25 | } | ||
| 26 | |||
| 27 | public IContextSource getClasspath() { | ||
| 28 | return classpathSource; | ||
| 29 | } | ||
| 30 | |||
| 31 | @Override | ||
| 32 | public String getName() { | ||
| 33 | return "Enigma-provided context for class " + className; | ||
| 34 | } | ||
| 35 | |||
| 36 | @Override | ||
| 37 | public Entries getEntries() { | ||
| 38 | computeEntriesIfNecessary(); | ||
| 39 | return entries; | ||
| 40 | } | ||
| 41 | |||
| 42 | private void computeEntriesIfNecessary() { | ||
| 43 | if (entries != null) { | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | |||
| 47 | synchronized (this) { | ||
| 48 | if (entries != null) return; | ||
| 49 | |||
| 50 | List<String> classNames = new ArrayList<>(); | ||
| 51 | classNames.add(className); | ||
| 52 | |||
| 53 | int dollarIndex = className.indexOf('$'); | ||
| 54 | String outermostClass = dollarIndex == -1 ? className : className.substring(0, className.indexOf('$')); | ||
| 55 | String outermostClassSuffixed = outermostClass + "$"; | ||
| 56 | |||
| 57 | for (String currentClass : classProvider.getClassNames()) { | ||
| 58 | if (currentClass.startsWith(outermostClassSuffixed) && !currentClass.equals(className)) { | ||
| 59 | classNames.add(currentClass); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | List<Entry> classes = classNames.stream() | ||
| 64 | .map(Entry::atBase) | ||
| 65 | .toList(); | ||
| 66 | |||
| 67 | entries = new Entries(classes, Collections.emptyList(), Collections.emptyList()); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | @Override | ||
| 72 | public InputStream getInputStream(String resource) { | ||
| 73 | ClassNode node = classProvider.get(resource.substring(0, resource.lastIndexOf(".class"))); | ||
| 74 | |||
| 75 | if (node == null) { | ||
| 76 | return null; | ||
| 77 | } | ||
| 78 | |||
| 79 | return new ByteArrayInputStream(AsmUtil.nodeToBytes(node)); | ||
| 80 | } | ||
| 81 | |||
| 82 | @Override | ||
| 83 | public IOutputSink createOutputSink(IResultSaver saver) { | ||
| 84 | return new IOutputSink() { | ||
| 85 | @Override | ||
| 86 | public void begin() { } | ||
| 87 | |||
| 88 | @Override | ||
| 89 | public void acceptClass(String qualifiedName, String fileName, String content, int[] mapping) { | ||
| 90 | if (qualifiedName.equals(VineflowerContextSource.this.className)) { | ||
| 91 | saver.saveClassFile(null, qualifiedName, fileName, content, mapping); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | @Override | ||
| 96 | public void acceptDirectory(String directory) { } | ||
| 97 | |||
| 98 | @Override | ||
| 99 | public void acceptOther(String path) { } | ||
| 100 | |||
| 101 | @Override | ||
| 102 | public void close() { } | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | public class ClasspathSource implements IContextSource { | ||
| 107 | @Override | ||
| 108 | public String getName() { | ||
| 109 | return "Enigma-provided classpath context for " + VineflowerContextSource.this.className; | ||
| 110 | } | ||
| 111 | |||
| 112 | @Override | ||
| 113 | public Entries getEntries() { | ||
| 114 | return Entries.EMPTY; | ||
| 115 | } | ||
| 116 | |||
| 117 | @Override | ||
| 118 | public boolean isLazy() { | ||
| 119 | return true; | ||
| 120 | } | ||
| 121 | |||
| 122 | @Override | ||
| 123 | public InputStream getInputStream(String resource) { | ||
| 124 | return VineflowerContextSource.this.getInputStream(resource); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerDecompiler.java new file mode 100644 index 0000000..56fd0b9 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerDecompiler.java | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | package cuchaz.enigma.source.vineflower; | ||
| 2 | |||
| 3 | import org.checkerframework.checker.nullness.qual.Nullable; | ||
| 4 | |||
| 5 | import cuchaz.enigma.classprovider.ClassProvider; | ||
| 6 | import cuchaz.enigma.source.Decompiler; | ||
| 7 | import cuchaz.enigma.source.Source; | ||
| 8 | import cuchaz.enigma.source.SourceSettings; | ||
| 9 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 10 | |||
| 11 | public class VineflowerDecompiler implements Decompiler { | ||
| 12 | private final ClassProvider classProvider; | ||
| 13 | private final SourceSettings settings; | ||
| 14 | |||
| 15 | public VineflowerDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { | ||
| 16 | this.settings = sourceSettings; | ||
| 17 | this.classProvider = classProvider; | ||
| 18 | } | ||
| 19 | |||
| 20 | @Override | ||
| 21 | public Source getSource(String className, @Nullable EntryRemapper remapper) { | ||
| 22 | return new VineflowerSource(new VineflowerContextSource(classProvider, className), remapper, settings); | ||
| 23 | } | ||
| 24 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerJavadocProvider.java b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerJavadocProvider.java new file mode 100644 index 0000000..3757b02 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerJavadocProvider.java | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | package cuchaz.enigma.source.vineflower; | ||
| 2 | |||
| 3 | import java.util.Collection; | ||
| 4 | import java.util.LinkedList; | ||
| 5 | import java.util.List; | ||
| 6 | |||
| 7 | import net.fabricmc.fernflower.api.IFabricJavadocProvider; | ||
| 8 | import org.jetbrains.java.decompiler.struct.StructClass; | ||
| 9 | import org.jetbrains.java.decompiler.struct.StructField; | ||
| 10 | import org.jetbrains.java.decompiler.struct.StructMethod; | ||
| 11 | import org.jetbrains.java.decompiler.struct.StructRecordComponent; | ||
| 12 | import org.objectweb.asm.Opcodes; | ||
| 13 | |||
| 14 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 15 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 21 | |||
| 22 | class VineflowerJavadocProvider implements IFabricJavadocProvider { | ||
| 23 | private final EntryRemapper remapper; | ||
| 24 | |||
| 25 | VineflowerJavadocProvider(EntryRemapper remapper) { | ||
| 26 | this.remapper = remapper; | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public String getClassDoc(StructClass cls) { | ||
| 31 | if (remapper == null) return null; | ||
| 32 | |||
| 33 | List<String> recordComponentDocs = new LinkedList<>(); | ||
| 34 | |||
| 35 | if (isRecord(cls)) { | ||
| 36 | for (StructRecordComponent component : cls.getRecordComponents()) { | ||
| 37 | EntryMapping mapping = remapper.getDeobfMapping(fieldEntryOf(cls, component)); | ||
| 38 | String javadoc = mapping.javadoc(); | ||
| 39 | |||
| 40 | if (javadoc != null) { | ||
| 41 | recordComponentDocs.add(String.format("@param %s %s", mapping.targetName(), javadoc)); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | EntryMapping mapping = remapper.getDeobfMapping(classEntryOf(cls)); | ||
| 47 | StringBuilder builder = new StringBuilder(); | ||
| 48 | String javadoc = mapping.javadoc(); | ||
| 49 | |||
| 50 | if (javadoc != null) { | ||
| 51 | builder.append(javadoc); | ||
| 52 | } | ||
| 53 | |||
| 54 | if (!recordComponentDocs.isEmpty()) { | ||
| 55 | if (javadoc != null) { | ||
| 56 | builder.append('\n'); | ||
| 57 | } | ||
| 58 | |||
| 59 | for (String recordComponentDoc : recordComponentDocs) { | ||
| 60 | builder.append('\n').append(recordComponentDoc); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | javadoc = builder.toString(); | ||
| 65 | |||
| 66 | return javadoc.isBlank() ? null : javadoc.trim(); | ||
| 67 | } | ||
| 68 | |||
| 69 | @Override | ||
| 70 | public String getFieldDoc(StructClass cls, StructField fld) { | ||
| 71 | boolean isRecordComponent = isRecord(cls) && !fld.hasModifier(Opcodes.ACC_STATIC); | ||
| 72 | |||
| 73 | if (remapper == null || isRecordComponent) { | ||
| 74 | return null; | ||
| 75 | } | ||
| 76 | |||
| 77 | EntryMapping mapping = remapper.getDeobfMapping(fieldEntryOf(cls, fld)); | ||
| 78 | String javadoc = mapping.javadoc(); | ||
| 79 | |||
| 80 | return javadoc == null || javadoc.isBlank() ? null : javadoc.trim(); | ||
| 81 | } | ||
| 82 | |||
| 83 | @Override | ||
| 84 | public String getMethodDoc(StructClass cls, StructMethod mth) { | ||
| 85 | if (remapper == null) return null; | ||
| 86 | |||
| 87 | MethodEntry entry = methodEntryOf(cls, mth); | ||
| 88 | EntryMapping mapping = remapper.getDeobfMapping(entry); | ||
| 89 | StringBuilder builder = new StringBuilder(); | ||
| 90 | String javadoc = mapping.javadoc(); | ||
| 91 | |||
| 92 | if (javadoc != null) { | ||
| 93 | builder.append(javadoc); | ||
| 94 | } | ||
| 95 | |||
| 96 | Collection<Entry<?>> children = remapper.getObfChildren(entry); | ||
| 97 | boolean addedLf = false; | ||
| 98 | |||
| 99 | if (children != null && !children.isEmpty()) { | ||
| 100 | for (Entry<?> each : children) { | ||
| 101 | if (each instanceof LocalVariableEntry) { | ||
| 102 | mapping = remapper.getDeobfMapping(each); | ||
| 103 | javadoc = mapping.javadoc(); | ||
| 104 | |||
| 105 | if (javadoc != null) { | ||
| 106 | if (!addedLf) { | ||
| 107 | addedLf = true; | ||
| 108 | builder.append('\n'); | ||
| 109 | } | ||
| 110 | |||
| 111 | builder.append(String.format("\n@param %s %s", mapping.targetName(), javadoc)); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | javadoc = builder.toString(); | ||
| 118 | |||
| 119 | return javadoc.isBlank() ? null : javadoc.trim(); | ||
| 120 | } | ||
| 121 | |||
| 122 | private boolean isRecord(StructClass cls) { | ||
| 123 | if (cls.superClass == null) return false; | ||
| 124 | |||
| 125 | return cls.superClass.getString().equals("java/lang/Record"); | ||
| 126 | } | ||
| 127 | |||
| 128 | private ClassEntry classEntryOf(StructClass cls) { | ||
| 129 | return ClassEntry.parse(cls.qualifiedName); | ||
| 130 | } | ||
| 131 | |||
| 132 | private FieldEntry fieldEntryOf(StructClass cls, StructField fld) { | ||
| 133 | return FieldEntry.parse(cls.qualifiedName, fld.getName(), fld.getDescriptor()); | ||
| 134 | } | ||
| 135 | |||
| 136 | private MethodEntry methodEntryOf(StructClass cls, StructMethod mth) { | ||
| 137 | return MethodEntry.parse(cls.qualifiedName, mth.getName(), mth.getDescriptor()); | ||
| 138 | } | ||
| 139 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerSource.java b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerSource.java new file mode 100644 index 0000000..27b6edc --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerSource.java | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | package cuchaz.enigma.source.vineflower; | ||
| 2 | |||
| 3 | import java.util.HashMap; | ||
| 4 | import java.util.Map; | ||
| 5 | import java.util.concurrent.atomic.AtomicReference; | ||
| 6 | import java.util.jar.Manifest; | ||
| 7 | |||
| 8 | import net.fabricmc.fernflower.api.IFabricJavadocProvider; | ||
| 9 | import org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler; | ||
| 10 | import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger; | ||
| 11 | import org.jetbrains.java.decompiler.main.extern.IContextSource; | ||
| 12 | import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; | ||
| 13 | import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; | ||
| 14 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; | ||
| 15 | import org.jetbrains.java.decompiler.main.extern.TextTokenVisitor; | ||
| 16 | |||
| 17 | import cuchaz.enigma.source.Source; | ||
| 18 | import cuchaz.enigma.source.SourceIndex; | ||
| 19 | import cuchaz.enigma.source.SourceSettings; | ||
| 20 | import cuchaz.enigma.translation.mapping.EntryRemapper; | ||
| 21 | |||
| 22 | class VineflowerSource implements Source { | ||
| 23 | private final IContextSource contextSource; | ||
| 24 | private final IContextSource librarySource; | ||
| 25 | private final SourceSettings settings; | ||
| 26 | private EntryRemapper remapper; | ||
| 27 | private SourceIndex index; | ||
| 28 | |||
| 29 | VineflowerSource(VineflowerContextSource contextSource, EntryRemapper remapper, SourceSettings settings) { | ||
| 30 | this.contextSource = contextSource; | ||
| 31 | this.librarySource = contextSource.getClasspath(); | ||
| 32 | this.remapper = remapper; | ||
| 33 | this.settings = settings; | ||
| 34 | } | ||
| 35 | |||
| 36 | @Override | ||
| 37 | public String asString() { | ||
| 38 | ensureDecompiled(); | ||
| 39 | return index.getSource(); | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public Source withJavadocs(EntryRemapper remapper) { | ||
| 44 | this.remapper = remapper; | ||
| 45 | this.index = null; | ||
| 46 | return this; | ||
| 47 | } | ||
| 48 | |||
| 49 | @Override | ||
| 50 | public SourceIndex index() { | ||
| 51 | ensureDecompiled(); | ||
| 52 | return index; | ||
| 53 | } | ||
| 54 | |||
| 55 | private void ensureDecompiled() { | ||
| 56 | if (index != null) { | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | |||
| 60 | Map<String, Object> preferences = new HashMap<>(IFernflowerPreferences.DEFAULTS); | ||
| 61 | preferences.put(IFernflowerPreferences.INDENT_STRING, "\t"); | ||
| 62 | preferences.put(IFernflowerPreferences.LOG_LEVEL, IFernflowerLogger.Severity.WARN.name()); | ||
| 63 | preferences.put(IFernflowerPreferences.THREADS, String.valueOf(Math.max(1, Runtime.getRuntime().availableProcessors() - 2))); | ||
| 64 | preferences.put(IFabricJavadocProvider.PROPERTY_NAME, new VineflowerJavadocProvider(remapper)); | ||
| 65 | |||
| 66 | if (settings.removeImports) { | ||
| 67 | preferences.put(IFernflowerPreferences.REMOVE_IMPORTS, "1"); | ||
| 68 | } | ||
| 69 | |||
| 70 | index = new SourceIndex(); | ||
| 71 | IResultSaver saver = new ResultSaver(index); | ||
| 72 | IFernflowerLogger logger = new PrintStreamLogger(System.out); | ||
| 73 | BaseDecompiler decompiler = new BaseDecompiler(saver, preferences, logger); | ||
| 74 | |||
| 75 | AtomicReference<VineflowerTextTokenCollector> tokenCollector = new AtomicReference<>(); | ||
| 76 | TextTokenVisitor.addVisitor(next -> { | ||
| 77 | tokenCollector.set(new VineflowerTextTokenCollector(next)); | ||
| 78 | return tokenCollector.get(); | ||
| 79 | }); | ||
| 80 | |||
| 81 | decompiler.addSource(contextSource); | ||
| 82 | |||
| 83 | if (librarySource != null) { | ||
| 84 | decompiler.addLibrary(librarySource); | ||
| 85 | } | ||
| 86 | |||
| 87 | decompiler.decompileContext(); | ||
| 88 | tokenCollector.get().accept(index); | ||
| 89 | } | ||
| 90 | |||
| 91 | private class ResultSaver implements IResultSaver { | ||
| 92 | private final SourceIndex index; | ||
| 93 | |||
| 94 | private ResultSaver(SourceIndex index) { | ||
| 95 | this.index = index; | ||
| 96 | } | ||
| 97 | |||
| 98 | @Override | ||
| 99 | public void saveFolder(String path) { } | ||
| 100 | @Override | ||
| 101 | public void copyFile(String source, String path, String entryName) { } | ||
| 102 | |||
| 103 | @Override | ||
| 104 | public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { | ||
| 105 | index.setSource(content); | ||
| 106 | } | ||
| 107 | |||
| 108 | @Override | ||
| 109 | public void createArchive(String path, String archiveName, Manifest manifest) { } | ||
| 110 | @Override | ||
| 111 | public void saveDirEntry(String path, String archiveName, String entryName) { } | ||
| 112 | @Override | ||
| 113 | public void copyEntry(String source, String path, String archiveName, String entry) { } | ||
| 114 | @Override | ||
| 115 | public void closeArchive(String path, String archiveName) { } | ||
| 116 | @Override | ||
| 117 | public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { } | ||
| 118 | } | ||
| 119 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerTextTokenCollector.java b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerTextTokenCollector.java new file mode 100644 index 0000000..3d45712 --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/source/vineflower/VineflowerTextTokenCollector.java | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | package cuchaz.enigma.source.vineflower; | ||
| 2 | |||
| 3 | import java.util.HashMap; | ||
| 4 | import java.util.LinkedHashSet; | ||
| 5 | import java.util.Map; | ||
| 6 | import java.util.Set; | ||
| 7 | |||
| 8 | import org.jetbrains.java.decompiler.main.extern.TextTokenVisitor; | ||
| 9 | import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; | ||
| 10 | import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; | ||
| 11 | import org.jetbrains.java.decompiler.util.Pair; | ||
| 12 | import org.jetbrains.java.decompiler.util.token.TextRange; | ||
| 13 | |||
| 14 | import cuchaz.enigma.source.SourceIndex; | ||
| 15 | import cuchaz.enigma.source.Token; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 21 | |||
| 22 | class VineflowerTextTokenCollector extends TextTokenVisitor { | ||
| 23 | private final Map<Token, Entry<?>> declarations = new HashMap<>(); | ||
| 24 | private final Map<Token, Pair<Entry<?>, Entry<?>>> references = new HashMap<>(); | ||
| 25 | private final Set<Token> tokens = new LinkedHashSet<>(); | ||
| 26 | private String content; | ||
| 27 | private MethodEntry currentMethod; | ||
| 28 | |||
| 29 | VineflowerTextTokenCollector(TextTokenVisitor next) { | ||
| 30 | super(next); | ||
| 31 | } | ||
| 32 | |||
| 33 | @Override | ||
| 34 | public void start(String content) { | ||
| 35 | this.content = content; | ||
| 36 | } | ||
| 37 | |||
| 38 | @Override | ||
| 39 | public void visitClass(TextRange range, boolean declaration, String name) { | ||
| 40 | super.visitClass(range, declaration, name); | ||
| 41 | Token token = getToken(range); | ||
| 42 | |||
| 43 | if (declaration) { | ||
| 44 | addDeclaration(token, classEntryOf(name)); | ||
| 45 | } else { | ||
| 46 | addReference(token, classEntryOf(name), currentMethod); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | @Override | ||
| 51 | public void visitField(TextRange range, boolean declaration, String className, String name, FieldDescriptor descriptor) { | ||
| 52 | super.visitField(range, declaration, className, name, descriptor); | ||
| 53 | Token token = getToken(range); | ||
| 54 | |||
| 55 | if (declaration) { | ||
| 56 | addDeclaration(token, fieldEntryOf(className, name, descriptor)); | ||
| 57 | } else { | ||
| 58 | addReference(token, fieldEntryOf(className, name, descriptor), currentMethod); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | @Override | ||
| 63 | public void visitMethod(TextRange range, boolean declaration, String className, String name, MethodDescriptor descriptor) { | ||
| 64 | super.visitMethod(range, declaration, className, name, descriptor); | ||
| 65 | Token token = getToken(range); | ||
| 66 | |||
| 67 | if (token.text.equals("new")) { | ||
| 68 | return; | ||
| 69 | } | ||
| 70 | |||
| 71 | MethodEntry entry = methodEntryOf(className, name, descriptor); | ||
| 72 | |||
| 73 | if (declaration) { | ||
| 74 | addDeclaration(token, entry); | ||
| 75 | currentMethod = entry; | ||
| 76 | } else { | ||
| 77 | addReference(token, entry, currentMethod); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | @Override | ||
| 82 | public void visitParameter(TextRange range, boolean declaration, String className, String methodName, MethodDescriptor methodDescriptor, int lvIndex, String name) { | ||
| 83 | super.visitParameter(range, declaration, className, methodName, methodDescriptor, lvIndex, name); | ||
| 84 | Token token = getToken(range); | ||
| 85 | MethodEntry parent = methodEntryOf(className, methodName, methodDescriptor); | ||
| 86 | |||
| 87 | if (declaration) { | ||
| 88 | addDeclaration(token, argEntryOf(parent, lvIndex, name)); | ||
| 89 | } else { | ||
| 90 | addReference(token, argEntryOf(parent, lvIndex, name), currentMethod); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | @Override | ||
| 95 | public void visitLocal(TextRange range, boolean declaration, String className, String methodName, MethodDescriptor methodDescriptor, int lvIndex, String name) { | ||
| 96 | super.visitLocal(range, declaration, className, methodName, methodDescriptor, lvIndex, name); | ||
| 97 | Token token = getToken(range); | ||
| 98 | MethodEntry parent = methodEntryOf(className, methodName, methodDescriptor); | ||
| 99 | |||
| 100 | if (declaration) { | ||
| 101 | addDeclaration(token, varEntryOf(parent, lvIndex, name)); | ||
| 102 | } else { | ||
| 103 | addReference(token, varEntryOf(parent, lvIndex, name), currentMethod); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | private ClassEntry classEntryOf(String name) { | ||
| 108 | return ClassEntry.parse(name); | ||
| 109 | } | ||
| 110 | |||
| 111 | private FieldEntry fieldEntryOf(String className, String name, FieldDescriptor descriptor) { | ||
| 112 | return FieldEntry.parse(className, name, descriptor.descriptorString); | ||
| 113 | } | ||
| 114 | |||
| 115 | private MethodEntry methodEntryOf(String className, String name, MethodDescriptor descriptor) { | ||
| 116 | return MethodEntry.parse(className, name, descriptor.toString()); | ||
| 117 | } | ||
| 118 | |||
| 119 | private LocalVariableEntry argEntryOf(MethodEntry className, int lvIndex, String name) { | ||
| 120 | return new LocalVariableEntry(className, lvIndex, name, true, null); | ||
| 121 | } | ||
| 122 | |||
| 123 | private LocalVariableEntry varEntryOf(MethodEntry className, int lvIndex, String name) { | ||
| 124 | return new LocalVariableEntry(className, lvIndex, name, false, null); | ||
| 125 | } | ||
| 126 | |||
| 127 | private Token getToken(TextRange range) { | ||
| 128 | return new Token(range.start, range.start + range.length, content.substring(range.start, range.start + range.length)); | ||
| 129 | } | ||
| 130 | |||
| 131 | private void addDeclaration(Token token, Entry<?> entry) { | ||
| 132 | declarations.put(token, entry); | ||
| 133 | tokens.add(token); | ||
| 134 | } | ||
| 135 | |||
| 136 | private void addReference(Token token, Entry<?> entry, Entry<?> context) { | ||
| 137 | references.put(token, Pair.of(entry, context)); | ||
| 138 | tokens.add(token); | ||
| 139 | } | ||
| 140 | |||
| 141 | public void accept(SourceIndex index) { | ||
| 142 | for (Token token : tokens) { | ||
| 143 | if (declarations.get(token) != null) { | ||
| 144 | index.addDeclaration(token, declarations.get(token)); | ||
| 145 | } else { | ||
| 146 | Pair<Entry<?>, Entry<?>> reference = references.get(token); | ||
| 147 | index.addReference(token, reference.a, reference.b); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||