From 9c736848fb7aa82d295b3aa2946e6cd132ee998f Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 14 Sep 2022 13:12:55 +0100 Subject: Add checkstyle (#460) --- enigma/src/main/java/cuchaz/enigma/Enigma.java | 25 +- .../src/main/java/cuchaz/enigma/EnigmaProfile.java | 35 +- .../src/main/java/cuchaz/enigma/EnigmaProject.java | 97 +-- .../main/java/cuchaz/enigma/EnigmaServices.java | 5 +- .../main/java/cuchaz/enigma/analysis/Access.java | 29 +- .../java/cuchaz/enigma/analysis/BuiltinPlugin.java | 56 +- .../analysis/ClassImplementationsTreeNode.java | 31 +- .../enigma/analysis/ClassInheritanceTreeNode.java | 29 +- .../enigma/analysis/ClassReferenceTreeNode.java | 37 +- .../cuchaz/enigma/analysis/EntryReference.java | 31 +- .../enigma/analysis/FieldReferenceTreeNode.java | 24 +- .../enigma/analysis/IndexSimpleVerifier.java | 292 ++++---- .../cuchaz/enigma/analysis/IndexTreeBuilder.java | 15 +- .../cuchaz/enigma/analysis/InterpreterPair.java | 213 +++--- .../analysis/MethodImplementationsTreeNode.java | 34 +- .../enigma/analysis/MethodInheritanceTreeNode.java | 26 +- .../enigma/analysis/MethodNodeWithAction.java | 22 +- .../enigma/analysis/MethodReferenceTreeNode.java | 39 +- .../enigma/analysis/ReferenceTargetType.java | 136 ++-- .../cuchaz/enigma/analysis/ReferenceTreeNode.java | 18 +- .../cuchaz/enigma/analysis/StructureTreeNode.java | 354 +++++----- .../enigma/analysis/StructureTreeOptions.java | 108 ++- .../enigma/analysis/index/BridgeMethodIndex.java | 16 +- .../cuchaz/enigma/analysis/index/EntryIndex.java | 16 +- .../enigma/analysis/index/IndexClassVisitor.java | 7 +- .../analysis/index/IndexReferenceVisitor.java | 65 +- .../enigma/analysis/index/InheritanceIndex.java | 39 +- .../cuchaz/enigma/analysis/index/JarIndex.java | 42 +- .../cuchaz/enigma/analysis/index/JarIndexer.java | 6 +- .../analysis/index/PackageVisibilityIndex.java | 27 +- .../enigma/analysis/index/ReferenceIndex.java | 25 +- .../enigma/api/service/EnigmaServiceType.java | 4 +- .../enigma/api/service/JarIndexerService.java | 9 +- .../enigma/api/service/NameProposalService.java | 4 +- .../bytecode/translators/AsmObjectTranslator.java | 33 +- .../translators/LocalVariableFixVisitor.java | 17 +- .../bytecode/translators/SourceFixVisitor.java | 8 +- .../translators/TranslationAnnotationVisitor.java | 5 +- .../translators/TranslationClassVisitor.java | 39 +- .../translators/TranslationFieldVisitor.java | 7 +- .../translators/TranslationMethodVisitor.java | 21 +- .../TranslationRecordComponentVisitor.java | 5 +- .../translators/TranslationSignatureVisitor.java | 22 +- .../cuchaz/enigma/classhandle/ClassHandle.java | 2 - .../enigma/classhandle/ClassHandleError.java | 9 +- .../enigma/classhandle/ClassHandleProvider.java | 75 +- .../enigma/classprovider/CachingClassProvider.java | 43 +- .../cuchaz/enigma/classprovider/ClassProvider.java | 22 +- .../classprovider/ClasspathClassProvider.java | 35 +- .../classprovider/CombiningClassProvider.java | 34 +- .../enigma/classprovider/JarClassProvider.java | 88 +-- .../classprovider/ObfuscationFixClassProvider.java | 110 +-- .../java/cuchaz/enigma/config/ConfigContainer.java | 8 +- .../java/cuchaz/enigma/config/ConfigPaths.java | 30 +- .../java/cuchaz/enigma/config/ConfigSection.java | 24 +- .../cuchaz/enigma/config/ConfigSerializer.java | 136 ++-- .../enigma/config/ConfigStructureVisitor.java | 2 - .../cuchaz/enigma/events/ClassHandleListener.java | 2 - .../enigma/source/DecompiledClassSource.java | 16 +- .../main/java/cuchaz/enigma/source/Decompiler.java | 3 +- .../cuchaz/enigma/source/DecompilerService.java | 6 +- .../java/cuchaz/enigma/source/Decompilers.java | 6 +- .../src/main/java/cuchaz/enigma/source/Source.java | 6 +- .../java/cuchaz/enigma/source/SourceIndex.java | 316 +++++---- .../java/cuchaz/enigma/source/SourceRemapper.java | 2 + .../java/cuchaz/enigma/source/SourceSettings.java | 12 +- .../src/main/java/cuchaz/enigma/source/Token.java | 19 +- .../main/java/cuchaz/enigma/source/TokenStore.java | 25 +- .../enigma/source/bytecode/BytecodeDecompiler.java | 19 +- .../enigma/source/bytecode/BytecodeSource.java | 73 +- .../enigma/source/bytecode/EnigmaTextifier.java | 13 +- .../cuchaz/enigma/source/cfr/CfrDecompiler.java | 93 +-- .../java/cuchaz/enigma/source/cfr/CfrSource.java | 142 ++-- .../cuchaz/enigma/source/cfr/EnigmaDumper.java | 781 +++++++++++---------- .../cuchaz/enigma/source/procyon/EntryParser.java | 57 +- .../enigma/source/procyon/ProcyonDecompiler.java | 28 +- .../enigma/source/procyon/ProcyonSource.java | 71 +- .../procyon/index/SourceIndexClassVisitor.java | 38 +- .../procyon/index/SourceIndexMethodVisitor.java | 93 ++- .../source/procyon/index/SourceIndexVisitor.java | 20 +- .../enigma/source/procyon/index/TokenFactory.java | 68 +- .../transformers/AddJavadocsAstTransform.java | 54 +- .../transformers/DropVarModifiersAstTransform.java | 11 +- .../procyon/transformers/InvalidIdentifierFix.java | 6 +- .../source/procyon/transformers/Java8Generics.java | 53 +- .../ObfuscatedEnumSwitchRewriterTransform.java | 542 +++++++------- .../procyon/transformers/RemoveObjectCasts.java | 6 +- .../source/procyon/transformers/VarargsFixer.java | 50 +- .../enigma/translation/LocalNameGenerator.java | 2 + .../enigma/translation/MappingTranslator.java | 2 +- .../enigma/translation/ProposingTranslator.java | 17 +- .../enigma/translation/SignatureUpdater.java | 27 +- .../cuchaz/enigma/translation/Translatable.java | 2 - .../cuchaz/enigma/translation/TranslateResult.java | 15 +- .../enigma/translation/TranslationDirection.java | 21 +- .../java/cuchaz/enigma/translation/Translator.java | 32 +- .../cuchaz/enigma/translation/VoidTranslator.java | 1 - .../enigma/translation/mapping/AccessModifier.java | 13 +- .../enigma/translation/mapping/EntryChange.java | 17 +- .../enigma/translation/mapping/EntryMap.java | 5 +- .../enigma/translation/mapping/EntryMapping.java | 6 +- .../enigma/translation/mapping/EntryRemapper.java | 12 +- .../enigma/translation/mapping/EntryResolver.java | 15 +- .../enigma/translation/mapping/EntryUtil.java | 2 - .../translation/mapping/IdentifierValidation.java | 50 +- .../translation/mapping/IndexEntryResolver.java | 25 +- .../enigma/translation/mapping/MappingDelta.java | 5 +- .../translation/mapping/MappingOperations.java | 121 ++-- .../enigma/translation/mapping/MappingPair.java | 4 +- .../translation/mapping/MappingValidator.java | 11 +- .../translation/mapping/MappingsChecker.java | 33 +- .../translation/mapping/VoidEntryResolver.java | 6 +- .../translation/mapping/serde/LfPrintWriter.java | 16 +- .../translation/mapping/serde/MappingFormat.java | 16 +- .../translation/mapping/serde/MappingHelper.java | 8 +- .../mapping/serde/MappingParseException.java | 19 +- .../translation/mapping/serde/MappingsReader.java | 6 +- .../translation/mapping/serde/MappingsWriter.java | 4 +- .../mapping/serde/enigma/EnigmaMappingsReader.java | 76 +- .../mapping/serde/enigma/EnigmaMappingsWriter.java | 81 ++- .../serde/proguard/ProguardMappingsReader.java | 238 +++---- .../mapping/serde/recaf/RecafMappingsReader.java | 19 +- .../mapping/serde/recaf/RecafMappingsWriter.java | 27 +- .../mapping/serde/srg/SrgMappingsWriter.java | 29 +- .../mapping/serde/tiny/TinyMappingsReader.java | 35 +- .../mapping/serde/tiny/TinyMappingsWriter.java | 253 ++++--- .../mapping/serde/tinyv2/TinyV2Reader.java | 232 +++--- .../mapping/serde/tinyv2/TinyV2Writer.java | 36 +- .../mapping/tree/DeltaTrackingTree.java | 11 +- .../translation/mapping/tree/EntryTreeNode.java | 13 +- .../translation/mapping/tree/HashEntryTree.java | 46 +- .../translation/mapping/tree/HashTreeNode.java | 9 +- .../translation/representation/AccessFlags.java | 9 +- .../enigma/translation/representation/Lambda.java | 39 +- .../representation/MethodDescriptor.java | 29 +- .../translation/representation/Signature.java | 12 +- .../translation/representation/TypeDescriptor.java | 56 +- .../representation/entry/ClassDefEntry.java | 26 +- .../representation/entry/ClassEntry.java | 51 +- .../translation/representation/entry/Entry.java | 31 +- .../representation/entry/FieldDefEntry.java | 28 +- .../representation/entry/FieldEntry.java | 23 +- .../entry/LocalVariableDefEntry.java | 5 +- .../representation/entry/LocalVariableEntry.java | 6 +- .../representation/entry/MethodDefEntry.java | 23 +- .../representation/entry/MethodEntry.java | 25 +- .../representation/entry/ParentedEntry.java | 22 +- .../src/main/java/cuchaz/enigma/utils/AsmUtil.java | 22 +- enigma/src/main/java/cuchaz/enigma/utils/I18n.java | 16 +- enigma/src/main/java/cuchaz/enigma/utils/Os.java | 8 +- enigma/src/main/java/cuchaz/enigma/utils/Pair.java | 31 +- .../src/main/java/cuchaz/enigma/utils/Result.java | 55 +- .../java/cuchaz/enigma/utils/TristateChange.java | 20 +- .../src/main/java/cuchaz/enigma/utils/Utils.java | 160 +++-- .../cuchaz/enigma/utils/validation/Message.java | 2 - .../utils/validation/ParameterizedMessage.java | 16 +- .../enigma/utils/validation/PrintValidatable.java | 8 +- .../utils/validation/StandardValidation.java | 15 +- .../enigma/utils/validation/Validatable.java | 2 - .../enigma/utils/validation/ValidationContext.java | 14 +- 160 files changed, 4153 insertions(+), 3541 deletions(-) (limited to 'enigma/src/main/java/cuchaz') diff --git a/enigma/src/main/java/cuchaz/enigma/Enigma.java b/enigma/src/main/java/cuchaz/enigma/Enigma.java index a37f074..696a848 100644 --- a/enigma/src/main/java/cuchaz/enigma/Enigma.java +++ b/enigma/src/main/java/cuchaz/enigma/Enigma.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma; @@ -35,12 +35,12 @@ import cuchaz.enigma.classprovider.JarClassProvider; import cuchaz.enigma.utils.Utils; public class Enigma { - public static final String NAME = "Enigma"; + public static final String NAME = "Enigma"; public static final String VERSION; public static final String URL = "https://fabricmc.net"; - public static final int ASM_VERSION = Opcodes.ASM9; + public static final int ASM_VERSION = Opcodes.ASM9; - private final EnigmaProfile profile; + private final EnigmaProfile profile; private final EnigmaServices services; private Enigma(EnigmaProfile profile, EnigmaServices services) { @@ -97,6 +97,7 @@ public class Enigma { public Enigma build() { PluginContext pluginContext = new PluginContext(profile); + for (EnigmaPlugin plugin : plugins) { plugin.init(pluginContext); } diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java index daf2727..f95bf1e 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProfile.java @@ -1,5 +1,20 @@ package cuchaz.enigma; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import javax.annotation.Nullable; + import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -10,31 +25,16 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; + import cuchaz.enigma.api.service.EnigmaServiceType; import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; -import javax.annotation.Nullable; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - public final class EnigmaProfile { public static final EnigmaProfile EMPTY = new EnigmaProfile(new ServiceContainer(ImmutableMap.of())); private static final MappingSaveParameters DEFAULT_MAPPING_SAVE_PARAMETERS = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); - private static final Gson GSON = new GsonBuilder() - .registerTypeAdapter(ServiceContainer.class, (JsonDeserializer) EnigmaProfile::loadServiceContainer) - .create(); + private static final Gson GSON = new GsonBuilder().registerTypeAdapter(ServiceContainer.class, (JsonDeserializer) EnigmaProfile::loadServiceContainer).create(); private static final Type SERVICE_LIST_TYPE = new TypeToken>() { }.getType(); @@ -78,6 +78,7 @@ public final class EnigmaProfile { for (Map.Entry entry : object.entrySet()) { JsonElement value = entry.getValue(); + if (value.isJsonObject()) { builder.put(entry.getKey(), Collections.singletonList(GSON.fromJson(value, Service.class))); } else if (value.isJsonArray()) { diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 4f50f2f..15d5e98 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java @@ -18,16 +18,16 @@ import java.util.stream.Stream; import com.google.common.base.Functions; import com.google.common.base.Preconditions; -import cuchaz.enigma.api.service.ObfuscationTestService; -import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.api.service.NameProposalService; +import cuchaz.enigma.api.service.ObfuscationTestService; import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; import cuchaz.enigma.classprovider.ClassProvider; +import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.DecompilerService; import cuchaz.enigma.source.SourceSettings; @@ -101,6 +101,7 @@ public class EnigmaProject { DeltaTrackingTree mappings = mapper.getObfToDeobf(); Collection> dropped = dropMappings(mappings, progress); + for (Entry entry : dropped) { mappings.trackChange(entry); } @@ -112,6 +113,7 @@ public class EnigmaProject { MappingsChecker.Dropped dropped = checker.dropBrokenMappings(progress); Map, String> droppedMappings = dropped.getDroppedMappings(); + for (Map.Entry, String> mapping : droppedMappings.entrySet()) { System.out.println("WARNING: Couldn't find " + mapping.getKey() + " (" + mapping.getValue() + ") in jar. Mapping was dropped."); } @@ -124,6 +126,7 @@ public class EnigmaProject { // HACKHACK: Object methods are not obfuscated identifiers String name = obfMethodEntry.getName(); String sig = obfMethodEntry.getDesc().toString(); + //TODO replace with a map or check if declaring class is java.lang.Object if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { return false; @@ -163,6 +166,7 @@ public class EnigmaProject { String name = entry.getName(); List obfuscationTestServices = this.getEnigma().getServices().get(ObfuscationTestService.TYPE); + if (!obfuscationTestServices.isEmpty()) { for (ObfuscationTestService service : obfuscationTestServices) { if (service.testDeobfuscated(entry)) { @@ -172,6 +176,7 @@ public class EnigmaProject { } List nameProposalServices = this.getEnigma().getServices().get(NameProposalService.TYPE); + if (!nameProposalServices.isEmpty()) { for (NameProposalService service : nameProposalServices) { if (service.proposeName(entry, mapper).isPresent()) { @@ -181,6 +186,7 @@ public class EnigmaProject { } String mappedName = mapper.deobfuscate(entry).getName(); + if (mappedName != null && !mappedName.isEmpty() && !mappedName.equals(name)) { return false; } @@ -198,22 +204,20 @@ public class EnigmaProject { AtomicInteger count = new AtomicInteger(); progress.init(classEntries.size(), I18n.translate("progress.classes.deobfuscating")); - Map compiled = classEntries.parallelStream() - .map(entry -> { - ClassEntry translatedEntry = deobfuscator.translate(entry); - progress.step(count.getAndIncrement(), translatedEntry.toString()); + Map compiled = classEntries.parallelStream().map(entry -> { + ClassEntry translatedEntry = deobfuscator.translate(entry); + progress.step(count.getAndIncrement(), translatedEntry.toString()); - ClassNode node = fixingClassProvider.get(entry.getFullName()); - if (node != null) { - ClassNode translatedNode = new ClassNode(); - node.accept(new TranslationClassVisitor(deobfuscator, Enigma.ASM_VERSION, translatedNode)); - return translatedNode; - } + ClassNode node = fixingClassProvider.get(entry.getFullName()); - return null; - }) - .filter(Objects::nonNull) - .collect(Collectors.toMap(n -> n.name, Functions.identity())); + if (node != null) { + ClassNode translatedNode = new ClassNode(); + node.accept(new TranslationClassVisitor(deobfuscator, Enigma.ASM_VERSION, translatedNode)); + return translatedNode; + } + + return null; + }).filter(Objects::nonNull).collect(Collectors.toMap(n -> n.name, Functions.identity())); return new JarExport(mapper, compiled); } @@ -254,9 +258,7 @@ public class EnigmaProject { } public Stream decompileStream(ProgressListener progress, DecompilerService decompilerService, DecompileErrorStrategy errorStrategy) { - Collection classes = this.compiled.values().stream() - .filter(classNode -> classNode.name.indexOf('$') == -1) - .toList(); + Collection classes = this.compiled.values().stream().filter(classNode -> classNode.name.indexOf('$') == -1).toList(); progress.init(classes.size(), I18n.translate("progress.classes.decompiling")); @@ -265,33 +267,34 @@ public class EnigmaProject { AtomicInteger count = new AtomicInteger(); - return classes.parallelStream() - .map(translatedNode -> { - progress.step(count.getAndIncrement(), translatedNode.name); - - String source = null; - try { - source = decompileClass(translatedNode, decompiler); - } catch (Throwable throwable) { - switch (errorStrategy) { - case PROPAGATE: throw throwable; - case IGNORE: break; - case TRACE_AS_SOURCE: { - StringWriter writer = new StringWriter(); - throwable.printStackTrace(new PrintWriter(writer)); - source = writer.toString(); - break; - } - } - } - - if (source == null) { - return null; - } - - return new ClassSource(translatedNode.name, source); - }) - .filter(Objects::nonNull); + return classes.parallelStream().map(translatedNode -> { + progress.step(count.getAndIncrement(), translatedNode.name); + + String source = null; + + try { + source = decompileClass(translatedNode, decompiler); + } catch (Throwable throwable) { + switch (errorStrategy) { + case PROPAGATE: + throw throwable; + case IGNORE: + break; + case TRACE_AS_SOURCE: { + StringWriter writer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(writer)); + source = writer.toString(); + break; + } + } + } + + if (source == null) { + return null; + } + + return new ClassSource(translatedNode.name, source); + }).filter(Objects::nonNull); } private String decompileClass(ClassNode translatedNode, Decompiler decompiler) { @@ -310,6 +313,7 @@ public class EnigmaProject { progress.init(decompiled.size(), I18n.translate("progress.sources.writing")); int count = 0; + for (ClassSource source : decompiled) { progress.step(count++, source.name); @@ -329,7 +333,6 @@ public class EnigmaProject { } public void writeTo(Path path) throws IOException { - Files.createDirectories(path.getParent()); try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.write(source); } diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java b/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java index df3b7bb..bbdc684 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaServices.java @@ -1,11 +1,12 @@ package cuchaz.enigma; +import java.util.List; + import com.google.common.collect.ImmutableListMultimap; + import cuchaz.enigma.api.service.EnigmaService; import cuchaz.enigma.api.service.EnigmaServiceType; -import java.util.List; - public final class EnigmaServices { private final ImmutableListMultimap, EnigmaService> services; diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/Access.java b/enigma/src/main/java/cuchaz/enigma/analysis/Access.java index 82ca669..cc7d121 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/Access.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/Access.java @@ -1,23 +1,25 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; -import cuchaz.enigma.translation.representation.AccessFlags; - import java.lang.reflect.Modifier; -public enum Access { +import cuchaz.enigma.translation.representation.AccessFlags; - PUBLIC, PROTECTED, PACKAGE, PRIVATE; +public enum Access { + PUBLIC, + PROTECTED, + PACKAGE, + PRIVATE; public static Access get(AccessFlags flags) { return get(flags.getFlags()); @@ -37,6 +39,7 @@ public enum Access { } else if (!isPublic && !isProtected && !isPrivate) { return PACKAGE; } + // assume public by default return PUBLIC; } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java index 013c52f..45dac2c 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/BuiltinPlugin.java @@ -1,17 +1,13 @@ package cuchaz.enigma.analysis; -import cuchaz.enigma.Enigma; -import cuchaz.enigma.api.EnigmaPlugin; -import cuchaz.enigma.api.EnigmaPluginContext; -import cuchaz.enigma.api.service.JarIndexerService; -import cuchaz.enigma.api.service.NameProposalService; -import cuchaz.enigma.source.DecompilerService; -import cuchaz.enigma.source.Decompilers; -import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import cuchaz.enigma.translation.representation.entry.Entry; -import cuchaz.enigma.translation.representation.entry.FieldEntry; -import cuchaz.enigma.utils.Pair; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; @@ -27,16 +23,20 @@ import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.SourceInterpreter; import org.objectweb.asm.tree.analysis.SourceValue; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import cuchaz.enigma.Enigma; +import cuchaz.enigma.api.EnigmaPlugin; +import cuchaz.enigma.api.EnigmaPluginContext; +import cuchaz.enigma.api.service.JarIndexerService; +import cuchaz.enigma.api.service.NameProposalService; +import cuchaz.enigma.source.DecompilerService; +import cuchaz.enigma.source.Decompilers; +import cuchaz.enigma.translation.representation.TypeDescriptor; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.utils.Pair; public final class BuiltinPlugin implements EnigmaPlugin { - public BuiltinPlugin() { } @@ -61,7 +61,6 @@ public final class BuiltinPlugin implements EnigmaPlugin { } private static final class EnumFieldNameFindingVisitor extends ClassVisitor { - private ClassEntry clazz; private String className; private final Map, String> mappings; @@ -89,6 +88,7 @@ public final class BuiltinPlugin implements EnigmaPlugin { throw new IllegalArgumentException("Found two enum fields with the same name \"" + name + "\" and desc \"" + descriptor + "\"!"); } } + return super.visitField(access, name, descriptor, signature, value); } @@ -99,12 +99,14 @@ public final class BuiltinPlugin implements EnigmaPlugin { classInits.add(node); return node; } + return super.visitMethod(access, name, descriptor, signature, exceptions); } @Override public void visitEnd() { super.visitEnd(); + try { collectResults(); } catch (Exception ex) { @@ -118,21 +120,18 @@ public final class BuiltinPlugin implements EnigmaPlugin { for (MethodNode mn : classInits) { Frame[] frames = analyzer.analyze(className, mn); - InsnList instrs = mn.instructions; + for (int i = 1; i < instrs.size(); i++) { AbstractInsnNode instr1 = instrs.get(i - 1); AbstractInsnNode instr2 = instrs.get(i); String s = null; - if (instr2.getOpcode() == Opcodes.PUTSTATIC - && ((FieldInsnNode) instr2).owner.equals(owner) - && enumFields.contains(new Pair<>(((FieldInsnNode) instr2).name, ((FieldInsnNode) instr2).desc)) - && instr1.getOpcode() == Opcodes.INVOKESPECIAL - && "".equals(((MethodInsnNode) instr1).name)) { - + if (instr2.getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode) instr2).owner.equals(owner) && enumFields.contains(new Pair<>(((FieldInsnNode) instr2).name, ((FieldInsnNode) instr2).desc)) && instr1.getOpcode() == Opcodes.INVOKESPECIAL && "".equals( + ((MethodInsnNode) instr1).name)) { for (int j = 0; j < frames[i - 1].getStackSize(); j++) { SourceValue sv = frames[i - 1].getStack(j); + for (AbstractInsnNode ci : sv.insns) { if (ci instanceof LdcInsnNode && ((LdcInsnNode) ci).cst instanceof String) { //if (s == null || !s.equals(((LdcInsnNode) ci).cst)) { @@ -148,6 +147,7 @@ public final class BuiltinPlugin implements EnigmaPlugin { if (s != null) { mappings.put(new FieldEntry(clazz, ((FieldInsnNode) instr2).name, new TypeDescriptor(((FieldInsnNode) instr2).desc)), s); } + // report otherwise? } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java index 0fc44ca..8ef28d9 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/ClassImplementationsTreeNode.java @@ -1,27 +1,29 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.Collection; +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + import com.google.common.collect.Lists; + import cuchaz.enigma.analysis.index.InheritanceIndex; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.swing.tree.DefaultMutableTreeNode; -import java.util.Collection; -import java.util.List; - public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { private final Translator translator; private final ClassEntry entry; @@ -40,10 +42,12 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { // recurse for (int i = 0; i < node.getChildCount(); i++) { ClassImplementationsTreeNode foundNode = findNode((ClassImplementationsTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { return foundNode; } } + return null; } @@ -62,6 +66,7 @@ public class ClassImplementationsTreeNode extends DefaultMutableTreeNode { InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); Collection inheritors = inheritanceIndex.getChildren(entry); + for (ClassEntry inheritor : inheritors) { nodes.add(new ClassImplementationsTreeNode(translator, inheritor)); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java index 788c534..24da23c 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java @@ -1,24 +1,26 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + import com.google.common.collect.Lists; + import cuchaz.enigma.analysis.index.InheritanceIndex; import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.entry.ClassEntry; -import javax.swing.tree.DefaultMutableTreeNode; -import java.util.List; - public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { private final Translator translator; private final ClassEntry obfClassEntry; @@ -37,10 +39,12 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { // recurse for (int i = 0; i < node.getChildCount(); i++) { ClassInheritanceTreeNode foundNode = findNode((ClassInheritanceTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { return foundNode; } } + return null; } @@ -63,6 +67,7 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode { public void load(InheritanceIndex ancestries, boolean recurse) { // get all the child nodes List nodes = Lists.newArrayList(); + for (ClassEntry inheritor : ancestries.getChildren(this.obfClassEntry)) { nodes.add(new ClassInheritanceTreeNode(translator, inheritor.getFullName())); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java index 0142412..c76dca7 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/ClassReferenceTreeNode.java @@ -1,17 +1,23 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.Set; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; + import com.google.common.collect.Sets; + import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.analysis.index.ReferenceIndex; import cuchaz.enigma.translation.Translator; @@ -19,13 +25,7 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.MethodDefEntry; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; -import java.util.Set; - -public class ClassReferenceTreeNode extends DefaultMutableTreeNode - implements ReferenceTreeNode { - +public class ClassReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { private Translator deobfuscatingTranslator; private ClassEntry entry; private EntryReference reference; @@ -57,6 +57,7 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode if (this.reference != null) { return String.format("%s", this.deobfuscatingTranslator.translate(this.reference.context)); } + return this.deobfuscatingTranslator.translate(this.entry).getFullName(); } @@ -71,16 +72,18 @@ public class ClassReferenceTreeNode extends DefaultMutableTreeNode if (recurse && this.children != null) { for (Object child : this.children) { if (child instanceof ClassReferenceTreeNode node) { - // don't recurse into ancestor Set> ancestors = Sets.newHashSet(); TreeNode n = node; + while (n.getParent() != null) { n = n.getParent(); + if (n instanceof ClassReferenceTreeNode) { ancestors.add(((ClassReferenceTreeNode) n).getEntry()); } } + if (ancestors.contains(node.getEntry())) { continue; } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java b/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java index 281b05f..9c54281 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/EntryReference.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; @@ -26,7 +26,6 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.MethodEntry; public class EntryReference, C extends Entry> implements Translatable { - private static final List CONSTRUCTOR_NON_NAMES = Arrays.asList("this", "super", "static"); public final E entry; public final C context; @@ -60,8 +59,7 @@ public class EntryReference, C extends Entry> implements T this.targetType = targetType; this.declaration = declaration; - this.sourceName = sourceName != null && !sourceName.isEmpty() && - !(entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)); + this.sourceName = sourceName != null && !sourceName.isEmpty() && !(entry instanceof MethodEntry && ((MethodEntry) entry).isConstructor() && CONSTRUCTOR_NON_NAMES.contains(sourceName)); } public EntryReference(E entry, C context, EntryReference other) { @@ -76,6 +74,7 @@ public class EntryReference, C extends Entry> implements T if (context != null) { return context.getContainingClass(); } + return entry.getContainingClass(); } @@ -95,6 +94,7 @@ public class EntryReference, C extends Entry> implements T // renaming a constructor really means renaming the class return entry.getContainingClass(); } + return entry; } @@ -107,6 +107,7 @@ public class EntryReference, C extends Entry> implements T if (context != null) { return Objects.hash(entry.hashCode(), context.hashCode()); } + return entry.hashCode() ^ Boolean.hashCode(this.declaration); } @@ -116,10 +117,7 @@ public class EntryReference, C extends Entry> implements T } public boolean equals(EntryReference other) { - return other != null - && Objects.equals(entry, other.entry) - && Objects.equals(context, other.context) - && declaration == other.declaration; + return other != null && Objects.equals(entry, other.entry) && Objects.equals(context, other.context) && declaration == other.declaration; } @Override @@ -149,5 +147,4 @@ public class EntryReference, C extends Entry> implements T public TranslateResult> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap mappings) { return translator.extendedTranslate(this.entry).map(e -> new EntryReference<>(e, translator.translate(context), this)); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java index c93ac53..cc511f3 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/FieldReferenceTreeNode.java @@ -1,16 +1,18 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; +import javax.swing.tree.DefaultMutableTreeNode; + import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.analysis.index.ReferenceIndex; import cuchaz.enigma.translation.Translator; @@ -18,10 +20,7 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodDefEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.swing.tree.DefaultMutableTreeNode; - public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { - private final Translator translator; private FieldEntry entry; private EntryReference reference; @@ -53,6 +52,7 @@ public class FieldReferenceTreeNode extends DefaultMutableTreeNode implements Re if (this.reference != null) { return String.format("%s", translator.translate(this.reference.context)); } + return translator.translate(entry).toString(); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java b/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java index ec8f323..44a768e 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/IndexSimpleVerifier.java @@ -1,154 +1,160 @@ package cuchaz.enigma.analysis; +import java.util.Set; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.analysis.BasicValue; +import org.objectweb.asm.tree.analysis.SimpleVerifier; + import cuchaz.enigma.Enigma; import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.analysis.index.InheritanceIndex; import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.entry.ClassDefEntry; import cuchaz.enigma.translation.representation.entry.ClassEntry; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.analysis.BasicValue; -import org.objectweb.asm.tree.analysis.SimpleVerifier; - -import java.util.Set; public class IndexSimpleVerifier extends SimpleVerifier { - private static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); - private final EntryIndex entryIndex; - private final InheritanceIndex inheritanceIndex; - - public IndexSimpleVerifier(EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { - super(Enigma.ASM_VERSION, null, null, null, false); - this.entryIndex = entryIndex; - this.inheritanceIndex = inheritanceIndex; - } - - @Override - protected boolean isSubTypeOf(BasicValue value, BasicValue expected) { - Type expectedType = expected.getType(); - Type type = value.getType(); - switch (expectedType.getSort()) { - case Type.INT: - case Type.FLOAT: - case Type.LONG: - case Type.DOUBLE: - return type.equals(expectedType); - case Type.ARRAY: - case Type.OBJECT: - if (type.equals(NULL_TYPE)) { - return true; - } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { - if (isAssignableFrom(expectedType, type)) { - return true; - } else if (isInterface(expectedType)) { - return isAssignableFrom(OBJECT_TYPE, type); - } else { - return false; - } - } else { - return false; - } - default: - throw new AssertionError(); - } - } - - @Override - protected boolean isInterface(Type type) { - AccessFlags classAccess = entryIndex.getClassAccess(new ClassEntry(type.getInternalName())); - if (classAccess != null) { - return classAccess.isInterface(); - } - - Class clazz = getClass(type); - if (clazz != null) { - return clazz.isInterface(); - } - - return false; - } - - @Override - protected Type getSuperClass(Type type) { - ClassDefEntry definition = entryIndex.getDefinition(new ClassEntry(type.getInternalName())); - if (definition != null) { - return Type.getType('L' + definition.getSuperClass().getFullName() + ';'); - } - - Class clazz = getClass(type); - if (clazz != null) { - return Type.getType(clazz.getSuperclass()); - } - - return OBJECT_TYPE; - } - - @Override - protected boolean isAssignableFrom(Type type1, Type type2) { - if (type1.equals(type2)) { - return true; - } - - if (type2.equals(NULL_TYPE)) { - return true; - } - - if (type1.getSort() == Type.ARRAY) { - return type2.getSort() == Type.ARRAY && isAssignableFrom(Type.getType(type1.getDescriptor().substring(1)), Type.getType(type2.getDescriptor().substring(1))); - } - - if (type2.getSort() == Type.ARRAY) { - return type1.equals(OBJECT_TYPE); - } - - if (type1.getSort() == Type.OBJECT && type2.getSort() == Type.OBJECT) { - if (type1.equals(OBJECT_TYPE)) { - return true; - } - - ClassEntry class1 = new ClassEntry(type1.getInternalName()); - ClassEntry class2 = new ClassEntry(type2.getInternalName()); - - if (entryIndex.hasClass(class1) && entryIndex.hasClass(class2)) { - return inheritanceIndex.getAncestors(class2).contains(class1); - } - - Class class1Class = getClass(Type.getType('L' + class1.getFullName() + ';')); - Class class2Class = getClass(Type.getType('L' + class2.getFullName() + ';')); - - if (class1Class == null) { - return true; // missing classes to find out - } - - if (class2Class != null) { - return class1Class.isAssignableFrom(class2Class); - } - - if (entryIndex.hasClass(class2)) { - Set ancestors = inheritanceIndex.getAncestors(class2); - - for (ClassEntry ancestorEntry : ancestors) { - Class ancestor = getClass(Type.getType('L' + ancestorEntry.getFullName() + ';')); - if (ancestor == null || class1Class.isAssignableFrom(ancestor)) { - return true; // assignable, or missing classes to find out - } - } - - return false; - } - - return true; // missing classes to find out - } - - return false; - } - - @Override - protected final Class getClass(Type type) { - try { - return Class.forName(type.getSort() == Type.ARRAY ? type.getDescriptor().replace('/', '.') : type.getClassName(), false, null); - } catch (ClassNotFoundException e) { - return null; - } - } + private static final Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); + private final EntryIndex entryIndex; + private final InheritanceIndex inheritanceIndex; + + public IndexSimpleVerifier(EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { + super(Enigma.ASM_VERSION, null, null, null, false); + this.entryIndex = entryIndex; + this.inheritanceIndex = inheritanceIndex; + } + + @Override + protected boolean isSubTypeOf(BasicValue value, BasicValue expected) { + Type expectedType = expected.getType(); + Type type = value.getType(); + switch (expectedType.getSort()) { + case Type.INT: + case Type.FLOAT: + case Type.LONG: + case Type.DOUBLE: + return type.equals(expectedType); + case Type.ARRAY: + case Type.OBJECT: + if (type.equals(NULL_TYPE)) { + return true; + } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { + if (isAssignableFrom(expectedType, type)) { + return true; + } else if (isInterface(expectedType)) { + return isAssignableFrom(OBJECT_TYPE, type); + } else { + return false; + } + } else { + return false; + } + default: + throw new AssertionError(); + } + } + + @Override + protected boolean isInterface(Type type) { + AccessFlags classAccess = entryIndex.getClassAccess(new ClassEntry(type.getInternalName())); + + if (classAccess != null) { + return classAccess.isInterface(); + } + + Class clazz = getClass(type); + + if (clazz != null) { + return clazz.isInterface(); + } + + return false; + } + + @Override + protected Type getSuperClass(Type type) { + ClassDefEntry definition = entryIndex.getDefinition(new ClassEntry(type.getInternalName())); + + if (definition != null) { + return Type.getType('L' + definition.getSuperClass().getFullName() + ';'); + } + + Class clazz = getClass(type); + + if (clazz != null) { + return Type.getType(clazz.getSuperclass()); + } + + return OBJECT_TYPE; + } + + @Override + protected boolean isAssignableFrom(Type type1, Type type2) { + if (type1.equals(type2)) { + return true; + } + + if (type2.equals(NULL_TYPE)) { + return true; + } + + if (type1.getSort() == Type.ARRAY) { + return type2.getSort() == Type.ARRAY && isAssignableFrom(Type.getType(type1.getDescriptor().substring(1)), Type.getType(type2.getDescriptor().substring(1))); + } + + if (type2.getSort() == Type.ARRAY) { + return type1.equals(OBJECT_TYPE); + } + + if (type1.getSort() == Type.OBJECT && type2.getSort() == Type.OBJECT) { + if (type1.equals(OBJECT_TYPE)) { + return true; + } + + ClassEntry class1 = new ClassEntry(type1.getInternalName()); + ClassEntry class2 = new ClassEntry(type2.getInternalName()); + + if (entryIndex.hasClass(class1) && entryIndex.hasClass(class2)) { + return inheritanceIndex.getAncestors(class2).contains(class1); + } + + Class class1Class = getClass(Type.getType('L' + class1.getFullName() + ';')); + Class class2Class = getClass(Type.getType('L' + class2.getFullName() + ';')); + + if (class1Class == null) { + return true; // missing classes to find out + } + + if (class2Class != null) { + return class1Class.isAssignableFrom(class2Class); + } + + if (entryIndex.hasClass(class2)) { + Set ancestors = inheritanceIndex.getAncestors(class2); + + for (ClassEntry ancestorEntry : ancestors) { + Class ancestor = getClass(Type.getType('L' + ancestorEntry.getFullName() + ';')); + + if (ancestor == null || class1Class.isAssignableFrom(ancestor)) { + return true; // assignable, or missing classes to find out + } + } + + return false; + } + + return true; // missing classes to find out + } + + return false; + } + + @Override + protected final Class getClass(Type type) { + try { + return Class.forName(type.getSort() == Type.ARRAY ? type.getDescriptor().replace('/', '.') : type.getClassName(), false, null); + } catch (ClassNotFoundException e) { + return null; + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java b/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java index 0c2dfd7..3043577 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/IndexTreeBuilder.java @@ -1,6 +1,10 @@ package cuchaz.enigma.analysis; +import java.util.Collection; +import java.util.List; + import com.google.common.collect.Lists; + import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.EntryResolver; @@ -8,9 +12,6 @@ import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.util.Collection; -import java.util.List; - public class IndexTreeBuilder { private final JarIndex index; @@ -22,6 +23,7 @@ public class IndexTreeBuilder { // get the root node List ancestry = Lists.newArrayList(); ancestry.add(obfClassEntry.getFullName()); + for (ClassEntry classEntry : index.getInheritanceIndex().getAncestors(obfClassEntry)) { ancestry.add(classEntry.getFullName()); } @@ -40,6 +42,7 @@ public class IndexTreeBuilder { node.load(index); return node; } + return null; } @@ -47,10 +50,7 @@ public class IndexTreeBuilder { MethodEntry resolvedEntry = index.getEntryResolver().resolveFirstEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); // make a root node at the base - MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode( - translator, resolvedEntry, - index.getEntryIndex().hasMethod(resolvedEntry) - ); + MethodInheritanceTreeNode rootNode = new MethodInheritanceTreeNode(translator, resolvedEntry, index.getEntryIndex().hasMethod(resolvedEntry)); // expand the full tree rootNode.load(index); @@ -63,6 +63,7 @@ public class IndexTreeBuilder { Collection resolvedEntries = resolver.resolveEntry(obfMethodEntry, ResolutionStrategy.RESOLVE_ROOT); List nodes = Lists.newArrayList(); + for (MethodEntry resolvedEntry : resolvedEntries) { MethodImplementationsTreeNode node = new MethodImplementationsTreeNode(translator, resolvedEntry); node.load(index); diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java b/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java index a624b7c..2ca1dfd 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/InterpreterPair.java @@ -1,129 +1,106 @@ package cuchaz.enigma.analysis; -import cuchaz.enigma.Enigma; +import java.util.List; +import java.util.Objects; + import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Interpreter; import org.objectweb.asm.tree.analysis.Value; -import java.util.List; -import java.util.Objects; +import cuchaz.enigma.Enigma; public class InterpreterPair extends Interpreter> { - private final Interpreter left; - private final Interpreter right; - - public InterpreterPair(Interpreter left, Interpreter right) { - super(Enigma.ASM_VERSION); - this.left = left; - this.right = right; - } - - @Override - public PairValue newValue(Type type) { - return pair( - left.newValue(type), - right.newValue(type) - ); - } - - @Override - public PairValue newOperation(AbstractInsnNode insn) throws AnalyzerException { - return pair( - left.newOperation(insn), - right.newOperation(insn) - ); - } - - @Override - public PairValue copyOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { - return pair( - left.copyOperation(insn, value.left), - right.copyOperation(insn, value.right) - ); - } - - @Override - public PairValue unaryOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { - return pair( - left.unaryOperation(insn, value.left), - right.unaryOperation(insn, value.right) - ); - } - - @Override - public PairValue binaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2) throws AnalyzerException { - return pair( - left.binaryOperation(insn, value1.left, value2.left), - right.binaryOperation(insn, value1.right, value2.right) - ); - } - - @Override - public PairValue ternaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2, PairValue value3) throws AnalyzerException { - return pair( - left.ternaryOperation(insn, value1.left, value2.left, value3.left), - right.ternaryOperation(insn, value1.right, value2.right, value3.right) - ); - } - - @Override - public PairValue naryOperation(AbstractInsnNode insn, List> values) throws AnalyzerException { - return pair( - left.naryOperation(insn, values.stream().map(v -> v.left).toList()), - right.naryOperation(insn, values.stream().map(v -> v.right).toList()) - ); - } - - @Override - public void returnOperation(AbstractInsnNode insn, PairValue value, PairValue expected) throws AnalyzerException { - left.returnOperation(insn, value.left, expected.left); - right.returnOperation(insn, value.right, expected.right); - } - - @Override - public PairValue merge(PairValue value1, PairValue value2) { - return pair( - left.merge(value1.left, value2.left), - right.merge(value1.right, value2.right) - ); - } - - private PairValue pair(V left, W right) { - if (left == null && right == null) { - return null; - } - - return new PairValue<>(left, right); - } - - public static final class PairValue implements Value { - public final V left; - public final W right; - - public PairValue(V left, W right) { - if (left == null && right == null) { - throw new IllegalArgumentException("should use null rather than pair of nulls"); - } - - this.left = left; - this.right = right; - } - - @Override - public boolean equals(Object o) { - return o instanceof InterpreterPair.PairValue pairValue && Objects.equals(left, pairValue.left) && Objects.equals(right, pairValue.right); - } - - @Override - public int hashCode() { - return left.hashCode() * 31 + right.hashCode(); - } - - @Override - public int getSize() { - return (left == null ? right : left).getSize(); - } - } + private final Interpreter left; + private final Interpreter right; + + public InterpreterPair(Interpreter left, Interpreter right) { + super(Enigma.ASM_VERSION); + this.left = left; + this.right = right; + } + + @Override + public PairValue newValue(Type type) { + return pair(left.newValue(type), right.newValue(type)); + } + + @Override + public PairValue newOperation(AbstractInsnNode insn) throws AnalyzerException { + return pair(left.newOperation(insn), right.newOperation(insn)); + } + + @Override + public PairValue copyOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { + return pair(left.copyOperation(insn, value.left), right.copyOperation(insn, value.right)); + } + + @Override + public PairValue unaryOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { + return pair(left.unaryOperation(insn, value.left), right.unaryOperation(insn, value.right)); + } + + @Override + public PairValue binaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2) throws AnalyzerException { + return pair(left.binaryOperation(insn, value1.left, value2.left), right.binaryOperation(insn, value1.right, value2.right)); + } + + @Override + public PairValue ternaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2, PairValue value3) throws AnalyzerException { + return pair(left.ternaryOperation(insn, value1.left, value2.left, value3.left), right.ternaryOperation(insn, value1.right, value2.right, value3.right)); + } + + @Override + public PairValue naryOperation(AbstractInsnNode insn, List> values) throws AnalyzerException { + return pair(left.naryOperation(insn, values.stream().map(v -> v.left).toList()), right.naryOperation(insn, values.stream().map(v -> v.right).toList())); + } + + @Override + public void returnOperation(AbstractInsnNode insn, PairValue value, PairValue expected) throws AnalyzerException { + left.returnOperation(insn, value.left, expected.left); + right.returnOperation(insn, value.right, expected.right); + } + + @Override + public PairValue merge(PairValue value1, PairValue value2) { + return pair(left.merge(value1.left, value2.left), right.merge(value1.right, value2.right)); + } + + private PairValue pair(V left, W right) { + if (left == null && right == null) { + return null; + } + + return new PairValue<>(left, right); + } + + public static final class PairValue implements Value { + public final V left; + public final W right; + + public PairValue(V left, W right) { + if (left == null && right == null) { + throw new IllegalArgumentException("should use null rather than pair of nulls"); + } + + this.left = left; + this.right = right; + } + + @Override + public boolean equals(Object o) { + return o instanceof InterpreterPair.PairValue pairValue && Objects.equals(left, pairValue.left) && Objects.equals(right, pairValue.right); + } + + @Override + public int hashCode() { + return left.hashCode() * 31 + right.hashCode(); + } + + @Override + public int getSize() { + return (left == null ? right : left).getSize(); + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java index 4633ace..83275da 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodImplementationsTreeNode.java @@ -1,17 +1,23 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.Collection; +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + import com.google.common.collect.Lists; + import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.analysis.index.InheritanceIndex; import cuchaz.enigma.analysis.index.JarIndex; @@ -19,17 +25,13 @@ import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.swing.tree.DefaultMutableTreeNode; -import java.util.Collection; -import java.util.List; - public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { - private final Translator translator; private MethodEntry entry; public MethodImplementationsTreeNode(Translator translator, MethodEntry entry) { this.translator = translator; + if (entry == null) { throw new IllegalArgumentException("Entry cannot be null!"); } @@ -46,10 +48,12 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { // recurse for (int i = 0; i < node.getChildCount(); i++) { MethodImplementationsTreeNode foundNode = findNode((MethodImplementationsTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { return foundNode; } } + return null; } @@ -70,8 +74,10 @@ public class MethodImplementationsTreeNode extends DefaultMutableTreeNode { InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); Collection descendants = inheritanceIndex.getDescendants(entry.getParent()); + for (ClassEntry inheritor : descendants) { MethodEntry methodEntry = entry.withParent(inheritor); + if (entryIndex.hasMethod(methodEntry)) { nodes.add(new MethodImplementationsTreeNode(translator, methodEntry)); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java index 455456f..2afeed9 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java @@ -1,16 +1,18 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; +import javax.swing.tree.DefaultMutableTreeNode; + import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.analysis.index.InheritanceIndex; import cuchaz.enigma.analysis.index.JarIndex; @@ -18,10 +20,7 @@ import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.swing.tree.DefaultMutableTreeNode; - public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { - private final Translator translator; private MethodEntry entry; private boolean implemented; @@ -41,10 +40,12 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { // recurse for (int i = 0; i < node.getChildCount(); i++) { MethodInheritanceTreeNode foundNode = findNode((MethodInheritanceTreeNode) node.getChildAt(i), entry); + if (foundNode != null) { return foundNode; } } + return null; } @@ -79,6 +80,7 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode { InheritanceIndex inheritanceIndex = index.getInheritanceIndex(); boolean ret = false; + for (ClassEntry inheritorEntry : inheritanceIndex.getChildren(this.entry.getParent())) { MethodEntry methodEntry = new MethodEntry(inheritorEntry, this.entry.getName(), this.entry.getDesc()); diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java index 8117103..8dc7fe6 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodNodeWithAction.java @@ -1,19 +1,19 @@ package cuchaz.enigma.analysis; -import org.objectweb.asm.tree.MethodNode; - import java.util.function.Consumer; +import org.objectweb.asm.tree.MethodNode; + public class MethodNodeWithAction extends MethodNode { - private final Consumer action; + private final Consumer action; - public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer action) { - super(api, access, name, descriptor, signature, exceptions); - this.action = action; - } + public MethodNodeWithAction(int api, int access, String name, String descriptor, String signature, String[] exceptions, Consumer action) { + super(api, access, name, descriptor, signature, exceptions); + this.action = action; + } - @Override - public void visitEnd() { - action.accept(this); - } + @Override + public void visitEnd() { + action.accept(this); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java index 6803861..fc58c6d 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/MethodReferenceTreeNode.java @@ -1,17 +1,25 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; + import com.google.common.collect.Sets; + import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.analysis.index.ReferenceIndex; import cuchaz.enigma.translation.Translator; @@ -20,14 +28,7 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.MethodDefEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Set; - public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements ReferenceTreeNode { - private final Translator translator; private MethodEntry entry; private EntryReference reference; @@ -59,6 +60,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements R if (this.reference != null) { return String.format("%s", translator.translate(this.reference.context)); } + return translator.translate(this.entry).getName(); } @@ -73,16 +75,18 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements R if (recurse && this.children != null) { for (Object child : this.children) { if (child instanceof MethodReferenceTreeNode node) { - // don't recurse into ancestor Set> ancestors = Sets.newHashSet(); TreeNode n = node; + while (n.getParent() != null) { n = n.getParent(); + if (n instanceof MethodReferenceTreeNode) { ancestors.add(((MethodReferenceTreeNode) n).getEntry()); } } + if (ancestors.contains(node.getEntry())) { continue; } @@ -100,6 +104,7 @@ public class MethodReferenceTreeNode extends DefaultMutableTreeNode implements R Collection> references = new ArrayList<>(); EntryResolver entryResolver = index.getEntryResolver(); + for (MethodEntry methodEntry : entryResolver.resolveEquivalentMethods(entry)) { references.addAll(referenceIndex.getReferencesToMethod(methodEntry)); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java index 5b19d18..4dcb834 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTargetType.java @@ -3,72 +3,72 @@ package cuchaz.enigma.analysis; import cuchaz.enigma.translation.representation.entry.ClassEntry; public abstract class ReferenceTargetType { - private static final None NONE = new None(); - private static final Uninitialized UNINITIALIZED = new Uninitialized(); - - public abstract Kind getKind(); - - public static None none() { - return NONE; - } - - public static Uninitialized uninitialized() { - return UNINITIALIZED; - } - - public static ClassType classType(ClassEntry name) { - return new ClassType(name); - } - - public enum Kind { - NONE, - UNINITIALIZED, - CLASS_TYPE - } - - public static class None extends ReferenceTargetType { - @Override - public Kind getKind() { - return Kind.NONE; - } - - @Override - public String toString() { - return "(none)"; - } - } - - public static class Uninitialized extends ReferenceTargetType { - @Override - public Kind getKind() { - return Kind.UNINITIALIZED; - } - - @Override - public String toString() { - return "(uninitialized)"; - } - } - - public static class ClassType extends ReferenceTargetType { - private final ClassEntry entry; - - private ClassType(ClassEntry entry) { - this.entry = entry; - } - - public ClassEntry getEntry() { - return entry; - } - - @Override - public Kind getKind() { - return Kind.CLASS_TYPE; - } - - @Override - public String toString() { - return entry.toString(); - } - } + private static final None NONE = new None(); + private static final Uninitialized UNINITIALIZED = new Uninitialized(); + + public abstract Kind getKind(); + + public static None none() { + return NONE; + } + + public static Uninitialized uninitialized() { + return UNINITIALIZED; + } + + public static ClassType classType(ClassEntry name) { + return new ClassType(name); + } + + public enum Kind { + NONE, + UNINITIALIZED, + CLASS_TYPE + } + + public static class None extends ReferenceTargetType { + @Override + public Kind getKind() { + return Kind.NONE; + } + + @Override + public String toString() { + return "(none)"; + } + } + + public static class Uninitialized extends ReferenceTargetType { + @Override + public Kind getKind() { + return Kind.UNINITIALIZED; + } + + @Override + public String toString() { + return "(uninitialized)"; + } + } + + public static class ClassType extends ReferenceTargetType { + private final ClassEntry entry; + + private ClassType(ClassEntry entry) { + this.entry = entry; + } + + public ClassEntry getEntry() { + return entry; + } + + @Override + public Kind getKind() { + return Kind.CLASS_TYPE; + } + + @Override + public String toString() { + return entry.toString(); + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java index ce23cb6..8e0afd5 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/ReferenceTreeNode.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis; diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java index aea7618..b3ba896 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java @@ -1,180 +1,196 @@ package cuchaz.enigma.analysis; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Stream; + +import javax.swing.tree.DefaultMutableTreeNode; + import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.api.service.NameProposalService; import cuchaz.enigma.translation.TranslateResult; import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; - -import javax.swing.tree.DefaultMutableTreeNode; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Stream; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.DefEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; +import cuchaz.enigma.translation.representation.entry.ParentedEntry; public class StructureTreeNode extends DefaultMutableTreeNode { - private final List nameProposalServices; - private final EntryRemapper mapper; - private final ClassEntry parentEntry; - private final ParentedEntry entry; - - public StructureTreeNode(EnigmaProject project, ClassEntry parentEntry, ParentedEntry entry) { - this.nameProposalServices = project.getEnigma().getServices().get(NameProposalService.TYPE); - this.mapper = project.getMapper(); - this.parentEntry = parentEntry; - this.entry = entry; - } - - /** - * Returns the parented entry represented by this tree node. - */ - public ParentedEntry getEntry() { - return this.entry; - } - - public void load(EnigmaProject project, StructureTreeOptions options) { - Stream children = project.getJarIndex().getChildrenByClass().get(this.parentEntry).stream(); - - children = switch (options.obfuscationVisibility()) { - case ALL -> children; - case OBFUSCATED -> children - // remove deobfuscated members if only obfuscated, unless it's an inner class - .filter(e -> (e instanceof ClassEntry) || (project.isObfuscated(e) && project.isRenamable(e))) - // keep constructor methods if the class is obfuscated - .filter(e -> !(e instanceof MethodEntry m && m.isConstructor()) || project.isObfuscated(e.getParent())); - case DEOBFUSCATED -> children.filter(e -> (e instanceof ClassEntry) - || (!project.isObfuscated(e) && project.isRenamable(e)) - // keep constructor methods if the class is deobfuscated - || (e instanceof MethodEntry m && m.isConstructor()) && !project.isObfuscated(e.getParent())); - }; - - children = switch (options.documentationVisibility()) { - case ALL -> children; - // TODO remove EntryRemapper.deobfuscate() calls when javadocs will no longer be tied to deobfuscation - case DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() != null && !project.getMapper().deobfuscate(e).getJavadocs().isBlank())); - case NON_DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() == null || project.getMapper().deobfuscate(e).getJavadocs().isBlank())); - }; - - children = switch (options.sortingOrder()) { - case DEFAULT -> children; - case A_Z -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) - // compare the class name when the entry is a constructor - ? project.getMapper().deobfuscate(e.getParent()).getSimpleName().toLowerCase() - : project.getMapper().deobfuscate(e).getSimpleName().toLowerCase())); - case Z_A -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) - ? project.getMapper().deobfuscate(((ParentedEntry) e).getParent()).getSimpleName().toLowerCase() - : project.getMapper().deobfuscate((ParentedEntry) e).getSimpleName().toLowerCase()) - .reversed()); - }; - - for (ParentedEntry child : children.toList()) { - StructureTreeNode childNode = new StructureTreeNode(project, this.parentEntry, child); - - if (child instanceof ClassEntry) { - childNode = new StructureTreeNode(project, (ClassEntry) child, child); - childNode.load(project, options); - } - - this.add(childNode); - } - } - - @Override - public String toString() { - TranslateResult translateResult = this.mapper.extendedDeobfuscate(this.entry); - String result = translateResult.getValue().getName(); - - if (translateResult.isObfuscated()) { - if (!this.nameProposalServices.isEmpty()) { - for (NameProposalService service : this.nameProposalServices) { - if (service.proposeName(this.entry, this.mapper).isPresent()) { - result = service.proposeName(this.entry, this.mapper).get(); - } - } - } - } - - if (this.entry instanceof FieldDefEntry) { - FieldDefEntry field = (FieldDefEntry) translateResult.getValue(); - String returnType = this.parseDesc(field.getDesc()); - - result = result + ": " + returnType; - } else if (this.entry instanceof MethodDefEntry) { - MethodDefEntry method = (MethodDefEntry) translateResult.getValue(); - String args = this.parseArgs(method.getDesc().getArgumentDescs()); - String returnType = this.parseDesc(method.getDesc().getReturnDesc()); - - if (method.isConstructor()) { - result = method.getParent().getSimpleName() + args; - } else { - result = result + args + ": " + returnType; - } - } - - return result; - } - - public String toHtml() { - List modifiers = new ArrayList<>(); - - if (this.entry instanceof DefEntry defEntry) { - AccessFlags access = defEntry.getAccess(); - boolean isInterfaceMethod = false; - - if (this.entry instanceof MethodEntry && this.entry.getParent() instanceof ClassDefEntry parent) { - isInterfaceMethod = parent.getAccess().isInterface(); - } - - if (access.isStatic() && !access.isEnum()) { - // Static member, but not an enum constant - modifiers.add("static"); - } else if (isInterfaceMethod && !access.isAbstract()) { - // Non-static default interface method - modifiers.add("default"); - } - - if (access.isAbstract() && !access.isInterface() && !isInterfaceMethod && !access.isEnum()) { - // Abstract, but not an interface, an interface method or an enum class (abstract is the default or meaningless) - modifiers.add("abstract"); - } else if (access.isFinal() && !access.isEnum()) { - // Final, but not an enum or an enum constant (they're always final) - modifiers.add("final"); - } - } - - return "" + String.join(" ", modifiers) + " " + toString(); - } - - private String parseArgs(List args) { - if (args.size() > 0) { - String result = "("; - - for (int i = 0; i < args.size(); i++) { - if (i > 0) { - result += ", "; - } - - result += this.parseDesc(args.get(i)); - } - - return result + ")"; - } - - return "()"; - } - - private String parseDesc(TypeDescriptor desc) { - if (desc.isVoid()) return "void"; - if (desc.isPrimitive()) return desc.getPrimitive().getKeyword(); - if (desc.isType()) return desc.getTypeEntry().getSimpleName(); - - if (desc.isArray()) { - if (desc.getArrayType().isPrimitive()) return desc.getArrayType().getPrimitive().getKeyword() + "[]"; - if (desc.getArrayType().isType()) return desc.getArrayType().getTypeEntry().getSimpleName() + "[]"; - } - - return null; - } + private final List nameProposalServices; + private final EntryRemapper mapper; + private final ClassEntry parentEntry; + private final ParentedEntry entry; + + public StructureTreeNode(EnigmaProject project, ClassEntry parentEntry, ParentedEntry entry) { + this.nameProposalServices = project.getEnigma().getServices().get(NameProposalService.TYPE); + this.mapper = project.getMapper(); + this.parentEntry = parentEntry; + this.entry = entry; + } + + /** + * Returns the parented entry represented by this tree node. + */ + public ParentedEntry getEntry() { + return this.entry; + } + + public void load(EnigmaProject project, StructureTreeOptions options) { + Stream children = project.getJarIndex().getChildrenByClass().get(this.parentEntry).stream(); + + children = switch (options.obfuscationVisibility()) { + case ALL -> children; + case OBFUSCATED -> children + // remove deobfuscated members if only obfuscated, unless it's an inner class + .filter(e -> (e instanceof ClassEntry) || (project.isObfuscated(e) && project.isRenamable(e))) + // keep constructor methods if the class is obfuscated + .filter(e -> !(e instanceof MethodEntry m && m.isConstructor()) || project.isObfuscated(e.getParent())); + case DEOBFUSCATED -> children.filter(e -> (e instanceof ClassEntry) || (!project.isObfuscated(e) && project.isRenamable(e)) + // keep constructor methods if the class is deobfuscated + || (e instanceof MethodEntry m && m.isConstructor()) && !project.isObfuscated(e.getParent())); + }; + + children = switch (options.documentationVisibility()) { + case ALL -> children; + // TODO remove EntryRemapper.deobfuscate() calls when javadocs will no longer be tied to deobfuscation + case DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() != null && !project.getMapper().deobfuscate(e).getJavadocs().isBlank())); + case NON_DOCUMENTED -> children.filter(e -> (e instanceof ClassEntry) || (project.getMapper().deobfuscate(e).getJavadocs() == null || project.getMapper().deobfuscate(e).getJavadocs().isBlank())); + }; + + children = switch (options.sortingOrder()) { + case DEFAULT -> children; + case A_Z -> children.sorted(Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) + // compare the class name when the entry is a constructor + ? project.getMapper().deobfuscate(e.getParent()).getSimpleName().toLowerCase() : project.getMapper().deobfuscate(e).getSimpleName().toLowerCase())); + case Z_A -> children.sorted( + Comparator.comparing(e -> (e instanceof MethodEntry m && m.isConstructor()) ? project.getMapper().deobfuscate(((ParentedEntry) e).getParent()).getSimpleName().toLowerCase() : project.getMapper().deobfuscate((ParentedEntry) e).getSimpleName().toLowerCase()).reversed()); + }; + + for (ParentedEntry child : children.toList()) { + StructureTreeNode childNode = new StructureTreeNode(project, this.parentEntry, child); + + if (child instanceof ClassEntry) { + childNode = new StructureTreeNode(project, (ClassEntry) child, child); + childNode.load(project, options); + } + + this.add(childNode); + } + } + + @Override + public String toString() { + TranslateResult translateResult = this.mapper.extendedDeobfuscate(this.entry); + String result = translateResult.getValue().getName(); + + if (translateResult.isObfuscated()) { + if (!this.nameProposalServices.isEmpty()) { + for (NameProposalService service : this.nameProposalServices) { + if (service.proposeName(this.entry, this.mapper).isPresent()) { + result = service.proposeName(this.entry, this.mapper).get(); + } + } + } + } + + if (this.entry instanceof FieldDefEntry) { + FieldDefEntry field = (FieldDefEntry) translateResult.getValue(); + String returnType = this.parseDesc(field.getDesc()); + + result = result + ": " + returnType; + } else if (this.entry instanceof MethodDefEntry) { + MethodDefEntry method = (MethodDefEntry) translateResult.getValue(); + String args = this.parseArgs(method.getDesc().getArgumentDescs()); + String returnType = this.parseDesc(method.getDesc().getReturnDesc()); + + if (method.isConstructor()) { + result = method.getParent().getSimpleName() + args; + } else { + result = result + args + ": " + returnType; + } + } + + return result; + } + + public String toHtml() { + List modifiers = new ArrayList<>(); + + if (this.entry instanceof DefEntry defEntry) { + AccessFlags access = defEntry.getAccess(); + boolean isInterfaceMethod = false; + + if (this.entry instanceof MethodEntry && this.entry.getParent() instanceof ClassDefEntry parent) { + isInterfaceMethod = parent.getAccess().isInterface(); + } + + if (access.isStatic() && !access.isEnum()) { + // Static member, but not an enum constant + modifiers.add("static"); + } else if (isInterfaceMethod && !access.isAbstract()) { + // Non-static default interface method + modifiers.add("default"); + } + + if (access.isAbstract() && !access.isInterface() && !isInterfaceMethod && !access.isEnum()) { + // Abstract, but not an interface, an interface method or an enum class (abstract is the default or meaningless) + modifiers.add("abstract"); + } else if (access.isFinal() && !access.isEnum()) { + // Final, but not an enum or an enum constant (they're always final) + modifiers.add("final"); + } + } + + return "" + String.join(" ", modifiers) + " " + toString(); + } + + private String parseArgs(List args) { + if (args.size() > 0) { + String result = "("; + + for (int i = 0; i < args.size(); i++) { + if (i > 0) { + result += ", "; + } + + result += this.parseDesc(args.get(i)); + } + + return result + ")"; + } + + return "()"; + } + + private String parseDesc(TypeDescriptor desc) { + if (desc.isVoid()) { + return "void"; + } + + if (desc.isPrimitive()) { + return desc.getPrimitive().getKeyword(); + } + + if (desc.isType()) { + return desc.getTypeEntry().getSimpleName(); + } + + if (desc.isArray()) { + if (desc.getArrayType().isPrimitive()) { + return desc.getArrayType().getPrimitive().getKeyword() + "[]"; + } + + if (desc.getArrayType().isType()) { + return desc.getArrayType().getTypeEntry().getSimpleName() + "[]"; + } + } + + return null; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java index cfc80b4..e2e5084 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeOptions.java @@ -1,59 +1,55 @@ package cuchaz.enigma.analysis; -public record StructureTreeOptions( - ObfuscationVisibility obfuscationVisibility, - DocumentationVisibility documentationVisibility, - SortingOrder sortingOrder) { - - public enum ObfuscationVisibility implements Option { - ALL("structure.options.obfuscation.all"), - OBFUSCATED("structure.options.obfuscation.obfuscated"), - DEOBFUSCATED("structure.options.obfuscation.deobfuscated"); - - private final String translationKey; - - ObfuscationVisibility(String translationKey) { - this.translationKey = translationKey; - } - - public String getTranslationKey() { - return this.translationKey; - } - } - - public enum DocumentationVisibility implements Option { - ALL("structure.options.documentation.all"), - DOCUMENTED("structure.options.documentation.documented"), - NON_DOCUMENTED("structure.options.documentation.non_documented"); - - private final String translationKey; - - DocumentationVisibility(String translationKey) { - this.translationKey = translationKey; - } - - public String getTranslationKey() { - return this.translationKey; - } - } - - public enum SortingOrder implements Option { - DEFAULT("structure.options.sorting.default"), - A_Z("structure.options.sorting.a_z"), - Z_A("structure.options.sorting.z_a"); - - private final String translationKey; - - SortingOrder(String translationKey) { - this.translationKey = translationKey; - } - - public String getTranslationKey() { - return this.translationKey; - } - } - - public interface Option { - String getTranslationKey(); - } +public record StructureTreeOptions(ObfuscationVisibility obfuscationVisibility, DocumentationVisibility documentationVisibility, SortingOrder sortingOrder) { + public enum ObfuscationVisibility implements Option { + ALL("structure.options.obfuscation.all"), + OBFUSCATED("structure.options.obfuscation.obfuscated"), + DEOBFUSCATED("structure.options.obfuscation.deobfuscated"); + + private final String translationKey; + + ObfuscationVisibility(String translationKey) { + this.translationKey = translationKey; + } + + public String getTranslationKey() { + return this.translationKey; + } + } + + public enum DocumentationVisibility implements Option { + ALL("structure.options.documentation.all"), + DOCUMENTED("structure.options.documentation.documented"), + NON_DOCUMENTED("structure.options.documentation.non_documented"); + + private final String translationKey; + + DocumentationVisibility(String translationKey) { + this.translationKey = translationKey; + } + + public String getTranslationKey() { + return this.translationKey; + } + } + + public enum SortingOrder implements Option { + DEFAULT("structure.options.sorting.default"), + A_Z("structure.options.sorting.a_z"), + Z_A("structure.options.sorting.z_a"); + + private final String translationKey; + + SortingOrder(String translationKey) { + this.translationKey = translationKey; + } + + public String getTranslationKey() { + return this.translationKey; + } + } + + public interface Option { + String getTranslationKey(); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java index a4b1aac..26093c3 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java @@ -1,6 +1,15 @@ package cuchaz.enigma.analysis.index; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + import com.google.common.collect.Maps; + import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.TypeDescriptor; @@ -8,9 +17,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.MethodDefEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.annotation.Nullable; -import java.util.*; - public class BridgeMethodIndex implements JarIndexer { private final EntryIndex entryIndex; private final InheritanceIndex inheritanceIndex; @@ -31,6 +37,7 @@ public class BridgeMethodIndex implements JarIndexer { MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry; AccessFlags access = methodDefEntry.getAccess(); + if (access == null || !access.isSynthetic()) { continue; } @@ -46,6 +53,7 @@ public class BridgeMethodIndex implements JarIndexer { for (Map.Entry entry : copiedAccessToBridge.entrySet()) { MethodEntry specializedEntry = entry.getKey(); MethodEntry bridgeEntry = entry.getValue(); + if (bridgeEntry.getName().equals(specializedEntry.getName())) { continue; } @@ -57,6 +65,7 @@ public class BridgeMethodIndex implements JarIndexer { private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) { MethodEntry specializedMethod = findSpecializedMethod(syntheticMethod); + if (specializedMethod == null) { return; } @@ -84,6 +93,7 @@ public class BridgeMethodIndex implements JarIndexer { private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry specializedMethod) { // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited AccessFlags bridgeAccess = bridgeMethod.getAccess(); + if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) { return false; } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java index bb992b7..0e4cdcf 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java @@ -1,13 +1,21 @@ package cuchaz.enigma.analysis.index; -import cuchaz.enigma.translation.representation.AccessFlags; -import cuchaz.enigma.translation.representation.entry.*; - -import javax.annotation.Nullable; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nullable; + +import cuchaz.enigma.translation.representation.AccessFlags; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; + public class EntryIndex implements JarIndexer { private Map classes = new HashMap<>(); private Map fields = new HashMap<>(); diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java index f9cb23c..e697182 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexClassVisitor.java @@ -1,12 +1,13 @@ package cuchaz.enigma.analysis.index; -import cuchaz.enigma.translation.representation.entry.ClassDefEntry; -import cuchaz.enigma.translation.representation.entry.FieldDefEntry; -import cuchaz.enigma.translation.representation.entry.MethodDefEntry; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; + public class IndexClassVisitor extends ClassVisitor { private final JarIndexer indexer; private ClassDefEntry classEntry; diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java index efea83d..97fec47 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/IndexReferenceVisitor.java @@ -1,5 +1,22 @@ package cuchaz.enigma.analysis.index; +import java.util.List; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.AnalyzerException; +import org.objectweb.asm.tree.analysis.BasicValue; +import org.objectweb.asm.tree.analysis.SourceInterpreter; +import org.objectweb.asm.tree.analysis.SourceValue; + import cuchaz.enigma.analysis.IndexSimpleVerifier; import cuchaz.enigma.analysis.InterpreterPair; import cuchaz.enigma.analysis.MethodNodeWithAction; @@ -8,15 +25,11 @@ import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.Lambda; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.Signature; -import cuchaz.enigma.translation.representation.entry.*; -import org.objectweb.asm.*; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.InvokeDynamicInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.analysis.*; - -import java.util.List; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; +import cuchaz.enigma.translation.representation.entry.ParentedEntry; public class IndexReferenceVisitor extends ClassVisitor { private final JarIndexer indexer; @@ -54,7 +67,7 @@ public class IndexReferenceVisitor extends ClassVisitor { private final MethodDefEntry callerEntry; private JarIndexer indexer; - public MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { + MethodInterpreter(MethodDefEntry callerEntry, JarIndexer indexer, EntryIndex entryIndex, InheritanceIndex inheritanceIndex) { super(new IndexSimpleVerifier(entryIndex, inheritanceIndex), new SourceInterpreter()); this.callerEntry = callerEntry; this.indexer = indexer; @@ -85,7 +98,6 @@ public class IndexReferenceVisitor extends ClassVisitor { return super.unaryOperation(insn, value); } - @Override public PairValue binaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2) throws AnalyzerException { if (insn.getOpcode() == Opcodes.PUTFIELD) { @@ -119,6 +131,7 @@ public class IndexReferenceVisitor extends ClassVisitor { Type instantiatedMethodType = (Type) invokeDynamicInsn.bsmArgs[2]; ReferenceTargetType targetType; + if (implMethod.getTag() != Opcodes.H_GETSTATIC && implMethod.getTag() != Opcodes.H_PUTFIELD && implMethod.getTag() != Opcodes.H_INVOKESTATIC) { if (instantiatedMethodType.getArgumentTypes().length < Type.getArgumentTypes(implMethod.getDesc()).length) { targetType = getReferenceTargetType(values.get(0), insn); @@ -129,13 +142,7 @@ public class IndexReferenceVisitor extends ClassVisitor { targetType = ReferenceTargetType.none(); } - indexer.indexLambda(callerEntry, new Lambda( - invokeDynamicInsn.name, - new MethodDescriptor(invokeDynamicInsn.desc), - new MethodDescriptor(samMethodType.getDescriptor()), - getHandleEntry(implMethod), - new MethodDescriptor(instantiatedMethodType.getDescriptor()) - ), targetType); + indexer.indexLambda(callerEntry, new Lambda(invokeDynamicInsn.name, new MethodDescriptor(invokeDynamicInsn.desc), new MethodDescriptor(samMethodType.getDescriptor()), getHandleEntry(implMethod), new MethodDescriptor(instantiatedMethodType.getDescriptor())), targetType); } } @@ -160,17 +167,17 @@ public class IndexReferenceVisitor extends ClassVisitor { private static ParentedEntry getHandleEntry(Handle handle) { switch (handle.getTag()) { - case Opcodes.H_GETFIELD: - case Opcodes.H_GETSTATIC: - case Opcodes.H_PUTFIELD: - case Opcodes.H_PUTSTATIC: - return FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); - case Opcodes.H_INVOKEINTERFACE: - case Opcodes.H_INVOKESPECIAL: - case Opcodes.H_INVOKESTATIC: - case Opcodes.H_INVOKEVIRTUAL: - case Opcodes.H_NEWINVOKESPECIAL: - return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); + case Opcodes.H_GETFIELD: + case Opcodes.H_GETSTATIC: + case Opcodes.H_PUTFIELD: + case Opcodes.H_PUTSTATIC: + return FieldEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); + case Opcodes.H_INVOKEINTERFACE: + case Opcodes.H_INVOKESPECIAL: + case Opcodes.H_INVOKESTATIC: + case Opcodes.H_INVOKEVIRTUAL: + case Opcodes.H_NEWINVOKESPECIAL: + return MethodEntry.parse(handle.getOwner(), handle.getName(), handle.getDesc()); } throw new RuntimeException("Invalid handle tag " + handle.getTag()); diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java index 1ab2abd..1c60db9 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java @@ -1,27 +1,28 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis.index; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; + import cuchaz.enigma.translation.representation.entry.ClassDefEntry; import cuchaz.enigma.translation.representation.entry.ClassEntry; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Set; - public class InheritanceIndex implements JarIndexer { private final EntryIndex entryIndex; @@ -39,6 +40,7 @@ public class InheritanceIndex implements JarIndexer { } ClassEntry superClass = classEntry.getSuperClass(); + if (superClass != null && !superClass.getName().equals("java/lang/Object")) { indexParent(classEntry, superClass); } @@ -96,8 +98,13 @@ public class InheritanceIndex implements JarIndexer { } public Relation computeClassRelation(ClassEntry classEntry, ClassEntry potentialAncestor) { - if (potentialAncestor.getName().equals("java/lang/Object")) return Relation.RELATED; - if (!entryIndex.hasClass(classEntry)) return Relation.UNKNOWN; + if (potentialAncestor.getName().equals("java/lang/Object")) { + return Relation.RELATED; + } + + if (!entryIndex.hasClass(classEntry)) { + return Relation.UNKNOWN; + } for (ClassEntry ancestor : getAncestors(classEntry)) { if (potentialAncestor.equals(ancestor)) { diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java index 6c26282..60864ba 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java @@ -1,17 +1,26 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.analysis.index; -import com.google.common.collect.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; + import cuchaz.enigma.Enigma; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.ReferenceTargetType; @@ -19,11 +28,15 @@ import cuchaz.enigma.classprovider.ClassProvider; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.IndexEntryResolver; import cuchaz.enigma.translation.representation.Lambda; -import cuchaz.enigma.translation.representation.entry.*; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; +import cuchaz.enigma.translation.representation.entry.ParentedEntry; import cuchaz.enigma.utils.I18n; -import java.util.*; - public class JarIndex implements JarIndexer { private final Set indexedClasses = new HashSet<>(); private final EntryIndex entryIndex; @@ -99,6 +112,7 @@ public class JarIndex implements JarIndexer { } indexers.forEach(indexer -> indexer.indexClass(classEntry)); + if (classEntry.isInnerClass() && !classEntry.getAccess().isSynthetic()) { childrenByClass.put(classEntry.getParent(), classEntry); } @@ -111,6 +125,7 @@ public class JarIndex implements JarIndexer { } indexers.forEach(indexer -> indexer.indexField(fieldEntry)); + if (!fieldEntry.getAccess().isSynthetic()) { childrenByClass.put(fieldEntry.getParent(), fieldEntry); } @@ -123,6 +138,7 @@ public class JarIndex implements JarIndexer { } indexers.forEach(indexer -> indexer.indexMethod(methodEntry)); + if (!methodEntry.getAccess().isSynthetic() && !methodEntry.getName().equals("")) { childrenByClass.put(methodEntry.getParent(), methodEntry); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java index f17e7c9..8726fb5 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndexer.java @@ -2,7 +2,11 @@ package cuchaz.enigma.analysis.index; import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.representation.Lambda; -import cuchaz.enigma.translation.representation.entry.*; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public interface JarIndexer { default void indexClass(ClassDefEntry classEntry) { diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java index 64de5f3..b400a66 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/PackageVisibilityIndex.java @@ -1,15 +1,25 @@ package cuchaz.enigma.analysis.index; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; + import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.representation.AccessFlags; -import cuchaz.enigma.translation.representation.entry.*; - -import java.util.*; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public class PackageVisibilityIndex implements JarIndexer { private static boolean requiresSamePackage(AccessFlags entryAcc, EntryReference ref, InheritanceIndex inheritanceIndex) { @@ -30,9 +40,7 @@ public class PackageVisibilityIndex implements JarIndexer { } // access to instance member only valid if target's class assignable to context class - return !(ref.targetType.getKind() == ReferenceTargetType.Kind.UNINITIALIZED || - ((ReferenceTargetType.ClassType) ref.targetType).getEntry().equals(contextClass) || - inheritanceIndex.getAncestors(((ReferenceTargetType.ClassType) ref.targetType).getEntry()).contains(contextClass)); + return !(ref.targetType.getKind() == ReferenceTargetType.Kind.UNINITIALIZED || ((ReferenceTargetType.ClassType) ref.targetType).getEntry().equals(contextClass) || inheritanceIndex.getAncestors(((ReferenceTargetType.ClassType) ref.targetType).getEntry()).contains(contextClass)); } return true; @@ -61,6 +69,7 @@ public class PackageVisibilityIndex implements JarIndexer { private void addConnections(EntryIndex entryIndex, ReferenceIndex referenceIndex, InheritanceIndex inheritanceIndex) { for (FieldEntry entry : entryIndex.getFields()) { AccessFlags entryAcc = entryIndex.getFieldAccess(entry); + if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { for (EntryReference ref : referenceIndex.getReferencesToField(entry)) { if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { @@ -72,6 +81,7 @@ public class PackageVisibilityIndex implements JarIndexer { for (MethodEntry entry : entryIndex.getMethods()) { AccessFlags entryAcc = entryIndex.getMethodAccess(entry); + if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { for (EntryReference ref : referenceIndex.getReferencesToMethod(entry)) { if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { @@ -83,6 +93,7 @@ public class PackageVisibilityIndex implements JarIndexer { for (ClassEntry entry : entryIndex.getClasses()) { AccessFlags entryAcc = entryIndex.getClassAccess(entry); + if (!entryAcc.isPublic() && !entryAcc.isPrivate()) { for (EntryReference ref : referenceIndex.getFieldTypeReferencesToClass(entry)) { if (requiresSamePackage(entryAcc, ref, inheritanceIndex)) { @@ -99,12 +110,14 @@ public class PackageVisibilityIndex implements JarIndexer { for (ClassEntry parent : inheritanceIndex.getParents(entry)) { AccessFlags parentAcc = entryIndex.getClassAccess(parent); + if (parentAcc != null && !parentAcc.isPublic() && !parentAcc.isPrivate()) { addConnection(entry, parent); } } ClassEntry outerClass = entry.getOuterClass(); + if (outerClass != null) { addConnection(entry, outerClass); } @@ -113,6 +126,7 @@ public class PackageVisibilityIndex implements JarIndexer { private void addPartitions(EntryIndex entryIndex) { Set unassignedClasses = Sets.newHashSet(entryIndex.getClasses()); + while (!unassignedClasses.isEmpty()) { Iterator iterator = unassignedClasses.iterator(); ClassEntry initialEntry = iterator.next(); @@ -122,6 +136,7 @@ public class PackageVisibilityIndex implements JarIndexer { partition.add(initialEntry); buildPartition(unassignedClasses, partition, initialEntry); partitions.add(partition); + for (ClassEntry entry : partition) { classPartitions.put(entry, partition); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java index b6797c2..332a967 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java @@ -1,17 +1,23 @@ package cuchaz.enigma.analysis.index; +import java.util.Collection; +import java.util.Map; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; + import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.ReferenceTargetType; import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.representation.Lambda; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; - -import java.util.Collection; -import java.util.Map; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public class ReferenceIndex implements JarIndexer { private Multimap methodReferences = HashMultimap.create(); @@ -24,13 +30,14 @@ public class ReferenceIndex implements JarIndexer { @Override public void indexMethod(MethodDefEntry methodEntry) { - indexMethodDescriptor(methodEntry, methodEntry.getDesc()); + indexMethodDescriptor(methodEntry, methodEntry.getDesc()); } private void indexMethodDescriptor(MethodDefEntry entry, MethodDescriptor descriptor) { for (TypeDescriptor typeDescriptor : descriptor.getArgumentDescs()) { indexMethodTypeDescriptor(entry, typeDescriptor); } + indexMethodTypeDescriptor(entry, descriptor.getReturnDesc()); } @@ -45,7 +52,7 @@ public class ReferenceIndex implements JarIndexer { @Override public void indexField(FieldDefEntry fieldEntry) { - indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc()); + indexFieldTypeDescriptor(fieldEntry, fieldEntry.getDesc()); } private void indexFieldTypeDescriptor(FieldDefEntry field, TypeDescriptor typeDescriptor) { @@ -53,7 +60,7 @@ public class ReferenceIndex implements JarIndexer { ClassEntry referencedClass = typeDescriptor.getTypeEntry(); fieldTypeReferences.put(referencedClass, new EntryReference<>(referencedClass, referencedClass.getName(), field)); } else if (typeDescriptor.isArray()) { - indexFieldTypeDescriptor(field, typeDescriptor.getArrayType()); + indexFieldTypeDescriptor(field, typeDescriptor.getArrayType()); } } @@ -99,18 +106,22 @@ public class ReferenceIndex implements JarIndexer { private , V extends Entry> Multimap remapReferences(JarIndex index, Multimap multimap) { final int keySetSize = multimap.keySet().size(); Multimap resolved = HashMultimap.create(multimap.keySet().size(), keySetSize == 0 ? 0 : multimap.size() / keySetSize); + for (Map.Entry entry : multimap.entries()) { resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); } + return resolved; } private , C extends Entry> Multimap> remapReferencesTo(JarIndex index, Multimap> multimap) { final int keySetSize = multimap.keySet().size(); Multimap> resolved = HashMultimap.create(keySetSize, keySetSize == 0 ? 0 : multimap.size() / keySetSize); + for (Map.Entry> entry : multimap.entries()) { resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue())); } + return resolved; } diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java b/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java index 358828f..e2cb6b1 100644 --- a/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java +++ b/enigma/src/main/java/cuchaz/enigma/api/service/EnigmaServiceType.java @@ -18,7 +18,9 @@ public final class EnigmaServiceType { @Override public boolean equals(Object obj) { - if (obj == this) return true; + if (obj == this) { + return true; + } if (obj instanceof EnigmaServiceType) { return ((EnigmaServiceType) obj).key.equals(key); diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java b/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java index 5417531..3ed6d33 100644 --- a/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java +++ b/enigma/src/main/java/cuchaz/enigma/api/service/JarIndexerService.java @@ -1,10 +1,11 @@ package cuchaz.enigma.api.service; -import cuchaz.enigma.analysis.index.JarIndex; -import cuchaz.enigma.classprovider.ClassProvider; +import java.util.Set; + import org.objectweb.asm.ClassVisitor; -import java.util.Set; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.classprovider.ClassProvider; public interface JarIndexerService extends EnigmaService { EnigmaServiceType TYPE = EnigmaServiceType.create("jar_indexer"); @@ -12,7 +13,7 @@ public interface JarIndexerService extends EnigmaService { void acceptJar(Set scope, ClassProvider classProvider, JarIndex jarIndex); static JarIndexerService fromVisitor(ClassVisitor visitor) { - return (scope, classProvider, jarIndex) -> { + return (scope, classProvider, jarIndex) -> { for (String className : scope) { classProvider.get(className).accept(visitor); } diff --git a/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java b/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java index 4c357db..4c40868 100644 --- a/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java +++ b/enigma/src/main/java/cuchaz/enigma/api/service/NameProposalService.java @@ -1,10 +1,10 @@ package cuchaz.enigma.api.service; +import java.util.Optional; + import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.translation.representation.entry.Entry; -import java.util.Optional; - public interface NameProposalService extends EnigmaService { EnigmaServiceType TYPE = EnigmaServiceType.create("name_proposal"); diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java index 341cfce..891fe9d 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/AsmObjectTranslator.java @@ -1,32 +1,34 @@ package cuchaz.enigma.bytecode.translators; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.TypeDescriptor; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import org.objectweb.asm.Handle; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; public class AsmObjectTranslator { public static Type translateType(Translator translator, Type type) { String descString = type.getDescriptor(); switch (type.getSort()) { - case Type.OBJECT: { - ClassEntry classEntry = new ClassEntry(type.getInternalName()); - return Type.getObjectType(translator.translate(classEntry).getFullName()); - } - case Type.ARRAY: { - TypeDescriptor descriptor = new TypeDescriptor(descString); - return Type.getType(translator.translate(descriptor).toString()); - } - case Type.METHOD: { - MethodDescriptor descriptor = new MethodDescriptor(descString); - return Type.getMethodType(translator.translate(descriptor).toString()); - } + case Type.OBJECT: { + ClassEntry classEntry = new ClassEntry(type.getInternalName()); + return Type.getObjectType(translator.translate(classEntry).getFullName()); + } + case Type.ARRAY: { + TypeDescriptor descriptor = new TypeDescriptor(descString); + return Type.getType(translator.translate(descriptor).toString()); + } + case Type.METHOD: { + MethodDescriptor descriptor = new MethodDescriptor(descString); + return Type.getMethodType(translator.translate(descriptor).toString()); } + } + return type; } @@ -55,6 +57,7 @@ public class AsmObjectTranslator { } else if (value instanceof Handle) { return translateHandle(translator, (Handle) value); } + return value; } } diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java index cfd8fbe..dc399e5 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java @@ -1,18 +1,19 @@ package cuchaz.enigma.bytecode.translators; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.google.common.base.CharMatcher; -import cuchaz.enigma.translation.LocalNameGenerator; -import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.ClassDefEntry; -import cuchaz.enigma.translation.representation.entry.MethodDefEntry; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import cuchaz.enigma.translation.LocalNameGenerator; +import cuchaz.enigma.translation.representation.TypeDescriptor; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; public class LocalVariableFixVisitor extends ClassVisitor { private ClassDefEntry ownerEntry; @@ -46,6 +47,7 @@ public class LocalVariableFixVisitor extends ClassVisitor { int lvIndex = methodEntry.getAccess().isStatic() ? 0 : 1; List parameters = methodEntry.getDesc().getArgumentDescs(); + for (int parameterIndex = 0; parameterIndex < parameters.size(); parameterIndex++) { TypeDescriptor param = parameters.get(parameterIndex); parameterIndices.put(lvIndex, parameterIndex); @@ -81,6 +83,7 @@ public class LocalVariableFixVisitor extends ClassVisitor { public void visitEnd() { if (!hasParameterTable) { List arguments = methodEntry.getDesc().getArgumentDescs(); + for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) { super.visitParameter(fixParameterName(argumentIndex, null), fixParameterAccess(argumentIndex, 0)); } diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java index 2b750ea..51b21a6 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java @@ -1,12 +1,13 @@ package cuchaz.enigma.bytecode.translators; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + import cuchaz.enigma.analysis.index.BridgeMethodIndex; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.translation.representation.entry.ClassDefEntry; import cuchaz.enigma.translation.representation.entry.MethodDefEntry; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; public class SourceFixVisitor extends ClassVisitor { private final JarIndex index; @@ -28,6 +29,7 @@ public class SourceFixVisitor extends ClassVisitor { MethodDefEntry methodEntry = MethodDefEntry.parse(ownerEntry, access, name, descriptor, signature); BridgeMethodIndex bridgeIndex = index.getBridgeMethodIndex(); + if (bridgeIndex.isBridgeMethod(methodEntry)) { access |= Opcodes.ACC_BRIDGE; } else if (bridgeIndex.isSpecializedMethod(methodEntry)) { diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java index cb843ad..d105e4c 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationAnnotationVisitor.java @@ -1,10 +1,11 @@ package cuchaz.enigma.bytecode.translators; +import org.objectweb.asm.AnnotationVisitor; + import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.TypeDescriptor; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.FieldEntry; -import org.objectweb.asm.AnnotationVisitor; public class TranslationAnnotationVisitor extends AnnotationVisitor { private final Translator translator; @@ -29,6 +30,7 @@ public class TranslationAnnotationVisitor extends AnnotationVisitor { @Override public AnnotationVisitor visitAnnotation(String name, String desc) { TypeDescriptor type = new TypeDescriptor(desc); + if (name != null) { FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type)); return super.visitAnnotation(annotationField.getName(), annotationField.getDesc().toString()); @@ -41,6 +43,7 @@ public class TranslationAnnotationVisitor extends AnnotationVisitor { public void visitEnum(String name, String desc, String value) { TypeDescriptor type = new TypeDescriptor(desc); FieldEntry enumField = translator.translate(new FieldEntry(type.getTypeEntry(), value, type)); + if (name != null) { FieldEntry annotationField = translator.translate(new FieldEntry(annotationEntry, name, type)); super.visitEnum(annotationField.getName(), annotationField.getDesc().toString(), enumField.getName()); diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java index 0b2ca9a..66c8490 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java @@ -1,23 +1,33 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.bytecode.translators; +import java.util.Arrays; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.RecordComponentVisitor; +import org.objectweb.asm.TypePath; + import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; -import org.objectweb.asm.*; - -import java.util.Arrays; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public class TranslationClassVisitor extends ClassVisitor { private final Translator translator; @@ -53,9 +63,11 @@ public class TranslationClassVisitor extends ClassVisitor { MethodDefEntry entry = MethodDefEntry.parse(obfClassEntry, access, name, desc, signature); MethodDefEntry translatedEntry = translator.translate(entry); String[] translatedExceptions = new String[exceptions.length]; + for (int i = 0; i < exceptions.length; i++) { translatedExceptions[i] = translator.translate(new ClassEntry(exceptions[i])).getFullName(); } + MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions); return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv); } @@ -65,6 +77,7 @@ public class TranslationClassVisitor extends ClassVisitor { ClassDefEntry classEntry = ClassDefEntry.parse(access, name, obfClassEntry.getSignature().toString(), null, new String[0]); ClassDefEntry translatedEntry = translator.translate(classEntry); ClassEntry translatedOuterClass = translatedEntry.getOuterClass(); + if (translatedOuterClass == null) { throw new IllegalStateException("Translated inner class did not have outer class"); } diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java index 28fc199..d026f15 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationFieldVisitor.java @@ -1,12 +1,13 @@ package cuchaz.enigma.bytecode.translators; -import cuchaz.enigma.translation.Translator; -import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.FieldDefEntry; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.TypePath; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.TypeDescriptor; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; + public class TranslationFieldVisitor extends FieldVisitor { private final FieldDefEntry fieldEntry; private final Translator translator; diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java index a82df1b..932c123 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java @@ -1,11 +1,21 @@ package cuchaz.enigma.bytecode.translators; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.TypePath; + import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.Signature; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; -import org.objectweb.asm.*; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public class TranslationMethodVisitor extends MethodVisitor { private final MethodDefEntry methodEntry; @@ -55,13 +65,16 @@ public class TranslationMethodVisitor extends MethodVisitor { if (array == null) { return null; } + for (int i = 0; i < count; i++) { Object object = array[i]; + if (object instanceof String) { String type = (String) object; array[i] = translator.translate(new ClassEntry(type)).getFullName(); } } + return array; } @@ -96,9 +109,11 @@ public class TranslationMethodVisitor extends MethodVisitor { public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { MethodDescriptor translatedMethodDesc = translator.translate(new MethodDescriptor(desc)); Object[] translatedBsmArgs = new Object[bsmArgs.length]; + for (int i = 0; i < bsmArgs.length; i++) { translatedBsmArgs[i] = AsmObjectTranslator.translateValue(translator, bsmArgs[i]); } + super.visitInvokeDynamicInsn(name, translatedMethodDesc.toString(), AsmObjectTranslator.translateHandle(translator, bsm), translatedBsmArgs); } @@ -132,7 +147,7 @@ public class TranslationMethodVisitor extends MethodVisitor { } private String translateVariableName(int index, String name) { - LocalVariableEntry entry = new LocalVariableEntry(methodEntry, index, "", true,null); + LocalVariableEntry entry = new LocalVariableEntry(methodEntry, index, "", true, null); LocalVariableEntry translatedEntry = translator.translate(entry); String translatedName = translatedEntry.getName(); diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java index 06fd22b..f7de0f8 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationRecordComponentVisitor.java @@ -1,11 +1,12 @@ package cuchaz.enigma.bytecode.translators; -import cuchaz.enigma.translation.Translator; -import cuchaz.enigma.translation.representation.TypeDescriptor; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.RecordComponentVisitor; import org.objectweb.asm.TypePath; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.TypeDescriptor; + public class TranslationRecordComponentVisitor extends RecordComponentVisitor { private final Translator translator; diff --git a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java index 6cab22c..49350f6 100644 --- a/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java @@ -29,17 +29,23 @@ public class TranslationSignatureVisitor extends SignatureVisitor { @Override public void visitInnerClassType(String name) { String lastClass = classStack.pop(); - if (!name.startsWith(lastClass+"$")){//todo see if there's a way to base this on whether there were type params or not - name = lastClass+"$"+name; + + if (!name.startsWith(lastClass + "$")) { + //todo see if there's a way to base this on whether there were type params or not + name = lastClass + "$" + name; } + classStack.push(name); String translatedEntry = this.remapper.apply(name); - if (translatedEntry.contains("/")){ - translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("/")+1); + + if (translatedEntry.contains("/")) { + translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("/") + 1); } - if (translatedEntry.contains("$")){ - translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("$")+1); + + if (translatedEntry.contains("$")) { + translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("$") + 1); } + this.sv.visitInnerClassType(translatedEntry); } @@ -120,8 +126,10 @@ public class TranslationSignatureVisitor extends SignatureVisitor { @Override public void visitEnd() { this.sv.visitEnd(); - if (!classStack.empty()) + + if (!classStack.empty()) { classStack.pop(); + } } @Override diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java index 326197d..2a1643e 100644 --- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java +++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandle.java @@ -18,7 +18,6 @@ import cuchaz.enigma.utils.Result; * @see ClassHandleProvider */ public interface ClassHandle extends AutoCloseable { - /** * Gets the reference to this class. This is always obfuscated, for example * {@code net/minecraft/class_1000}. @@ -104,5 +103,4 @@ public interface ClassHandle extends AutoCloseable { */ @Override void close(); - } diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java index 20f847a..ce6b23f 100644 --- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java +++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleError.java @@ -6,7 +6,6 @@ import java.io.PrintStream; import javax.annotation.Nullable; public final class ClassHandleError { - public final Type type; public final Throwable cause; @@ -17,7 +16,10 @@ public final class ClassHandleError { @Nullable public String getStackTrace() { - if (cause == null) return null; + if (cause == null) { + return null; + } + ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(os); cause.printStackTrace(ps); @@ -36,5 +38,4 @@ public final class ClassHandleError { DECOMPILE, REMAP, } - -} \ No newline at end of file +} diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java index 229d18a..f18be67 100644 --- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java @@ -1,6 +1,15 @@ package cuchaz.enigma.classhandle; -import java.util.*; +import static cuchaz.enigma.utils.Utils.withLock; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -16,14 +25,16 @@ import cuchaz.enigma.classprovider.CachingClassProvider; import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; import cuchaz.enigma.events.ClassHandleListener; import cuchaz.enigma.events.ClassHandleListener.InvalidationType; -import cuchaz.enigma.source.*; +import cuchaz.enigma.source.DecompiledClassSource; +import cuchaz.enigma.source.Decompiler; +import cuchaz.enigma.source.DecompilerService; +import cuchaz.enigma.source.Source; +import cuchaz.enigma.source.SourceIndex; +import cuchaz.enigma.source.SourceSettings; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.utils.Result; -import static cuchaz.enigma.utils.Utils.withLock; - public final class ClassHandleProvider { - private final EnigmaProject project; private final ExecutorService pool = Executors.newWorkStealingPool(); @@ -50,7 +61,9 @@ public final class ClassHandleProvider { */ @Nullable public ClassHandle openClass(ClassEntry entry) { - if (!project.getJarIndex().getEntryIndex().hasClass(entry)) return null; + if (!project.getJarIndex().getEntryIndex().hasClass(entry)) { + return null; + } return withLock(lock.writeLock(), () -> { Entry e = handles.computeIfAbsent(entry, entry1 -> new Entry(this, entry1)); @@ -68,7 +81,9 @@ public final class ClassHandleProvider { * @param ds the decompiler service to use */ public void setDecompilerService(DecompilerService ds) { - if (this.ds.equals(ds)) return; + if (this.ds.equals(ds)) { + return; + } this.ds = ds; this.decompiler = createDecompiler(); @@ -111,6 +126,7 @@ public final class ClassHandleProvider { public void invalidateMapped(ClassEntry entry) { withLock(lock.readLock(), () -> { Entry e = handles.get(entry); + if (e != null) { e.invalidateMapped(); } @@ -136,6 +152,7 @@ public final class ClassHandleProvider { public void invalidateJavadoc(ClassEntry entry) { withLock(lock.readLock(), () -> { Entry e = handles.get(entry); + if (e != null) { e.invalidateJavadoc(); } @@ -163,6 +180,7 @@ public final class ClassHandleProvider { */ public void destroy() { pool.shutdown(); + try { pool.awaitTermination(30, TimeUnit.SECONDS); } catch (InterruptedException e) { @@ -176,7 +194,6 @@ public final class ClassHandleProvider { } private static final class Entry { - private final ClassHandleProvider p; private final ClassEntry entry; private ClassEntry deobfRef; @@ -216,6 +233,7 @@ public final class ClassHandleProvider { private void checkDeobfRefForUpdate() { ClassEntry newDeobf = p.project.getMapper().deobfuscate(entry); + if (!Objects.equals(deobfRef, newDeobf)) { deobfRef = newDeobf; // copy the list so we don't call event listener code with the lock active @@ -244,7 +262,9 @@ public final class ClassHandleProvider { private CompletableFuture> decompile() { int v = decompileVersion.incrementAndGet(); return CompletableFuture.supplyAsync(() -> { - if (decompileVersion.get() != v) return null; + if (decompileVersion.get() != v) { + return null; + } Result uncommentedSource = Result.ok(p.decompiler.getSource(entry.getFullName())); Entry.this.uncommentedSource = uncommentedSource; @@ -258,7 +278,10 @@ public final class ClassHandleProvider { private CompletableFuture> continueInsertJavadoc(CompletableFuture> f) { int v = javadocVersion.incrementAndGet(); return f.thenApplyAsync(res -> { - if (res == null || javadocVersion.get() != v) return null; + if (res == null || javadocVersion.get() != v) { + return null; + } + Result jdSource = res.map(s -> s.withJavadocs(p.project.getMapper())); withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource)); return jdSource; @@ -268,7 +291,10 @@ public final class ClassHandleProvider { private CompletableFuture> continueIndexSource(CompletableFuture> f) { int v = indexVersion.incrementAndGet(); return f.thenApplyAsync(res -> { - if (res == null || indexVersion.get() != v) return null; + if (res == null || indexVersion.get() != v) { + return null; + } + return res.andThen(jdSource -> { SourceIndex index = jdSource.index(); index.resolveReferences(p.project.getMapper().getObfResolver()); @@ -281,11 +307,20 @@ public final class ClassHandleProvider { private void continueMapSource(CompletableFuture> f) { int v = mappedVersion.incrementAndGet(); f.thenApplyAsync(res -> { - if (res == null || mappedVersion.get() != v) return null; + if (res == null || mappedVersion.get() != v) { + return null; + } + return res.andThen(source -> Result.ok(source.remapSource(p.project, p.project.getMapper().getDeobfuscator()))); }, p.pool).whenComplete((res, e) -> { - if (e != null) res = Result.err(ClassHandleError.remap(e)); - if (res == null) return; + if (e != null) { + res = Result.err(ClassHandleError.remap(e)); + } + + if (res == null) { + return; + } + Entry.this.source = res; Entry.this.waitingSources.forEach(s -> s.complete(source)); Entry.this.waitingSources.clear(); @@ -297,6 +332,7 @@ public final class ClassHandleProvider { classHandle.destroy(); withLock(lock.writeLock(), () -> { handles.remove(classHandle); + if (handles.isEmpty()) { p.deleteEntry(this); } @@ -332,7 +368,6 @@ public final class ClassHandleProvider { } private static final class ClassHandleImpl implements ClassHandle { - private final Entry entry; private boolean valid = true; @@ -425,18 +460,20 @@ public final class ClassHandleProvider { @Override public void close() { - if (valid) entry.closeHandle(this); + if (valid) { + entry.closeHandle(this); + } } private void checkValid() { - if (!valid) throw new IllegalStateException("Class handle no longer valid"); + if (!valid) { + throw new IllegalStateException("Class handle no longer valid"); + } } public void destroy() { listeners.forEach(l -> l.onDeleted(this)); valid = false; } - } - } diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java index 47f5eb8..eaba6df 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CachingClassProvider.java @@ -1,36 +1,33 @@ package cuchaz.enigma.classprovider; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import org.objectweb.asm.tree.ClassNode; - -import javax.annotation.Nullable; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import org.objectweb.asm.tree.ClassNode; + /** * Wraps a ClassProvider to provide caching and synchronization. */ public class CachingClassProvider implements ClassProvider { - private final ClassProvider classProvider; - private final Cache> cache = CacheBuilder.newBuilder() - .maximumSize(128) - .expireAfterAccess(1, TimeUnit.MINUTES) - .concurrencyLevel(1) - .build(); + private final ClassProvider classProvider; + private final Cache> cache = CacheBuilder.newBuilder().maximumSize(128).expireAfterAccess(1, TimeUnit.MINUTES).concurrencyLevel(1).build(); - public CachingClassProvider(ClassProvider classProvider) { - this.classProvider = classProvider; - } + public CachingClassProvider(ClassProvider classProvider) { + this.classProvider = classProvider; + } - @Override - @Nullable - public ClassNode get(String name) { - try { - return cache.get(name, () -> Optional.ofNullable(classProvider.get(name))).orElse(null); - } catch (ExecutionException e) { - throw new RuntimeException(e); - } - } + @Override + @Nullable + public ClassNode get(String name) { + try { + return cache.get(name, () -> Optional.ofNullable(classProvider.get(name))).orElse(null); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java index 6e4a665..6eec0f3 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClassProvider.java @@ -1,17 +1,17 @@ package cuchaz.enigma.classprovider; -import org.objectweb.asm.tree.ClassNode; - import javax.annotation.Nullable; +import org.objectweb.asm.tree.ClassNode; + public interface ClassProvider { - /** - * Gets the {@linkplain ClassNode} for a class. The class provider may return a cached result, - * so it's important to not mutate it. - * - * @param name the internal name of the class - * @return the {@linkplain ClassNode} for that class, or {@code null} if it was not found - */ - @Nullable - ClassNode get(String name); + /** + * Gets the {@linkplain ClassNode} for a class. The class provider may return a cached result, + * so it's important to not mutate it. + * + * @param name the internal name of the class + * @return the {@linkplain ClassNode} for that class, or {@code null} if it was not found + */ + @Nullable + ClassNode get(String name); } diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java index e9472fa..224093f 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ClasspathClassProvider.java @@ -1,27 +1,30 @@ package cuchaz.enigma.classprovider; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.tree.ClassNode; - -import javax.annotation.Nullable; import java.io.IOException; import java.io.InputStream; +import javax.annotation.Nullable; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; + /** * Provides classes by loading them from the classpath. */ public class ClasspathClassProvider implements ClassProvider { - @Nullable @Override public ClassNode get(String name) { - try (InputStream in = ClasspathClassProvider.class.getResourceAsStream("/" + name + ".class")) { - if (in == null) { - return null; - } + @Nullable + @Override + public ClassNode get(String name) { + try (InputStream in = ClasspathClassProvider.class.getResourceAsStream("/" + name + ".class")) { + if (in == null) { + return null; + } - ClassNode node = new ClassNode(); - new ClassReader(in).accept(node, 0); - return node; - } catch (IOException e) { - return null; - } - } + ClassNode node = new ClassNode(); + new ClassReader(in).accept(node, 0); + return node; + } catch (IOException e) { + return null; + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java index 865464c..6856540 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/CombiningClassProvider.java @@ -1,31 +1,31 @@ package cuchaz.enigma.classprovider; -import org.objectweb.asm.tree.ClassNode; - import javax.annotation.Nullable; +import org.objectweb.asm.tree.ClassNode; + /** * Combines a list of {@link ClassProvider}s into one, calling each one in a row * until one can provide the class. */ public class CombiningClassProvider implements ClassProvider { - private final ClassProvider[] classProviders; + private final ClassProvider[] classProviders; - public CombiningClassProvider(ClassProvider... classProviders) { - this.classProviders = classProviders; - } + public CombiningClassProvider(ClassProvider... classProviders) { + this.classProviders = classProviders; + } - @Override - @Nullable - public ClassNode get(String name) { - for (ClassProvider cp : classProviders) { - ClassNode node = cp.get(name); + @Override + @Nullable + public ClassNode get(String name) { + for (ClassProvider cp : classProviders) { + ClassNode node = cp.get(name); - if (node != null) { - return node; - } - } + if (node != null) { + return node; + } + } - return null; - } + return null; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java index c614b0a..900a0c8 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/JarClassProvider.java @@ -1,10 +1,5 @@ package cuchaz.enigma.classprovider; -import com.google.common.collect.ImmutableSet; -import cuchaz.enigma.utils.AsmUtil; -import org.objectweb.asm.tree.ClassNode; - -import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -12,53 +7,60 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; +import javax.annotation.Nullable; + +import com.google.common.collect.ImmutableSet; +import org.objectweb.asm.tree.ClassNode; + +import cuchaz.enigma.utils.AsmUtil; + /** * Provides classes by loading them from a JAR file. */ public class JarClassProvider implements AutoCloseable, ClassProvider { - private final FileSystem fileSystem; - private final Set classNames; + private final FileSystem fileSystem; + private final Set classNames; + + public JarClassProvider(Path jarPath) throws IOException { + this.fileSystem = FileSystems.newFileSystem(jarPath, (ClassLoader) null); + this.classNames = collectClassNames(fileSystem); + } - public JarClassProvider(Path jarPath) throws IOException { - this.fileSystem = FileSystems.newFileSystem(jarPath, (ClassLoader) null); - this.classNames = collectClassNames(fileSystem); - } + private static ImmutableSet collectClassNames(FileSystem fileSystem) throws IOException { + ImmutableSet.Builder classNames = ImmutableSet.builder(); - private static ImmutableSet collectClassNames(FileSystem fileSystem) throws IOException { - ImmutableSet.Builder classNames = ImmutableSet.builder(); - for (Path root : fileSystem.getRootDirectories()) { - Files.walk(root).map(Path::toString) - .forEach(path -> { - if (path.endsWith(".class")) { - String name = path.substring(1, path.length() - ".class".length()); - classNames.add(name); - } - }); - } + for (Path root : fileSystem.getRootDirectories()) { + Files.walk(root).map(Path::toString).forEach(path -> { + if (path.endsWith(".class")) { + String name = path.substring(1, path.length() - ".class".length()); + classNames.add(name); + } + }); + } - return classNames.build(); - } + return classNames.build(); + } - public Set getClassNames() { - return classNames; - } + public Set getClassNames() { + return classNames; + } - @Nullable - @Override - public ClassNode get(String name) { - if (!classNames.contains(name)) { - return null; - } + @Nullable + @Override + public ClassNode get(String name) { + if (!classNames.contains(name)) { + return null; + } - try { - return AsmUtil.bytesToNode(Files.readAllBytes(fileSystem.getPath(name + ".class"))); - } catch (IOException e) { - throw new RuntimeException(e); - } - } + try { + return AsmUtil.bytesToNode(Files.readAllBytes(fileSystem.getPath(name + ".class"))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } - @Override - public void close() throws Exception { - fileSystem.close(); - } + @Override + public void close() throws Exception { + fileSystem.close(); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java index 36236a8..604bf49 100644 --- a/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classprovider/ObfuscationFixClassProvider.java @@ -1,10 +1,7 @@ package cuchaz.enigma.classprovider; -import cuchaz.enigma.Enigma; -import cuchaz.enigma.analysis.index.JarIndex; -import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; -import cuchaz.enigma.bytecode.translators.SourceFixVisitor; -import cuchaz.enigma.classprovider.ClassProvider; +import javax.annotation.Nullable; + import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; @@ -12,7 +9,10 @@ import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; -import javax.annotation.Nullable; +import cuchaz.enigma.Enigma; +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; +import cuchaz.enigma.bytecode.translators.SourceFixVisitor; /** * Wraps a ClassProvider to apply fixes to the following problems introduced by the obfuscator, @@ -26,59 +26,63 @@ import javax.annotation.Nullable; *

  • Enum constructor parameters that are incorrectly named or missing the "synthetic" access modifier *
  • "this" parameter which is incorrectly named * - *

    - * These fixes are only applied to classes that were indexed by the JarIndex provided, and not library classes. + * + *

    These fixes are only applied to classes that were indexed by the JarIndex provided, and not library classes. */ public class ObfuscationFixClassProvider implements ClassProvider { - private final ClassProvider classProvider; - private final JarIndex jarIndex; + private final ClassProvider classProvider; + private final JarIndex jarIndex; + + public ObfuscationFixClassProvider(ClassProvider classProvider, JarIndex jarIndex) { + this.classProvider = classProvider; + this.jarIndex = jarIndex; + } + + @Override + @Nullable + public ClassNode get(String name) { + ClassNode node = classProvider.get(name); + + if (!jarIndex.isIndexed(name)) { + return node; + } - public ObfuscationFixClassProvider(ClassProvider classProvider, JarIndex jarIndex) { - this.classProvider = classProvider; - this.jarIndex = jarIndex; - } + ClassNode fixedNode = new ClassNode(); + ClassVisitor visitor = fixedNode; + visitor = new LocalVariableFixVisitor(Enigma.ASM_VERSION, visitor); + visitor = new SourceFixVisitor(Enigma.ASM_VERSION, visitor, jarIndex); + node.accept(visitor); + removeRedundantClassCalls(fixedNode); - @Override - @Nullable - public ClassNode get(String name) { - ClassNode node = classProvider.get(name); + return fixedNode; + } - if (!jarIndex.isIndexed(name)) { - return node; - } + private void removeRedundantClassCalls(ClassNode node) { + // Removes .getClass() calls added by Proguard: + // DUP + // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; + // POP + for (MethodNode methodNode : node.methods) { + AbstractInsnNode insnNode = methodNode.instructions.getFirst(); - ClassNode fixedNode = new ClassNode(); - ClassVisitor visitor = fixedNode; - visitor = new LocalVariableFixVisitor(Enigma.ASM_VERSION, visitor); - visitor = new SourceFixVisitor(Enigma.ASM_VERSION, visitor, jarIndex); - node.accept(visitor); - removeRedundantClassCalls(fixedNode); + while (insnNode != null) { + if (insnNode instanceof MethodInsnNode methodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) { + if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) { + AbstractInsnNode previous = methodInsnNode.getPrevious(); + AbstractInsnNode next = methodInsnNode.getNext(); - return fixedNode; - } + if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) { + //reset the iterator so it gets the new next instruction + insnNode = previous.getPrevious(); + methodNode.instructions.remove(previous); + methodNode.instructions.remove(methodInsnNode); + methodNode.instructions.remove(next); + } + } + } - private void removeRedundantClassCalls(ClassNode node) { - // Removes .getClass() calls added by Proguard: - // DUP - // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; - // POP - for (MethodNode methodNode : node.methods) { - AbstractInsnNode insnNode = methodNode.instructions.getFirst(); - while (insnNode != null) { - if (insnNode instanceof MethodInsnNode methodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) { - if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")) { - AbstractInsnNode previous = methodInsnNode.getPrevious(); - AbstractInsnNode next = methodInsnNode.getNext(); - if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP) { - insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction - methodNode.instructions.remove(previous); - methodNode.instructions.remove(methodInsnNode); - methodNode.instructions.remove(next); - } - } - } - insnNode = insnNode.getNext(); - } - } - } + insnNode = insnNode.getNext(); + } + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java index cb9cbc2..fd078a2 100644 --- a/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigContainer.java @@ -8,7 +8,6 @@ import java.util.Deque; import java.util.LinkedList; public class ConfigContainer { - private Path configPath; private boolean existsOnDisk; @@ -19,7 +18,10 @@ public class ConfigContainer { } public void save() { - if (this.configPath == null) throw new IllegalStateException("File has no config path set!"); + if (this.configPath == null) { + throw new IllegalStateException("File has no config path set!"); + } + try { Files.createDirectories(this.configPath.getParent()); Files.write(this.configPath, this.serialize().getBytes(StandardCharsets.UTF_8)); @@ -52,6 +54,7 @@ public class ConfigContainer { public static ConfigContainer getOrCreate(Path path) { ConfigContainer cc = null; + try { if (Files.exists(path)) { String s = String.join("\n", Files.readAllLines(path)); @@ -93,5 +96,4 @@ public class ConfigContainer { }); return cc; } - } diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java index b3f3d0c..6d9d304 100644 --- a/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigPaths.java @@ -6,7 +6,6 @@ import java.nio.file.Paths; import cuchaz.enigma.utils.Os; public class ConfigPaths { - public static Path getConfigFilePath(String name) { String fileName = Os.getOs() == Os.LINUX ? String.format("%src", name) : String.format("%s.ini", name); return getConfigPathRoot().resolve(fileName); @@ -14,27 +13,30 @@ public class ConfigPaths { public static Path getConfigPathRoot() { switch (Os.getOs()) { - case LINUX: - String configHome = System.getenv("XDG_CONFIG_HOME"); - if (configHome == null) { - return getUserHomeUnix().resolve(".config"); - } - return Paths.get(configHome); - case MAC: - return getUserHomeUnix().resolve("Library").resolve("Application Support"); - case WINDOWS: - return Paths.get(System.getenv("LOCALAPPDATA")); - default: - return Paths.get(System.getProperty("user.dir")); + case LINUX: + String configHome = System.getenv("XDG_CONFIG_HOME"); + + if (configHome == null) { + return getUserHomeUnix().resolve(".config"); + } + + return Paths.get(configHome); + case MAC: + return getUserHomeUnix().resolve("Library").resolve("Application Support"); + case WINDOWS: + return Paths.get(System.getenv("LOCALAPPDATA")); + default: + return Paths.get(System.getProperty("user.dir")); } } private static Path getUserHomeUnix() { String userHome = System.getenv("HOME"); + if (userHome == null) { userHome = System.getProperty("user.dir"); } + return Paths.get(userHome); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java index 3e7bf6d..fba7da3 100644 --- a/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigSection.java @@ -1,10 +1,16 @@ package cuchaz.enigma.config; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; import java.util.function.Function; public class ConfigSection { - private final Map values; private final Map sections; @@ -163,11 +169,16 @@ public class ConfigSection { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ConfigSection)) return false; + if (this == o) { + return true; + } + + if (!(o instanceof ConfigSection)) { + return false; + } + ConfigSection that = (ConfigSection) o; - return values.equals(that.values) && - sections.equals(that.sections); + return values.equals(that.values) && sections.equals(that.sections); } @Override @@ -179,5 +190,4 @@ public class ConfigSection { public String toString() { return String.format("ConfigSection { values: %s, sections: %s }", values, sections); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java index dccb585..a1e3e55 100644 --- a/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigSerializer.java @@ -1,13 +1,17 @@ package cuchaz.enigma.config; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Map.Entry; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; public final class ConfigSerializer { - private static final Pattern FULL_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{6}"); private static final Pattern MIN_RGB_COLOR = Pattern.compile("#[0-9A-Fa-f]{3}"); @@ -19,6 +23,7 @@ public final class ConfigSerializer { // join escaped newlines int len = lines.length; + for (int i = len - 2; i >= 0; i--) { if (lines[i].endsWith("\\")) { lines[i] = String.format("%s\n%s", lines[i], lines[i + 1]); @@ -31,24 +36,30 @@ public final class ConfigSerializer { String line = lines[i]; // skip empty lines and comment lines - if (line.trim().isEmpty() || line.trim().startsWith(";")) continue; + if (line.trim().isEmpty() || line.trim().startsWith(";")) { + continue; + } int r; - boolean fail = (r = parseSectionLine(line, 0, visitor)) == NO_MATCH && - (r = parseKeyValue(line, 0, visitor)) == NO_MATCH; + boolean fail = (r = parseSectionLine(line, 0, visitor)) == NO_MATCH && (r = parseKeyValue(line, 0, visitor)) == NO_MATCH; } } private static int parseSectionLine(String v, int idx, ConfigStructureVisitor visitor) { if (v.startsWith("[")) { List path = new ArrayList<>(); + while (idx < v.length() && v.charAt(idx) == '[') { idx = parseSection(v, idx, path); - if (idx == UNEXPECTED_TOKEN) return UNEXPECTED_TOKEN; + + if (idx == UNEXPECTED_TOKEN) { + return UNEXPECTED_TOKEN; + } } if (!path.isEmpty()) { visitor.jumpToRootSection(); + for (String s : path) { visitor.visitSection(s); } @@ -63,10 +74,12 @@ public final class ConfigSerializer { private static int parseSection(String v, int idx, List path) { idx += 1; // skip leading [ StringBuilder sb = new StringBuilder(); + while (idx < v.length()) { int nextCloseBracket = v.indexOf(']', idx); int nextEscape = v.indexOf('\\', idx); int next = optMin(nextCloseBracket, nextEscape); + if (next == -1) { // unexpected return UNEXPECTED_TOKEN; @@ -79,16 +92,19 @@ public final class ConfigSerializer { idx = parseEscape(v, nextEscape, sb); } } + return idx; } private static int parseKeyValue(String v, int idx, ConfigStructureVisitor visitor) { StringBuilder sb = new StringBuilder(); String k = null; + while (idx < v.length()) { int nextEq = v.indexOf('=', idx); int nextEscape = v.indexOf('\\', idx); int next = optMin(nextEq, nextEscape); + if (next == -1) { break; } else if (next == nextEq) { @@ -102,8 +118,10 @@ public final class ConfigSerializer { idx = parseEscape(v, nextEscape, sb); } } + while (idx < v.length()) { int nextEscape = v.indexOf('\\', idx); + if (nextEscape != -1) { sb.append(v, idx, nextEscape); idx = parseEscape(v, nextEscape, sb); @@ -111,8 +129,13 @@ public final class ConfigSerializer { break; } } + sb.append(v, idx, v.length()); - if (k == null) return NO_MATCH; + + if (k == null) { + return NO_MATCH; + } + visitor.visitKeyValue(k, sb.toString()); return idx; } @@ -122,11 +145,14 @@ public final class ConfigSerializer { if (v.charAt(idx + 1) == 'u') { if (idx + 5 < v.length()) { String codePoint = v.substring(idx + 2, idx + 6); + try { int c = Integer.parseUnsignedInt(codePoint, 16); sb.append((char) c); } catch (NumberFormatException ignored) { + // ignored } + idx = idx + 6; } } else if (v.charAt(idx + 1) == 'n') { @@ -139,6 +165,7 @@ public final class ConfigSerializer { } else { idx = idx + 1; } + return idx; } @@ -150,12 +177,17 @@ public final class ConfigSerializer { private static void structureToString(ConfigSection section, StringBuilder sb, List pathStack) { if (!section.values().isEmpty()) { - if (sb.length() > 0) sb.append('\n'); + if (sb.length() > 0) { + sb.append('\n'); + } + pathStack.forEach(n -> sb.append('[').append(escapeSection(n)).append(']')); - if (!pathStack.isEmpty()) sb.append('\n'); - section.values().entrySet().stream() - .sorted(Entry.comparingByKey()) - .forEach(e -> sb.append(escapeKey(e.getKey())).append('=').append(escapeValue(e.getValue())).append('\n')); + + if (!pathStack.isEmpty()) { + sb.append('\n'); + } + + section.values().entrySet().stream().sorted(Entry.comparingByKey()).forEach(e -> sb.append(escapeKey(e.getKey())).append('=').append(escapeValue(e.getValue())).append('\n')); } section.sections().entrySet().stream().sorted(Entry.comparingByKey()).forEach(e -> { @@ -166,43 +198,37 @@ public final class ConfigSerializer { } private static String escapeSection(String s) { - return s - .replace("\\", "\\\\") - .replace("\n", "\\n") - .replace("]", "\\]") - .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); + return s.replace("\\", "\\\\").replace("\n", "\\n").replace("]", "\\]").chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); } private static String escapeKey(String s) { - return s - .replace("\\", "\\\\") - .replace("[", "\\[") - .replace("\n", "\\n") - .replace("=", "\\=") - .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); + return s.replace("\\", "\\\\").replace("[", "\\[").replace("\n", "\\n").replace("=", "\\=").chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); } private static String escapeValue(String s) { - return s - .replace("\\", "\\\\") - .replace("\n", "\\n") - .chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); + return s.replace("\\", "\\\\").replace("\n", "\\n").chars().mapToObj(c -> c >= 32 && c < 127 ? Character.toString((char) c) : String.format("\\u%04x", c)).collect(Collectors.joining()); } public static Optional parseBool(String v) { - if (v == null) return Optional.empty(); + if (v == null) { + return Optional.empty(); + } + switch (v) { - case "true": - return Optional.of(true); - case "false": - return Optional.of(false); - default: - return Optional.empty(); + case "true": + return Optional.of(true); + case "false": + return Optional.of(false); + default: + return Optional.empty(); } } public static OptionalInt parseInt(String v) { - if (v == null) return OptionalInt.empty(); + if (v == null) { + return OptionalInt.empty(); + } + try { return OptionalInt.of(Integer.parseInt(v)); } catch (NumberFormatException e) { @@ -211,7 +237,10 @@ public final class ConfigSerializer { } public static OptionalDouble parseDouble(String v) { - if (v == null) return OptionalDouble.empty(); + if (v == null) { + return OptionalDouble.empty(); + } + try { return OptionalDouble.of(Double.parseDouble(v)); } catch (NumberFormatException e) { @@ -220,7 +249,10 @@ public final class ConfigSerializer { } public static OptionalInt parseRgbColor(String v) { - if (v == null) return OptionalInt.empty(); + if (v == null) { + return OptionalInt.empty(); + } + try { if (FULL_RGB_COLOR.matcher(v).matches()) { return OptionalInt.of(Integer.parseUnsignedInt(v.substring(1), 16)); @@ -241,6 +273,7 @@ public final class ConfigSerializer { public static String rgbColorToString(int color) { color = color & 0xFFFFFF; boolean isShort = ((color & 0xF0F0F0) >> 4 ^ color & 0x0F0F0F) == 0; + if (isShort) { int packed = color & 0x0F0F0F; packed = packed & 0xF | packed >> 4; @@ -252,14 +285,19 @@ public final class ConfigSerializer { } public static Optional parseArray(String v) { - if (v == null) return Optional.empty(); + if (v == null) { + return Optional.empty(); + } + List l = new ArrayList<>(); int idx = 0; StringBuilder cur = new StringBuilder(); + while (true) { int nextSep = v.indexOf(',', idx); int nextEsc = v.indexOf('\\', idx); int next = optMin(nextSep, nextEsc); + if (next == -1) { cur.append(v, idx, v.length()); l.add(cur.toString()); @@ -271,22 +309,25 @@ public final class ConfigSerializer { idx = nextSep + 1; } else if (next == nextEsc) { cur.append(v, idx, nextEsc); + if (nextEsc + 1 < v.length()) { cur.append(v.charAt(nextEsc + 1)); } + idx = nextEsc + 2; } } } public static String arrayToString(String[] values) { - return Arrays.stream(values) - .map(s -> s.replace("\\", "\\\\").replace(",", "\\,")) - .collect(Collectors.joining(",")); + return Arrays.stream(values).map(s -> s.replace("\\", "\\\\").replace(",", "\\,")).collect(Collectors.joining(",")); } public static > Optional parseEnum(Function byName, String v) { - if (v == null) return Optional.empty(); + if (v == null) { + return Optional.empty(); + } + try { return Optional.of(byName.apply(v)); } catch (IllegalArgumentException e) { @@ -295,9 +336,14 @@ public final class ConfigSerializer { } private static int optMin(int v1, int v2) { - if (v1 == -1) return v2; - if (v2 == -1) return v1; + if (v1 == -1) { + return v2; + } + + if (v2 == -1) { + return v1; + } + return Math.min(v1, v2); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java b/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java index 12d7ec4..5374314 100644 --- a/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/config/ConfigStructureVisitor.java @@ -1,11 +1,9 @@ package cuchaz.enigma.config; public interface ConfigStructureVisitor { - void visitKeyValue(String key, String value); void visitSection(String section); void jumpToRootSection(); - } diff --git a/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java b/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java index 61fea4e..22be2e0 100644 --- a/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java +++ b/enigma/src/main/java/cuchaz/enigma/events/ClassHandleListener.java @@ -8,7 +8,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.utils.Result; public interface ClassHandleListener { - default void onDeobfRefChanged(ClassHandle h, ClassEntry deobfRef) { } @@ -32,5 +31,4 @@ public interface ClassHandleListener { JAVADOC, MAPPINGS, } - } diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java index 5f371a5..9475d74 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java +++ b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java @@ -1,6 +1,10 @@ package cuchaz.enigma.source; -import java.util.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; import javax.annotation.Nullable; @@ -62,6 +66,7 @@ public class DecompiledClassSource { return translatedEntry.getValue().getSourceRemapName(); } else { Optional proposedName = proposeName(project, entry); + if (proposedName.isPresent()) { target.add(RenamableTokenType.PROPOSED, movedToken); return proposedName.get(); @@ -72,6 +77,7 @@ public class DecompiledClassSource { } String defaultName = generateDefaultName(translatedEntry.getValue()); + if (defaultName != null) { return defaultName; } @@ -86,10 +92,7 @@ public class DecompiledClassSource { EntryRemapper mapper = project.getMapper(); Collection> resolved = mapper.getObfResolver().resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT); - return resolved.stream() - .map(e -> nameProposalService.proposeName(e, mapper)) - .filter(Optional::isPresent) - .map(Optional::get); + return resolved.stream().map(e -> nameProposalService.proposeName(e, mapper)).filter(Optional::isPresent).map(Optional::get); }).findFirst(); } @@ -99,6 +102,7 @@ public class DecompiledClassSource { LocalVariableDefEntry localVariable = (LocalVariableDefEntry) entry; int index = localVariable.getIndex(); + if (localVariable.isArgument()) { List arguments = localVariable.getParent().getDesc().getArgumentDescs(); return LocalNameGenerator.generateArgumentName(index, localVariable.getDesc(), arguments); @@ -139,9 +143,11 @@ public class DecompiledClassSource { Iterator fromTokenItr = fromIndex.referenceTokens().iterator(); Iterator toTokenItr = toIndex.referenceTokens().iterator(); + while (fromTokenItr.hasNext() && toTokenItr.hasNext()) { Token fromToken = fromTokenItr.next(); Token toToken = toTokenItr.next(); + if (fromToken.end > fromOffset) { break; } diff --git a/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java b/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java index 938a736..b31dcc4 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java +++ b/enigma/src/main/java/cuchaz/enigma/source/Decompiler.java @@ -1,8 +1,9 @@ package cuchaz.enigma.source; -import cuchaz.enigma.translation.mapping.EntryRemapper; import org.checkerframework.checker.nullness.qual.Nullable; +import cuchaz.enigma.translation.mapping.EntryRemapper; + public interface Decompiler { @Deprecated // use remapper specific one for easy doc inclusion default Source getSource(String className) { diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java b/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java index 638498f..a87fc98 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java +++ b/enigma/src/main/java/cuchaz/enigma/source/DecompilerService.java @@ -1,11 +1,11 @@ package cuchaz.enigma.source; -import cuchaz.enigma.classprovider.ClassProvider; import cuchaz.enigma.api.service.EnigmaService; import cuchaz.enigma.api.service.EnigmaServiceType; +import cuchaz.enigma.classprovider.ClassProvider; public interface DecompilerService extends EnigmaService { - EnigmaServiceType TYPE = EnigmaServiceType.create("decompiler"); + EnigmaServiceType TYPE = EnigmaServiceType.create("decompiler"); - Decompiler create(ClassProvider classProvider, SourceSettings settings); + Decompiler create(ClassProvider classProvider, SourceSettings settings); } diff --git a/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java b/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java index 643ea7a..0e3244d 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java +++ b/enigma/src/main/java/cuchaz/enigma/source/Decompilers.java @@ -5,7 +5,7 @@ import cuchaz.enigma.source.cfr.CfrDecompiler; import cuchaz.enigma.source.procyon.ProcyonDecompiler; public class Decompilers { - public static final DecompilerService PROCYON = ProcyonDecompiler::new; - public static final DecompilerService CFR = CfrDecompiler::new; - public static final DecompilerService BYTECODE = BytecodeDecompiler::new; + public static final DecompilerService PROCYON = ProcyonDecompiler::new; + public static final DecompilerService CFR = CfrDecompiler::new; + public static final DecompilerService BYTECODE = BytecodeDecompiler::new; } diff --git a/enigma/src/main/java/cuchaz/enigma/source/Source.java b/enigma/src/main/java/cuchaz/enigma/source/Source.java index 6ecce7c..fe45805 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/Source.java +++ b/enigma/src/main/java/cuchaz/enigma/source/Source.java @@ -3,9 +3,9 @@ package cuchaz.enigma.source; import cuchaz.enigma.translation.mapping.EntryRemapper; public interface Source { - String asString(); + String asString(); - Source withJavadocs(EntryRemapper remapper); + Source withJavadocs(EntryRemapper remapper); - SourceIndex index(); + SourceIndex index(); } diff --git a/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java b/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java index 971252e..e9d928e 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/source/SourceIndex.java @@ -1,172 +1,170 @@ package cuchaz.enigma.source; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; + import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.representation.entry.Entry; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - public class SourceIndex { - private String source; - private List lineOffsets; - private final TreeMap, Entry>> tokenToReference; - private final Multimap, Entry>, Token> referenceToTokens; - private final Map, Token> declarationToToken; - - public SourceIndex() { - tokenToReference = new TreeMap<>(); - referenceToTokens = HashMultimap.create(); - declarationToToken = Maps.newHashMap(); - } - - public SourceIndex(String source) { - this(); - setSource(source); - } - - public void setSource(String source) { - this.source = source; - lineOffsets = Lists.newArrayList(); - lineOffsets.add(0); - - for (int i = 0; i < this.source.length(); i++) { - if (this.source.charAt(i) == '\n') { - lineOffsets.add(i + 1); - } - } - } - - public String getSource() { - return source; - } - - public int getLineNumber(int position) { - int line = 0; - - for (int offset : lineOffsets) { - if (offset > position) { - break; - } - - line++; - } - - return line; - } - - public int getColumnNumber(int position) { - return position - lineOffsets.get(getLineNumber(position) - 1) + 1; - } - - public int getPosition(int line, int column) { - return lineOffsets.get(line - 1) + column - 1; - } - - public Iterable> declarations() { - return declarationToToken.keySet(); - } - - public Iterable declarationTokens() { - return declarationToToken.values(); - } - - public Token getDeclarationToken(Entry entry) { - return declarationToToken.get(entry); - } - - public void addDeclaration(Token token, Entry deobfEntry) { - if (token != null) { - EntryReference, Entry> reference = new EntryReference<>(deobfEntry, token.text); - tokenToReference.put(token, reference); - referenceToTokens.put(reference, token); - referenceToTokens.put(EntryReference.declaration(deobfEntry, token.text), token); - declarationToToken.put(deobfEntry, token); - } - } - - public Iterable, Entry>> references() { - return referenceToTokens.keySet(); - } - - public EntryReference, Entry> getReference(Token token) { - if (token == null) { - return null; - } - - return tokenToReference.get(token); - } - - public Iterable referenceTokens() { - return tokenToReference.keySet(); - } - - public Token getReferenceToken(int pos) { - Token token = tokenToReference.floorKey(new Token(pos, pos, null)); - - if (token != null && token.contains(pos)) { - return token; - } - - return null; - } - - public Collection getReferenceTokens(EntryReference, Entry> deobfReference) { - return referenceToTokens.get(deobfReference); - } - - public void addReference(Token token, Entry deobfEntry, Entry deobfContext) { - if (token != null) { - EntryReference, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); - tokenToReference.put(token, deobfReference); - referenceToTokens.put(deobfReference, token); - } - } - - public void resolveReferences(EntryResolver resolver) { - // resolve all the classes in the source references - for (Token token : Lists.newArrayList(referenceToTokens.values())) { - EntryReference, Entry> reference = tokenToReference.get(token); - EntryReference, Entry> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); - - // replace the reference - tokenToReference.replace(token, resolvedReference); - - Collection tokens = referenceToTokens.removeAll(reference); - referenceToTokens.putAll(resolvedReference, tokens); - } - } - - public SourceIndex remapTo(SourceRemapper.Result result) { - SourceIndex remapped = new SourceIndex(result.getSource()); - - for (Map.Entry, Token> entry : declarationToToken.entrySet()) { - remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue())); - } - - for (Map.Entry, Entry>, Collection> entry : referenceToTokens.asMap().entrySet()) { - EntryReference, Entry> reference = entry.getKey(); - Collection oldTokens = entry.getValue(); - - Collection newTokens = oldTokens - .stream() - .map(result::getRemappedToken) - .toList(); - - remapped.referenceToTokens.putAll(reference, newTokens); - } - - for (Map.Entry, Entry>> entry : tokenToReference.entrySet()) { - remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue()); - } - - return remapped; - } + private String source; + private List lineOffsets; + private final TreeMap, Entry>> tokenToReference; + private final Multimap, Entry>, Token> referenceToTokens; + private final Map, Token> declarationToToken; + + public SourceIndex() { + tokenToReference = new TreeMap<>(); + referenceToTokens = HashMultimap.create(); + declarationToToken = Maps.newHashMap(); + } + + public SourceIndex(String source) { + this(); + setSource(source); + } + + public void setSource(String source) { + this.source = source; + lineOffsets = Lists.newArrayList(); + lineOffsets.add(0); + + for (int i = 0; i < this.source.length(); i++) { + if (this.source.charAt(i) == '\n') { + lineOffsets.add(i + 1); + } + } + } + + public String getSource() { + return source; + } + + public int getLineNumber(int position) { + int line = 0; + + for (int offset : lineOffsets) { + if (offset > position) { + break; + } + + line++; + } + + return line; + } + + public int getColumnNumber(int position) { + return position - lineOffsets.get(getLineNumber(position) - 1) + 1; + } + + public int getPosition(int line, int column) { + return lineOffsets.get(line - 1) + column - 1; + } + + public Iterable> declarations() { + return declarationToToken.keySet(); + } + + public Iterable declarationTokens() { + return declarationToToken.values(); + } + + public Token getDeclarationToken(Entry entry) { + return declarationToToken.get(entry); + } + + public void addDeclaration(Token token, Entry deobfEntry) { + if (token != null) { + EntryReference, Entry> reference = new EntryReference<>(deobfEntry, token.text); + tokenToReference.put(token, reference); + referenceToTokens.put(reference, token); + referenceToTokens.put(EntryReference.declaration(deobfEntry, token.text), token); + declarationToToken.put(deobfEntry, token); + } + } + + public Iterable, Entry>> references() { + return referenceToTokens.keySet(); + } + + public EntryReference, Entry> getReference(Token token) { + if (token == null) { + return null; + } + + return tokenToReference.get(token); + } + + public Iterable referenceTokens() { + return tokenToReference.keySet(); + } + + public Token getReferenceToken(int pos) { + Token token = tokenToReference.floorKey(new Token(pos, pos, null)); + + if (token != null && token.contains(pos)) { + return token; + } + + return null; + } + + public Collection getReferenceTokens(EntryReference, Entry> deobfReference) { + return referenceToTokens.get(deobfReference); + } + + public void addReference(Token token, Entry deobfEntry, Entry deobfContext) { + if (token != null) { + EntryReference, Entry> deobfReference = new EntryReference<>(deobfEntry, token.text, deobfContext); + tokenToReference.put(token, deobfReference); + referenceToTokens.put(deobfReference, token); + } + } + + public void resolveReferences(EntryResolver resolver) { + // resolve all the classes in the source references + for (Token token : Lists.newArrayList(referenceToTokens.values())) { + EntryReference, Entry> reference = tokenToReference.get(token); + EntryReference, Entry> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); + + // replace the reference + tokenToReference.replace(token, resolvedReference); + + Collection tokens = referenceToTokens.removeAll(reference); + referenceToTokens.putAll(resolvedReference, tokens); + } + } + + public SourceIndex remapTo(SourceRemapper.Result result) { + SourceIndex remapped = new SourceIndex(result.getSource()); + + for (Map.Entry, Token> entry : declarationToToken.entrySet()) { + remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue())); + } + + for (Map.Entry, Entry>, Collection> entry : referenceToTokens.asMap().entrySet()) { + EntryReference, Entry> reference = entry.getKey(); + Collection oldTokens = entry.getValue(); + + Collection newTokens = oldTokens.stream().map(result::getRemappedToken).toList(); + + remapped.referenceToTokens.putAll(reference, newTokens); + } + + for (Map.Entry, Entry>> entry : tokenToReference.entrySet()) { + remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue()); + } + + return remapped; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java b/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java index b5f8006..f12c387 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java +++ b/enigma/src/main/java/cuchaz/enigma/source/SourceRemapper.java @@ -17,10 +17,12 @@ public class SourceRemapper { Map remappedTokens = new HashMap<>(); int accumulatedOffset = 0; + for (Token token : tokens) { Token movedToken = token.move(accumulatedOffset); String remappedName = remapper.remap(token, movedToken); + if (remappedName != null) { accumulatedOffset += movedToken.getRenameOffset(remappedName); movedToken.rename(remappedSource, remappedName); diff --git a/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java b/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java index f6c68e9..1754770 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java +++ b/enigma/src/main/java/cuchaz/enigma/source/SourceSettings.java @@ -1,11 +1,11 @@ package cuchaz.enigma.source; public class SourceSettings { - public final boolean removeImports; - public final boolean removeVariableFinal; + public final boolean removeImports; + public final boolean removeVariableFinal; - public SourceSettings(boolean removeImports, boolean removeVariableFinal) { - this.removeImports = removeImports; - this.removeVariableFinal = removeVariableFinal; - } + public SourceSettings(boolean removeImports, boolean removeVariableFinal) { + this.removeImports = removeImports; + this.removeVariableFinal = removeVariableFinal; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/Token.java b/enigma/src/main/java/cuchaz/enigma/source/Token.java index 7d729ab..5c14234 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/Token.java +++ b/enigma/src/main/java/cuchaz/enigma/source/Token.java @@ -1,18 +1,17 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.source; public class Token implements Comparable { - public int start; public int end; public String text; diff --git a/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java b/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java index f32d918..179eece 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java +++ b/enigma/src/main/java/cuchaz/enigma/source/TokenStore.java @@ -1,9 +1,14 @@ package cuchaz.enigma.source; -import java.util.*; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NavigableSet; +import java.util.TreeSet; public final class TokenStore { - private static final TokenStore EMPTY = new TokenStore(Collections.emptyNavigableSet(), Collections.emptyMap(), null); private final NavigableSet tokens; @@ -18,9 +23,11 @@ public final class TokenStore { public static TokenStore create(SourceIndex obfuscatedIndex) { EnumMap> map = new EnumMap<>(RenamableTokenType.class); + for (RenamableTokenType value : RenamableTokenType.values()) { map.put(value, new TreeSet<>(Comparator.comparing(t -> t.start))); } + return new TokenStore(new TreeSet<>(Comparator.comparing(t -> t.start)), Collections.unmodifiableMap(map), obfuscatedIndex.getSource()); } @@ -34,22 +41,25 @@ public final class TokenStore { } public boolean isCompatible(TokenStore other) { - return this.obfSource != null && other.obfSource != null && - this.obfSource.equals(other.obfSource) && - this.tokens.size() == other.tokens.size(); + return this.obfSource != null && other.obfSource != null && this.obfSource.equals(other.obfSource) && this.tokens.size() == other.tokens.size(); } public int mapPosition(TokenStore to, int position) { - if (!this.isCompatible(to)) return 0; + if (!this.isCompatible(to)) { + return 0; + } int newPos = position; Iterator thisIter = this.tokens.iterator(); Iterator toIter = to.tokens.iterator(); + while (thisIter.hasNext()) { Token token = thisIter.next(); Token newToken = toIter.next(); - if (position < token.start) break; + if (position < token.start) { + break; + } // if we're inside the token and the text changed, // snap the cursor to the beginning @@ -67,5 +77,4 @@ public final class TokenStore { public Map> getByType() { return byType; } - } diff --git a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java index 97d2969..6461d20 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java +++ b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeDecompiler.java @@ -1,21 +1,22 @@ package cuchaz.enigma.source.bytecode; +import org.checkerframework.checker.nullness.qual.Nullable; + import cuchaz.enigma.classprovider.ClassProvider; import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.Source; import cuchaz.enigma.source.SourceSettings; import cuchaz.enigma.translation.mapping.EntryRemapper; -import org.checkerframework.checker.nullness.qual.Nullable; public class BytecodeDecompiler implements Decompiler { - private final ClassProvider classProvider; + private final ClassProvider classProvider; - public BytecodeDecompiler(ClassProvider classProvider, SourceSettings settings) { - this.classProvider = classProvider; - } + public BytecodeDecompiler(ClassProvider classProvider, SourceSettings settings) { + this.classProvider = classProvider; + } - @Override - public Source getSource(String className, @Nullable EntryRemapper remapper) { - return new BytecodeSource(classProvider.get(className), remapper); - } + @Override + public Source getSource(String className, @Nullable EntryRemapper remapper) { + return new BytecodeSource(classProvider.get(className), remapper); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java index 4364b40..bcb5455 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java +++ b/enigma/src/main/java/cuchaz/enigma/source/bytecode/BytecodeSource.java @@ -1,56 +1,57 @@ package cuchaz.enigma.source.bytecode; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.util.TraceClassVisitor; + import cuchaz.enigma.Enigma; import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; import cuchaz.enigma.source.Source; import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.translation.mapping.EntryRemapper; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.util.TraceClassVisitor; - -import java.io.PrintWriter; -import java.io.StringWriter; public class BytecodeSource implements Source { - private final ClassNode classNode; - private final EntryRemapper remapper; + private final ClassNode classNode; + private final EntryRemapper remapper; - public BytecodeSource(ClassNode classNode, EntryRemapper remapper) { - this.classNode = classNode; - this.remapper = remapper; - } + public BytecodeSource(ClassNode classNode, EntryRemapper remapper) { + this.classNode = classNode; + this.remapper = remapper; + } - @Override - public String asString() { - return index().getSource(); - } + @Override + public String asString() { + return index().getSource(); + } - @Override - public Source withJavadocs(EntryRemapper remapper) { - return new BytecodeSource(classNode, remapper); - } + @Override + public Source withJavadocs(EntryRemapper remapper) { + return new BytecodeSource(classNode, remapper); + } - @Override - public SourceIndex index() { - SourceIndex index = new SourceIndex(); + @Override + public SourceIndex index() { + SourceIndex index = new SourceIndex(); - EnigmaTextifier textifier = new EnigmaTextifier(index); - StringWriter out = new StringWriter(); - PrintWriter writer = new PrintWriter(out); + EnigmaTextifier textifier = new EnigmaTextifier(index); + StringWriter out = new StringWriter(); + PrintWriter writer = new PrintWriter(out); - TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, textifier, writer); + TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, textifier, writer); - ClassNode node = this.classNode; + ClassNode node = this.classNode; - if (remapper != null) { - ClassNode translatedNode = new ClassNode(); - node.accept(new TranslationClassVisitor(remapper.getDeobfuscator(), Enigma.ASM_VERSION, translatedNode)); - node = translatedNode; - } + if (remapper != null) { + ClassNode translatedNode = new ClassNode(); + node.accept(new TranslationClassVisitor(remapper.getDeobfuscator(), Enigma.ASM_VERSION, translatedNode)); + node = translatedNode; + } - node.accept(traceClassVisitor); - index.setSource(out.toString()); + node.accept(traceClassVisitor); + index.setSource(out.toString()); - return index; - } + return index; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java b/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java index 2f3fcf2..f1586ee 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java +++ b/enigma/src/main/java/cuchaz/enigma/source/bytecode/EnigmaTextifier.java @@ -1,14 +1,15 @@ package cuchaz.enigma.source.bytecode; +import org.objectweb.asm.util.Textifier; + import cuchaz.enigma.Enigma; import cuchaz.enigma.source.SourceIndex; -import org.objectweb.asm.util.Textifier; public class EnigmaTextifier extends Textifier { - private final SourceIndex sourceIndex; + private final SourceIndex sourceIndex; - public EnigmaTextifier(SourceIndex sourceIndex) { - super(Enigma.ASM_VERSION); - this.sourceIndex = sourceIndex; - } + public EnigmaTextifier(SourceIndex sourceIndex) { + super(Enigma.ASM_VERSION); + this.sourceIndex = sourceIndex; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java index cd7b2aa..5531236 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrDecompiler.java @@ -1,11 +1,8 @@ package cuchaz.enigma.source.cfr; -import cuchaz.enigma.classprovider.ClassProvider; -import cuchaz.enigma.source.Decompiler; -import cuchaz.enigma.source.Source; -import cuchaz.enigma.source.SourceSettings; -import cuchaz.enigma.translation.mapping.EntryRemapper; -import cuchaz.enigma.utils.AsmUtil; +import java.util.Collection; +import java.util.Map; + import org.benf.cfr.reader.apiunreleased.ClassFileSource2; import org.benf.cfr.reader.apiunreleased.JarContent; import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; @@ -15,55 +12,59 @@ import org.benf.cfr.reader.util.getopt.OptionsImpl; import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.tree.ClassNode; -import java.util.Collection; -import java.util.Map; +import cuchaz.enigma.classprovider.ClassProvider; +import cuchaz.enigma.source.Decompiler; +import cuchaz.enigma.source.Source; +import cuchaz.enigma.source.SourceSettings; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.utils.AsmUtil; public class CfrDecompiler implements Decompiler { - // cfr doesn't add final on params so final setting is ignored - private final SourceSettings settings; - private final Options options; - private final ClassFileSource2 classFileSource; + // cfr doesn't add final on params so final setting is ignored + private final SourceSettings settings; + private final Options options; + private final ClassFileSource2 classFileSource; - public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { - this.options = OptionsImpl.getFactory().create( Map.of("trackbytecodeloc", "true")); - this.settings = sourceSettings; - this.classFileSource = new ClassFileSource(classProvider); - } + public CfrDecompiler(ClassProvider classProvider, SourceSettings sourceSettings) { + this.options = OptionsImpl.getFactory().create(Map.of("trackbytecodeloc", "true")); + this.settings = sourceSettings; + this.classFileSource = new ClassFileSource(classProvider); + } - @Override - public Source getSource(String className, @Nullable EntryRemapper mapper) { - return new CfrSource(className, settings, this.options, this.classFileSource, mapper); - } + @Override + public Source getSource(String className, @Nullable EntryRemapper mapper) { + return new CfrSource(className, settings, this.options, this.classFileSource, mapper); + } - private record ClassFileSource(ClassProvider classProvider) implements ClassFileSource2 { - @Override - public JarContent addJarContent(String s, AnalysisType analysisType) { - return null; - } + private record ClassFileSource(ClassProvider classProvider) implements ClassFileSource2 { + @Override + public JarContent addJarContent(String s, AnalysisType analysisType) { + return null; + } - @Override - public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { - } + @Override + public void informAnalysisRelativePathDetail(String usePath, String classFilePath) { + } - @Override - public Collection addJar(String jarPath) { - return null; - } + @Override + public Collection addJar(String jarPath) { + return null; + } - @Override - public String getPossiblyRenamedPath(String path) { - return path; - } + @Override + public String getPossiblyRenamedPath(String path) { + return path; + } - @Override - public Pair getClassFileContent(String path) { - ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); + @Override + public Pair getClassFileContent(String path) { + ClassNode node = classProvider.get(path.substring(0, path.lastIndexOf('.'))); - if (node == null) { - return null; - } + if (node == null) { + return null; + } - return new Pair<>(AsmUtil.nodeToBytes(node), path); - } - } + return new Pair<>(AsmUtil.nodeToBytes(node), path); + } + } } 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 fb44ef9..70b43ac 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/CfrSource.java @@ -1,9 +1,5 @@ package cuchaz.enigma.source.cfr; -import cuchaz.enigma.source.Source; -import cuchaz.enigma.source.SourceIndex; -import cuchaz.enigma.source.SourceSettings; -import cuchaz.enigma.translation.mapping.EntryRemapper; import org.benf.cfr.reader.apiunreleased.ClassFileSource2; import org.benf.cfr.reader.entities.ClassFile; import org.benf.cfr.reader.mapping.MappingFactory; @@ -17,71 +13,77 @@ import org.benf.cfr.reader.util.getopt.Options; import org.benf.cfr.reader.util.getopt.OptionsImpl; import org.checkerframework.checker.nullness.qual.Nullable; +import cuchaz.enigma.source.Source; +import cuchaz.enigma.source.SourceIndex; +import cuchaz.enigma.source.SourceSettings; +import cuchaz.enigma.translation.mapping.EntryRemapper; + public class CfrSource implements Source { - private final String className; - private final SourceSettings settings; - private final Options options; - private final ClassFileSource2 classFileSource; - private final EntryRemapper mapper; - - private SourceIndex index; - - public CfrSource(String className, SourceSettings settings, Options options, ClassFileSource2 classFileSource, @Nullable EntryRemapper mapper) { - this.className = className; - this.settings = settings; - this.options = options; - this.classFileSource = classFileSource; - this.mapper = mapper; - } - - @Override - public Source withJavadocs(EntryRemapper mapper) { - return new CfrSource(className, settings, options, classFileSource, mapper); - } - - @Override - public SourceIndex index() { - ensureDecompiled(); - return index; - } - - @Override - public String asString() { - ensureDecompiled(); - return index.getSource(); - } - - private void ensureDecompiled() { - if (index != null) { - return; - } - - DCCommonState commonState = new DCCommonState(options, classFileSource); - ObfuscationMapping mapping = MappingFactory.get(options, commonState); - DCCommonState state = new DCCommonState(commonState, mapping); - ClassFile tree = state.getClassFileMaybePath(className); - - state.configureWith(tree); - - // To make sure we're analysing the cached version - try { - tree = state.getClassFile(tree.getClassType()); - } catch (CannotLoadClassException ignored) { - } - - if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { - tree.loadInnerClasses(state); - } - - if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { - MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); - } - - TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); - tree.analyseTop(state, typeUsageCollector); - - EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); - tree.dump(state.getObfuscationMapping().wrap(dumper)); - index = dumper.getIndex(); - } + private final String className; + private final SourceSettings settings; + private final Options options; + private final ClassFileSource2 classFileSource; + private final EntryRemapper mapper; + + private SourceIndex index; + + public CfrSource(String className, SourceSettings settings, Options options, ClassFileSource2 classFileSource, @Nullable EntryRemapper mapper) { + this.className = className; + this.settings = settings; + this.options = options; + this.classFileSource = classFileSource; + this.mapper = mapper; + } + + @Override + public Source withJavadocs(EntryRemapper mapper) { + return new CfrSource(className, settings, options, classFileSource, mapper); + } + + @Override + public SourceIndex index() { + ensureDecompiled(); + return index; + } + + @Override + public String asString() { + ensureDecompiled(); + return index.getSource(); + } + + private void ensureDecompiled() { + if (index != null) { + return; + } + + DCCommonState commonState = new DCCommonState(options, classFileSource); + ObfuscationMapping mapping = MappingFactory.get(options, commonState); + DCCommonState state = new DCCommonState(commonState, mapping); + ClassFile tree = state.getClassFileMaybePath(className); + + state.configureWith(tree); + + // To make sure we're analysing the cached version + try { + tree = state.getClassFile(tree.getClassType()); + } catch (CannotLoadClassException ignored) { + // ignored + } + + if (options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)) { + tree.loadInnerClasses(state); + } + + if (options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)) { + MemberNameResolver.resolveNames(state, ListFactory.newList(state.getClassCache().getLoadedTypes())); + } + + TypeUsageCollectingDumper typeUsageCollector = new TypeUsageCollectingDumper(options, tree); + tree.analyseTop(state, typeUsageCollector); + + EnigmaDumper dumper = new EnigmaDumper(new StringBuilder(), settings, typeUsageCollector.getRealTypeUsageInformation(), options, mapper); + tree.dump(state.getObfuscationMapping().wrap(dumper)); + index = dumper.getIndex(); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java index 14fd168..93cc64f 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java +++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java @@ -1,17 +1,13 @@ package cuchaz.enigma.source.cfr; -import cuchaz.enigma.source.SourceIndex; -import cuchaz.enigma.source.SourceSettings; -import cuchaz.enigma.source.Token; -import cuchaz.enigma.translation.mapping.EntryMapping; -import cuchaz.enigma.translation.mapping.EntryRemapper; -import cuchaz.enigma.translation.representation.MethodDescriptor; -import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import cuchaz.enigma.translation.representation.entry.Entry; -import cuchaz.enigma.translation.representation.entry.FieldEntry; -import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; -import cuchaz.enigma.translation.representation.entry.MethodEntry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + import org.benf.cfr.reader.bytecode.analysis.loc.HasByteCodeLoc; import org.benf.cfr.reader.bytecode.analysis.types.JavaRefTypeInstance; import org.benf.cfr.reader.bytecode.analysis.types.JavaTypeInstance; @@ -31,371 +27,404 @@ import org.benf.cfr.reader.util.output.StringStreamDumper; import org.benf.cfr.reader.util.output.TypeContext; import org.checkerframework.checker.nullness.qual.Nullable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import cuchaz.enigma.source.SourceIndex; +import cuchaz.enigma.source.SourceSettings; +import cuchaz.enigma.source.Token; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.translation.representation.MethodDescriptor; +import cuchaz.enigma.translation.representation.TypeDescriptor; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public class EnigmaDumper extends StringStreamDumper { - private final StringBuilder sb; - private final SourceSettings sourceSettings; - private final SourceIndex index; - private final @Nullable EntryRemapper mapper; - private final Map> refs = new HashMap<>(); - private final TypeUsageInformation typeUsage; - private final MovableDumperContext dumperContext; - private boolean muteLine = false; - private MethodEntry contextMethod = null; - - public EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, - @Nullable EntryRemapper mapper) { - this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext()); - } - - protected EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, - @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) { - super((m, e) -> { - }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context); - this.sb = sb; - this.sourceSettings = sourceSettings; - this.typeUsage = typeUsage; - this.mapper = mapper; - this.dumperContext = context; - this.index = index; - } - - private MethodEntry getMethodEntry(MethodPrototype method) { - if (method == null || method.getOwner() == null) { - return null; - } - - MethodDescriptor desc = new MethodDescriptor(method.getOriginalDescriptor()); - - return new MethodEntry(getClassEntry(method.getOwner()), method.getName(), desc); - } - - private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) { - MethodEntry owner = getMethodEntry(method); - // params may be not computed if cfr creates a lambda expression fallback, e.g. in PointOfInterestSet - if (owner == null || !method.parametersComputed()) { - return null; - } - - int variableIndex = method.getParameterLValues().get(parameterIndex).localVariable.getIdx(); - - return new LocalVariableEntry(owner, variableIndex, name, true, null); - } - - private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, String desc) { - return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(desc)); - } - - private ClassEntry getClassEntry(JavaTypeInstance type) { - return new ClassEntry(type.getRawName().replace('.', '/')); - } - - @Override - public Dumper packageName(JavaRefTypeInstance t) { - if (sourceSettings.removeImports) { - return this; - } - return super.packageName(t); - } - - @Override - public Dumper keyword(String s) { - if (sourceSettings.removeImports && s.startsWith("import")) { - muteLine = true; - return this; - } - return super.keyword(s); - } - - @Override - public Dumper endCodeln() { - if (muteLine) { - muteLine = false; - return this; - } - return super.endCodeln(); - } - - @Override - public Dumper print(String s) { - if (muteLine) { - return this; - } - return super.print(s); - } - - @Override - public Dumper dumpClassDoc(JavaTypeInstance owner) { - if (mapper != null) { - List recordComponentDocs = new LinkedList<>(); - - if (isRecord(owner)) { - ClassFile classFile = ((JavaRefTypeInstance) owner).getClassFile(); - for (ClassFileField field : classFile.getFields()) { - if (field.getField().testAccessFlag(AccessFlag.ACC_STATIC)) { - continue; - } - - EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getField().getDescriptor())); - if (mapping == null) { - continue; - } - - String javaDoc = mapping.javadoc(); - if (javaDoc != null) { - recordComponentDocs.add(String.format("@param %s %s", mapping.targetName(), javaDoc)); - } - } - } - - EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner)); - - String javadoc = null; - if (mapping != null) { - javadoc = mapping.javadoc(); - } - - if (javadoc != null || !recordComponentDocs.isEmpty()) { - print("/**").newln(); - if (javadoc != null) { - for (String line : javadoc.split("\\R")) { - print(" * ").print(line).newln(); - } - - if (!recordComponentDocs.isEmpty()) { - print(" * ").newln(); - } - } - - for (String componentDoc : recordComponentDocs) { - print(" * ").print(componentDoc).newln(); - } - - print(" */").newln(); - } - } - return this; - } - - @Override - public Dumper dumpMethodDoc(MethodPrototype method) { - if (mapper != null) { - List lines = new ArrayList<>(); - MethodEntry methodEntry = getMethodEntry(method); - EntryMapping mapping = mapper.getDeobfMapping(methodEntry); - if (mapping != null) { - String javadoc = mapping.javadoc(); - if (javadoc != null) { - lines.addAll(Arrays.asList(javadoc.split("\\R"))); - } - } - - Collection> children = mapper.getObfChildren(methodEntry); - - if (children != null && !children.isEmpty()) { - for (Entry each : children) { - if (each instanceof LocalVariableEntry) { - EntryMapping paramMapping = mapper.getDeobfMapping(each); - if (paramMapping != null) { - String javadoc = paramMapping.javadoc(); - if (javadoc != null) { - lines.addAll(Arrays.asList(("@param " + paramMapping.targetName() + " " + javadoc).split("\\R"))); - } - } - } - } - } - - if (!lines.isEmpty()) { - print("/**").newln(); - for (String line : lines) { - print(" * ").print(line).newln(); - } - print(" */").newln(); - } - } - return this; - } - - @Override - public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) { - boolean recordComponent = isRecord(owner) && !field.testAccessFlag(AccessFlag.ACC_STATIC); - if (mapper != null && !recordComponent) { - EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor())); - if (mapping != null) { - String javadoc = mapping.javadoc(); - if (javadoc != null) { - print("/**").newln(); - for (String line : javadoc.split("\\R")) { - print(" * ").print(line).newln(); - } - print(" */").newln(); - } - } - } - return this; - } - - @Override - public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) { - Entry entry = getMethodEntry(method); - super.methodName(name, method, special, defines); - int now = sb.length(); - Token token = new Token(now - name.length(), now, name); - - if (entry != null) { - if (defines) { - index.addDeclaration(token, entry); // override as cfr reuses local vars - } else { - index.addReference(token, entry, contextMethod); - } - } - - return this; - } - - @Override - public Dumper parameterName(String name, Object ref, MethodPrototype method, int index, boolean defines) { - super.parameterName(name, ref, method, index, defines); - int now = sb.length(); - Token token = new Token(now - name.length(), now, name); - Entry entry; - if (defines) { - refs.put(ref, entry = getParameterEntry(method, index, name)); - } else { - entry = refs.get(ref); - } - - if (entry != null) { - if (defines) { - this.index.addDeclaration(token, entry); - } else { - this.index.addReference(token, entry, contextMethod); - } - } - - return this; - } - - @Override - public Dumper variableName(String name, NamedVariable variable, boolean defines) { - // todo catch var declarations in the future - return super.variableName(name, variable, defines); - } - - @Override - public Dumper identifier(String name, Object ref, boolean defines) { - super.identifier(name, ref, defines); - Entry entry; - if (defines) { - refs.remove(ref); - return this; - } - if ((entry = refs.get(ref)) == null) { - return this; - } - int now = sb.length(); - Token token = new Token(now - name.length(), now, name); - index.addReference(token, entry, contextMethod); - return this; - } - - @Override - public Dumper fieldName(String name, String descriptor, JavaTypeInstance owner, boolean hiddenDeclaration, boolean isStatic, boolean defines) { - super.fieldName(name, descriptor, owner, hiddenDeclaration, isStatic, defines); - int now = sb.length(); - Token token = new Token(now - name.length(), now, name); - if (descriptor != null) { - Entry entry = getFieldEntry(owner, name, descriptor); - - if (defines) { - index.addDeclaration(token, entry); - } else { - index.addReference(token, entry, contextMethod); - } - } - - return this; - } - - @Override - public Dumper dump(JavaTypeInstance type) { - dumpClass(TypeContext.None, type, false); - return this; - } - - @Override - public Dumper dump(JavaTypeInstance type, boolean defines) { - dumpClass(TypeContext.None, type, defines); - return this; - } - - @Override - public Dumper dump(JavaTypeInstance type, TypeContext context) { - dumpClass(context, type, false); - return this; - } - - private void dumpClass(TypeContext context, JavaTypeInstance type, boolean defines) { - if (type instanceof JavaRefTypeInstance) { - type.dumpInto(this, typeUsage, context); - String name = typeUsage.getName(type, context); // the actually used name, dump will indent - int now = sb.length(); - Token token = new Token(now - name.length(), now, name); - - if (defines) { - index.addDeclaration(token, getClassEntry(type)); - } else { - index.addReference(token, getClassEntry(type), contextMethod); - } - return; - } - - type.dumpInto(this, typeUsage, context); - } - - /** - * {@inheritDoc} - * - *

    Otherwise the type usage override dumper will not go through the type instance dump - * we have here. - */ - @Override - public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { - return new EnigmaDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext); - } - - @Override - public void informBytecodeLoc(HasByteCodeLoc loc) { - Collection methods = loc.getLoc().getMethods(); - if (!methods.isEmpty()) { - this.contextMethod = getMethodEntry(methods.iterator().next().getMethodPrototype()); - } - } - - public SourceIndex getIndex() { - index.setSource(getString()); - return index; - } - - public String getString() { - return sb.toString(); - } - - private boolean isRecord(JavaTypeInstance javaTypeInstance) { - if (javaTypeInstance instanceof JavaRefTypeInstance) { - ClassFile classFile = ((JavaRefTypeInstance) javaTypeInstance).getClassFile(); - return classFile.getClassSignature().getSuperClass().getRawName().equals("java.lang.Record"); - } - - return false; - } + private final StringBuilder sb; + private final SourceSettings sourceSettings; + private final SourceIndex index; + private final @Nullable EntryRemapper mapper; + private final Map> refs = new HashMap<>(); + private final TypeUsageInformation typeUsage; + private final MovableDumperContext dumperContext; + private boolean muteLine = false; + private MethodEntry contextMethod = null; + + public EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper) { + this(sb, sourceSettings, typeUsage, options, mapper, new SourceIndex(), new MovableDumperContext()); + } + + protected EnigmaDumper(StringBuilder sb, SourceSettings sourceSettings, TypeUsageInformation typeUsage, Options options, @Nullable EntryRemapper mapper, SourceIndex index, MovableDumperContext context) { + super((m, e) -> { + }, sb, typeUsage, options, IllegalIdentifierDump.Nop.getInstance(), context); + this.sb = sb; + this.sourceSettings = sourceSettings; + this.typeUsage = typeUsage; + this.mapper = mapper; + this.dumperContext = context; + this.index = index; + } + + private MethodEntry getMethodEntry(MethodPrototype method) { + if (method == null || method.getOwner() == null) { + return null; + } + + MethodDescriptor desc = new MethodDescriptor(method.getOriginalDescriptor()); + + return new MethodEntry(getClassEntry(method.getOwner()), method.getName(), desc); + } + + private LocalVariableEntry getParameterEntry(MethodPrototype method, int parameterIndex, String name) { + MethodEntry owner = getMethodEntry(method); + + // params may be not computed if cfr creates a lambda expression fallback, e.g. in PointOfInterestSet + if (owner == null || !method.parametersComputed()) { + return null; + } + + int variableIndex = method.getParameterLValues().get(parameterIndex).localVariable.getIdx(); + + return new LocalVariableEntry(owner, variableIndex, name, true, null); + } + + private FieldEntry getFieldEntry(JavaTypeInstance owner, String name, String desc) { + return new FieldEntry(getClassEntry(owner), name, new TypeDescriptor(desc)); + } + + private ClassEntry getClassEntry(JavaTypeInstance type) { + return new ClassEntry(type.getRawName().replace('.', '/')); + } + + @Override + public Dumper packageName(JavaRefTypeInstance t) { + if (sourceSettings.removeImports) { + return this; + } + + return super.packageName(t); + } + + @Override + public Dumper keyword(String s) { + if (sourceSettings.removeImports && s.startsWith("import")) { + muteLine = true; + return this; + } + + return super.keyword(s); + } + + @Override + public Dumper endCodeln() { + if (muteLine) { + muteLine = false; + return this; + } + + return super.endCodeln(); + } + + @Override + public Dumper print(String s) { + if (muteLine) { + return this; + } + + return super.print(s); + } + + @Override + public Dumper dumpClassDoc(JavaTypeInstance owner) { + if (mapper != null) { + List recordComponentDocs = new LinkedList<>(); + + if (isRecord(owner)) { + ClassFile classFile = ((JavaRefTypeInstance) owner).getClassFile(); + + for (ClassFileField field : classFile.getFields()) { + if (field.getField().testAccessFlag(AccessFlag.ACC_STATIC)) { + continue; + } + + EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getField().getDescriptor())); + + if (mapping == null) { + continue; + } + + String javaDoc = mapping.javadoc(); + + if (javaDoc != null) { + recordComponentDocs.add(String.format("@param %s %s", mapping.targetName(), javaDoc)); + } + } + } + + EntryMapping mapping = mapper.getDeobfMapping(getClassEntry(owner)); + + String javadoc = null; + + if (mapping != null) { + javadoc = mapping.javadoc(); + } + + if (javadoc != null || !recordComponentDocs.isEmpty()) { + print("/**").newln(); + if (javadoc != null) { + for (String line : javadoc.split("\\R")) { + print(" * ").print(line).newln(); + } + + if (!recordComponentDocs.isEmpty()) { + print(" * ").newln(); + } + } + + for (String componentDoc : recordComponentDocs) { + print(" * ").print(componentDoc).newln(); + } + + print(" */").newln(); + } + } + + return this; + } + + @Override + public Dumper dumpMethodDoc(MethodPrototype method) { + if (mapper != null) { + List lines = new ArrayList<>(); + MethodEntry methodEntry = getMethodEntry(method); + EntryMapping mapping = mapper.getDeobfMapping(methodEntry); + + if (mapping != null) { + String javadoc = mapping.javadoc(); + + if (javadoc != null) { + lines.addAll(Arrays.asList(javadoc.split("\\R"))); + } + } + + Collection> children = mapper.getObfChildren(methodEntry); + + if (children != null && !children.isEmpty()) { + for (Entry each : children) { + if (each instanceof LocalVariableEntry) { + EntryMapping paramMapping = mapper.getDeobfMapping(each); + + if (paramMapping != null) { + String javadoc = paramMapping.javadoc(); + + if (javadoc != null) { + lines.addAll(Arrays.asList(("@param " + paramMapping.targetName() + " " + javadoc).split("\\R"))); + } + } + } + } + } + + if (!lines.isEmpty()) { + print("/**").newln(); + + for (String line : lines) { + print(" * ").print(line).newln(); + } + + print(" */").newln(); + } + } + + return this; + } + + @Override + public Dumper dumpFieldDoc(Field field, JavaTypeInstance owner) { + boolean recordComponent = isRecord(owner) && !field.testAccessFlag(AccessFlag.ACC_STATIC); + + if (mapper != null && !recordComponent) { + EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor())); + + if (mapping != null) { + String javadoc = mapping.javadoc(); + + if (javadoc != null) { + print("/**").newln(); + + for (String line : javadoc.split("\\R")) { + print(" * ").print(line).newln(); + } + + print(" */").newln(); + } + } + } + + return this; + } + + @Override + public Dumper methodName(String name, MethodPrototype method, boolean special, boolean defines) { + Entry entry = getMethodEntry(method); + super.methodName(name, method, special, defines); + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + + if (entry != null) { + if (defines) { + index.addDeclaration(token, entry); // override as cfr reuses local vars + } else { + index.addReference(token, entry, contextMethod); + } + } + + return this; + } + + @Override + public Dumper parameterName(String name, Object ref, MethodPrototype method, int index, boolean defines) { + super.parameterName(name, ref, method, index, defines); + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + Entry entry; + + if (defines) { + refs.put(ref, entry = getParameterEntry(method, index, name)); + } else { + entry = refs.get(ref); + } + + if (entry != null) { + if (defines) { + this.index.addDeclaration(token, entry); + } else { + this.index.addReference(token, entry, contextMethod); + } + } + + return this; + } + + @Override + public Dumper variableName(String name, NamedVariable variable, boolean defines) { + // todo catch var declarations in the future + return super.variableName(name, variable, defines); + } + + @Override + public Dumper identifier(String name, Object ref, boolean defines) { + super.identifier(name, ref, defines); + Entry entry; + + if (defines) { + refs.remove(ref); + return this; + } + + if ((entry = refs.get(ref)) == null) { + return this; + } + + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + index.addReference(token, entry, contextMethod); + return this; + } + + @Override + public Dumper fieldName(String name, String descriptor, JavaTypeInstance owner, boolean hiddenDeclaration, boolean isStatic, boolean defines) { + super.fieldName(name, descriptor, owner, hiddenDeclaration, isStatic, defines); + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + + if (descriptor != null) { + Entry entry = getFieldEntry(owner, name, descriptor); + + if (defines) { + index.addDeclaration(token, entry); + } else { + index.addReference(token, entry, contextMethod); + } + } + + return this; + } + + @Override + public Dumper dump(JavaTypeInstance type) { + dumpClass(TypeContext.None, type, false); + return this; + } + + @Override + public Dumper dump(JavaTypeInstance type, boolean defines) { + dumpClass(TypeContext.None, type, defines); + return this; + } + + @Override + public Dumper dump(JavaTypeInstance type, TypeContext context) { + dumpClass(context, type, false); + return this; + } + + private void dumpClass(TypeContext context, JavaTypeInstance type, boolean defines) { + if (type instanceof JavaRefTypeInstance) { + type.dumpInto(this, typeUsage, context); + String name = typeUsage.getName(type, context); // the actually used name, dump will indent + int now = sb.length(); + Token token = new Token(now - name.length(), now, name); + + if (defines) { + index.addDeclaration(token, getClassEntry(type)); + } else { + index.addReference(token, getClassEntry(type), contextMethod); + } + + return; + } + + type.dumpInto(this, typeUsage, context); + } + + /** + * {@inheritDoc} + * + *

    Otherwise the type usage override dumper will not go through the type instance dump + * we have here. + */ + @Override + public Dumper withTypeUsageInformation(TypeUsageInformation innerclassTypeUsageInformation) { + return new EnigmaDumper(this.sb, sourceSettings, innerclassTypeUsageInformation, options, mapper, index, dumperContext); + } + + @Override + public void informBytecodeLoc(HasByteCodeLoc loc) { + Collection methods = loc.getLoc().getMethods(); + + if (!methods.isEmpty()) { + this.contextMethod = getMethodEntry(methods.iterator().next().getMethodPrototype()); + } + } + + public SourceIndex getIndex() { + index.setSource(getString()); + return index; + } + + public String getString() { + return sb.toString(); + } + + private boolean isRecord(JavaTypeInstance javaTypeInstance) { + if (javaTypeInstance instanceof JavaRefTypeInstance) { + ClassFile classFile = ((JavaRefTypeInstance) javaTypeInstance).getClassFile(); + return classFile.getClassSignature().getSuperClass().getRawName().equals("java.lang.Record"); + } + + return false; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java index 2fae61a..dade7c6 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/EntryParser.java @@ -4,6 +4,7 @@ import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; + import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.Signature; @@ -14,36 +15,36 @@ import cuchaz.enigma.translation.representation.entry.FieldDefEntry; import cuchaz.enigma.translation.representation.entry.MethodDefEntry; public class EntryParser { - public static FieldDefEntry parse(FieldDefinition definition) { - ClassEntry owner = parse(definition.getDeclaringType()); - TypeDescriptor descriptor = new TypeDescriptor(definition.getErasedSignature()); - Signature signature = Signature.createTypedSignature(definition.getSignature()); - AccessFlags access = new AccessFlags(definition.getModifiers()); - return new FieldDefEntry(owner, definition.getName(), descriptor, signature, access, null); - } + public static FieldDefEntry parse(FieldDefinition definition) { + ClassEntry owner = parse(definition.getDeclaringType()); + TypeDescriptor descriptor = new TypeDescriptor(definition.getErasedSignature()); + Signature signature = Signature.createTypedSignature(definition.getSignature()); + AccessFlags access = new AccessFlags(definition.getModifiers()); + return new FieldDefEntry(owner, definition.getName(), descriptor, signature, access, null); + } - public static ClassDefEntry parse(TypeDefinition def) { - String name = def.getInternalName(); - Signature signature = Signature.createSignature(def.getSignature()); - AccessFlags access = new AccessFlags(def.getModifiers()); - ClassEntry superClass = def.getBaseType() != null ? parse(def.getBaseType()) : null; - ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(EntryParser::parse).toArray(ClassEntry[]::new); - return new ClassDefEntry(name, signature, access, superClass, interfaces); - } + public static ClassDefEntry parse(TypeDefinition def) { + String name = def.getInternalName(); + Signature signature = Signature.createSignature(def.getSignature()); + AccessFlags access = new AccessFlags(def.getModifiers()); + ClassEntry superClass = def.getBaseType() != null ? parse(def.getBaseType()) : null; + ClassEntry[] interfaces = def.getExplicitInterfaces().stream().map(EntryParser::parse).toArray(ClassEntry[]::new); + return new ClassDefEntry(name, signature, access, superClass, interfaces); + } - public static ClassEntry parse(TypeReference typeReference) { - return new ClassEntry(typeReference.getInternalName()); - } + public static ClassEntry parse(TypeReference typeReference) { + return new ClassEntry(typeReference.getInternalName()); + } - public static MethodDefEntry parse(MethodDefinition definition) { - ClassEntry classEntry = parse(definition.getDeclaringType()); - MethodDescriptor descriptor = new MethodDescriptor(definition.getErasedSignature()); - Signature signature = Signature.createSignature(definition.getSignature()); - AccessFlags access = new AccessFlags(definition.getModifiers()); - return new MethodDefEntry(classEntry, definition.getName(), descriptor, signature, access, null); - } + public static MethodDefEntry parse(MethodDefinition definition) { + ClassEntry classEntry = parse(definition.getDeclaringType()); + MethodDescriptor descriptor = new MethodDescriptor(definition.getErasedSignature()); + Signature signature = Signature.createSignature(definition.getSignature()); + AccessFlags access = new AccessFlags(definition.getModifiers()); + return new MethodDefEntry(classEntry, definition.getName(), descriptor, signature, access, null); + } - public static TypeDescriptor parseTypeDescriptor(TypeReference type) { - return new TypeDescriptor(type.getErasedSignature()); - } + public static TypeDescriptor parseTypeDescriptor(TypeReference type) { + return new TypeDescriptor(type.getErasedSignature()); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java index bc3d072..3ba112f 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonDecompiler.java @@ -11,15 +11,23 @@ import com.strobel.decompiler.languages.java.JavaFormattingOptions; import com.strobel.decompiler.languages.java.ast.AstBuilder; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.objectweb.asm.tree.ClassNode; + import cuchaz.enigma.classprovider.ClassProvider; -import cuchaz.enigma.source.Source; import cuchaz.enigma.source.Decompiler; +import cuchaz.enigma.source.Source; import cuchaz.enigma.source.SourceSettings; -import cuchaz.enigma.source.procyon.transformers.*; +import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform; +import cuchaz.enigma.source.procyon.transformers.DropImportAstTransform; +import cuchaz.enigma.source.procyon.transformers.DropVarModifiersAstTransform; +import cuchaz.enigma.source.procyon.transformers.InvalidIdentifierFix; +import cuchaz.enigma.source.procyon.transformers.Java8Generics; +import cuchaz.enigma.source.procyon.transformers.ObfuscatedEnumSwitchRewriterTransform; +import cuchaz.enigma.source.procyon.transformers.RemoveObjectCasts; +import cuchaz.enigma.source.procyon.transformers.VarargsFixer; import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.utils.AsmUtil; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.objectweb.asm.tree.ClassNode; public class ProcyonDecompiler implements Decompiler { private final SourceSettings settings; @@ -63,6 +71,7 @@ public class ProcyonDecompiler implements Decompiler { @Override public Source getSource(String className, @Nullable EntryRemapper remapper) { TypeReference type = metadataSystem.lookupType(className); + if (type == null) { throw new Error(String.format("Unable to find desc: %s", className)); } @@ -83,8 +92,15 @@ public class ProcyonDecompiler implements Decompiler { new RemoveObjectCasts(context).run(source); new Java8Generics().run(source); new InvalidIdentifierFix().run(source); - if (settings.removeImports) DropImportAstTransform.INSTANCE.run(source); - if (settings.removeVariableFinal) DropVarModifiersAstTransform.INSTANCE.run(source); + + if (settings.removeImports) { + DropImportAstTransform.INSTANCE.run(source); + } + + if (settings.removeVariableFinal) { + DropVarModifiersAstTransform.INSTANCE.run(source); + } + source.acceptVisitor(new InsertParenthesesVisitor(), null); if (remapper != null) { diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java index 5b5b70e..a3f878b 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/ProcyonSource.java @@ -1,49 +1,50 @@ package cuchaz.enigma.source.procyon; +import java.io.StringWriter; + import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; import com.strobel.decompiler.languages.java.JavaOutputVisitor; import com.strobel.decompiler.languages.java.ast.CompilationUnit; + import cuchaz.enigma.source.Source; import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.source.procyon.index.SourceIndexVisitor; import cuchaz.enigma.source.procyon.transformers.AddJavadocsAstTransform; import cuchaz.enigma.translation.mapping.EntryRemapper; -import java.io.StringWriter; - public class ProcyonSource implements Source { - private final DecompilerSettings settings; - private final CompilationUnit tree; - private String string; - - public ProcyonSource(CompilationUnit tree, DecompilerSettings settings) { - this.settings = settings; - this.tree = tree; - } - - @Override - public SourceIndex index() { - SourceIndex index = new SourceIndex(asString()); - tree.acceptVisitor(new SourceIndexVisitor(), index); - return index; - } - - @Override - public String asString() { - if (string == null) { - StringWriter writer = new StringWriter(); - tree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null); - string = writer.toString(); - } - - return string; - } - - @Override - public Source withJavadocs(EntryRemapper remapper) { - CompilationUnit remappedTree = (CompilationUnit) tree.clone(); - new AddJavadocsAstTransform(remapper).run(remappedTree); - return new ProcyonSource(remappedTree, settings); - } + private final DecompilerSettings settings; + private final CompilationUnit tree; + private String string; + + public ProcyonSource(CompilationUnit tree, DecompilerSettings settings) { + this.settings = settings; + this.tree = tree; + } + + @Override + public SourceIndex index() { + SourceIndex index = new SourceIndex(asString()); + tree.acceptVisitor(new SourceIndexVisitor(), index); + return index; + } + + @Override + public String asString() { + if (string == null) { + StringWriter writer = new StringWriter(); + tree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null); + string = writer.toString(); + } + + return string; + } + + @Override + public Source withJavadocs(EntryRemapper remapper) { + CompilationUnit remappedTree = (CompilationUnit) tree.clone(); + new AddJavadocsAstTransform(remapper).run(remappedTree); + return new ProcyonSource(remappedTree, settings); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java index f6eeb15..174431f 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexClassVisitor.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.source.procyon.index; @@ -16,10 +16,22 @@ import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; import com.strobel.decompiler.languages.TextLocation; -import com.strobel.decompiler.languages.java.ast.*; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; + import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.source.procyon.EntryParser; -import cuchaz.enigma.translation.representation.entry.*; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.FieldDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; public class SourceIndexClassVisitor extends SourceIndexVisitor { private ClassDefEntry classEntry; @@ -33,6 +45,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { // is this this class, or a subtype? TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); ClassDefEntry classEntry = EntryParser.parse(def); + if (!classEntry.equals(this.classEntry)) { // it's a subtype, recurse index.addDeclaration(TokenFactory.createToken(index, node.getNameToken()), classEntry); @@ -45,6 +58,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { @Override public Void visitSimpleType(SimpleType node, SourceIndex index) { TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { ClassEntry classEntry = new ClassEntry(ref.getInternalName()); index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.classEntry); @@ -58,10 +72,12 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); MethodDefEntry methodEntry = EntryParser.parse(def); AstNode tokenNode = node.getNameToken(); + if (methodEntry.isConstructor() && methodEntry.getName().equals("")) { // for static initializers, check elsewhere for the token node tokenNode = node.getModifiers().firstOrNullObject(); } + index.addDeclaration(TokenFactory.createToken(index, tokenNode), methodEntry); return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index); } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java index 8fc8e82..12dca73 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexMethodVisitor.java @@ -1,31 +1,56 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.source.procyon.index; +import java.util.HashMap; +import java.util.Map; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; -import com.strobel.assembler.metadata.*; +import com.strobel.assembler.metadata.FieldReference; +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; +import com.strobel.assembler.metadata.ParameterDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.assembler.metadata.VariableDefinition; import com.strobel.decompiler.ast.Variable; import com.strobel.decompiler.languages.TextLocation; -import com.strobel.decompiler.languages.java.ast.*; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.AstNodeCollection; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.IdentifierExpression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.MethodGroupExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.SuperReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ThisReferenceExpression; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.VariableInitializer; + import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.source.procyon.EntryParser; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; - -import java.lang.Error; -import java.util.HashMap; -import java.util.Map; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; public class SourceIndexMethodVisitor extends SourceIndexVisitor { private final MethodDefEntry methodEntry; @@ -45,12 +70,15 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { // get the behavior entry ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); MethodEntry methodEntry = null; + if (ref instanceof MethodReference) { methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature())); } + if (methodEntry != null) { // get the node for the token AstNode tokenNode = null; + if (node.getTarget() instanceof MemberReferenceExpression) { tokenNode = ((MemberReferenceExpression) node.getTarget()).getMemberNameToken(); } else if (node.getTarget() instanceof SuperReferenceExpression) { @@ -58,6 +86,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { } else if (node.getTarget() instanceof ThisReferenceExpression) { tokenNode = node.getTarget(); } + if (tokenNode != null) { index.addReference(TokenFactory.createToken(index, tokenNode), methodEntry, this.methodEntry); } @@ -65,17 +94,18 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { } // Check for identifier - node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression) - .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); + node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression).forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index)); return visitChildren(node, index); } @Override public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref instanceof FieldReference) { // make sure this is actually a field String erasedSignature = ref.getErasedSignature(); + if (erasedSignature.indexOf('(') >= 0) { throw new Error("Expected a field here! got " + ref); } @@ -91,6 +121,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { @Override public Void visitSimpleType(SimpleType node, SourceIndex index) { TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); + if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { ClassEntry classEntry = new ClassEntry(ref.getInternalName()); index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), classEntry, this.methodEntry); @@ -106,6 +137,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { if (parameterIndex >= 0) { MethodDefEntry ownerMethod = methodEntry; + if (def.getMethod() instanceof MethodDefinition) { ownerMethod = EntryParser.parse((MethodDefinition) def.getMethod()); } @@ -124,36 +156,46 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { @Override public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null) { ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); index.addReference(TokenFactory.createToken(index, node.getIdentifierToken()), fieldEntry, this.methodEntry); - } else + } else { this.checkIdentifier(node, index); + } + return visitChildren(node, index); } private void checkIdentifier(IdentifierExpression node, SourceIndex index) { - if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! + if (identifierEntryCache.containsKey(node.getIdentifier())) { + // If it's in the argument cache, create a token! index.addDeclaration(TokenFactory.createToken(index, node.getIdentifierToken()), identifierEntryCache.get(node.getIdentifier())); - else + } else { unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it! + } } private void addDeclarationToUnmatched(String key, SourceIndex index) { Entry entry = identifierEntryCache.get(key); // This cannot happened in theory - if (entry == null) + if (entry == null) { return; - for (Identifier identifier : unmatchedIdentifier.get(key)) + } + + for (Identifier identifier : unmatchedIdentifier.get(key)) { index.addDeclaration(TokenFactory.createToken(index, identifier), entry); + } + unmatchedIdentifier.removeAll(key); } @Override public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); + if (ref != null && node.getType() instanceof SimpleType) { SimpleType simpleTypeNode = (SimpleType) node.getType(); ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); @@ -171,13 +213,17 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { // Single assignation if (variables.size() == 1) { VariableInitializer initializer = variables.firstOrNullObject(); + if (initializer != null && node.getType() instanceof SimpleType) { Identifier identifier = initializer.getNameToken(); Variable variable = initializer.getUserData(Keys.VARIABLE); + if (variable != null) { VariableDefinition originalVariable = variable.getOriginalVariable(); + if (originalVariable != null) { int variableIndex = originalVariable.getSlot(); + if (variableIndex >= 0) { MethodDefEntry ownerMethod = EntryParser.parse(originalVariable.getDeclaringMethod()); TypeDescriptor variableType = EntryParser.parseTypeDescriptor(originalVariable.getVariableType()); @@ -190,6 +236,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { } } } + return visitChildren(node, index); } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java index dad505f..56450c7 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/SourceIndexVisitor.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.source.procyon.index; @@ -16,6 +16,7 @@ import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; import com.strobel.decompiler.languages.java.ast.Keys; import com.strobel.decompiler.languages.java.ast.TypeDeclaration; + import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.source.procyon.EntryParser; import cuchaz.enigma.translation.representation.entry.ClassDefEntry; @@ -35,6 +36,7 @@ public class SourceIndexVisitor extends DepthFirstAstVisitor for (final AstNode child : node.getChildren()) { child.acceptVisitor(this, index); } + return null; } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java index dc36865..6f87895 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/index/TokenFactory.java @@ -1,46 +1,44 @@ package cuchaz.enigma.source.procyon.index; +import java.util.regex.Pattern; + import com.strobel.decompiler.languages.Region; import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.Identifier; import com.strobel.decompiler.languages.java.ast.TypeDeclaration; -import cuchaz.enigma.source.Token; -import cuchaz.enigma.source.SourceIndex; -import java.util.regex.Pattern; +import cuchaz.enigma.source.SourceIndex; +import cuchaz.enigma.source.Token; public class TokenFactory { - private static final Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); - - public static Token createToken(SourceIndex index, AstNode node) { - String name = node instanceof Identifier ? ((Identifier) node).getName() : ""; - Region region = node.getRegion(); - - if (region.getBeginLine() == 0) { - System.err.println("Got bad region from Procyon for node " + node); - return null; - } - - int start = index.getPosition(region.getBeginLine(), region.getBeginColumn()); - int end = index.getPosition(region.getEndLine(), region.getEndColumn()); - String text = index.getSource().substring(start, end); - Token token = new Token(start, end, text); - - boolean isAnonymousInner = - node instanceof Identifier && - name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && - name.lastIndexOf('$') >= 0 && - !ANONYMOUS_INNER.matcher(name).matches(); - - if (isAnonymousInner) { - TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; - if (type != null) { - name = type.getName(); - token.end = token.start + name.length(); - } - } - - return token; - } + private static final Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); + + public static Token createToken(SourceIndex index, AstNode node) { + String name = node instanceof Identifier ? ((Identifier) node).getName() : ""; + Region region = node.getRegion(); + + if (region.getBeginLine() == 0) { + System.err.println("Got bad region from Procyon for node " + node); + return null; + } + + int start = index.getPosition(region.getBeginLine(), region.getBeginColumn()); + int end = index.getPosition(region.getEndLine(), region.getEndColumn()); + String text = index.getSource().substring(start, end); + Token token = new Token(start, end, text); + + boolean isAnonymousInner = node instanceof Identifier && name.indexOf('$') >= 0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches(); + + if (isAnonymousInner) { + TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; + + if (type != null) { + name = type.getName(); + token.end = token.start + name.length(); + } + } + + return token; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java index 1e5beb1..c48aba5 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java @@ -1,24 +1,37 @@ package cuchaz.enigma.source.procyon.transformers; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + import com.google.common.base.Function; import com.google.common.base.Strings; import com.strobel.assembler.metadata.ParameterDefinition; -import com.strobel.decompiler.languages.java.ast.*; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.Comment; +import com.strobel.decompiler.languages.java.ast.CommentType; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; +import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; +import com.strobel.decompiler.languages.java.ast.EnumValueDeclaration; +import com.strobel.decompiler.languages.java.ast.FieldDeclaration; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MethodDeclaration; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.Roles; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; + import cuchaz.enigma.source.procyon.EntryParser; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.translation.mapping.ResolutionStrategy; -import cuchaz.enigma.translation.representation.entry.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry; +import cuchaz.enigma.translation.representation.entry.MethodDefEntry; public final class AddJavadocsAstTransform implements IAstTransform { - private final EntryRemapper remapper; public AddJavadocsAstTransform(EntryRemapper remapper) { @@ -31,7 +44,6 @@ public final class AddJavadocsAstTransform implements IAstTransform { } static class Visitor extends DepthFirstAstVisitor { - private final EntryRemapper remapper; Visitor(EntryRemapper remapper) { @@ -40,6 +52,7 @@ public final class AddJavadocsAstTransform implements IAstTransform { private void addDoc(T node, Function> retriever) { final Comment[] comments = getComments(node, retriever); + if (comments != null) { node.insertChildrenBefore(node.getFirstChild(), Roles.COMMENT, comments); } @@ -48,22 +61,24 @@ public final class AddJavadocsAstTransform implements IAstTransform { private Comment[] getComments(T node, Function> retriever) { final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node)); final String docs = Strings.emptyToNull(mapping.javadoc()); - return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st, - CommentType.Documentation)).toArray(Comment[]::new); + return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st, CommentType.Documentation)).toArray(Comment[]::new); } private Comment[] getParameterComments(ParameterDeclaration node, Function> retriever) { Entry entry = retriever.apply(node); final EntryMapping mapping = remapper.getDeobfMapping(entry); final Comment[] ret = getComments(node, retriever); + if (ret != null) { final String paramPrefix = "@param " + (mapping.targetName() != null ? mapping.targetName() : entry.getName()) + " "; final String indent = Strings.repeat(" ", paramPrefix.length()); ret[0].setContent(paramPrefix + ret[0].getContent()); + for (int i = 1; i < ret.length; i++) { ret[i].setContent(indent + ret[i].getContent()); } } + return ret; } @@ -71,23 +86,27 @@ public final class AddJavadocsAstTransform implements IAstTransform { final MethodDefEntry methodDefEntry = EntryParser.parse(node.getUserData(Keys.METHOD_DEFINITION)); final Comment[] baseComments = getComments(node, $ -> methodDefEntry); List comments = new ArrayList<>(); - if (baseComments != null) + + if (baseComments != null) { Collections.addAll(comments, baseComments); + } for (ParameterDeclaration dec : node.getChildrenByRole(Roles.PARAMETER)) { ParameterDefinition def = dec.getUserData(Keys.PARAMETER_DEFINITION); - final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(), - true, - EntryParser.parseTypeDescriptor(def.getParameterType()), null)); - if (paramComments != null) + final Comment[] paramComments = getParameterComments(dec, $ -> new LocalVariableDefEntry(methodDefEntry, def.getSlot(), def.getName(), true, EntryParser.parseTypeDescriptor(def.getParameterType()), null)); + + if (paramComments != null) { Collections.addAll(comments, paramComments); + } } if (!comments.isEmpty()) { if (remapper.getObfResolver().resolveEntry(methodDefEntry, ResolutionStrategy.RESOLVE_ROOT).stream().noneMatch(e -> Objects.equals(e, methodDefEntry))) { comments.add(0, new Comment("{@inheritDoc}", CommentType.Documentation)); } + final AstNode oldFirst = node.getFirstChild(); + for (Comment comment : comments) { node.insertChildBefore(oldFirst, comment, Roles.COMMENT); } @@ -99,6 +118,7 @@ public final class AddJavadocsAstTransform implements IAstTransform { for (final AstNode child : node.getChildren()) { child.acceptVisitor(this, data); } + return null; } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java index b8c087b..defd251 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/DropVarModifiersAstTransform.java @@ -1,10 +1,15 @@ package cuchaz.enigma.source.procyon.transformers; -import com.strobel.decompiler.languages.java.ast.*; -import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; - import javax.lang.model.element.Modifier; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; +import com.strobel.decompiler.languages.java.ast.EntityDeclaration; +import com.strobel.decompiler.languages.java.ast.JavaModifierToken; +import com.strobel.decompiler.languages.java.ast.ParameterDeclaration; +import com.strobel.decompiler.languages.java.ast.VariableDeclarationStatement; +import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; + public final class DropVarModifiersAstTransform implements IAstTransform { public static final DropVarModifiersAstTransform INSTANCE = new DropVarModifiersAstTransform(); diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java index 34d95fa..bc7d5a2 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/InvalidIdentifierFix.java @@ -14,15 +14,17 @@ public class InvalidIdentifierFix implements IAstTransform { compilationUnit.acceptVisitor(new Visitor(), null); } - class Visitor extends DepthFirstAstVisitor{ + class Visitor extends DepthFirstAstVisitor { @Override public Void visitIdentifier(Identifier node, Void data) { super.visitIdentifier(node, data); - if (node.getName().equals("do") || node.getName().equals("if")){ + + if (node.getName().equals("do") || node.getName().equals("if")) { Identifier newIdentifier = Identifier.create(node.getName() + "_", node.getStartLocation()); newIdentifier.copyUserDataFrom(node); node.replaceWith(newIdentifier); } + return null; } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java index 8accfc7..3edc06c 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/Java8Generics.java @@ -1,66 +1,62 @@ package cuchaz.enigma.source.procyon.transformers; -import com.strobel.assembler.metadata.BuiltinTypes; import com.strobel.assembler.metadata.CommonTypeReferences; -import com.strobel.assembler.metadata.Flags; import com.strobel.assembler.metadata.IGenericInstance; -import com.strobel.assembler.metadata.IMemberDefinition; -import com.strobel.assembler.metadata.JvmType; -import com.strobel.assembler.metadata.MemberReference; -import com.strobel.assembler.metadata.MethodDefinition; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; -import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.AstNodeCollection; import com.strobel.decompiler.languages.java.ast.AstType; import com.strobel.decompiler.languages.java.ast.CastExpression; -import com.strobel.decompiler.languages.java.ast.ComposedType; import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; import com.strobel.decompiler.languages.java.ast.Expression; import com.strobel.decompiler.languages.java.ast.Identifier; import com.strobel.decompiler.languages.java.ast.InvocationExpression; -import com.strobel.decompiler.languages.java.ast.Keys; import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; import com.strobel.decompiler.languages.java.ast.Roles; import com.strobel.decompiler.languages.java.ast.SimpleType; -import com.strobel.decompiler.languages.java.ast.WildcardType; import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; /** * Created by Thiakil on 12/07/2018. */ public class Java8Generics implements IAstTransform { - @Override public void run(AstNode compilationUnit) { compilationUnit.acceptVisitor(new Visitor(), null); } - static class Visitor extends DepthFirstAstVisitor{ - + static class Visitor extends DepthFirstAstVisitor { @Override public Void visitInvocationExpression(InvocationExpression node, Void data) { super.visitInvocationExpression(node, data); - if (node.getTarget() instanceof MemberReferenceExpression){ + + if (node.getTarget() instanceof MemberReferenceExpression) { MemberReferenceExpression referenceExpression = (MemberReferenceExpression) node.getTarget(); - if (referenceExpression.getTypeArguments().stream().map(t->{ + + if (referenceExpression.getTypeArguments().stream().map(t -> { TypeReference tr = t.toTypeReference(); - if (tr.getDeclaringType() != null){//ensure that inner types are resolved so we can get the TypeDefinition below + + //ensure that inner types are resolved so we can get the TypeDefinition below + if (tr.getDeclaringType() != null) { TypeReference resolved = tr.resolve(); - if (resolved != null) + + if (resolved != null) { return resolved; + } } + return tr; }).anyMatch(t -> t.isWildcardType() || (t instanceof TypeDefinition && ((TypeDefinition) t).isAnonymous()))) { //these are invalid for invocations, let the compiler work it out referenceExpression.getTypeArguments().clear(); - } else if (referenceExpression.getTypeArguments().stream().allMatch(t->t.toTypeReference().equals(CommonTypeReferences.Object))){ + } else if (referenceExpression.getTypeArguments().stream().allMatch(t -> t.toTypeReference().equals(CommonTypeReferences.Object))) { //all are , thereby redundant and/or bad referenceExpression.getTypeArguments().clear(); } } + return null; } @@ -68,14 +64,17 @@ public class Java8Generics implements IAstTransform { public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) { super.visitObjectCreationExpression(node, data); AstType type = node.getType(); - if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()){ + + if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()) { SimpleType simpleType = (SimpleType) type; AstNodeCollection typeArguments = simpleType.getTypeArguments(); - if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)){ + + if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)) { //all are , thereby redundant and/or bad typeArguments.firstOrNullObject().getChildByRole(Roles.IDENTIFIER).replaceWith(Identifier.create("")); } } + return null; } @@ -83,24 +82,30 @@ public class Java8Generics implements IAstTransform { public Void visitCastExpression(CastExpression node, Void data) { boolean doReplace = false; TypeReference typeReference = node.getType().toTypeReference(); - if (typeReference.isArray() && typeReference.getElementType().isGenericType()){ + + if (typeReference.isArray() && typeReference.getElementType().isGenericType()) { doReplace = true; } else if (typeReference.isGenericType()) { Expression target = node.getExpression(); - if (typeReference instanceof IGenericInstance && ((IGenericInstance)typeReference).getTypeArguments().stream().anyMatch(t->t.isWildcardType())){ + + if (typeReference instanceof IGenericInstance && ((IGenericInstance) typeReference).getTypeArguments().stream().anyMatch(t -> t.isWildcardType())) { doReplace = true; } else if (target instanceof InvocationExpression) { - InvocationExpression invocationExpression = (InvocationExpression)target; + InvocationExpression invocationExpression = (InvocationExpression) target; + if (invocationExpression.getTarget() instanceof MemberReferenceExpression && !((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().isEmpty()) { ((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().clear(); doReplace = true; } } } + super.visitCastExpression(node, data); - if (doReplace){ + + if (doReplace) { node.replaceWith(node.getExpression()); } + return null; } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java index 32bb72f..204351e 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/ObfuscatedEnumSwitchRewriterTransform.java @@ -17,6 +17,12 @@ package cuchaz.enigma.source.procyon.transformers; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + import com.strobel.assembler.metadata.BuiltinTypes; import com.strobel.assembler.metadata.FieldDefinition; import com.strobel.assembler.metadata.MethodDefinition; @@ -43,372 +49,356 @@ import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression; import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; -import java.util.ArrayList; -import java.util.IdentityHashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - /** - * Copy of {@link com.strobel.decompiler.languages.java.ast.transforms.EnumSwitchRewriterTransform} modified to: + * Copy of {@link com.strobel.decompiler.languages.java.ast.transforms.EnumSwitchRewriterTransform} modified to. * - Not rely on a field containing "$SwitchMap$" (Proguard strips it) * - Ignore classes *with* SwitchMap$ names (so the original can handle it) * - Ignores inner synthetics that are not package private */ @SuppressWarnings("Duplicates") public class ObfuscatedEnumSwitchRewriterTransform implements IAstTransform { - private final DecompilerContext _context; + private final DecompilerContext _context; - public ObfuscatedEnumSwitchRewriterTransform(final DecompilerContext context) { - _context = VerifyArgument.notNull(context, "context"); - } + public ObfuscatedEnumSwitchRewriterTransform(final DecompilerContext context) { + _context = VerifyArgument.notNull(context, "context"); + } - @Override - public void run(final AstNode compilationUnit) { - compilationUnit.acceptVisitor(new Visitor(_context), null); - } + @Override + public void run(final AstNode compilationUnit) { + compilationUnit.acceptVisitor(new Visitor(_context), null); + } - private final static class Visitor extends ContextTrackingVisitor { - private final static class SwitchMapInfo { - final String enclosingType; - final Map> switches = new LinkedHashMap<>(); - final Map> mappings = new LinkedHashMap<>(); + private static final class Visitor extends ContextTrackingVisitor { + private static final class SwitchMapInfo { + final String enclosingType; + final Map> switches = new LinkedHashMap<>(); + final Map> mappings = new LinkedHashMap<>(); - TypeDeclaration enclosingTypeDeclaration; + TypeDeclaration enclosingTypeDeclaration; - SwitchMapInfo(final String enclosingType) { - this.enclosingType = enclosingType; - } - } + SwitchMapInfo(final String enclosingType) { + this.enclosingType = enclosingType; + } + } - private final Map _switchMaps = new LinkedHashMap<>(); - private boolean _isSwitchMapWrapper; + private final Map _switchMaps = new LinkedHashMap<>(); + private boolean _isSwitchMapWrapper; - protected Visitor(final DecompilerContext context) { - super(context); - } + protected Visitor(final DecompilerContext context) { + super(context); + } - @Override - public Void visitTypeDeclaration(final TypeDeclaration typeDeclaration, final Void p) { - final boolean oldIsSwitchMapWrapper = _isSwitchMapWrapper; - final TypeDefinition typeDefinition = typeDeclaration.getUserData(Keys.TYPE_DEFINITION); - final boolean isSwitchMapWrapper = isSwitchMapWrapper(typeDefinition); + @Override + public Void visitTypeDeclaration(final TypeDeclaration typeDeclaration, final Void p) { + final boolean oldIsSwitchMapWrapper = _isSwitchMapWrapper; + final TypeDefinition typeDefinition = typeDeclaration.getUserData(Keys.TYPE_DEFINITION); + final boolean isSwitchMapWrapper = isSwitchMapWrapper(typeDefinition); - if (isSwitchMapWrapper) { - final String internalName = typeDefinition.getInternalName(); + if (isSwitchMapWrapper) { + final String internalName = typeDefinition.getInternalName(); - SwitchMapInfo info = _switchMaps.get(internalName); + SwitchMapInfo info = _switchMaps.get(internalName); - if (info == null) { - _switchMaps.put(internalName, info = new SwitchMapInfo(internalName)); - } + if (info == null) { + _switchMaps.put(internalName, info = new SwitchMapInfo(internalName)); + } - info.enclosingTypeDeclaration = typeDeclaration; - } + info.enclosingTypeDeclaration = typeDeclaration; + } - _isSwitchMapWrapper = isSwitchMapWrapper; + _isSwitchMapWrapper = isSwitchMapWrapper; - try { - super.visitTypeDeclaration(typeDeclaration, p); - } - finally { - _isSwitchMapWrapper = oldIsSwitchMapWrapper; - } + try { + super.visitTypeDeclaration(typeDeclaration, p); + } finally { + _isSwitchMapWrapper = oldIsSwitchMapWrapper; + } - rewrite(); + rewrite(); - return null; - } + return null; + } - @Override - public Void visitSwitchStatement(final SwitchStatement node, final Void data) { - final Expression test = node.getExpression(); + @Override + public Void visitSwitchStatement(final SwitchStatement node, final Void data) { + final Expression test = node.getExpression(); - if (test instanceof IndexerExpression) { - final IndexerExpression indexer = (IndexerExpression) test; - final Expression array = indexer.getTarget(); - final Expression argument = indexer.getArgument(); + if (test instanceof IndexerExpression) { + final IndexerExpression indexer = (IndexerExpression) test; + final Expression array = indexer.getTarget(); + final Expression argument = indexer.getArgument(); - if (!(array instanceof MemberReferenceExpression)) { - return super.visitSwitchStatement(node, data); - } + if (!(array instanceof MemberReferenceExpression)) { + return super.visitSwitchStatement(node, data); + } - final MemberReferenceExpression arrayAccess = (MemberReferenceExpression) array; - final Expression arrayOwner = arrayAccess.getTarget(); - final String mapName = arrayAccess.getMemberName(); + final MemberReferenceExpression arrayAccess = (MemberReferenceExpression) array; + final Expression arrayOwner = arrayAccess.getTarget(); + final String mapName = arrayAccess.getMemberName(); - if (mapName == null || mapName.startsWith("$SwitchMap$") || !(arrayOwner instanceof TypeReferenceExpression)) { - return super.visitSwitchStatement(node, data); - } + if (mapName == null || mapName.startsWith("$SwitchMap$") || !(arrayOwner instanceof TypeReferenceExpression)) { + return super.visitSwitchStatement(node, data); + } - final TypeReferenceExpression enclosingTypeExpression = (TypeReferenceExpression) arrayOwner; - final TypeReference enclosingType = enclosingTypeExpression.getType().getUserData(Keys.TYPE_REFERENCE); + final TypeReferenceExpression enclosingTypeExpression = (TypeReferenceExpression) arrayOwner; + final TypeReference enclosingType = enclosingTypeExpression.getType().getUserData(Keys.TYPE_REFERENCE); - if (!isSwitchMapWrapper(enclosingType) || !(argument instanceof InvocationExpression)) { - return super.visitSwitchStatement(node, data); - } + if (!isSwitchMapWrapper(enclosingType) || !(argument instanceof InvocationExpression)) { + return super.visitSwitchStatement(node, data); + } - final InvocationExpression invocation = (InvocationExpression) argument; - final Expression invocationTarget = invocation.getTarget(); + final InvocationExpression invocation = (InvocationExpression) argument; + final Expression invocationTarget = invocation.getTarget(); - if (!(invocationTarget instanceof MemberReferenceExpression)) { - return super.visitSwitchStatement(node, data); - } + if (!(invocationTarget instanceof MemberReferenceExpression)) { + return super.visitSwitchStatement(node, data); + } - final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget; + final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget; - if (!"ordinal".equals(memberReference.getMemberName())) { - return super.visitSwitchStatement(node, data); - } + if (!"ordinal".equals(memberReference.getMemberName())) { + return super.visitSwitchStatement(node, data); + } - final String enclosingTypeName = enclosingType.getInternalName(); + final String enclosingTypeName = enclosingType.getInternalName(); - SwitchMapInfo info = _switchMaps.get(enclosingTypeName); + SwitchMapInfo info = _switchMaps.get(enclosingTypeName); - if (info == null) { - _switchMaps.put(enclosingTypeName, info = new SwitchMapInfo(enclosingTypeName)); + if (info == null) { + _switchMaps.put(enclosingTypeName, info = new SwitchMapInfo(enclosingTypeName)); - final TypeDefinition resolvedType = enclosingType.resolve(); + final TypeDefinition resolvedType = enclosingType.resolve(); - if (resolvedType != null) { - AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER); + if (resolvedType != null) { + AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER); - if (astBuilder == null) { - astBuilder = new AstBuilder(context); - } + if (astBuilder == null) { + astBuilder = new AstBuilder(context); + } - try (final SafeCloseable importSuppression = astBuilder.suppressImports()) { - final TypeDeclaration declaration = astBuilder.createType(resolvedType); + try (SafeCloseable importSuppression = astBuilder.suppressImports()) { + final TypeDeclaration declaration = astBuilder.createType(resolvedType); - declaration.acceptVisitor(this, data); - } - } - } + declaration.acceptVisitor(this, data); + } + } + } - List switches = info.switches.get(mapName); + List switches = info.switches.get(mapName); - if (switches == null) { - info.switches.put(mapName, switches = new ArrayList<>()); - } + if (switches == null) { + info.switches.put(mapName, switches = new ArrayList<>()); + } - switches.add(node); - } + switches.add(node); + } - return super.visitSwitchStatement(node, data); - } + return super.visitSwitchStatement(node, data); + } - @Override - public Void visitAssignmentExpression(final AssignmentExpression node, final Void data) { - final TypeDefinition currentType = context.getCurrentType(); - final MethodDefinition currentMethod = context.getCurrentMethod(); + @Override + public Void visitAssignmentExpression(final AssignmentExpression node, final Void data) { + final TypeDefinition currentType = context.getCurrentType(); + final MethodDefinition currentMethod = context.getCurrentMethod(); - if (_isSwitchMapWrapper && - currentType != null && - currentMethod != null && - currentMethod.isTypeInitializer()) { + if (_isSwitchMapWrapper && currentType != null && currentMethod != null && currentMethod.isTypeInitializer()) { + final Expression left = node.getLeft(); + final Expression right = node.getRight(); - final Expression left = node.getLeft(); - final Expression right = node.getRight(); + if (left instanceof IndexerExpression && right instanceof PrimitiveExpression) { + String mapName = null; - if (left instanceof IndexerExpression && - right instanceof PrimitiveExpression) { + final Expression array = ((IndexerExpression) left).getTarget(); + final Expression argument = ((IndexerExpression) left).getArgument(); - String mapName = null; + if (array instanceof MemberReferenceExpression) { + mapName = ((MemberReferenceExpression) array).getMemberName(); + } else if (array instanceof IdentifierExpression) { + mapName = ((IdentifierExpression) array).getIdentifier(); + } - final Expression array = ((IndexerExpression) left).getTarget(); - final Expression argument = ((IndexerExpression) left).getArgument(); + if (mapName == null || mapName.startsWith("$SwitchMap$")) { + return super.visitAssignmentExpression(node, data); + } - if (array instanceof MemberReferenceExpression) { - mapName = ((MemberReferenceExpression) array).getMemberName(); - } - else if (array instanceof IdentifierExpression) { - mapName = ((IdentifierExpression) array).getIdentifier(); - } + if (!(argument instanceof InvocationExpression)) { + return super.visitAssignmentExpression(node, data); + } - if (mapName == null || mapName.startsWith("$SwitchMap$")) { - return super.visitAssignmentExpression(node, data); - } + final InvocationExpression invocation = (InvocationExpression) argument; + final Expression invocationTarget = invocation.getTarget(); - if (!(argument instanceof InvocationExpression)) { - return super.visitAssignmentExpression(node, data); - } + if (!(invocationTarget instanceof MemberReferenceExpression)) { + return super.visitAssignmentExpression(node, data); + } - final InvocationExpression invocation = (InvocationExpression) argument; - final Expression invocationTarget = invocation.getTarget(); + final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget; + final Expression memberTarget = memberReference.getTarget(); - if (!(invocationTarget instanceof MemberReferenceExpression)) { - return super.visitAssignmentExpression(node, data); - } + if (!(memberTarget instanceof MemberReferenceExpression) || !"ordinal".equals(memberReference.getMemberName())) { + return super.visitAssignmentExpression(node, data); + } - final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget; - final Expression memberTarget = memberReference.getTarget(); + final MemberReferenceExpression outerMemberReference = (MemberReferenceExpression) memberTarget; + final Expression outerMemberTarget = outerMemberReference.getTarget(); - if (!(memberTarget instanceof MemberReferenceExpression) || !"ordinal".equals(memberReference.getMemberName())) { - return super.visitAssignmentExpression(node, data); - } + if (!(outerMemberTarget instanceof TypeReferenceExpression)) { + return super.visitAssignmentExpression(node, data); + } - final MemberReferenceExpression outerMemberReference = (MemberReferenceExpression) memberTarget; - final Expression outerMemberTarget = outerMemberReference.getTarget(); + final String enclosingType = currentType.getInternalName(); - if (!(outerMemberTarget instanceof TypeReferenceExpression)) { - return super.visitAssignmentExpression(node, data); - } + SwitchMapInfo info = _switchMaps.get(enclosingType); - final String enclosingType = currentType.getInternalName(); + if (info == null) { + _switchMaps.put(enclosingType, info = new SwitchMapInfo(enclosingType)); - SwitchMapInfo info = _switchMaps.get(enclosingType); + AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER); - if (info == null) { - _switchMaps.put(enclosingType, info = new SwitchMapInfo(enclosingType)); + if (astBuilder == null) { + astBuilder = new AstBuilder(context); + } - AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER); + info.enclosingTypeDeclaration = astBuilder.createType(currentType); + } - if (astBuilder == null) { - astBuilder = new AstBuilder(context); - } + final PrimitiveExpression value = (PrimitiveExpression) right; - info.enclosingTypeDeclaration = astBuilder.createType(currentType); - } + assert value.getValue() instanceof Integer; - final PrimitiveExpression value = (PrimitiveExpression) right; + Map mapping = info.mappings.get(mapName); - assert value.getValue() instanceof Integer; + if (mapping == null) { + info.mappings.put(mapName, mapping = new LinkedHashMap<>()); + } - Map mapping = info.mappings.get(mapName); + final IdentifierExpression enumValue = new IdentifierExpression(Expression.MYSTERY_OFFSET, outerMemberReference.getMemberName()); - if (mapping == null) { - info.mappings.put(mapName, mapping = new LinkedHashMap<>()); - } + enumValue.putUserData(Keys.MEMBER_REFERENCE, outerMemberReference.getUserData(Keys.MEMBER_REFERENCE)); - final IdentifierExpression enumValue = new IdentifierExpression( Expression.MYSTERY_OFFSET, outerMemberReference.getMemberName()); + mapping.put(((Number) value.getValue()).intValue(), enumValue); + } + } - enumValue.putUserData(Keys.MEMBER_REFERENCE, outerMemberReference.getUserData(Keys.MEMBER_REFERENCE)); + return super.visitAssignmentExpression(node, data); + } - mapping.put(((Number) value.getValue()).intValue(), enumValue); - } - } + private void rewrite() { + if (_switchMaps.isEmpty()) { + return; + } - return super.visitAssignmentExpression(node, data); - } + for (final SwitchMapInfo info : _switchMaps.values()) { + rewrite(info); + } - private void rewrite() { - if (_switchMaps.isEmpty()) { - return; - } + // + // Remove switch map type wrappers that are no longer referenced. + // - for (final SwitchMapInfo info : _switchMaps.values()) { - rewrite(info); - } + outer: - // - // Remove switch map type wrappers that are no longer referenced. - // + for (final SwitchMapInfo info : _switchMaps.values()) { + for (final String mapName : info.switches.keySet()) { + final List switches = info.switches.get(mapName); - outer: - for (final SwitchMapInfo info : _switchMaps.values()) { - for (final String mapName : info.switches.keySet()) { - final List switches = info.switches.get(mapName); + if (switches != null && !switches.isEmpty()) { + continue outer; + } + } - if (switches != null && !switches.isEmpty()) { - continue outer; - } - } + final TypeDeclaration enclosingTypeDeclaration = info.enclosingTypeDeclaration; - final TypeDeclaration enclosingTypeDeclaration = info.enclosingTypeDeclaration; + if (enclosingTypeDeclaration != null) { + enclosingTypeDeclaration.remove(); + } + } + } - if (enclosingTypeDeclaration != null) { - enclosingTypeDeclaration.remove(); - } - } - } + private void rewrite(final SwitchMapInfo info) { + if (info.switches.isEmpty()) { + return; + } - private void rewrite(final SwitchMapInfo info) { - if (info.switches.isEmpty()) { - return; - } + for (final String mapName : info.switches.keySet()) { + final List switches = info.switches.get(mapName); + final Map mappings = info.mappings.get(mapName); - for (final String mapName : info.switches.keySet()) { - final List switches = info.switches.get(mapName); - final Map mappings = info.mappings.get(mapName); + if (switches != null && mappings != null) { + for (int i = 0; i < switches.size(); i++) { + if (rewriteSwitch(switches.get(i), mappings)) { + switches.remove(i--); + } + } + } + } + } - if (switches != null && mappings != null) { - for (int i = 0; i < switches.size(); i++) { - if (rewriteSwitch(switches.get(i), mappings)) { - switches.remove(i--); - } - } - } - } - } + private boolean rewriteSwitch(final SwitchStatement s, final Map mappings) { + final Map replacements = new IdentityHashMap<>(); - private boolean rewriteSwitch(final SwitchStatement s, final Map mappings) { - final Map replacements = new IdentityHashMap<>(); + for (final SwitchSection section : s.getSwitchSections()) { + for (final CaseLabel caseLabel : section.getCaseLabels()) { + final Expression expression = caseLabel.getExpression(); - for (final SwitchSection section : s.getSwitchSections()) { - for (final CaseLabel caseLabel : section.getCaseLabels()) { - final Expression expression = caseLabel.getExpression(); + if (expression.isNull()) { + continue; + } - if (expression.isNull()) { - continue; - } + if (expression instanceof PrimitiveExpression) { + final Object value = ((PrimitiveExpression) expression).getValue(); - if (expression instanceof PrimitiveExpression) { - final Object value = ((PrimitiveExpression) expression).getValue(); + if (value instanceof Integer) { + final Expression replacement = mappings.get(value); - if (value instanceof Integer) { - final Expression replacement = mappings.get(value); + if (replacement != null) { + replacements.put(expression, replacement); + continue; + } + } + } - if (replacement != null) { - replacements.put(expression, replacement); - continue; - } - } - } + // + // If we can't rewrite all cases, we abort. + // - // - // If we can't rewrite all cases, we abort. - // + return false; + } + } - return false; - } - } - - final IndexerExpression indexer = (IndexerExpression) s.getExpression(); - final InvocationExpression argument = (InvocationExpression) indexer.getArgument(); - final MemberReferenceExpression memberReference = (MemberReferenceExpression) argument.getTarget(); - final Expression newTest = memberReference.getTarget(); - - newTest.remove(); - indexer.replaceWith(newTest); - - for (final Map.Entry entry : replacements.entrySet()) { - entry.getKey().replaceWith(entry.getValue().clone()); - } - - return true; - } - - private static boolean isSwitchMapWrapper(final TypeReference type) { - if (type == null) { - return false; - } - - final TypeDefinition definition = type instanceof TypeDefinition ? (TypeDefinition) type - : type.resolve(); - - if (definition == null || !definition.isSynthetic() || !definition.isInnerClass() || !definition.isPackagePrivate()) { - return false; - } - - for (final FieldDefinition field : definition.getDeclaredFields()) { - if (!field.getName().startsWith("$SwitchMap$") && - BuiltinTypes.Integer.makeArrayType().equals(field.getFieldType())) { - - return true; - } - } - - return false; - } - } -} \ No newline at end of file + final IndexerExpression indexer = (IndexerExpression) s.getExpression(); + final InvocationExpression argument = (InvocationExpression) indexer.getArgument(); + final MemberReferenceExpression memberReference = (MemberReferenceExpression) argument.getTarget(); + final Expression newTest = memberReference.getTarget(); + + newTest.remove(); + indexer.replaceWith(newTest); + + for (final Map.Entry entry : replacements.entrySet()) { + entry.getKey().replaceWith(entry.getValue().clone()); + } + + return true; + } + + private static boolean isSwitchMapWrapper(final TypeReference type) { + if (type == null) { + return false; + } + + final TypeDefinition definition = type instanceof TypeDefinition ? (TypeDefinition) type : type.resolve(); + + if (definition == null || !definition.isSynthetic() || !definition.isInnerClass() || !definition.isPackagePrivate()) { + return false; + } + + for (final FieldDefinition field : definition.getDeclaredFields()) { + if (!field.getName().startsWith("$SwitchMap$") && BuiltinTypes.Integer.makeArrayType().equals(field.getFieldType())) { + return true; + } + } + + return false; + } + } +} diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java index cf0376f..679b168 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/RemoveObjectCasts.java @@ -22,17 +22,17 @@ public class RemoveObjectCasts implements IAstTransform { compilationUnit.acceptVisitor(new Visitor(_context), null); } - private final static class Visitor extends ContextTrackingVisitor{ - + private static final class Visitor extends ContextTrackingVisitor { protected Visitor(DecompilerContext context) { super(context); } @Override public Void visitCastExpression(CastExpression node, Void data) { - if (node.getType().toTypeReference().equals(BuiltinTypes.Object)){ + if (node.getType().toTypeReference().equals(BuiltinTypes.Object)) { node.replaceWith(node.getExpression()); } + return super.visitCastExpression(node, data); } } diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java index d3ddaab..234834b 100644 --- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java +++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/VarargsFixer.java @@ -1,5 +1,8 @@ package cuchaz.enigma.source.procyon.transformers; +import java.util.ArrayList; +import java.util.List; + import com.strobel.assembler.metadata.MemberReference; import com.strobel.assembler.metadata.MetadataFilters; import com.strobel.assembler.metadata.MetadataHelper; @@ -16,7 +19,6 @@ import com.strobel.decompiler.languages.java.ast.AstNode; import com.strobel.decompiler.languages.java.ast.AstNodeCollection; import com.strobel.decompiler.languages.java.ast.CastExpression; import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; -import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; import com.strobel.decompiler.languages.java.ast.Expression; import com.strobel.decompiler.languages.java.ast.InvocationExpression; import com.strobel.decompiler.languages.java.ast.JavaResolver; @@ -26,9 +28,6 @@ import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; import com.strobel.decompiler.semantics.ResolveResult; -import java.util.ArrayList; -import java.util.List; - /** * Created by Thiakil on 12/07/2018. */ @@ -46,6 +45,7 @@ public class VarargsFixer implements IAstTransform { class Visitor extends ContextTrackingVisitor { private final JavaResolver _resolver; + protected Visitor(DecompilerContext context) { super(context); _resolver = new JavaResolver(context); @@ -56,21 +56,26 @@ public class VarargsFixer implements IAstTransform { public Void visitInvocationExpression(InvocationExpression node, Void data) { super.visitInvocationExpression(node, data); MemberReference definition = node.getUserData(Keys.MEMBER_REFERENCE); - if (definition instanceof MethodDefinition && ((MethodDefinition) definition).isVarArgs()){ + + if (definition instanceof MethodDefinition && ((MethodDefinition) definition).isVarArgs()) { AstNodeCollection arguments = node.getArguments(); Expression lastParam = arguments.lastOrNullObject(); - if (!lastParam.isNull() && lastParam instanceof ArrayCreationExpression){ - ArrayCreationExpression varargArray = (ArrayCreationExpression)lastParam; - if (varargArray.getInitializer().isNull() || varargArray.getInitializer().getElements().isEmpty()){ + + if (!lastParam.isNull() && lastParam instanceof ArrayCreationExpression) { + ArrayCreationExpression varargArray = (ArrayCreationExpression) lastParam; + + if (varargArray.getInitializer().isNull() || varargArray.getInitializer().getElements().isEmpty()) { lastParam.remove(); } else { - for (Expression e : varargArray.getInitializer().getElements()){ + for (Expression e : varargArray.getInitializer().getElements()) { arguments.insertBefore(varargArray, e.clone()); } + varargArray.remove(); } } } + return null; } @@ -83,14 +88,11 @@ public class VarargsFixer implements IAstTransform { Expression arrayArg = lastArgument; - if (arrayArg instanceof CastExpression) + if (arrayArg instanceof CastExpression) { arrayArg = ((CastExpression) arrayArg).getExpression(); + } - if (arrayArg == null || - arrayArg.isNull() || - !(arrayArg instanceof ArrayCreationExpression && - node.getTarget() instanceof MemberReferenceExpression)) { - + if (arrayArg == null || arrayArg.isNull() || !(arrayArg instanceof ArrayCreationExpression && node.getTarget() instanceof MemberReferenceExpression)) { return null; } @@ -117,22 +119,15 @@ public class VarargsFixer implements IAstTransform { final Expression invocationTarget = target.getTarget(); if (invocationTarget == null || invocationTarget.isNull()) { - candidates = MetadataHelper.findMethods( - context.getCurrentType(), - MetadataFilters.matchName(resolved.getName()) - ); - } - else { + candidates = MetadataHelper.findMethods(context.getCurrentType(), MetadataFilters.matchName(resolved.getName())); + } else { final ResolveResult targetResult = _resolver.apply(invocationTarget); if (targetResult == null || targetResult.getType() == null) { return null; } - candidates = MetadataHelper.findMethods( - targetResult.getType(), - MetadataFilters.matchName(resolved.getName()) - ); + candidates = MetadataHelper.findMethods(targetResult.getType(), MetadataFilters.matchName(resolved.getName())); } final List argTypes = new ArrayList<>(); @@ -172,10 +167,7 @@ public class VarargsFixer implements IAstTransform { final MethodBinder.BindResult c2 = MethodBinder.selectMethod(candidates, argTypes); - if (c2.isFailure() || - c2.isAmbiguous() || - !StringUtilities.equals(c2.getMethod().getErasedSignature(), c1.getMethod().getErasedSignature())) { - + if (c2.isFailure() || c2.isAmbiguous() || !StringUtilities.equals(c2.getMethod().getErasedSignature(), c1.getMethod().getErasedSignature())) { return null; } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java b/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java index dec75ff..92d5cfc 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java @@ -12,9 +12,11 @@ public class LocalNameGenerator { String translatedName; int nameIndex = index + 1; StringBuilder nameBuilder = new StringBuilder(getTypeName(desc)); + if (!uniqueType || IdentifierValidation.isReservedMethodName(nameBuilder.toString())) { nameBuilder.append(nameIndex); } + translatedName = nameBuilder.toString(); return translatedName; } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java b/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java index 51e5d86..833ea29 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/MappingTranslator.java @@ -22,7 +22,7 @@ public class MappingTranslator implements Translator { if (translatable == null) { return null; } + return (TranslateResult) translatable.extendedTranslate(this, resolver, mappings); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java b/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java index 5ab16c8..33a38fe 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/ProposingTranslator.java @@ -29,25 +29,14 @@ public class ProposingTranslator implements Translator { TranslateResult deobfuscated = mapper.extendedDeobfuscate(translatable); if (translatable instanceof Entry && ((Entry) deobfuscated.getValue()).getName().equals(((Entry) translatable).getName())) { - return mapper.getObfResolver() - .resolveEntry((Entry) translatable, ResolutionStrategy.RESOLVE_ROOT) - .stream() - .map(this::proposeName) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst() - .map(newName -> TranslateResult.proposed((T) ((Entry) deobfuscated.getValue()).withName(newName))) - .orElse(deobfuscated); + return mapper.getObfResolver().resolveEntry((Entry) translatable, ResolutionStrategy.RESOLVE_ROOT).stream().map(this::proposeName).filter(Optional::isPresent).map(Optional::get).findFirst().map( + newName -> TranslateResult.proposed((T) ((Entry) deobfuscated.getValue()).withName(newName))).orElse(deobfuscated); } return deobfuscated; } private Optional proposeName(Entry entry) { - return Arrays.stream(nameProposalServices) - .map(service -> service.proposeName(entry, mapper)) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); + return Arrays.stream(nameProposalServices).map(service -> service.proposeName(entry, mapper)).filter(Optional::isPresent).map(Optional::get).findFirst(); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java b/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java index 3783053..9966014 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java @@ -1,24 +1,23 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation; -import com.google.common.collect.Lists; - import java.io.IOException; import java.io.StringReader; import java.util.List; -public class SignatureUpdater { +import com.google.common.collect.Lists; +public class SignatureUpdater { public static String update(String signature, ClassNameUpdater updater) { try { StringBuilder buf = new StringBuilder(); @@ -26,6 +25,7 @@ public class SignatureUpdater { // read the signature character-by-character StringReader reader = new StringReader(signature); int i; + while ((i = reader.read()) != -1) { char c = (char) i; @@ -34,9 +34,11 @@ public class SignatureUpdater { // update the class name and add it to the buffer buf.append('L'); String className = readClass(reader); + if (className == null) { throw new IllegalArgumentException("Malformed signature: " + signature); } + buf.append(updater.update(className)); buf.append(';'); } else { @@ -58,6 +60,7 @@ public class SignatureUpdater { StringBuilder buf = new StringBuilder(); int depth = 0; int i; + while ((i = reader.read()) != -1) { char c = (char) i; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java b/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java index 7061bfa..af6fbc9 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/Translatable.java @@ -5,12 +5,10 @@ import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.EntryResolver; public interface Translatable { - TranslateResult extendedTranslate(Translator translator, EntryResolver resolver, EntryMap mappings); @Deprecated default Translatable translate(Translator translator, EntryResolver resolver, EntryMap mappings) { return this.extendedTranslate(translator, resolver, mappings).getValue(); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java b/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java index bb26235..0cf4acd 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/TranslateResult.java @@ -6,7 +6,6 @@ import java.util.function.Function; import cuchaz.enigma.source.RenamableTokenType; public final class TranslateResult { - private final RenamableTokenType type; private final T value; @@ -65,11 +64,16 @@ public final class TranslateResult { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + TranslateResult that = (TranslateResult) o; - return type == that.type && - Objects.equals(value, that.value); + return type == that.type && Objects.equals(value, that.value); } @Override @@ -81,5 +85,4 @@ public final class TranslateResult { public String toString() { return String.format("TranslateResult { type: %s, value: %s }", type, value); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java b/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java index 2ecb30b..0725ebb 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/TranslationDirection.java @@ -1,24 +1,24 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation; public enum TranslationDirection { - DEOBFUSCATING { @Override public T choose(T deobfChoice, T obfChoice) { if (deobfChoice == null) { return obfChoice; } + return deobfChoice; } }, @@ -28,6 +28,7 @@ public enum TranslationDirection { if (obfChoice == null) { return deobfChoice; } + return obfChoice; } }; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/Translator.java b/enigma/src/main/java/cuchaz/enigma/translation/Translator.java index 66c2f9e..ed58197 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/Translator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/Translator.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation; @@ -34,38 +34,40 @@ public interface Translator { } default Collection translate(Collection translatable) { - return translatable.stream() - .map(this::translate) - .toList(); + return translatable.stream().map(this::translate).toList(); } default Set translate(Set translatable) { - return translatable.stream() - .map(this::translate) - .collect(Collectors.toSet()); + return translatable.stream().map(this::translate).collect(Collectors.toSet()); } default Map translateKeys(Map translatable) { Map result = new HashMap<>(translatable.size()); + for (Map.Entry entry : translatable.entrySet()) { result.put(translate(entry.getKey()), entry.getValue()); } + return result; } default Map translate(Map translatable) { Map result = new HashMap<>(translatable.size()); + for (Map.Entry entry : translatable.entrySet()) { result.put(translate(entry.getKey()), translate(entry.getValue())); } + return result; } default Multimap translate(Multimap translatable) { Multimap result = HashMultimap.create(translatable.size(), 1); + for (Map.Entry> entry : translatable.asMap().entrySet()) { result.putAll(translate(entry.getKey()), translate(entry.getValue())); } + return result; } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java b/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java index 28364f7..7d4755d 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/VoidTranslator.java @@ -7,5 +7,4 @@ public enum VoidTranslator implements Translator { public TranslateResult extendedTranslate(T translatable) { return TranslateResult.obfuscated(translatable); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java index f57dd90..6233784 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java @@ -3,7 +3,10 @@ package cuchaz.enigma.translation.mapping; import cuchaz.enigma.translation.representation.AccessFlags; public enum AccessModifier { - UNCHANGED, PUBLIC, PROTECTED, PRIVATE; + UNCHANGED, + PUBLIC, + PROTECTED, + PRIVATE; public String getFormattedName() { return "ACC:" + super.toString(); @@ -11,10 +14,10 @@ public enum AccessModifier { public AccessFlags transform(AccessFlags access) { return switch (this) { - case PUBLIC -> access.setPublic(); - case PROTECTED -> access.setProtected(); - case PRIVATE -> access.setPrivate(); - default -> access; + case PUBLIC -> access.setPublic(); + case PROTECTED -> access.setProtected(); + case PRIVATE -> access.setPrivate(); + default -> access; }; } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java index b5ec855..4cd79b9 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java @@ -11,7 +11,6 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.utils.TristateChange; public final class EntryChange> { - private final E target; private final TristateChange deobfName; private final TristateChange javadoc; @@ -75,13 +74,16 @@ public final class EntryChange> { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof EntryChange)) return false; + if (this == o) { + return true; + } + + if (!(o instanceof EntryChange)) { + return false; + } + EntryChange that = (EntryChange) o; - return Objects.equals(this.target, that.target) && - Objects.equals(this.deobfName, that.deobfName) && - Objects.equals(this.javadoc, that.javadoc) && - Objects.equals(this.access, that.access); + return Objects.equals(this.target, that.target) && Objects.equals(this.deobfName, that.deobfName) && Objects.equals(this.javadoc, that.javadoc) && Objects.equals(this.access, that.access); } @Override @@ -93,5 +95,4 @@ public final class EntryChange> { public String toString() { return String.format("EntryChange { target: %s, deobfName: %s, javadoc: %s, access: %s }", this.target, this.deobfName, this.javadoc, this.access); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java index e1a3253..b398d41 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java @@ -1,9 +1,10 @@ package cuchaz.enigma.translation.mapping; -import cuchaz.enigma.translation.representation.entry.Entry; +import java.util.stream.Stream; import javax.annotation.Nullable; -import java.util.stream.Stream; + +import cuchaz.enigma.translation.representation.entry.Entry; public interface EntryMap { void insert(Entry entry, T value); diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java index e916bf3..72a8fd4 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java @@ -5,11 +5,7 @@ import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public record EntryMapping( - @Nullable String targetName, - @Nonnull AccessModifier accessModifier, - @Nullable String javadoc -) { +public record EntryMapping(@Nullable String targetName, @Nonnull AccessModifier accessModifier, @Nullable String javadoc) { public static final EntryMapping DEFAULT = new EntryMapping(null, AccessModifier.UNCHANGED, null); public EntryMapping { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java index 0268834..ed5dba5 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java @@ -1,8 +1,8 @@ package cuchaz.enigma.translation.mapping; import java.util.Collection; -import java.util.Objects; import java.util.List; +import java.util.Objects; import java.util.stream.Stream; import javax.annotation.Nonnull; @@ -18,7 +18,6 @@ import cuchaz.enigma.translation.mapping.tree.HashEntryTree; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.FieldEntry; - import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.ValidationContext; @@ -77,7 +76,9 @@ public class EntryRemapper { } } - if (validateOnly || !vc.canProceed()) return; + if (validateOnly || !vc.canProceed()) { + return; + } for (Entry resolvedEntry : resolvedEntries) { if (deobfMapping.equals(EntryMapping.DEFAULT)) { @@ -95,9 +96,7 @@ public class EntryRemapper { } // Find all the methods in this record class - List classMethods = jarIndex.getEntryIndex().getMethods().stream() - .filter(entry -> classEntry.equals(entry.getParent())) - .toList(); + List classMethods = jarIndex.getEntryIndex().getMethods().stream().filter(entry -> classEntry.equals(entry.getParent())).toList(); MethodEntry methodEntry = null; @@ -163,5 +162,4 @@ public class EntryRemapper { public MappingValidator getValidator() { return validator; } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java index 9dd0c3a..39e6825 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java @@ -1,13 +1,14 @@ package cuchaz.enigma.translation.mapping; +import java.util.Collection; +import java.util.Set; + import com.google.common.collect.Streams; + import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.util.Collection; -import java.util.Set; - public interface EntryResolver { > Collection resolveEntry(E entry, ResolutionStrategy strategy); @@ -17,14 +18,12 @@ public interface EntryResolver { default , C extends Entry> Collection> resolveReference(EntryReference reference, ResolutionStrategy strategy) { Collection entry = resolveEntry(reference.entry, strategy); + if (reference.context != null) { Collection context = resolveEntry(reference.context, strategy); - return Streams.zip(entry.stream(), context.stream(), (e, c) -> new EntryReference<>(e, c, reference)) - .toList(); + return Streams.zip(entry.stream(), context.stream(), (e, c) -> new EntryReference<>(e, c, reference)).toList(); } else { - return entry.stream() - .map(e -> new EntryReference<>(e, null, reference)) - .toList(); + return entry.stream().map(e -> new EntryReference<>(e, null, reference)).toList(); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java index 582076c..f9a1060 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java @@ -6,7 +6,6 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.utils.validation.ValidationContext; public class EntryUtil { - public static EntryMapping applyChange(ValidationContext vc, EntryRemapper remapper, EntryChange change) { Entry target = change.getTarget(); EntryMapping prev = remapper.getDeobfMapping(target); @@ -38,5 +37,4 @@ public class EntryUtil { return self; } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java index e3aeb20..06e8d76 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IdentifierValidation.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.mapping; @@ -18,21 +18,17 @@ import cuchaz.enigma.utils.validation.StandardValidation; import cuchaz.enigma.utils.validation.ValidationContext; public final class IdentifierValidation { - private IdentifierValidation() { } - private static final List ILLEGAL_IDENTIFIERS = List.of( - "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", - "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", - "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", - "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", - "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", - "void", "volatile", "while", "_" - ); + private static final List ILLEGAL_IDENTIFIERS = List.of("abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", + "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", + "transient", "true", "try", "void", "volatile", "while", "_"); public static boolean validateClassName(ValidationContext vc, String name, boolean isInner) { - if (!StandardValidation.notBlank(vc, name)) return false; + if (!StandardValidation.notBlank(vc, name)) { + return false; + } if (isInner) { // When renaming, inner class names do not contain the package name, @@ -41,27 +37,37 @@ public final class IdentifierValidation { } String[] parts = name.split("/"); + for (String part : parts) { validateIdentifier(vc, part); } + return true; } public static boolean validateIdentifier(ValidationContext vc, String name) { - if (!StandardValidation.notBlank(vc, name)) return false; - if (checkForReservedName(vc, name)) return false; + if (!StandardValidation.notBlank(vc, name)) { + return false; + } + + if (checkForReservedName(vc, name)) { + return false; + } // Adapted from javax.lang.model.SourceVersion.isIdentifier int cp = name.codePointAt(0); int position = 1; + if (!Character.isJavaIdentifierStart(cp)) { vc.raise(Message.ILLEGAL_IDENTIFIER, name, new String(Character.toChars(cp)), position); return false; } + for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { cp = name.codePointAt(i); position += 1; + if (!Character.isJavaIdentifierPart(cp)) { vc.raise(Message.ILLEGAL_IDENTIFIER, name, new String(Character.toChars(cp)), position); return false; @@ -76,11 +82,11 @@ public final class IdentifierValidation { vc.raise(Message.RESERVED_IDENTIFIER, name); return true; } + return false; } public static boolean isReservedMethodName(String name) { return ILLEGAL_IDENTIFIERS.contains(name); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java index 00168ba..8dc5659 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java @@ -1,6 +1,10 @@ package cuchaz.enigma.translation.mapping; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import javax.annotation.Nullable; @@ -40,6 +44,7 @@ public class IndexEntryResolver implements EntryResolver { } Entry classChild = getClassChild(entry); + if (classChild != null && !(classChild instanceof ClassEntry)) { AccessFlags access = entryIndex.getEntryAccess(classChild); @@ -50,10 +55,9 @@ public class IndexEntryResolver implements EntryResolver { if (access == null || !access.isPrivate()) { Collection> resolvedChildren = resolveChildEntry(classChild, strategy); + if (!resolvedChildren.isEmpty()) { - return resolvedChildren.stream() - .map(resolvedChild -> (E) entry.replaceAncestor(classChild, resolvedChild)) - .toList(); + return resolvedChildren.stream().map(resolvedChild -> (E) entry.replaceAncestor(classChild, resolvedChild)).toList(); } } } @@ -69,9 +73,11 @@ public class IndexEntryResolver implements EntryResolver { // get the entry in the hierarchy that is the child of a class List> ancestry = entry.getAncestry(); + for (int i = ancestry.size() - 1; i > 0; i--) { Entry child = ancestry.get(i); Entry cast = child.castParent(ClassEntry.class); + if (cast != null && !(cast instanceof ClassEntry)) { // we found the entry which is a child of a class, we are now able to resolve the owner of this entry return cast; @@ -86,8 +92,10 @@ public class IndexEntryResolver implements EntryResolver { if (entry instanceof MethodEntry) { MethodEntry bridgeMethod = bridgeMethodIndex.getBridgeFromSpecialized((MethodEntry) entry); + if (bridgeMethod != null && ownerClass.equals(bridgeMethod.getParent())) { Set> resolvedBridge = resolveChildEntry(bridgeMethod, strategy); + if (!resolvedBridge.isEmpty()) { return resolvedBridge; } else { @@ -117,6 +125,7 @@ public class IndexEntryResolver implements EntryResolver { if (parentResolution.isEmpty()) { AccessFlags parentAccess = entryIndex.getEntryAccess(entry); + if (parentAccess != null && !parentAccess.isPrivate()) { return Collections.singleton(entry); } @@ -128,6 +137,7 @@ public class IndexEntryResolver implements EntryResolver { private Collection> resolveClosest(Entry entry, ResolutionStrategy strategy) { // When resolving closest, we want to first check if we exist before looking further down AccessFlags parentAccess = entryIndex.getEntryAccess(entry); + if (parentAccess != null && !parentAccess.isPrivate()) { return Collections.singleton(entry); } else { @@ -138,6 +148,7 @@ public class IndexEntryResolver implements EntryResolver { @Override public Set> resolveEquivalentEntries(Entry entry) { MethodEntry relevantMethod = entry.findAncestor(MethodEntry.class); + if (relevantMethod == null || !entryIndex.hasMethod(relevantMethod)) { return Collections.singleton(entry); } @@ -162,6 +173,7 @@ public class IndexEntryResolver implements EntryResolver { private void resolveEquivalentMethods(Set methodEntries, MethodEntry methodEntry) { AccessFlags access = entryIndex.getMethodAccess(methodEntry); + if (access == null) { throw new IllegalArgumentException("Could not find method " + methodEntry); } @@ -176,11 +188,13 @@ public class IndexEntryResolver implements EntryResolver { private void resolveEquivalentMethods(Set methodEntries, MethodInheritanceTreeNode node) { MethodEntry methodEntry = node.getMethodEntry(); + if (methodEntries.contains(methodEntry)) { return; } AccessFlags flags = entryIndex.getMethodAccess(methodEntry); + if (flags != null && canInherit(methodEntry, flags)) { // collect the entry methodEntries.add(methodEntry); @@ -188,6 +202,7 @@ public class IndexEntryResolver implements EntryResolver { // look at bridge methods! MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry); + while (bridgedMethod != null) { resolveEquivalentMethods(methodEntries, bridgedMethod); bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod); @@ -207,6 +222,7 @@ public class IndexEntryResolver implements EntryResolver { private void resolveEquivalentMethods(Set methodEntries, MethodImplementationsTreeNode node) { MethodEntry methodEntry = node.getMethodEntry(); AccessFlags flags = entryIndex.getMethodAccess(methodEntry); + if (flags != null && !flags.isPrivate() && !flags.isStatic()) { // collect the entry methodEntries.add(methodEntry); @@ -214,6 +230,7 @@ public class IndexEntryResolver implements EntryResolver { // look at bridge methods! MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry); + while (bridgedMethod != null) { resolveEquivalentMethods(methodEntries, bridgedMethod); bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod); diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java index 250851c..765c1f2 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java @@ -49,9 +49,6 @@ public class MappingDelta implements Translatable { public TranslateResult> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap mappings) { // there's no concept of deobfuscated for this as far as I can see, so // it will always be marked as obfuscated - return TranslateResult.ungrouped(new MappingDelta<>( - translator.translate(baseMappings), - translator.translate(changes) - )); + return TranslateResult.ungrouped(new MappingDelta<>(translator.translate(baseMappings), translator.translate(changes))); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java index 2c03748..3b756c6 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingOperations.java @@ -1,71 +1,74 @@ package cuchaz.enigma.translation.mapping; +import java.util.HashSet; +import java.util.Set; + +import cuchaz.enigma.translation.MappingTranslator; +import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; import cuchaz.enigma.translation.mapping.tree.HashEntryTree; -import cuchaz.enigma.translation.MappingTranslator; -import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.util.HashSet; -import java.util.Set; - public class MappingOperations { - public static EntryTree invert(EntryTree mappings) { - Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); - EntryTree result = new HashEntryTree<>(); - - for (EntryTreeNode node : mappings) { - Entry leftEntry = node.getEntry(); - EntryMapping leftMapping = node.getValue(); - - if (!(leftEntry instanceof ClassEntry || leftEntry instanceof MethodEntry || leftEntry instanceof FieldEntry)) { - result.insert(translator.translate(leftEntry), leftMapping); - continue; - } - - Entry rightEntry = translator.translate(leftEntry); - - result.insert(rightEntry, leftMapping == null ? null : leftMapping.withName(leftEntry.getName())); - } - - return result; - } - - public static EntryTree compose(EntryTree left, EntryTree right, boolean keepLeftOnly, boolean keepRightOnly) { - Translator leftTranslator = new MappingTranslator(left, VoidEntryResolver.INSTANCE); - EntryTree result = new HashEntryTree<>(); - Set> addedMappings = new HashSet<>(); - - for (EntryTreeNode node : left) { - Entry leftEntry = node.getEntry(); - EntryMapping leftMapping = node.getValue(); - - Entry rightEntry = leftTranslator.translate(leftEntry); - - EntryMapping rightMapping = right.get(rightEntry); - if (rightMapping != null) { - result.insert(leftEntry, rightMapping); - addedMappings.add(rightEntry); - } else if (keepLeftOnly) { - result.insert(leftEntry, leftMapping); - } - } - - if (keepRightOnly) { - Translator leftInverseTranslator = new MappingTranslator(invert(left), VoidEntryResolver.INSTANCE); - for (EntryTreeNode node : right) { - Entry rightEntry = node.getEntry(); - EntryMapping rightMapping = node.getValue(); - - if (!addedMappings.contains(rightEntry)) { - result.insert(leftInverseTranslator.translate(rightEntry), rightMapping); - } - } - } - return result; - } + public static EntryTree invert(EntryTree mappings) { + Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); + EntryTree result = new HashEntryTree<>(); + + for (EntryTreeNode node : mappings) { + Entry leftEntry = node.getEntry(); + EntryMapping leftMapping = node.getValue(); + + if (!(leftEntry instanceof ClassEntry || leftEntry instanceof MethodEntry || leftEntry instanceof FieldEntry)) { + result.insert(translator.translate(leftEntry), leftMapping); + continue; + } + + Entry rightEntry = translator.translate(leftEntry); + + result.insert(rightEntry, leftMapping == null ? null : leftMapping.withName(leftEntry.getName())); + } + + return result; + } + + public static EntryTree compose(EntryTree left, EntryTree right, boolean keepLeftOnly, boolean keepRightOnly) { + Translator leftTranslator = new MappingTranslator(left, VoidEntryResolver.INSTANCE); + EntryTree result = new HashEntryTree<>(); + Set> addedMappings = new HashSet<>(); + + for (EntryTreeNode node : left) { + Entry leftEntry = node.getEntry(); + EntryMapping leftMapping = node.getValue(); + + Entry rightEntry = leftTranslator.translate(leftEntry); + + EntryMapping rightMapping = right.get(rightEntry); + + if (rightMapping != null) { + result.insert(leftEntry, rightMapping); + addedMappings.add(rightEntry); + } else if (keepLeftOnly) { + result.insert(leftEntry, leftMapping); + } + } + + if (keepRightOnly) { + Translator leftInverseTranslator = new MappingTranslator(invert(left), VoidEntryResolver.INSTANCE); + + for (EntryTreeNode node : right) { + Entry rightEntry = node.getEntry(); + EntryMapping rightMapping = node.getValue(); + + if (!addedMappings.contains(rightEntry)) { + result.insert(leftInverseTranslator.translate(rightEntry), rightMapping); + } + } + } + + return result; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java index 5d39e3d..21c78cf 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java @@ -1,9 +1,9 @@ package cuchaz.enigma.translation.mapping; -import cuchaz.enigma.translation.representation.entry.Entry; - import javax.annotation.Nullable; +import cuchaz.enigma.translation.representation.entry.Entry; + public class MappingPair, M> { private final E entry; private M mapping; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java index 065e5c3..5f42373 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java @@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.ValidationContext; public class MappingValidator { - private final EntryTree obfToDeobf; private final Translator deobfuscator; private final JarIndex index; @@ -28,10 +27,12 @@ public class MappingValidator { public boolean validateRename(ValidationContext vc, Entry entry, String name) { Collection> equivalentEntries = index.getEntryResolver().resolveEquivalentEntries(entry); boolean error = false; + for (Entry equivalentEntry : equivalentEntries) { equivalentEntry.validateName(vc, name); error |= validateUnique(vc, equivalentEntry, name); } + return error; } @@ -45,17 +46,17 @@ public class MappingValidator { Entry relatedEntry = entry.replaceAncestor(containingClass, relatedClass); Entry translatedEntry = deobfuscator.translate(relatedEntry); - List> translatedSiblings = obfToDeobf.getSiblings(relatedEntry).stream() - .map(deobfuscator::translate) - .toList(); + List> translatedSiblings = obfToDeobf.getSiblings(relatedEntry).stream().map(deobfuscator::translate).toList(); if (!isUnique(translatedEntry, translatedSiblings, name)) { Entry parent = translatedEntry.getParent(); + if (parent != null) { vc.raise(Message.NONUNIQUE_NAME_CLASS, name, parent); } else { vc.raise(Message.NONUNIQUE_NAME, name); } + error = true; } } @@ -80,7 +81,7 @@ public class MappingValidator { return false; } } + return true; } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java index 33247fa..dc8055b 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java @@ -1,16 +1,20 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.mapping; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.translation.mapping.tree.EntryTree; @@ -21,10 +25,6 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - public class MappingsChecker { private final JarIndex index; private final EntryTree mappings; @@ -37,13 +37,12 @@ public class MappingsChecker { public Dropped dropBrokenMappings(ProgressListener progress) { Dropped dropped = new Dropped(); - Collection> obfEntries = mappings.getAllEntries() - .filter(e -> e instanceof ClassEntry || e instanceof MethodEntry || e instanceof FieldEntry || e instanceof LocalVariableEntry) - .toList(); + Collection> obfEntries = mappings.getAllEntries().filter(e -> e instanceof ClassEntry || e instanceof MethodEntry || e instanceof FieldEntry || e instanceof LocalVariableEntry).toList(); progress.init(obfEntries.size(), "Checking for dropped mappings"); int steps = 0; + for (Entry entry : obfEntries) { progress.step(steps++, entry.toString()); tryDropEntry(dropped, entry); @@ -57,6 +56,7 @@ public class MappingsChecker { private void tryDropEntry(Dropped dropped, Entry entry) { if (shouldDropEntry(entry)) { EntryMapping mapping = mappings.get(entry); + if (mapping != null) { dropped.drop(entry, mapping); } @@ -102,6 +102,7 @@ public class MappingsChecker { void apply(EntryTree mappings) { for (Entry entry : droppedMappings.keySet()) { EntryTreeNode node = mappings.findNode(entry); + if (node == null) { continue; } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java index 2eab55f..1997572 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java @@ -1,12 +1,12 @@ package cuchaz.enigma.translation.mapping; -import cuchaz.enigma.translation.representation.entry.Entry; -import cuchaz.enigma.translation.representation.entry.MethodEntry; - import java.util.Collection; import java.util.Collections; import java.util.Set; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; + public enum VoidEntryResolver implements EntryResolver { INSTANCE; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java index 441949c..f118e64 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/LfPrintWriter.java @@ -4,13 +4,13 @@ import java.io.PrintWriter; import java.io.Writer; public class LfPrintWriter extends PrintWriter { - public LfPrintWriter(Writer out) { - super(out); - } + public LfPrintWriter(Writer out) { + super(out); + } - @Override - public void println() { - // https://stackoverflow.com/a/14749004 - write('\n'); - } + @Override + public void println() { + // https://stackoverflow.com/a/14749004 + write('\n'); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java index 062c877..3be8048 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java @@ -1,5 +1,10 @@ package cuchaz.enigma.translation.mapping.serde; +import java.io.IOException; +import java.nio.file.Path; + +import javax.annotation.Nullable; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingDelta; @@ -14,10 +19,6 @@ import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Reader; import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import javax.annotation.Nullable; -import java.io.IOException; -import java.nio.file.Path; - public enum MappingFormat { ENIGMA_FILE(EnigmaMappingsWriter.FILE, EnigmaMappingsReader.FILE), ENIGMA_DIRECTORY(EnigmaMappingsWriter.DIRECTORY, EnigmaMappingsReader.DIRECTORY), @@ -28,7 +29,6 @@ public enum MappingFormat { PROGUARD(null, ProguardMappingsReader.INSTANCE), RECAF(null, RecafMappingsReader.INSTANCE); - private final MappingsWriter writer; private final MappingsReader reader; @@ -37,14 +37,15 @@ public enum MappingFormat { this.reader = reader; } - public void write(EntryTree mappings, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) { + public void write(EntryTree mappings, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) { write(mappings, MappingDelta.added(mappings), path, progressListener, saveParameters); } - public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) { + public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) { if (writer == null) { throw new IllegalStateException(name() + " does not support writing"); } + writer.write(mappings, delta, path, progressListener, saveParameters); } @@ -52,6 +53,7 @@ public enum MappingFormat { if (reader == null) { throw new IllegalStateException(name() + " does not support reading"); } + return reader.read(path, progressListener, saveParameters); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java index 7c8f6cc..5f466bb 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java @@ -6,21 +6,27 @@ public final class MappingHelper { public static String escape(String raw) { StringBuilder builder = new StringBuilder(raw.length() + 1); + for (int i = 0; i < raw.length(); i++) { final char c = raw.charAt(i); final int r = TO_ESCAPE.indexOf(c); + if (r < 0) { builder.append(c); } else { builder.append('\\').append(ESCAPED.charAt(r)); } } + return builder.toString(); } public static String unescape(String str) { int pos = str.indexOf('\\'); - if (pos < 0) return str; + + if (pos < 0) { + return str; + } StringBuilder ret = new StringBuilder(str.length() - 1); int start = 0; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java index 9d04b97..6f5d7d6 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingParseException.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.mapping.serde; @@ -15,7 +15,6 @@ import java.io.File; import java.util.function.Supplier; public class MappingParseException extends Exception { - private int line; private String message; private String filePath; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java index 2f01375..4fdfdcb 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java @@ -1,12 +1,12 @@ package cuchaz.enigma.translation.mapping.serde; +import java.io.IOException; +import java.nio.file.Path; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import java.io.IOException; -import java.nio.file.Path; - public interface MappingsReader { EntryTree read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException; } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java index 68a8dbb..5c273ad 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java @@ -1,12 +1,12 @@ package cuchaz.enigma.translation.mapping.serde; +import java.nio.file.Path; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingDelta; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import java.nio.file.Path; - public interface MappingsWriter { void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters); diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java index 61dbe93..1f8b755 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java @@ -5,7 +5,11 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; +import java.util.List; +import java.util.Locale; import javax.annotation.Nullable; @@ -15,12 +19,20 @@ import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.AccessModifier; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingPair; -import cuchaz.enigma.translation.mapping.serde.*; +import cuchaz.enigma.translation.mapping.serde.MappingHelper; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.serde.MappingsReader; +import cuchaz.enigma.translation.mapping.serde.RawEntryMapping; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.HashEntryTree; import cuchaz.enigma.translation.representation.MethodDescriptor; import cuchaz.enigma.translation.representation.TypeDescriptor; -import cuchaz.enigma.translation.representation.entry.*; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; public enum EnigmaMappingsReader implements MappingsReader { @@ -42,19 +54,18 @@ public enum EnigmaMappingsReader implements MappingsReader { public EntryTree read(Path root, ProgressListener progress, MappingSaveParameters saveParameters) throws IOException, MappingParseException { EntryTree mappings = new HashEntryTree<>(); - List files = Files.walk(root) - .filter(f -> !Files.isDirectory(f)) - .filter(f -> f.toString().endsWith(".mapping")) - .toList(); + List files = Files.walk(root).filter(f -> !Files.isDirectory(f)).filter(f -> f.toString().endsWith(".mapping")).toList(); progress.init(files.size(), I18n.translate("progress.mappings.enigma_directory.loading")); int step = 0; for (Path file : files) { progress.step(step++, root.relativize(file).toString()); + if (Files.isHidden(file)) { continue; } + readFile(file, mappings); } @@ -74,10 +85,10 @@ public enum EnigmaMappingsReader implements MappingsReader { * Reads multiple Enigma mapping files. * * @param progress the progress listener - * @param paths the Enigma files to read; cannot be empty + * @param paths the Enigma files to read; cannot be empty * @return the parsed mappings - * @throws MappingParseException if a mapping file cannot be parsed - * @throws IOException if an IO error occurs + * @throws MappingParseException if a mapping file cannot be parsed + * @throws IOException if an IO error occurs * @throws IllegalArgumentException if there are no paths to read */ public static EntryTree readFiles(ProgressListener progress, Path... paths) throws MappingParseException, IOException { @@ -107,6 +118,7 @@ public enum EnigmaMappingsReader implements MappingsReader { int indentation = countIndentation(line, path, lineNumber); line = formatLine(line); + if (line == null) { continue; } @@ -115,6 +127,7 @@ public enum EnigmaMappingsReader implements MappingsReader { try { MappingPair pair = parseLine(mappingStack.peek(), line); + if (pair != null) { mappingStack.push(pair); } @@ -131,6 +144,7 @@ public enum EnigmaMappingsReader implements MappingsReader { private static void cleanMappingStack(int indentation, Deque> mappingStack, EntryTree mappings) { while (indentation < mappingStack.size()) { MappingPair pair = mappingStack.pop(); + if (pair.getMapping() != null) { mappings.insert(pair.getEntry(), pair.getMapping().bake()); } @@ -156,14 +170,17 @@ public enum EnigmaMappingsReader implements MappingsReader { } int commentPos = line.indexOf('#'); + if (commentPos >= 0) { return line.substring(0, commentPos); } + return line; } private static int countIndentation(String line, Path path, int lineNumber) throws MappingParseException { int indent = 0; + for (int i = 0; i < line.length(); i++) { if (line.charAt(i) == ' ') { throw new MappingParseException(path::toString, lineNumber + 1, "Spaces must not be used to indent lines!"); @@ -172,8 +189,10 @@ public enum EnigmaMappingsReader implements MappingsReader { if (line.charAt(i) != '\t') { break; } + indent++; } + return indent; } @@ -183,36 +202,41 @@ public enum EnigmaMappingsReader implements MappingsReader { Entry parentEntry = parent == null ? null : parent.getEntry(); switch (keyToken) { - case EnigmaFormat.CLASS: - return parseClass(parentEntry, tokens); - case EnigmaFormat.FIELD: - return parseField(parentEntry, tokens); - case EnigmaFormat.METHOD: - return parseMethod(parentEntry, tokens); - case EnigmaFormat.PARAMETER: - return parseArgument(parentEntry, tokens); - case EnigmaFormat.COMMENT: - readJavadoc(parent, tokens); - return null; - default: - throw new RuntimeException("Unknown token '" + keyToken + "'"); + case EnigmaFormat.CLASS: + return parseClass(parentEntry, tokens); + case EnigmaFormat.FIELD: + return parseField(parentEntry, tokens); + case EnigmaFormat.METHOD: + return parseMethod(parentEntry, tokens); + case EnigmaFormat.PARAMETER: + return parseArgument(parentEntry, tokens); + case EnigmaFormat.COMMENT: + readJavadoc(parent, tokens); + return null; + default: + throw new RuntimeException("Unknown token '" + keyToken + "'"); } } private static void readJavadoc(MappingPair parent, String[] tokens) { - if (parent == null) + if (parent == null) { throw new IllegalStateException("Javadoc has no parent!"); + } + // Empty string to concat String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens, 1, tokens.length)) : ""; + if (parent.getMapping() == null) { parent.setMapping(new RawEntryMapping(parent.getEntry().getName(), AccessModifier.UNCHANGED)); } + parent.getMapping().addJavadocLine(MappingHelper.unescape(jdLine)); } private static MappingPair parseClass(@Nullable Entry parent, String[] tokens) { String obfuscatedName = ClassEntry.getInnerName(tokens[1]); ClassEntry obfuscatedEntry; + if (parent instanceof ClassEntry) { obfuscatedEntry = new ClassEntry((ClassEntry) parent, obfuscatedName); } else { @@ -224,6 +248,7 @@ public enum EnigmaMappingsReader implements MappingsReader { if (tokens.length == 3) { AccessModifier parsedModifier = parseModifier(tokens[2]); + if (parsedModifier != null) { modifier = parsedModifier; mapping = obfuscatedName; @@ -254,6 +279,7 @@ public enum EnigmaMappingsReader implements MappingsReader { descriptor = new TypeDescriptor(tokens[2]); } else if (tokens.length == 4) { AccessModifier parsedModifier = parseModifier(tokens[3]); + if (parsedModifier != null) { descriptor = new TypeDescriptor(tokens[2]); modifier = parsedModifier; @@ -289,6 +315,7 @@ public enum EnigmaMappingsReader implements MappingsReader { descriptor = new MethodDescriptor(tokens[2]); } else if (tokens.length == 4) { AccessModifier parsedModifier = parseModifier(tokens[3]); + if (parsedModifier != null) { modifier = parsedModifier; mapping = obfuscatedName; @@ -326,6 +353,7 @@ public enum EnigmaMappingsReader implements MappingsReader { if (token.startsWith("ACC:")) { return AccessModifier.valueOf(token.substring(4)); } + return null; } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java index 5d7a789..cd00ef7 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.mapping.serde.enigma; @@ -15,7 +15,12 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.*; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -32,24 +37,30 @@ import cuchaz.enigma.translation.mapping.AccessModifier; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingDelta; import cuchaz.enigma.translation.mapping.VoidEntryResolver; -import cuchaz.enigma.translation.mapping.serde.*; +import cuchaz.enigma.translation.mapping.serde.LfPrintWriter; +import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingHelper; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; +import cuchaz.enigma.translation.mapping.serde.MappingsWriter; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; -import cuchaz.enigma.translation.representation.entry.*; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; public enum EnigmaMappingsWriter implements MappingsWriter { FILE { @Override public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) { - Collection classes = mappings.getRootNodes() - .filter(entry -> entry.getEntry() instanceof ClassEntry) - .map(entry -> (ClassEntry) entry.getEntry()) - .toList(); + Collection classes = mappings.getRootNodes().filter(entry -> entry.getEntry() instanceof ClassEntry).map(entry -> (ClassEntry) entry.getEntry()).toList(); progress.init(classes.size(), I18n.translate("progress.mappings.enigma_file.writing")); int steps = 0; + try (PrintWriter writer = new LfPrintWriter(Files.newBufferedWriter(path))) { for (ClassEntry classEntry : classes) { progress.step(steps++, classEntry.getFullName()); @@ -63,10 +74,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { DIRECTORY { @Override public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) { - Collection changedClasses = delta.getChangedRoots() - .filter(entry -> entry instanceof ClassEntry) - .map(entry -> (ClassEntry) entry) - .toList(); + Collection changedClasses = delta.getChangedRoots().filter(entry -> entry instanceof ClassEntry).map(entry -> (ClassEntry) entry).toList(); applyDeletions(path, changedClasses, mappings, delta.getBaseMappings(), saveParameters.getFileNameFormat()); @@ -80,6 +88,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { try { ClassEntry fileEntry = classEntry; + if (saveParameters.getFileNameFormat() == MappingFileNameFormat.BY_DEOBF) { fileEntry = translator.translate(fileEntry); } @@ -101,8 +110,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { private void applyDeletions(Path root, Collection changedClasses, EntryTree mappings, EntryTree oldMappings, MappingFileNameFormat fileNameFormat) { Translator oldMappingTranslator = new MappingTranslator(oldMappings, VoidEntryResolver.INSTANCE); - Stream deletedClassStream = changedClasses.stream() - .filter(e -> !Objects.equals(oldMappings.get(e), mappings.get(e))); + Stream deletedClassStream = changedClasses.stream().filter(e -> !Objects.equals(oldMappings.get(e), mappings.get(e))); if (fileNameFormat == MappingFileNameFormat.BY_DEOBF) { deletedClassStream = deletedClassStream.map(oldMappingTranslator::translate); @@ -121,8 +129,10 @@ public enum EnigmaMappingsWriter implements MappingsWriter { for (ClassEntry classEntry : deletedClasses) { String packageName = classEntry.getPackageName(); + if (packageName != null) { Path packagePath = Paths.get(packageName); + try { deleteDeadPackages(root, packagePath); } catch (IOException e) { @@ -137,6 +147,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { for (int i = packagePath.getNameCount() - 1; i >= 0; i--) { Path subPath = packagePath.subpath(0, i + 1); Path packagePart = root.resolve(subPath.toString()); + if (isEmpty(packagePart)) { Files.deleteIfExists(packagePart); } @@ -178,6 +189,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { } writer.println(writeClass(classEntry, classEntryMapping).trim()); + if (classEntryMapping.javadoc() != null) { writeDocs(writer, classEntryMapping, 0); } @@ -189,6 +201,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { private void writeDocs(PrintWriter writer, EntryMapping mapping, int depth) { String jd = mapping.javadoc(); + if (jd != null) { for (String line : jd.split("\\R")) { writer.println(indent(EnigmaFormat.COMMENT + " " + MappingHelper.escape(line), depth + 1)); @@ -198,6 +211,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { protected void writeEntry(PrintWriter writer, EntryTree mappings, Entry entry, int depth) { EntryTreeNode node = mappings.findNode(entry); + if (node == null) { return; } @@ -209,6 +223,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { } String line = null; + if (entry instanceof ClassEntry classEntry) { line = writeClass(classEntry, mapping); } else if (entry instanceof MethodEntry methodEntry) { @@ -228,6 +243,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { } Collection> children = groupChildren(node.getChildren()); + for (Entry child : children) { writeEntry(writer, mappings, child, depth + 1); } @@ -236,25 +252,13 @@ public enum EnigmaMappingsWriter implements MappingsWriter { private Collection> groupChildren(Collection> children) { Collection> result = new ArrayList<>(children.size()); - children.stream().filter(e -> e instanceof FieldEntry) - .map(e -> (FieldEntry) e) - .sorted() - .forEach(result::add); + children.stream().filter(e -> e instanceof FieldEntry).map(e -> (FieldEntry) e).sorted().forEach(result::add); - children.stream().filter(e -> e instanceof MethodEntry) - .map(e -> (MethodEntry) e) - .sorted() - .forEach(result::add); + children.stream().filter(e -> e instanceof MethodEntry).map(e -> (MethodEntry) e).sorted().forEach(result::add); - children.stream().filter(e -> e instanceof LocalVariableEntry) - .map(e -> (LocalVariableEntry) e) - .sorted() - .forEach(result::add); + children.stream().filter(e -> e instanceof LocalVariableEntry).map(e -> (LocalVariableEntry) e).sorted().forEach(result::add); - children.stream().filter(e -> e instanceof ClassEntry) - .map(e -> (ClassEntry) e) - .sorted() - .forEach(result::add); + children.stream().filter(e -> e instanceof ClassEntry).map(e -> (ClassEntry) e).sorted().forEach(result::add); return result; } @@ -294,6 +298,7 @@ public enum EnigmaMappingsWriter implements MappingsWriter { private void writeMapping(StringBuilder builder, EntryMapping mapping) { if (mapping.targetName() != null) { builder.append(mapping.targetName()).append(' '); + if (mapping.accessModifier() != AccessModifier.UNCHANGED) { builder.append(mapping.accessModifier().getFormattedName()).append(' '); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java index 034fb41..fc550d4 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/proguard/ProguardMappingsReader.java @@ -1,9 +1,16 @@ package cuchaz.enigma.translation.mapping.serde.proguard; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingOperations; import cuchaz.enigma.translation.mapping.serde.MappingParseException; -import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingsReader; import cuchaz.enigma.translation.mapping.tree.EntryTree; @@ -14,122 +21,117 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - public class ProguardMappingsReader implements MappingsReader { - public static final ProguardMappingsReader INSTANCE = new ProguardMappingsReader(); - private static final String NAME = "[a-zA-Z0-9_\\-.$<>]+"; - private static final String TYPE = NAME + "(?:\\[])*"; - private static final String TYPE_LIST = "|(?:(?:" + TYPE + ",)*" + TYPE + ")"; - private static final Pattern CLASS = Pattern.compile("(" + NAME + ") -> (" + NAME + "):"); - private static final Pattern FIELD = Pattern.compile(" {4}(" + TYPE + ") (" + NAME + ") -> (" + NAME + ")"); - private static final Pattern METHOD = Pattern.compile(" {4}(?:[0-9]+:[0-9]+:)?(" + TYPE + ") (" + NAME + ")\\((" + TYPE_LIST + ")\\) -> (" + NAME + ")"); - - public ProguardMappingsReader() {} - - @Override - public EntryTree read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException { - EntryTree mappings = new HashEntryTree<>(); - - int lineNumber = 0; - ClassEntry currentClass = null; - for (String line : Files.readAllLines(path, StandardCharsets.UTF_8)) { - lineNumber++; - - if (line.startsWith("#") || line.isEmpty()) { - continue; - } - - Matcher classMatcher = CLASS.matcher(line); - Matcher fieldMatcher = FIELD.matcher(line); - Matcher methodMatcher = METHOD.matcher(line); - - if (classMatcher.matches()) { - String name = classMatcher.group(1); - String targetName = classMatcher.group(2); - - mappings.insert(currentClass = new ClassEntry(name.replace('.', '/')), new EntryMapping(ClassEntry.getInnerName(targetName.replace('.', '/')))); - } else if (fieldMatcher.matches()) { - String type = fieldMatcher.group(1); - String name = fieldMatcher.group(2); - String targetName = fieldMatcher.group(3); - - if (currentClass == null) { - throw new MappingParseException(path::toString, lineNumber, "field mapping not inside class: " + line); - } - - mappings.insert(new FieldEntry(currentClass, name, new TypeDescriptor(getDescriptor(type))), new EntryMapping(targetName)); - } else if (methodMatcher.matches()) { - String returnType = methodMatcher.group(1); - String name = methodMatcher.group(2); - String[] parameterTypes = methodMatcher.group(3).isEmpty() ? new String[0] : methodMatcher.group(3).split(","); - String targetName = methodMatcher.group(4); - - if (currentClass == null) { - throw new MappingParseException(path::toString, lineNumber, "method mapping not inside class: " + line); - } - - mappings.insert(new MethodEntry(currentClass, name, new MethodDescriptor(getDescriptor(returnType, parameterTypes))), new EntryMapping(targetName)); - } else { - throw new MappingParseException(path::toString, lineNumber, "invalid mapping line: " + line); - } - } - - return MappingOperations.invert(mappings); - } - - private String getDescriptor(String type) { - StringBuilder descriptor = new StringBuilder(); - - while (type.endsWith("[]")) { - descriptor.append("["); - type = type.substring(0, type.length() - 2); - } - - switch (type) { - case "byte": - return descriptor + "B"; - case "char": - return descriptor + "C"; - case "short": - return descriptor + "S"; - case "int": - return descriptor + "I"; - case "long": - return descriptor + "J"; - case "float": - return descriptor + "F"; - case "double": - return descriptor + "D"; - case "boolean": - return descriptor + "Z"; - case "void": - return descriptor + "V"; - } - - descriptor.append("L"); - descriptor.append(type.replace('.', '/')); - descriptor.append(";"); - - return descriptor.toString(); - } - - private String getDescriptor(String returnType, String[] parameterTypes) { - StringBuilder descriptor = new StringBuilder(); - descriptor.append('('); - - for (String parameterType : parameterTypes) { - descriptor.append(getDescriptor(parameterType)); - } - - descriptor.append(')'); - descriptor.append(getDescriptor(returnType)); - - return descriptor.toString(); - } + public static final ProguardMappingsReader INSTANCE = new ProguardMappingsReader(); + private static final String NAME = "[a-zA-Z0-9_\\-.$<>]+"; + private static final String TYPE = NAME + "(?:\\[])*"; + private static final String TYPE_LIST = "|(?:(?:" + TYPE + ",)*" + TYPE + ")"; + private static final Pattern CLASS = Pattern.compile("(" + NAME + ") -> (" + NAME + "):"); + private static final Pattern FIELD = Pattern.compile(" {4}(" + TYPE + ") (" + NAME + ") -> (" + NAME + ")"); + private static final Pattern METHOD = Pattern.compile(" {4}(?:[0-9]+:[0-9]+:)?(" + TYPE + ") (" + NAME + ")\\((" + TYPE_LIST + ")\\) -> (" + NAME + ")"); + + public ProguardMappingsReader() { + } + + @Override + public EntryTree read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException { + EntryTree mappings = new HashEntryTree<>(); + + int lineNumber = 0; + ClassEntry currentClass = null; + + for (String line : Files.readAllLines(path, StandardCharsets.UTF_8)) { + lineNumber++; + + if (line.startsWith("#") || line.isEmpty()) { + continue; + } + + Matcher classMatcher = CLASS.matcher(line); + Matcher fieldMatcher = FIELD.matcher(line); + Matcher methodMatcher = METHOD.matcher(line); + + if (classMatcher.matches()) { + String name = classMatcher.group(1); + String targetName = classMatcher.group(2); + + mappings.insert(currentClass = new ClassEntry(name.replace('.', '/')), new EntryMapping(ClassEntry.getInnerName(targetName.replace('.', '/')))); + } else if (fieldMatcher.matches()) { + String type = fieldMatcher.group(1); + String name = fieldMatcher.group(2); + String targetName = fieldMatcher.group(3); + + if (currentClass == null) { + throw new MappingParseException(path::toString, lineNumber, "field mapping not inside class: " + line); + } + + mappings.insert(new FieldEntry(currentClass, name, new TypeDescriptor(getDescriptor(type))), new EntryMapping(targetName)); + } else if (methodMatcher.matches()) { + String returnType = methodMatcher.group(1); + String name = methodMatcher.group(2); + String[] parameterTypes = methodMatcher.group(3).isEmpty() ? new String[0] : methodMatcher.group(3).split(","); + String targetName = methodMatcher.group(4); + + if (currentClass == null) { + throw new MappingParseException(path::toString, lineNumber, "method mapping not inside class: " + line); + } + + mappings.insert(new MethodEntry(currentClass, name, new MethodDescriptor(getDescriptor(returnType, parameterTypes))), new EntryMapping(targetName)); + } else { + throw new MappingParseException(path::toString, lineNumber, "invalid mapping line: " + line); + } + } + + return MappingOperations.invert(mappings); + } + + private String getDescriptor(String type) { + StringBuilder descriptor = new StringBuilder(); + + while (type.endsWith("[]")) { + descriptor.append("["); + type = type.substring(0, type.length() - 2); + } + + switch (type) { + case "byte": + return descriptor + "B"; + case "char": + return descriptor + "C"; + case "short": + return descriptor + "S"; + case "int": + return descriptor + "I"; + case "long": + return descriptor + "J"; + case "float": + return descriptor + "F"; + case "double": + return descriptor + "D"; + case "boolean": + return descriptor + "Z"; + case "void": + return descriptor + "V"; + } + + descriptor.append("L"); + descriptor.append(type.replace('.', '/')); + descriptor.append(";"); + + return descriptor.toString(); + } + + private String getDescriptor(String returnType, String[] parameterTypes) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append('('); + + for (String parameterType : parameterTypes) { + descriptor.append(getDescriptor(parameterType)); + } + + descriptor.append(')'); + descriptor.append(getDescriptor(returnType)); + + return descriptor.toString(); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java index 483e4e4..ce54fea 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsReader.java @@ -1,5 +1,12 @@ package cuchaz.enigma.translation.mapping.serde.recaf; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.serde.MappingParseException; @@ -13,15 +20,7 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - public class RecafMappingsReader implements MappingsReader { - public static final RecafMappingsReader INSTANCE = new RecafMappingsReader(); private static final Pattern METHOD_PATTERN = Pattern.compile("(.*?)\\.(.*?)(\\(.*?) (.*)"); private static final Pattern FIELD_PATTERN = Pattern.compile("(.*?)\\.(.*?) (.*?) (.*)"); @@ -34,6 +33,7 @@ public class RecafMappingsReader implements MappingsReader { for (String line : lines) { Matcher methodMatcher = METHOD_PATTERN.matcher(line); + if (methodMatcher.find()) { ClassEntry owner = new ClassEntry(methodMatcher.group(1)); String name = methodMatcher.group(2); @@ -43,6 +43,7 @@ public class RecafMappingsReader implements MappingsReader { } Matcher fieldMatcher = FIELD_PATTERN.matcher(line); + if (fieldMatcher.find()) { ClassEntry owner = new ClassEntry(fieldMatcher.group(1)); String name = fieldMatcher.group(2); @@ -52,10 +53,12 @@ public class RecafMappingsReader implements MappingsReader { } Matcher classMatcher = CLASS_PATTERN.matcher(line); + if (classMatcher.find()) { mappings.insert(new ClassEntry(classMatcher.group(1)), new EntryMapping(classMatcher.group(2))); } } + return mappings; } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java index aa29ff6..df65b3c 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/recaf/RecafMappingsWriter.java @@ -1,6 +1,13 @@ package cuchaz.enigma.translation.mapping.serde.recaf; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; + import com.google.common.collect.Lists; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingDelta; @@ -13,14 +20,7 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.Writer; -import java.nio.file.Files; -import java.nio.file.Path; - public class RecafMappingsWriter implements MappingsWriter { - public static final RecafMappingsWriter INSTANCE = new RecafMappingsWriter(); @Override @@ -33,10 +33,7 @@ public class RecafMappingsWriter implements MappingsWriter { } try (BufferedWriter writer = Files.newBufferedWriter(path)) { - Lists.newArrayList(mappings) - .stream() - .map(EntryTreeNode::getEntry) - .forEach(entry -> writeEntry(writer, mappings, entry)); + Lists.newArrayList(mappings).stream().map(EntryTreeNode::getEntry).forEach(entry -> writeEntry(writer, mappings, entry)); } catch (IOException e) { e.printStackTrace(); } @@ -44,6 +41,7 @@ public class RecafMappingsWriter implements MappingsWriter { private void writeEntry(Writer writer, EntryTree mappings, Entry entry) { EntryTreeNode node = mappings.findNode(entry); + if (node == null) { return; } @@ -53,27 +51,22 @@ public class RecafMappingsWriter implements MappingsWriter { try { if (mapping != null && mapping.targetName() != null) { if (entry instanceof ClassEntry classEntry) { - writer.write(classEntry.getFullName()); writer.write(" "); writer.write(mapping.targetName()); - } else if (entry instanceof FieldEntry fieldEntry) { - writer.write(fieldEntry.getFullName()); writer.write(" "); writer.write(fieldEntry.getDesc().toString()); writer.write(" "); writer.write(mapping.targetName()); - } else if (entry instanceof MethodEntry methodEntry) { - writer.write(methodEntry.getFullName()); writer.write(methodEntry.getDesc().toString()); writer.write(" "); writer.write(mapping.targetName()); - } + writer.write("\n"); } } catch (IOException e) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java index 9275847..4621efe 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/srg/SrgMappingsWriter.java @@ -1,14 +1,24 @@ package cuchaz.enigma.translation.mapping.serde.srg; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + import com.google.common.collect.Lists; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.MappingTranslator; import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingDelta; -import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.VoidEntryResolver; import cuchaz.enigma.translation.mapping.serde.LfPrintWriter; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingsWriter; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; @@ -18,15 +28,6 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; - public enum SrgMappingsWriter implements MappingsWriter { INSTANCE; @@ -43,18 +44,18 @@ public enum SrgMappingsWriter implements MappingsWriter { List fieldLines = new ArrayList<>(); List methodLines = new ArrayList<>(); - List> rootEntries = Lists.newArrayList(mappings).stream() - .map(EntryTreeNode::getEntry) - .toList(); + List> rootEntries = Lists.newArrayList(mappings).stream().map(EntryTreeNode::getEntry).toList(); progress.init(rootEntries.size(), I18n.translate("progress.mappings.srg_file.generating")); int steps = 0; + for (Entry entry : sorted(rootEntries)) { progress.step(steps++, entry.getName()); writeEntry(classLines, fieldLines, methodLines, mappings, entry); } progress.init(3, I18n.translate("progress.mappings.srg_file.writing")); + try (PrintWriter writer = new LfPrintWriter(Files.newBufferedWriter(path))) { progress.step(0, I18n.translate("type.classes")); classLines.forEach(writer::println); @@ -69,11 +70,13 @@ public enum SrgMappingsWriter implements MappingsWriter { private void writeEntry(List classes, List fields, List methods, EntryTree mappings, Entry entry) { EntryTreeNode node = mappings.findNode(entry); + if (node == null) { return; } Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); + if (entry instanceof ClassEntry) { classes.add(generateClassLine((ClassEntry) entry, translator)); } else if (entry instanceof FieldEntry) { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java index f3c66df..e08c867 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsReader.java @@ -1,10 +1,16 @@ package cuchaz.enigma.translation.mapping.serde.tiny; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + import com.google.common.base.Charsets; + import cuchaz.enigma.ProgressListener; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingPair; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingsReader; import cuchaz.enigma.translation.mapping.tree.EntryTree; @@ -17,11 +23,6 @@ import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - public enum TinyMappingsReader implements MappingsReader { INSTANCE; @@ -62,26 +63,28 @@ public enum TinyMappingsReader implements MappingsReader { String key = tokens[0]; switch (key) { - case "CLASS": - return parseClass(tokens); - case "FIELD": - return parseField(tokens); - case "METHOD": - return parseMethod(tokens); - case "MTH-ARG": - return parseArgument(tokens); - default: - throw new RuntimeException("Unknown token '" + key + "'!"); + case "CLASS": + return parseClass(tokens); + case "FIELD": + return parseField(tokens); + case "METHOD": + return parseMethod(tokens); + case "MTH-ARG": + return parseArgument(tokens); + default: + throw new RuntimeException("Unknown token '" + key + "'!"); } } private MappingPair parseClass(String[] tokens) { ClassEntry obfuscatedEntry = new ClassEntry(tokens[1]); String mapping = tokens[2]; + if (mapping.indexOf('$') > 0) { // inner classes should map to only the final part mapping = mapping.substring(mapping.lastIndexOf('$') + 1); } + return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping)); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java index 1f785e1..972d180 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java @@ -1,14 +1,25 @@ package cuchaz.enigma.translation.mapping.serde.tiny; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + import com.google.common.base.Joiner; import com.google.common.collect.Lists; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.MappingTranslator; import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingDelta; -import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.VoidEntryResolver; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingsWriter; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; @@ -17,136 +28,120 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Set; - public class TinyMappingsWriter implements MappingsWriter { - private static final String VERSION_CONSTANT = "v1"; - private static final Joiner TAB_JOINER = Joiner.on('\t'); - - //Possibly add a gui or a way to select the namespaces when exporting from the gui - public static final TinyMappingsWriter INSTANCE = new TinyMappingsWriter("intermediary", "named"); - - // HACK: as of enigma 0.13.1, some fields seem to appear duplicated? - private final Set writtenLines = new HashSet<>(); - private final String nameObf; - private final String nameDeobf; - - public TinyMappingsWriter(String nameObf, String nameDeobf) { - this.nameObf = nameObf; - this.nameDeobf = nameDeobf; - } - - @Override - public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) { - try { - Files.deleteIfExists(path); - Files.createFile(path); - } catch (IOException e) { - e.printStackTrace(); - } - - try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { - writeLine(writer, new String[]{VERSION_CONSTANT, nameObf, nameDeobf}); - - Lists.newArrayList(mappings).stream() - .map(EntryTreeNode::getEntry).sorted(Comparator.comparing(Object::toString)) - .forEach(entry -> writeEntry(writer, mappings, entry)); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void writeEntry(Writer writer, EntryTree mappings, Entry entry) { - EntryTreeNode node = mappings.findNode(entry); - if (node == null) { - return; - } - - Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); - - EntryMapping mapping = mappings.get(entry); - - // Do not write mappings without deobfuscated name since tiny v1 doesn't - // support comments anyway + private static final String VERSION_CONSTANT = "v1"; + private static final Joiner TAB_JOINER = Joiner.on('\t'); + + //Possibly add a gui or a way to select the namespaces when exporting from the gui + public static final TinyMappingsWriter INSTANCE = new TinyMappingsWriter("intermediary", "named"); + + // HACK: as of enigma 0.13.1, some fields seem to appear duplicated? + private final Set writtenLines = new HashSet<>(); + private final String nameObf; + private final String nameDeobf; + + public TinyMappingsWriter(String nameObf, String nameDeobf) { + this.nameObf = nameObf; + this.nameDeobf = nameDeobf; + } + + @Override + public void write(EntryTree mappings, MappingDelta delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) { + try { + Files.deleteIfExists(path); + Files.createFile(path); + } catch (IOException e) { + e.printStackTrace(); + } + + try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { + writeLine(writer, new String[]{VERSION_CONSTANT, nameObf, nameDeobf}); + + Lists.newArrayList(mappings).stream().map(EntryTreeNode::getEntry).sorted(Comparator.comparing(Object::toString)).forEach(entry -> writeEntry(writer, mappings, entry)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void writeEntry(Writer writer, EntryTree mappings, Entry entry) { + EntryTreeNode node = mappings.findNode(entry); + + if (node == null) { + return; + } + + Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); + + EntryMapping mapping = mappings.get(entry); + + // Do not write mappings without deobfuscated name since tiny v1 doesn't + // support comments anyway if (mapping != null && mapping.targetName() != null) { - if (entry instanceof ClassEntry) { - writeClass(writer, (ClassEntry) entry, translator); - } else if (entry instanceof FieldEntry) { + if (entry instanceof ClassEntry) { + writeClass(writer, (ClassEntry) entry, translator); + } else if (entry instanceof FieldEntry) { writeLine(writer, serializeEntry(entry, mapping.targetName())); - } else if (entry instanceof MethodEntry) { + } else if (entry instanceof MethodEntry) { writeLine(writer, serializeEntry(entry, mapping.targetName())); - } - } - - writeChildren(writer, mappings, node); - } - - private void writeChildren(Writer writer, EntryTree mappings, EntryTreeNode node) { - node.getChildren().stream() - .filter(e -> e instanceof FieldEntry).sorted() - .forEach(child -> writeEntry(writer, mappings, child)); - - node.getChildren().stream() - .filter(e -> e instanceof MethodEntry).sorted() - .forEach(child -> writeEntry(writer, mappings, child)); - - node.getChildren().stream() - .filter(e -> e instanceof ClassEntry).sorted() - .forEach(child -> writeEntry(writer, mappings, child)); - } - - private void writeClass(Writer writer, ClassEntry entry, Translator translator) { - ClassEntry translatedEntry = translator.translate(entry); - - String obfClassName = entry.getFullName(); - String deobfClassName = translatedEntry.getFullName(); - writeLine(writer, new String[]{"CLASS", obfClassName, deobfClassName}); - } - - private void writeLine(Writer writer, String[] data) { - try { - String line = TAB_JOINER.join(data) + "\n"; - if (writtenLines.add(line)) { - writer.write(line); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private String[] serializeEntry(Entry entry, String... extraFields) { - String[] data = null; - - if (entry instanceof FieldEntry) { - data = new String[4 + extraFields.length]; - data[0] = "FIELD"; - data[1] = entry.getContainingClass().getFullName(); - data[2] = ((FieldEntry) entry).getDesc().toString(); - data[3] = entry.getName(); - } else if (entry instanceof MethodEntry) { - data = new String[4 + extraFields.length]; - data[0] = "METHOD"; - data[1] = entry.getContainingClass().getFullName(); - data[2] = ((MethodEntry) entry).getDesc().toString(); - data[3] = entry.getName(); - } else if (entry instanceof ClassEntry) { - data = new String[2 + extraFields.length]; - data[0] = "CLASS"; - data[1] = ((ClassEntry) entry).getFullName(); - } - - if (data != null) { - System.arraycopy(extraFields, 0, data, data.length - extraFields.length, extraFields.length); - } - - return data; - } + } + } + + writeChildren(writer, mappings, node); + } + + private void writeChildren(Writer writer, EntryTree mappings, EntryTreeNode node) { + node.getChildren().stream().filter(e -> e instanceof FieldEntry).sorted().forEach(child -> writeEntry(writer, mappings, child)); + + node.getChildren().stream().filter(e -> e instanceof MethodEntry).sorted().forEach(child -> writeEntry(writer, mappings, child)); + + node.getChildren().stream().filter(e -> e instanceof ClassEntry).sorted().forEach(child -> writeEntry(writer, mappings, child)); + } + + private void writeClass(Writer writer, ClassEntry entry, Translator translator) { + ClassEntry translatedEntry = translator.translate(entry); + + String obfClassName = entry.getFullName(); + String deobfClassName = translatedEntry.getFullName(); + writeLine(writer, new String[]{"CLASS", obfClassName, deobfClassName}); + } + + private void writeLine(Writer writer, String[] data) { + try { + String line = TAB_JOINER.join(data) + "\n"; + + if (writtenLines.add(line)) { + writer.write(line); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String[] serializeEntry(Entry entry, String... extraFields) { + String[] data = null; + + if (entry instanceof FieldEntry) { + data = new String[4 + extraFields.length]; + data[0] = "FIELD"; + data[1] = entry.getContainingClass().getFullName(); + data[2] = ((FieldEntry) entry).getDesc().toString(); + data[3] = entry.getName(); + } else if (entry instanceof MethodEntry) { + data = new String[4 + extraFields.length]; + data[0] = "METHOD"; + data[1] = entry.getContainingClass().getFullName(); + data[2] = ((MethodEntry) entry).getDesc().toString(); + data[3] = entry.getName(); + } else if (entry instanceof ClassEntry) { + data = new String[2 + extraFields.length]; + data[0] = "CLASS"; + data[1] = ((ClassEntry) entry).getFullName(); + } + + if (data != null) { + System.arraycopy(extraFields, 0, data, data.length - extraFields.length, extraFields.length); + } + + return data; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java index dc3246d..61bc41d 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Reader.java @@ -1,9 +1,16 @@ package cuchaz.enigma.translation.mapping.serde.tinyv2; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.BitSet; +import java.util.List; + import cuchaz.enigma.ProgressListener; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingPair; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingsReader; import cuchaz.enigma.translation.mapping.serde.RawEntryMapping; @@ -17,15 +24,7 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.BitSet; -import java.util.List; - public final class TinyV2Reader implements MappingsReader { - private static final String MINOR_VERSION = "0"; // 0 indent private static final int IN_HEADER = 0; @@ -50,8 +49,7 @@ public final class TinyV2Reader implements MappingsReader { progress.init(lines.size(), "progress.mappings.tiny_v2.loading"); BitSet state = new BitSet(STATE_SIZE); - @SuppressWarnings({"unchecked", "rawtypes"}) - MappingPair, RawEntryMapping>[] holds = new MappingPair[STATE_SIZE]; + @SuppressWarnings({"unchecked", "rawtypes"}) MappingPair, RawEntryMapping>[] holds = new MappingPair[STATE_SIZE]; boolean escapeNames = false; for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { @@ -60,16 +58,21 @@ public final class TinyV2Reader implements MappingsReader { String line = lines.get(lineNumber); int indent = 0; - while (line.charAt(indent) == '\t') + + while (line.charAt(indent) == '\t') { indent++; + } String[] parts = line.substring(indent).split("\t", -1); - if (parts.length == 0 || indent >= INDENT_CLEAR_START.length) + + if (parts.length == 0 || indent >= INDENT_CLEAR_START.length) { throw new IllegalArgumentException("Invalid format"); + } // clean and register stuff in stack for (int i = INDENT_CLEAR_START[indent]; i < STATE_SIZE; i++) { state.clear(i); + if (holds[i] != null) { bakeHeld(mappings, holds[i]); holds[i] = null; @@ -77,104 +80,112 @@ public final class TinyV2Reader implements MappingsReader { } switch (indent) { - case 0: - switch (parts[0]) { - case "tiny": // header - if (lineNumber != 0) { - throw new IllegalArgumentException("Header can only be on the first line"); - } - if (parts.length < 5) { - throw new IllegalArgumentException("Not enough header columns, needs at least 5"); - } - if (!"2".equals(parts[1]) || !MINOR_VERSION.equals(parts[2])) { - throw new IllegalArgumentException("Unsupported TinyV2 version, requires major " + "2" + " and minor " + MINOR_VERSION + ""); - } - state.set(IN_HEADER); - break; - case "c": // class - state.set(IN_CLASS); - holds[IN_CLASS] = parseClass(parts, escapeNames); - break; - default: - unsupportKey(parts); + case 0: + switch (parts[0]) { + case "tiny": // header + if (lineNumber != 0) { + throw new IllegalArgumentException("Header can only be on the first line"); + } + + if (parts.length < 5) { + throw new IllegalArgumentException("Not enough header columns, needs at least 5"); } + if (!"2".equals(parts[1]) || !MINOR_VERSION.equals(parts[2])) { + throw new IllegalArgumentException("Unsupported TinyV2 version, requires major " + "2" + " and minor " + MINOR_VERSION + ""); + } + + state.set(IN_HEADER); + break; + case "c": // class + state.set(IN_CLASS); + holds[IN_CLASS] = parseClass(parts, escapeNames); break; - case 1: - if (state.get(IN_HEADER)) { - if (parts[0].equals("esacpe-names")) { - escapeNames = true; - } + default: + unsupportKey(parts); + } - break; + break; + case 1: + if (state.get(IN_HEADER)) { + if (parts[0].equals("esacpe-names")) { + escapeNames = true; } - if (state.get(IN_CLASS)) { - switch (parts[0]) { - case "m": // method - state.set(IN_METHOD); - holds[IN_METHOD] = parseMethod(holds[IN_CLASS], parts, escapeNames); - break; - case "f": // field - state.set(IN_FIELD); - holds[IN_FIELD] = parseField(holds[IN_CLASS], parts, escapeNames); - break; - case "c": // class javadoc - addJavadoc(holds[IN_CLASS], parts); - break; - default: - unsupportKey(parts); - } + break; + } + + if (state.get(IN_CLASS)) { + switch (parts[0]) { + case "m": // method + state.set(IN_METHOD); + holds[IN_METHOD] = parseMethod(holds[IN_CLASS], parts, escapeNames); + break; + case "f": // field + state.set(IN_FIELD); + holds[IN_FIELD] = parseField(holds[IN_CLASS], parts, escapeNames); + break; + case "c": // class javadoc + addJavadoc(holds[IN_CLASS], parts); break; + default: + unsupportKey(parts); } - unsupportKey(parts); - case 2: - if (state.get(IN_METHOD)) { - switch (parts[0]) { - case "p": // parameter - state.set(IN_PARAMETER); - holds[IN_PARAMETER] = parseArgument(holds[IN_METHOD], parts, escapeNames); - break; - case "v": // local variable - // TODO add local var mapping - break; - case "c": // method javadoc - addJavadoc(holds[IN_METHOD], parts); - break; - default: - unsupportKey(parts); - } + break; + } + + unsupportKey(parts); + case 2: + if (state.get(IN_METHOD)) { + switch (parts[0]) { + case "p": // parameter + state.set(IN_PARAMETER); + holds[IN_PARAMETER] = parseArgument(holds[IN_METHOD], parts, escapeNames); break; + case "v": // local variable + // TODO add local var mapping + break; + case "c": // method javadoc + addJavadoc(holds[IN_METHOD], parts); + break; + default: + unsupportKey(parts); } - if (state.get(IN_FIELD)) { - switch (parts[0]) { - case "c": // field javadoc - addJavadoc(holds[IN_FIELD], parts); - break; - default: - unsupportKey(parts); - } + break; + } + + if (state.get(IN_FIELD)) { + switch (parts[0]) { + case "c": // field javadoc + addJavadoc(holds[IN_FIELD], parts); break; + default: + unsupportKey(parts); } - unsupportKey(parts); - case 3: - if (state.get(IN_PARAMETER)) { - switch (parts[0]) { - case "c": - addJavadoc(holds[IN_PARAMETER], parts); - break; - default: - unsupportKey(parts); - } + + break; + } + + unsupportKey(parts); + case 3: + if (state.get(IN_PARAMETER)) { + switch (parts[0]) { + case "c": + addJavadoc(holds[IN_PARAMETER], parts); break; + default: + unsupportKey(parts); } - unsupportKey(parts); - default: - unsupportKey(parts); - } + break; + } + + unsupportKey(parts); + default: + unsupportKey(parts); + } } catch (Throwable t) { t.printStackTrace(); throw new MappingParseException(path::toString, lineNumber + 1, t.toString()); @@ -193,8 +204,10 @@ public final class TinyV2Reader implements MappingsReader { private static void bakeHeld(EntryTree mappings, MappingPair, RawEntryMapping> hold2) { RawEntryMapping mapping = hold2.getMapping(); + if (mapping != null) { EntryMapping baked = mapping.bake(); + if (baked != null) { mappings.insert(hold2.getEntry(), baked); } @@ -215,8 +228,11 @@ public final class TinyV2Reader implements MappingsReader { private MappingPair parseClass(String[] tokens, boolean escapeNames) { ClassEntry obfuscatedEntry = new ClassEntry(unescapeOpt(tokens[1], escapeNames)); - if (tokens.length <= 2) + + if (tokens.length <= 2) { return new MappingPair<>(obfuscatedEntry); + } + String token2 = unescapeOpt(tokens[2], escapeNames); String mapping = token2.substring(token2.lastIndexOf('$') + 1); return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); @@ -227,8 +243,11 @@ public final class TinyV2Reader implements MappingsReader { TypeDescriptor descriptor = new TypeDescriptor(unescapeOpt(tokens[1], escapeNames)); FieldEntry obfuscatedEntry = new FieldEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor); - if (tokens.length <= 3) + + if (tokens.length <= 3) { return new MappingPair<>(obfuscatedEntry); + } + String mapping = unescapeOpt(tokens[3], escapeNames); return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); } @@ -238,24 +257,25 @@ public final class TinyV2Reader implements MappingsReader { MethodDescriptor descriptor = new MethodDescriptor(unescapeOpt(tokens[1], escapeNames)); MethodEntry obfuscatedEntry = new MethodEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor); - if (tokens.length <= 3) + + if (tokens.length <= 3) { return new MappingPair<>(obfuscatedEntry); + } + String mapping = unescapeOpt(tokens[3], escapeNames); return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); } - - private void addJavadoc(MappingPair pair, String javadoc) { RawEntryMapping mapping = pair.getMapping(); + if (mapping == null) { throw new IllegalArgumentException("Javadoc requires a mapping in enigma!"); } + mapping.addJavadocLine(unescape(javadoc)); } - - private MappingPair parseArgument(MappingPair parent, String[] tokens, boolean escapeNames) { MethodEntry ownerMethod = (MethodEntry) parent.getEntry(); int variableIndex = Integer.parseInt(tokens[1]); @@ -263,8 +283,11 @@ public final class TinyV2Reader implements MappingsReader { // tokens[2] is the useless obf name LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerMethod, variableIndex, "", true, null); - if (tokens.length <= 3) + + if (tokens.length <= 3) { return new MappingPair<>(obfuscatedEntry); + } + String mapping = unescapeOpt(tokens[3], escapeNames); return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping)); } @@ -279,7 +302,10 @@ public final class TinyV2Reader implements MappingsReader { private static String unescape(String str) { // copied from matcher, lazy! int pos = str.indexOf('\\'); - if (pos < 0) return str; + + if (pos < 0) { + return str; + } StringBuilder ret = new StringBuilder(str.length() - 1); int start = 0; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java index c400568..959d2d8 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java @@ -1,13 +1,23 @@ package cuchaz.enigma.translation.mapping.serde.tinyv2; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.StreamSupport; + import com.google.common.base.Strings; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.EntryMap; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.MappingDelta; -import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.LfPrintWriter; import cuchaz.enigma.translation.mapping.serde.MappingHelper; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingsWriter; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; @@ -17,17 +27,7 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Deque; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.StreamSupport; - public final class TinyV2Writer implements MappingsWriter { - private static final String MINOR_VERSION = "0"; private final String obfHeader; private final String deobfHeader; @@ -60,13 +60,16 @@ public final class TinyV2Writer implements MappingsWriter { String fullName = classEntry.getFullName(); writer.print(fullName); Deque parts = new LinkedList<>(); + do { EntryMapping mapping = tree.get(classEntry); + if (mapping != null && mapping.targetName() != null) { parts.addFirst(mapping.targetName()); } else { parts.addFirst(classEntry.getName()); } + classEntry = classEntry.getOuterClass(); } while (classEntry != null); @@ -82,6 +85,7 @@ public final class TinyV2Writer implements MappingsWriter { for (EntryTreeNode child : node.getChildNodes()) { Entry entry = child.getEntry(); + if (entry instanceof FieldEntry) { writeField(writer, child); } else if (entry instanceof MethodEntry) { @@ -113,16 +117,19 @@ public final class TinyV2Writer implements MappingsWriter { for (EntryTreeNode child : node.getChildNodes()) { Entry entry = child.getEntry(); + if (entry instanceof LocalVariableEntry) { writeParameter(writer, child); } + // TODO write actual local variables } } private void writeField(PrintWriter writer, EntryTreeNode node) { - if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) + if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) { return; // Shortcut + } writer.print(indent(1)); writer.print("f\t"); @@ -146,8 +153,9 @@ public final class TinyV2Writer implements MappingsWriter { } private void writeParameter(PrintWriter writer, EntryTreeNode node) { - if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) + if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT)) { return; // Shortcut + } writer.print(indent(2)); writer.print("p\t"); @@ -156,6 +164,7 @@ public final class TinyV2Writer implements MappingsWriter { writer.print(node.getEntry().getName()); writer.print("\t"); EntryMapping mapping = node.getValue(); + if (mapping == null || mapping.targetName() == null) { writer.println(); // todo ??? } else { @@ -177,5 +186,4 @@ public final class TinyV2Writer implements MappingsWriter { private String indent(int level) { return Strings.repeat("\t", level); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java index 255fa5f..b943cc8 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java @@ -1,5 +1,11 @@ package cuchaz.enigma.translation.mapping.tree; +import java.util.Collection; +import java.util.Iterator; +import java.util.stream.Stream; + +import javax.annotation.Nullable; + import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.EntryMap; import cuchaz.enigma.translation.mapping.EntryMapping; @@ -7,11 +13,6 @@ import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.MappingDelta; import cuchaz.enigma.translation.representation.entry.Entry; -import javax.annotation.Nullable; -import java.util.Collection; -import java.util.Iterator; -import java.util.stream.Stream; - public class DeltaTrackingTree implements EntryTree { private final EntryTree delegate; diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java index eb26ea9..254b331 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java @@ -1,12 +1,13 @@ package cuchaz.enigma.translation.mapping.tree; -import cuchaz.enigma.translation.representation.entry.Entry; - -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.annotation.Nullable; + +import cuchaz.enigma.translation.representation.entry.Entry; + public interface EntryTreeNode { @Nullable T getValue(); @@ -22,16 +23,16 @@ public interface EntryTreeNode { default Collection> getNodesRecursively() { Collection> nodes = new ArrayList<>(); nodes.add(this); + for (EntryTreeNode node : getChildNodes()) { nodes.addAll(node.getNodesRecursively()); } + return nodes; } default List> getChildrenRecursively() { - return getNodesRecursively().stream() - .map(EntryTreeNode::getEntry) - .toList(); + return getNodesRecursively().stream().map(EntryTreeNode::getEntry).toList(); } default boolean hasValue() { diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java index 0992d34..2902373 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java @@ -1,18 +1,27 @@ package cuchaz.enigma.translation.mapping.tree; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.EntryMap; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.representation.entry.Entry; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.*; -import java.util.function.Function; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - public class HashEntryTree implements EntryTree { private final Map, HashTreeNode> root = new HashMap<>(); @@ -29,6 +38,7 @@ public class HashEntryTree implements EntryTree { public void insert(Entry entry, T value) { List> path = computePath(entry, true); path.get(path.size() - 1).putValue(value); + if (value == null) { removeDeadAlong(path); } @@ -38,6 +48,7 @@ public class HashEntryTree implements EntryTree { @Nullable public T remove(Entry entry) { List> path = computePath(entry, false); + if (path.isEmpty()) { return null; } @@ -53,9 +64,11 @@ public class HashEntryTree implements EntryTree { @Nullable public T get(Entry entry) { HashTreeNode node = findNode(entry); + if (node == null) { return null; } + return node.getValue(); } @@ -67,18 +80,22 @@ public class HashEntryTree implements EntryTree { @Override public Collection> getChildren(Entry entry) { HashTreeNode leaf = findNode(entry); + if (leaf == null) { return Collections.emptyList(); } + return leaf.getChildren(); } @Override public Collection> getSiblings(Entry entry) { Entry parent = entry.getParent(); + if (parent == null) { return getSiblings(entry, root.keySet()); } + return getSiblings(entry, getChildren(parent)); } @@ -92,15 +109,18 @@ public class HashEntryTree implements EntryTree { @Nullable public HashTreeNode findNode(Entry target) { List> parentChain = target.getAncestry(); + if (parentChain.isEmpty()) { return null; } HashTreeNode node = root.get(parentChain.get(0)); + for (int i = 1; i < parentChain.size(); i++) { if (node == null) { return null; } + node = node.getChild(parentChain.get(i)); } @@ -109,6 +129,7 @@ public class HashEntryTree implements EntryTree { private List> computePath(Entry target, boolean make) { List> ancestry = target.getAncestry(); + if (ancestry.isEmpty()) { return Collections.emptyList(); } @@ -117,6 +138,7 @@ public class HashEntryTree implements EntryTree { Entry rootEntry = ancestry.get(0); HashTreeNode node = make ? root.computeIfAbsent(rootEntry, HashTreeNode::new) : root.get(rootEntry); + if (node == null) { return Collections.emptyList(); } @@ -126,6 +148,7 @@ public class HashEntryTree implements EntryTree { for (int i = 1; i < ancestry.size(); i++) { Entry ancestor = ancestry.get(i); node = make ? node.computeChild(ancestor) : node.getChild(ancestor); + if (node == null) { return Collections.emptyList(); } @@ -139,6 +162,7 @@ public class HashEntryTree implements EntryTree { private void removeDeadAlong(List> path) { for (int i = path.size() - 1; i >= 0; i--) { HashTreeNode node = path.get(i); + if (node.isEmpty()) { if (i > 0) { HashTreeNode parentNode = path.get(i - 1); @@ -156,17 +180,17 @@ public class HashEntryTree implements EntryTree { @Nonnull public Iterator> iterator() { Collection> nodes = new ArrayList<>(); + for (EntryTreeNode node : root.values()) { nodes.addAll(node.getNodesRecursively()); } + return nodes.iterator(); } @Override public Stream> getAllEntries() { - return StreamSupport.stream(spliterator(), false) - .filter(EntryTreeNode::hasValue) - .map(EntryTreeNode::getEntry); + return StreamSupport.stream(spliterator(), false).filter(EntryTreeNode::hasValue).map(EntryTreeNode::getEntry); } @Override @@ -182,9 +206,11 @@ public class HashEntryTree implements EntryTree { @Override public HashEntryTree translate(Translator translator, EntryResolver resolver, EntryMap mappings) { HashEntryTree translatedTree = new HashEntryTree<>(); + for (EntryTreeNode node : this) { translatedTree.insert(translator.translate(node.getEntry()), node.getValue()); } + return translatedTree; } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java index 0a990bd..3ddaf81 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java @@ -1,14 +1,15 @@ package cuchaz.enigma.translation.mapping.tree; -import cuchaz.enigma.translation.representation.entry.Entry; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import cuchaz.enigma.translation.representation.entry.Entry; + public class HashTreeNode implements EntryTreeNode, Iterable> { private final Entry entry; private final Map, HashTreeNode> children = new HashMap<>(); diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java index e8480a2..24204f8 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java @@ -1,9 +1,10 @@ package cuchaz.enigma.translation.representation; -import cuchaz.enigma.analysis.Access; +import java.lang.reflect.Modifier; + import org.objectweb.asm.Opcodes; -import java.lang.reflect.Modifier; +import cuchaz.enigma.analysis.Access; public class AccessFlags { public static final AccessFlags PRIVATE = new AccessFlags(Opcodes.ACC_PRIVATE); @@ -110,15 +111,19 @@ public class AccessFlags { @Override public String toString() { StringBuilder builder = new StringBuilder(Access.get(this).toString().toLowerCase()); + if (isStatic()) { builder.append(" static"); } + if (isSynthetic()) { builder.append(" synthetic"); } + if (isBridge()) { builder.append(" bridge"); } + return builder.toString(); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java index 13c7cd4..0854699 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java @@ -34,25 +34,20 @@ public class Lambda implements Translatable { MethodEntry samMethod = new MethodEntry(getInterface(), invokedName, samMethodType); EntryMapping samMethodMapping = resolveMapping(resolver, mappings, samMethod); - return TranslateResult.of( - samMethodMapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new Lambda( - samMethodMapping.targetName() != null ? samMethodMapping.targetName() : invokedName, - invokedType.extendedTranslate(translator, resolver, mappings).getValue(), - samMethodType.extendedTranslate(translator, resolver, mappings).getValue(), - implMethod.extendedTranslate(translator, resolver, mappings).getValue(), - instantiatedMethodType.extendedTranslate(translator, resolver, mappings).getValue() - ) - ); + return TranslateResult.of(samMethodMapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, + new Lambda(samMethodMapping.targetName() != null ? samMethodMapping.targetName() : invokedName, invokedType.extendedTranslate(translator, resolver, mappings).getValue(), samMethodType.extendedTranslate(translator, resolver, mappings).getValue(), + implMethod.extendedTranslate(translator, resolver, mappings).getValue(), instantiatedMethodType.extendedTranslate(translator, resolver, mappings).getValue())); } private EntryMapping resolveMapping(EntryResolver resolver, EntryMap mappings, MethodEntry methodEntry) { for (MethodEntry entry : resolver.resolveEntry(methodEntry, ResolutionStrategy.RESOLVE_ROOT)) { EntryMapping mapping = mappings.get(entry); + if (mapping != null) { return mapping; } } + return EntryMapping.DEFAULT; } @@ -82,14 +77,16 @@ public class Lambda implements Translatable { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + Lambda lambda = (Lambda) o; - return Objects.equals(invokedName, lambda.invokedName) && - Objects.equals(invokedType, lambda.invokedType) && - Objects.equals(samMethodType, lambda.samMethodType) && - Objects.equals(implMethod, lambda.implMethod) && - Objects.equals(instantiatedMethodType, lambda.instantiatedMethodType); + return Objects.equals(invokedName, lambda.invokedName) && Objects.equals(invokedType, lambda.invokedType) && Objects.equals(samMethodType, lambda.samMethodType) && Objects.equals(implMethod, lambda.implMethod) && Objects.equals(instantiatedMethodType, lambda.instantiatedMethodType); } @Override @@ -99,12 +96,6 @@ public class Lambda implements Translatable { @Override public String toString() { - return "Lambda{" + - "invokedName='" + invokedName + '\'' + - ", invokedType=" + invokedType + - ", samMethodType=" + samMethodType + - ", implMethod=" + implMethod + - ", instantiatedMethodType=" + instantiatedMethodType + - '}'; + return "Lambda{" + "invokedName='" + invokedName + '\'' + ", invokedType=" + invokedType + ", samMethodType=" + samMethodType + ", implMethod=" + implMethod + ", instantiatedMethodType=" + instantiatedMethodType + '}'; } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java index 998c944..571488c 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation; @@ -27,7 +27,6 @@ import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.representation.entry.ClassEntry; public class MethodDescriptor implements Translatable { - private List argumentDescs; private TypeDescriptor returnDesc; @@ -35,8 +34,10 @@ public class MethodDescriptor implements Translatable { try { this.argumentDescs = Lists.newArrayList(); int i = 0; + while (i < desc.length()) { char c = desc.charAt(i); + if (c == '(') { assert (this.argumentDescs.isEmpty()); assert (this.returnDesc == null); @@ -50,6 +51,7 @@ public class MethodDescriptor implements Translatable { i += type.length(); } } + this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i))); } catch (Exception ex) { throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex); @@ -73,9 +75,11 @@ public class MethodDescriptor implements Translatable { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("("); + for (TypeDescriptor desc : this.argumentDescs) { buf.append(desc); } + buf.append(")"); buf.append(this.returnDesc); return buf.toString(); @@ -108,23 +112,28 @@ public class MethodDescriptor implements Translatable { return true; } } + return false; } public MethodDescriptor remap(Function remapper) { List argumentDescs = new ArrayList<>(this.argumentDescs.size()); + for (TypeDescriptor desc : this.argumentDescs) { argumentDescs.add(desc.remap(remapper)); } + return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper)); } @Override public TranslateResult extendedTranslate(Translator translator, EntryResolver resolver, EntryMap mappings) { List translatedArguments = new ArrayList<>(argumentDescs.size()); + for (TypeDescriptor argument : argumentDescs) { translatedArguments.add(translator.translate(argument)); } + return TranslateResult.ungrouped(new MethodDescriptor(translatedArguments, translator.translate(returnDesc))); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java index 33b9797..a8278fc 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/Signature.java @@ -35,6 +35,7 @@ public class Signature implements Translatable { if (signature != null && !signature.isEmpty()) { return new Signature(signature, true); } + return new Signature(null, true); } @@ -42,6 +43,7 @@ public class Signature implements Translatable { if (signature != null && !signature.isEmpty()) { return new Signature(signature, false); } + return new Signature(null, false); } @@ -57,13 +59,16 @@ public class Signature implements Translatable { if (signature == null) { return this; } + SignatureWriter writer = new SignatureWriter(); SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer); + if (isType) { new SignatureReader(signature).acceptType(visitor); } else { new SignatureReader(signature).accept(visitor); } + return new Signature(writer.toString(), isType); } @@ -71,16 +76,16 @@ public class Signature implements Translatable { public boolean equals(Object obj) { if (obj instanceof Signature) { Signature other = (Signature) obj; - return (other.signature == null && signature == null || other.signature != null - && signature != null && other.signature.equals(signature)) - && other.isType == this.isType; + return (other.signature == null && signature == null || other.signature != null && signature != null && other.signature.equals(signature)) && other.isType == this.isType; } + return false; } @Override public int hashCode() { int hash = (isType ? 1 : 0) << 16; + if (signature != null) { hash |= signature.hashCode(); } @@ -97,5 +102,4 @@ public class Signature implements Translatable { public TranslateResult extendedTranslate(Translator translator, EntryResolver resolver, EntryMap mappings) { return TranslateResult.ungrouped(this.remap(name -> translator.translate(new ClassEntry(name)).getFullName())); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java index 6a1b82f..fd53522 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation; @@ -26,7 +26,6 @@ import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.representation.entry.ClassEntry; public class TypeDescriptor implements Translatable { - protected final String desc; public TypeDescriptor(String desc) { @@ -42,7 +41,6 @@ public class TypeDescriptor implements Translatable { } public static String parseFirst(String in) { - if (in == null || in.length() <= 0) { throw new IllegalArgumentException("No desc to parse, input is empty!"); } @@ -58,6 +56,7 @@ public class TypeDescriptor implements Translatable { // then check for primitives Primitive primitive = Primitive.get(c); + if (primitive != null) { return in.substring(0, 1); } @@ -74,6 +73,7 @@ public class TypeDescriptor implements Translatable { // then check for arrays int dim = countArrayDimension(in); + if (dim > 0) { String arrayType = TypeDescriptor.parseFirst(in.substring(dim)); return in.substring(0, dim + arrayType.length()); @@ -84,8 +84,11 @@ public class TypeDescriptor implements Translatable { private static int countArrayDimension(String in) { int i = 0; - while (i < in.length() && in.charAt(i) == '[') + + while (i < in.length() && in.charAt(i) == '[') { i++; + } + return i; } @@ -94,6 +97,7 @@ public class TypeDescriptor implements Translatable { // include the parameters too StringBuilder buf = new StringBuilder(); int depth = 0; + for (int i = 0; i < in.length(); i++) { char c = in.charAt(i); buf.append(c); @@ -106,6 +110,7 @@ public class TypeDescriptor implements Translatable { return buf.toString(); } } + return null; } @@ -130,6 +135,7 @@ public class TypeDescriptor implements Translatable { if (!isPrimitive()) { throw new IllegalStateException("not a primitive"); } + return Primitive.get(this.desc.charAt(0)); } @@ -142,13 +148,13 @@ public class TypeDescriptor implements Translatable { String name = this.desc.substring(1, this.desc.length() - 1); int pos = name.indexOf('<'); + if (pos >= 0) { // remove the parameters from the class name name = name.substring(0, pos); } return new ClassEntry(name); - } else if (isArray() && getArrayType().isType()) { return getArrayType().getTypeEntry(); } else { @@ -164,6 +170,7 @@ public class TypeDescriptor implements Translatable { if (!isArray()) { throw new IllegalStateException("not an array"); } + return countArrayDimension(this.desc); } @@ -171,6 +178,7 @@ public class TypeDescriptor implements Translatable { if (!isArray()) { throw new IllegalStateException("not an array"); } + return new TypeDescriptor(this.desc.substring(getArrayDimension())); } @@ -194,8 +202,10 @@ public class TypeDescriptor implements Translatable { public TypeDescriptor remap(Function remapper) { String desc = this.desc; + if (isType() || (isArray() && containsType())) { String replacedName = remapper.apply(this.getTypeEntry().getFullName()); + if (replacedName != null) { if (this.isType()) { desc = "L" + replacedName + ";"; @@ -204,28 +214,31 @@ public class TypeDescriptor implements Translatable { } } } + return new TypeDescriptor(desc); } private static String getArrayPrefix(int dimension) { StringBuilder buf = new StringBuilder(); + for (int i = 0; i < dimension; i++) { buf.append("["); } + return buf.toString(); } public int getSize() { switch (desc.charAt(0)) { - case 'J': - case 'D': - if (desc.length() == 1) { - return 2; - } else { - return 1; - } - default: + case 'J': + case 'D': + if (desc.length() == 1) { + return 2; + } else { return 1; + } + default: + return 1; } } @@ -248,6 +261,7 @@ public class TypeDescriptor implements Translatable { static { lookup = Maps.newTreeMap(); + for (Primitive val : values()) { lookup.put(val.getCode(), val); } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java index ab5a422..cb2faf0 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; @@ -39,8 +39,7 @@ public class ClassDefEntry extends ClassEntry implements DefEntry { this(parent, className, signature, access, superClass, interfaces, null); } - public ClassDefEntry(ClassEntry parent, String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, - ClassEntry[] interfaces, String javadocs) { + public ClassDefEntry(ClassEntry parent, String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, ClassEntry[] interfaces, String javadocs) { super(parent, className, javadocs); Preconditions.checkNotNull(signature, "Class signature cannot be null"); Preconditions.checkNotNull(access, "Class access cannot be null"); @@ -87,10 +86,7 @@ public class ClassDefEntry extends ClassEntry implements DefEntry { ClassEntry translatedSuper = translator.translate(superClass); ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new); String docs = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs)); } @Override diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java index b0adb2c..be5f0b3 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java @@ -1,16 +1,22 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; +import java.util.List; +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import cuchaz.enigma.source.RenamableTokenType; import cuchaz.enigma.translation.TranslateResult; import cuchaz.enigma.translation.Translator; @@ -19,11 +25,6 @@ import cuchaz.enigma.translation.mapping.IdentifierValidation; import cuchaz.enigma.translation.representation.TypeDescriptor; import cuchaz.enigma.utils.validation.ValidationContext; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.List; -import java.util.Objects; - public class ClassEntry extends ParentedEntry implements Comparable { private final String fullName; @@ -37,6 +38,7 @@ public class ClassEntry extends ParentedEntry implements Comparable< public ClassEntry(@Nullable ClassEntry parent, String className, @Nullable String javadocs) { super(parent, className, javadocs); + if (parent != null) { fullName = parent.getFullName() + "$" + name; } else { @@ -61,9 +63,11 @@ public class ClassEntry extends ParentedEntry implements Comparable< @Override public String getSimpleName() { int packagePos = name.lastIndexOf('/'); + if (packagePos > 0) { return name.substring(packagePos + 1); } + return name; } @@ -77,6 +81,7 @@ public class ClassEntry extends ParentedEntry implements Comparable< if (this.isInnerClass()) { return this.parent.getSimpleName() + "$" + this.name; } + return this.getSimpleName(); } @@ -89,10 +94,7 @@ public class ClassEntry extends ParentedEntry implements Comparable< String translatedName = mapping.targetName() != null ? mapping.targetName() : name; String docs = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new ClassEntry(parent, translatedName, docs) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new ClassEntry(parent, translatedName, docs)); } @Override @@ -160,12 +162,14 @@ public class ClassEntry extends ParentedEntry implements Comparable< if (parent == null) { return this; } + return parent.getOutermostClass(); } public ClassEntry buildClassEntry(List classChain) { assert (classChain.contains(this)); StringBuilder buf = new StringBuilder(); + for (ClassEntry chainEntry : classChain) { if (buf.length() == 0) { buf.append(chainEntry.getFullName()); @@ -178,6 +182,7 @@ public class ClassEntry extends ParentedEntry implements Comparable< break; } } + return new ClassEntry(buf.toString()); } @@ -188,9 +193,11 @@ public class ClassEntry extends ParentedEntry implements Comparable< public static String getParentPackage(String name) { int pos = name.lastIndexOf('/'); + if (pos > 0) { return name.substring(0, pos); } + return null; } @@ -215,9 +222,11 @@ public class ClassEntry extends ParentedEntry implements Comparable< } int index = name.lastIndexOf('$'); + if (index >= 0) { return new ClassEntry(name.substring(0, index)); } + return null; } @@ -227,18 +236,22 @@ public class ClassEntry extends ParentedEntry implements Comparable< } int innerClassPos = name.lastIndexOf('$'); + if (innerClassPos > 0) { return name.substring(innerClassPos + 1); } + return name; } @Override public String getSourceRemapName() { ClassEntry outerClass = getOuterClass(); + if (outerClass != null) { return outerClass.getSourceRemapName() + "." + name; } + return getSimpleName(); } @@ -246,9 +259,11 @@ public class ClassEntry extends ParentedEntry implements Comparable< public int compareTo(ClassEntry entry) { String fullName = getFullName(); String otherFullName = entry.getFullName(); + if (fullName.length() != otherFullName.length()) { return fullName.length() - otherFullName.length(); } + return fullName.compareTo(otherFullName); } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java index 956f32c..9615ca8 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; @@ -111,34 +111,42 @@ public interface Entry

    > extends Translatable { default ClassEntry getContainingClass() { ClassEntry last = null; Entry current = this; + while (current != null) { if (current instanceof ClassEntry) { last = (ClassEntry) current; break; } + current = current.getParent(); } + return Objects.requireNonNull(last, () -> String.format("%s has no containing class?", this)); } default ClassEntry getTopLevelClass() { ClassEntry last = null; Entry current = this; + while (current != null) { if (current instanceof ClassEntry) { last = (ClassEntry) current; } + current = current.getParent(); } + return Objects.requireNonNull(last, () -> String.format("%s has no top level class?", this)); } default List> getAncestry() { P parent = getParent(); List> entries = new ArrayList<>(); + if (parent != null) { entries.addAll(parent.getAncestry()); } + entries.add(this); return entries; } @@ -147,12 +155,15 @@ public interface Entry

    > extends Translatable { @SuppressWarnings("unchecked") default > E findAncestor(Class type) { List> ancestry = getAncestry(); + for (int i = ancestry.size() - 1; i >= 0; i--) { Entry ancestor = ancestry.get(i); + if (type.isAssignableFrom(ancestor.getClass())) { return (E) ancestor; } } + return null; } @@ -167,6 +178,7 @@ public interface Entry

    > extends Translatable { } P parent = getParent(); + if (parent == null) { return this; } @@ -184,6 +196,7 @@ public interface Entry

    > extends Translatable { if (parentType.equals(getParentType())) { return (Entry) this; } + return null; } } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java index 0efb6a9..492d72e 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java @@ -1,16 +1,18 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; +import javax.annotation.Nonnull; + import com.google.common.base.Preconditions; import cuchaz.enigma.source.RenamableTokenType; @@ -21,8 +23,6 @@ import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.Signature; import cuchaz.enigma.translation.representation.TypeDescriptor; -import javax.annotation.Nonnull; - public class FieldDefEntry extends FieldEntry implements DefEntry { private final AccessFlags access; private final Signature signature; @@ -59,13 +59,9 @@ public class FieldDefEntry extends FieldEntry implements DefEntry { String translatedName = mapping.targetName() != null ? mapping.targetName() : name; AccessFlags translatedAccess = mapping.accessModifier().transform(access); String docs = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs)); } - @Override public FieldDefEntry withName(String name) { return new FieldDefEntry(parent, name, desc, signature, access, javadocs); diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java index db94011..c1592a4 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; @@ -66,10 +66,7 @@ public class FieldEntry extends ParentedEntry implements Comparable< protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { String translatedName = mapping.targetName() != null ? mapping.targetName() : name; String docs = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new FieldEntry(parent, translatedName, translator.translate(desc), docs) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new FieldEntry(parent, translatedName, translator.translate(desc), docs)); } @Override diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java index c151de4..ac36a48 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java @@ -34,10 +34,7 @@ public class LocalVariableDefEntry extends LocalVariableEntry { TypeDescriptor translatedDesc = translator.translate(desc); String translatedName = mapping.targetName() != null ? mapping.targetName() : name; String javadoc = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc)); } @Override diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java index 1cf1a83..d22188b 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java @@ -17,7 +17,6 @@ import cuchaz.enigma.translation.mapping.EntryMapping; * 19/10/2016 */ public class LocalVariableEntry extends ParentedEntry implements Comparable { - protected final int index; protected final boolean parameter; @@ -53,10 +52,7 @@ public class LocalVariableEntry extends ParentedEntry implements Co protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { String translatedName = mapping.targetName() != null ? mapping.targetName() : name; String javadoc = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new LocalVariableEntry(parent, index, translatedName, parameter, javadoc) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new LocalVariableEntry(parent, index, translatedName, parameter, javadoc)); } @Override diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java index 30ef706..c6a4ab7 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; @@ -59,10 +59,7 @@ public class MethodDefEntry extends MethodEntry implements DefEntry String translatedName = mapping.targetName() != null ? mapping.targetName() : name; AccessFlags translatedAccess = mapping.accessModifier().transform(access); String docs = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs)); } @Override diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java index ab9c2d1..6fc3f0a 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; @@ -24,7 +24,6 @@ import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.representation.MethodDescriptor; public class MethodEntry extends ParentedEntry implements Comparable { - protected final MethodDescriptor descriptor; public MethodEntry(ClassEntry parent, String name, MethodDescriptor descriptor) { @@ -61,10 +60,7 @@ public class MethodEntry extends ParentedEntry implements Comparable protected TranslateResult extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) { String translatedName = mapping.targetName() != null ? mapping.targetName() : name; String docs = mapping.javadoc(); - return TranslateResult.of( - mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, - new MethodEntry(parent, translatedName, translator.translate(descriptor), docs) - ); + return TranslateResult.of(mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, new MethodEntry(parent, translatedName, translator.translate(descriptor), docs)); } @Override @@ -97,6 +93,7 @@ public class MethodEntry extends ParentedEntry implements Comparable MethodEntry methodEntry = (MethodEntry) entry; return methodEntry.parent.equals(parent) && methodEntry.descriptor.canConflictWith(descriptor); } + return false; } diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java index 267bc11..ff5ffa3 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.translation.representation.entry; @@ -80,9 +80,11 @@ public abstract class ParentedEntry

    > implements Entry

    { public TranslateResult> extendedTranslate(Translator translator, EntryResolver resolver, EntryMap mappings) { P parent = getParent(); EntryMapping mapping = resolveMapping(resolver, mappings); + if (parent == null) { return this.extendedTranslate(translator, mapping); } + P translatedParent = translator.translate(parent); return this.withParent(translatedParent).extendedTranslate(translator, mapping); } @@ -90,10 +92,12 @@ public abstract class ParentedEntry

    > implements Entry

    { private EntryMapping resolveMapping(EntryResolver resolver, EntryMap mappings) { for (ParentedEntry

    entry : resolver.resolveEntry(this, ResolutionStrategy.RESOLVE_ROOT)) { EntryMapping mapping = mappings.get(entry); + if (mapping != null) { return mapping; } } + return EntryMapping.DEFAULT; } } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java b/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java index 7d34b02..6732a59 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/AsmUtil.java @@ -5,16 +5,16 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; public class AsmUtil { - public static byte[] nodeToBytes(ClassNode node) { - ClassWriter w = new ClassWriter(0); - node.accept(w); - return w.toByteArray(); - } + public static byte[] nodeToBytes(ClassNode node) { + ClassWriter w = new ClassWriter(0); + node.accept(w); + return w.toByteArray(); + } - public static ClassNode bytesToNode(byte[] bytes) { - ClassReader r = new ClassReader(bytes); - ClassNode node = new ClassNode(); - r.accept(node, 0); - return node; - } + public static ClassNode bytesToNode(byte[] bytes) { + ClassReader r = new ClassReader(bytes); + ClassNode node = new ClassNode(); + r.accept(node, 0); + return node; + } } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/I18n.java b/enigma/src/main/java/cuchaz/enigma/utils/I18n.java index 26e5b27..9551202 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/I18n.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/I18n.java @@ -5,7 +5,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -33,12 +37,16 @@ public class I18n { } catch (IOException e) { e.printStackTrace(); } + return Collections.emptyMap(); } public static String translateOrNull(String key) { String value = translations.get(key); - if (value != null) return value; + + if (value != null) { + return value; + } return defaultTranslations.get(key); } @@ -50,6 +58,7 @@ public class I18n { public static String translateOrEmpty(String key, Object... args) { String text = translateOrNull(key); + if (text != null) { return String.format(text, args); } else { @@ -59,6 +68,7 @@ public class I18n { public static String translateFormatted(String key, Object... args) { String text = translateOrNull(key); + if (text != null) { return String.format(text, args); } else if (args.length == 0) { @@ -84,6 +94,7 @@ public class I18n { Stream dirStream = resources.stream(); dirStream.forEach(context -> { String file = context.getResourceName(); + if (file.startsWith("lang/") && file.endsWith(".json")) { String fileName = file.substring(5, file.length() - 5); list.add(fileName); @@ -93,6 +104,7 @@ public class I18n { } catch (IOException e) { e.printStackTrace(); } + return list; } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Os.java b/enigma/src/main/java/cuchaz/enigma/utils/Os.java index b493c04..eaa9360 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/Os.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/Os.java @@ -14,12 +14,12 @@ public enum Os { public static Os getOs() { if (os == null) { String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); + if (osName.contains("mac") || osName.contains("darwin")) { os = MAC; } else if (osName.contains("win")) { os = WINDOWS; - } else if (osName.contains("nix") || osName.contains("nux") - || osName.contains("aix")) { + } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) { os = LINUX; } else if (osName.contains("sunos")) { os = SOLARIS; @@ -27,7 +27,7 @@ public enum Os { os = OTHER; } } + return os; } - -} \ No newline at end of file +} diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Pair.java b/enigma/src/main/java/cuchaz/enigma/utils/Pair.java index bf02cef..10752ac 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/Pair.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/Pair.java @@ -3,24 +3,21 @@ package cuchaz.enigma.utils; import java.util.Objects; public class Pair { - public final A a; - public final B b; + public final A a; + public final B b; - public Pair(A a, B b) { - this.a = a; - this.b = b; - } + public Pair(A a, B b) { + this.a = a; + this.b = b; + } - @Override - public int hashCode() { - return Objects.hashCode(a) * 31 + - Objects.hashCode(b); - } + @Override + public int hashCode() { + return Objects.hashCode(a) * 31 + Objects.hashCode(b); + } - @Override - public boolean equals(Object o) { - return o instanceof Pair && - Objects.equals(a, ((Pair) o).a) && - Objects.equals(b, ((Pair) o).b); - } + @Override + public boolean equals(Object o) { + return o instanceof Pair && Objects.equals(a, ((Pair) o).a) && Objects.equals(b, ((Pair) o).b); + } } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Result.java b/enigma/src/main/java/cuchaz/enigma/utils/Result.java index dcaabd5..354418a 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/Result.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/Result.java @@ -5,7 +5,6 @@ import java.util.Optional; import java.util.function.Function; public final class Result { - private final T ok; private final E err; @@ -39,56 +38,85 @@ public final class Result { } public T unwrap() { - if (this.isOk()) return this.ok; + if (this.isOk()) { + return this.ok; + } + throw new IllegalStateException(String.format("Called Result.unwrap on an Err value: %s", this.err)); } public E unwrapErr() { - if (this.isErr()) return this.err; + if (this.isErr()) { + return this.err; + } + throw new IllegalStateException(String.format("Called Result.unwrapErr on an Ok value: %s", this.ok)); } public T unwrapOr(T fallback) { - if (this.isOk()) return this.ok; + if (this.isOk()) { + return this.ok; + } + return fallback; } public T unwrapOrElse(Function fn) { - if (this.isOk()) return this.ok; + if (this.isOk()) { + return this.ok; + } + return fn.apply(this.err); } @SuppressWarnings("unchecked") public Result map(Function op) { - if (!this.isOk()) return (Result) this; + if (!this.isOk()) { + return (Result) this; + } + return Result.ok(op.apply(this.ok)); } @SuppressWarnings("unchecked") public Result mapErr(Function op) { - if (!this.isErr()) return (Result) this; + if (!this.isErr()) { + return (Result) this; + } + return Result.err(op.apply(this.err)); } @SuppressWarnings("unchecked") public Result and(Result next) { - if (this.isErr()) return (Result) this; + if (this.isErr()) { + return (Result) this; + } + return next; } @SuppressWarnings("unchecked") public Result andThen(Function> op) { - if (this.isErr()) return (Result) this; + if (this.isErr()) { + return (Result) this; + } + return op.apply(this.ok); } @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + Result result = (Result) o; - return Objects.equals(ok, result.ok) && - Objects.equals(err, result.err); + return Objects.equals(ok, result.ok) && Objects.equals(err, result.err); } @Override @@ -104,5 +132,4 @@ public final class Result { return String.format("Result.Err(%s)", this.err); } } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java index 864154c..2b4ed37 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java @@ -3,7 +3,6 @@ package cuchaz.enigma.utils; import java.util.Objects; public final class TristateChange { - private static final TristateChange UNCHANGED = new TristateChange<>(Type.UNCHANGED, null); private static final TristateChange RESET = new TristateChange<>(Type.RESET, null); @@ -46,17 +45,25 @@ public final class TristateChange { } public T getNewValue() { - if (this.type != Type.SET) throw new IllegalStateException(String.format("No concrete value in %s", this)); + if (this.type != Type.SET) { + throw new IllegalStateException(String.format("No concrete value in %s", this)); + } + return this.val; } @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + TristateChange that = (TristateChange) o; - return type == that.type && - Objects.equals(val, that.val); + return type == that.type && Objects.equals(val, that.val); } @Override @@ -74,5 +81,4 @@ public final class TristateChange { RESET, SET, } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/Utils.java b/enigma/src/main/java/cuchaz/enigma/utils/Utils.java index ad4e936..081c941 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/Utils.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/Utils.java @@ -1,18 +1,16 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

    - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ +* Copyright (c) 2015 Jeff Martin. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the GNU Lesser General Public +* License v3.0 which accompanies this distribution, and is available at +* http://www.gnu.org/licenses/lgpl.html +* +*

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.utils; -import com.google.common.io.CharStreams; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,70 +28,80 @@ import java.util.function.Supplier; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import com.google.common.io.CharStreams; + public class Utils { - public static String readStreamToString(InputStream in) throws IOException { - return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)); - } - - public static String readResourceToString(String path) throws IOException { - InputStream in = Utils.class.getResourceAsStream(path); - if (in == null) { - throw new IllegalArgumentException("Resource not found! " + path); - } - return readStreamToString(in); - } - - public static void delete(Path path) throws IOException { - if (Files.exists(path)) { - for (Path p : Files.walk(path).sorted(Comparator.reverseOrder()).toList()) { - Files.delete(p); - } - } - } - - public static byte[] zipSha1(Path path) throws IOException { - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - // Algorithm guaranteed to be supported - throw new RuntimeException(e); - } - try (ZipFile zip = new ZipFile(path.toFile())) { - List entries = Collections.list(zip.entries()); - // only compare classes (some implementations may not generate directory entries) - entries.removeIf(entry -> !entry.getName().toLowerCase(Locale.ROOT).endsWith(".class")); - // different implementations may add zip entries in a different order - entries.sort(Comparator.comparing(ZipEntry::getName)); - byte[] buffer = new byte[8192]; - for (ZipEntry entry : entries) { - digest.update(entry.getName().getBytes(StandardCharsets.UTF_8)); - try (InputStream in = zip.getInputStream(entry)) { - int n; - while ((n = in.read(buffer)) != -1) { - digest.update(buffer, 0, n); - } - } - } - } - return digest.digest(); - } - - public static void withLock(Lock l, Runnable op) { - try { - l.lock(); - op.run(); - } finally { - l.unlock(); - } - } - - public static R withLock(Lock l, Supplier op) { - try { - l.lock(); - return op.get(); - } finally { - l.unlock(); - } - } + public static String readStreamToString(InputStream in) throws IOException { + return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)); + } + + public static String readResourceToString(String path) throws IOException { + InputStream in = Utils.class.getResourceAsStream(path); + + if (in == null) { + throw new IllegalArgumentException("Resource not found! " + path); + } + + return readStreamToString(in); + } + + public static void delete(Path path) throws IOException { + if (Files.exists(path)) { + for (Path p : Files.walk(path).sorted(Comparator.reverseOrder()).toList()) { + Files.delete(p); + } + } + } + + public static byte[] zipSha1(Path path) throws IOException { + MessageDigest digest; + + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + // Algorithm guaranteed to be supported + throw new RuntimeException(e); + } + + try (ZipFile zip = new ZipFile(path.toFile())) { + List entries = Collections.list(zip.entries()); + // only compare classes (some implementations may not generate directory entries) + entries.removeIf(entry -> !entry.getName().toLowerCase(Locale.ROOT).endsWith(".class")); + // different implementations may add zip entries in a different order + entries.sort(Comparator.comparing(ZipEntry::getName)); + byte[] buffer = new byte[8192]; + + for (ZipEntry entry : entries) { + digest.update(entry.getName().getBytes(StandardCharsets.UTF_8)); + + try (InputStream in = zip.getInputStream(entry)) { + int n; + + while ((n = in.read(buffer)) != -1) { + digest.update(buffer, 0, n); + } + } + } + } + + return digest.digest(); + } + + public static void withLock(Lock l, Runnable op) { + try { + l.lock(); + op.run(); + } finally { + l.unlock(); + } + } + + public static R withLock(Lock l, Supplier op) { + try { + l.lock(); + return op.get(); + } finally { + l.unlock(); + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java index 6bcdbde..b7e67f2 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java @@ -3,7 +3,6 @@ package cuchaz.enigma.utils.validation; import cuchaz.enigma.utils.I18n; public class Message { - public static final Message EMPTY_FIELD = create(Type.ERROR, "empty_field"); public static final Message INVALID_IP = create(Type.ERROR, "invalid_ip"); public static final Message NOT_INT = create(Type.ERROR, "not_int"); @@ -46,5 +45,4 @@ public class Message { WARNING, ERROR, } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java index 9ad5867..afcbf62 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.Objects; public final class ParameterizedMessage { - public final Message message; private final Object[] params; private final Validatable target; @@ -25,11 +24,15 @@ public final class ParameterizedMessage { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ParameterizedMessage that)) return false; - return Objects.equals(message, that.message) && - Arrays.equals(params, that.params) && - Objects.equals(target, that.target); + if (this == o) { + return true; + } + + if (!(o instanceof ParameterizedMessage that)) { + return false; + } + + return Objects.equals(message, that.message) && Arrays.equals(params, that.params) && Objects.equals(target, that.target); } @Override @@ -43,5 +46,4 @@ public final class ParameterizedMessage { public String toString() { return String.format("ParameterizedMessage { message: %s, params: %s, target: %s }", message, Arrays.toString(params), target); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java index 5067d7e..8a7a9ed 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java @@ -4,7 +4,6 @@ import java.io.PrintStream; import java.util.Arrays; public class PrintValidatable implements Validatable { - public static final PrintValidatable INSTANCE = new PrintValidatable(); @Override @@ -16,9 +15,9 @@ public class PrintValidatable implements Validatable { String text = message.getText(); String longText = message.getLongText(); String type = switch (message.message.type) { - case INFO -> "info"; - case WARNING -> "warning"; - case ERROR -> "error"; + case INFO -> "info"; + case WARNING -> "warning"; + case ERROR -> "error"; }; w.printf("%s: %s\n", type, text); @@ -30,5 +29,4 @@ public class PrintValidatable implements Validatable { @Override public void clearMessages() { } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java index 871b59d..123c9b6 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java @@ -1,17 +1,20 @@ package cuchaz.enigma.utils.validation; public class StandardValidation { - public static boolean notBlank(ValidationContext vc, String value) { if (value.trim().isEmpty()) { vc.raise(Message.EMPTY_FIELD); return false; } + return true; } public static boolean isInt(ValidationContext vc, String value) { - if (!notBlank(vc, value)) return false; + if (!notBlank(vc, value)) { + return false; + } + try { Integer.parseInt(value); return true; @@ -22,13 +25,17 @@ public class StandardValidation { } public static boolean isIntInRange(ValidationContext vc, String value, int min, int max) { - if (!isInt(vc, value)) return false; + if (!isInt(vc, value)) { + return false; + } + int intVal = Integer.parseInt(value); + if (intVal < min || intVal > max) { vc.raise(Message.FIELD_OUT_OF_RANGE_INT, min, max); return false; } + return true; } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java index 765ee08..39e8c41 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java @@ -1,9 +1,7 @@ package cuchaz.enigma.utils.validation; public interface Validatable { - void addMessage(ParameterizedMessage message); void clearMessages(); - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java index 0ecb9fb..416e8a0 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java @@ -1,6 +1,10 @@ package cuchaz.enigma.utils.validation; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import javax.annotation.Nullable; @@ -15,7 +19,6 @@ import cuchaz.enigma.utils.validation.Message.Type; * multiple errors and displaying them to the user at the same time. */ public class ValidationContext { - private Validatable activeElement = null; private final Set elements = new HashSet<>(); private final List messages = new ArrayList<>(); @@ -30,6 +33,7 @@ public class ValidationContext { if (v != null) { elements.add(v); } + activeElement = v; } @@ -38,14 +42,16 @@ public class ValidationContext { * that element about the message. * * @param message the message to raise - * @param args the arguments used when formatting the message text + * @param args the arguments used when formatting the message text */ public void raise(Message message, Object... args) { ParameterizedMessage pm = new ParameterizedMessage(message, args, this.activeElement); + if (!this.messages.contains(pm)) { if (activeElement != null) { activeElement.addMessage(pm); } + messages.add(pm); } } @@ -72,6 +78,7 @@ public class ValidationContext { for (ParameterizedMessage message : this.messages) { PrintValidatable.formatMessage(System.err, message); } + throw new IllegalStateException("Errors encountered; cannot continue! Check error log for details."); } } @@ -90,5 +97,4 @@ public class ValidationContext { elements.clear(); messages.clear(); } - } -- cgit v1.2.3