From 9c736848fb7aa82d295b3aa2946e6cd132ee998f Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 14 Sep 2022 13:12:55 +0100 Subject: Add checkstyle (#460) --- .editorconfig | 5 + .github/workflows/build.yml | 9 +- .github/workflows/release.yml | 2 +- README.md | 15 +- build.gradle | 156 ++-- checkstyle.xml | 170 +++++ enigma-cli/build.gradle | 16 +- .../enigma/command/CheckMappingsCommand.java | 25 +- .../main/java/cuchaz/enigma/command/Command.java | 32 +- .../enigma/command/ComposeMappingsCommand.java | 64 +- .../enigma/command/ConvertMappingsCommand.java | 58 +- .../cuchaz/enigma/command/DecompileCommand.java | 11 +- .../cuchaz/enigma/command/DeobfuscateCommand.java | 5 +- .../enigma/command/InvertMappingsCommand.java | 62 +- .../src/main/java/cuchaz/enigma/command/Main.java | 33 +- .../command/MapSpecializedMethodsCommand.java | 92 +-- .../cuchaz/enigma/command/MappingCommandsUtil.java | 151 ++-- .../enigma/command/CheckMappingsCommandTest.java | 10 +- enigma-server/build.gradle | 6 +- enigma-server/docs/protocol.md | 50 +- .../cuchaz/enigma/network/ClientPacketHandler.java | 18 +- .../enigma/network/DedicatedEnigmaServer.java | 74 +- .../java/cuchaz/enigma/network/EnigmaClient.java | 25 +- .../java/cuchaz/enigma/network/EnigmaServer.java | 47 +- .../enigma/network/IntegratedEnigmaServer.java | 4 +- .../main/java/cuchaz/enigma/network/Message.java | 155 ++-- .../java/cuchaz/enigma/network/ServerAddress.java | 44 +- .../cuchaz/enigma/network/ServerPacketHandler.java | 1 - .../network/packet/ConfirmChangeC2SPacket.java | 4 +- .../network/packet/EntryChangeC2SPacket.java | 2 - .../network/packet/EntryChangeS2CPacket.java | 2 - .../enigma/network/packet/KickS2CPacket.java | 4 +- .../enigma/network/packet/LoginC2SPacket.java | 13 +- .../enigma/network/packet/MessageC2SPacket.java | 5 +- .../enigma/network/packet/MessageS2CPacket.java | 2 - .../java/cuchaz/enigma/network/packet/Packet.java | 2 - .../cuchaz/enigma/network/packet/PacketHelper.java | 109 +-- .../enigma/network/packet/PacketRegistry.java | 2 - .../network/packet/SyncMappingsS2CPacket.java | 9 +- .../enigma/network/packet/UserListS2CPacket.java | 8 +- .../cuchaz/enigma/network/ServerAddressTest.java | 6 +- enigma-swing/build.gradle | 30 +- .../main/java/cuchaz/enigma/gui/BrowserCaret.java | 20 +- .../main/java/cuchaz/enigma/gui/ClassSelector.java | 69 +- .../main/java/cuchaz/enigma/gui/EditableType.java | 9 +- .../cuchaz/enigma/gui/EnigmaQuickFindDialog.java | 26 +- .../java/cuchaz/enigma/gui/EnigmaSyntaxKit.java | 55 +- .../java/cuchaz/enigma/gui/ExceptionIgnorer.java | 24 +- .../src/main/java/cuchaz/enigma/gui/Gui.java | 127 ++-- .../main/java/cuchaz/enigma/gui/GuiController.java | 149 ++-- .../src/main/java/cuchaz/enigma/gui/Main.java | 106 ++- .../java/cuchaz/enigma/gui/NestedPackages.java | 37 +- .../java/cuchaz/enigma/gui/QuickFindAction.java | 9 +- .../main/java/cuchaz/enigma/gui/ReadableToken.java | 19 +- .../cuchaz/enigma/gui/TokenListCellRenderer.java | 30 +- .../java/cuchaz/enigma/gui/config/LookAndFeel.java | 11 +- .../java/cuchaz/enigma/gui/config/NetConfig.java | 2 - .../enigma/gui/config/OldConfigImporter.java | 4 +- .../main/java/cuchaz/enigma/gui/config/Themes.java | 9 +- .../java/cuchaz/enigma/gui/config/UiConfig.java | 15 +- .../cuchaz/enigma/gui/config/legacy/Config.java | 20 +- .../java/cuchaz/enigma/gui/dialog/AboutDialog.java | 31 +- .../cuchaz/enigma/gui/dialog/AbstractDialog.java | 12 +- .../cuchaz/enigma/gui/dialog/ChangeDialog.java | 2 - .../enigma/gui/dialog/ConnectToServerDialog.java | 27 +- .../java/cuchaz/enigma/gui/dialog/CrashDialog.java | 50 +- .../enigma/gui/dialog/CreateServerDialog.java | 24 +- .../java/cuchaz/enigma/gui/dialog/FontDialog.java | 30 +- .../cuchaz/enigma/gui/dialog/JavadocDialog.java | 74 +- .../cuchaz/enigma/gui/dialog/ProgressDialog.java | 33 +- .../cuchaz/enigma/gui/dialog/SearchDialog.java | 92 +-- .../java/cuchaz/enigma/gui/dialog/StatsDialog.java | 23 +- .../gui/elements/AbstractInheritanceTree.java | 9 +- .../java/cuchaz/enigma/gui/elements/CallsTree.java | 16 +- .../enigma/gui/elements/CollapsibleTabbedPane.java | 11 +- .../enigma/gui/elements/ConvertingTextField.java | 43 +- .../enigma/gui/elements/DeobfPanelPopupMenu.java | 101 ++- .../enigma/gui/elements/EditorPopupMenu.java | 66 +- .../enigma/gui/elements/EditorTabPopupMenu.java | 1 - .../enigma/gui/elements/EditorTabbedPane.java | 11 +- .../enigma/gui/elements/JMultiLineToolTip.java | 19 +- .../java/cuchaz/enigma/gui/elements/MenuBar.java | 99 ++- .../java/cuchaz/enigma/gui/elements/StatusBar.java | 2 +- .../gui/elements/ValidatablePasswordField.java | 2 - .../enigma/gui/elements/ValidatableTextArea.java | 2 - .../enigma/gui/elements/ValidatableTextField.java | 2 - .../cuchaz/enigma/gui/elements/ValidatableUi.java | 43 +- .../gui/events/ConvertingTextFieldListener.java | 2 - .../enigma/gui/events/EditorActionListener.java | 2 - .../enigma/gui/events/ThemeChangeListener.java | 2 - .../enigma/gui/highlight/BoxHighlightPainter.java | 19 +- .../gui/highlight/SelectionHighlightPainter.java | 26 +- .../enigma/gui/newabstraction/EntryValidation.java | 3 +- .../enigma/gui/node/ClassSelectorClassNode.java | 34 +- .../enigma/gui/node/ClassSelectorPackageNode.java | 27 +- .../enigma/gui/panels/ClosableTabTitlePane.java | 28 +- .../java/cuchaz/enigma/gui/panels/DeobfPanel.java | 3 +- .../java/cuchaz/enigma/gui/panels/EditorPanel.java | 223 ++++-- .../cuchaz/enigma/gui/panels/IdentifierPanel.java | 48 +- .../java/cuchaz/enigma/gui/panels/ObfPanel.java | 4 +- .../cuchaz/enigma/gui/panels/StructurePanel.java | 291 ++++---- .../enigma/gui/renderer/CallsTreeCellRenderer.java | 78 +- .../renderer/ImplementationsTreeCellRenderer.java | 39 +- .../gui/renderer/InheritanceTreeCellRenderer.java | 19 +- .../gui/renderer/MessageListCellRenderer.java | 4 +- .../renderer/StructureOptionListCellRenderer.java | 25 +- .../java/cuchaz/enigma/gui/search/SearchEntry.java | 2 - .../java/cuchaz/enigma/gui/search/SearchUtil.java | 71 +- .../cuchaz/enigma/gui/stats/StatsGenerator.java | 212 +++--- .../java/cuchaz/enigma/gui/stats/StatsMember.java | 8 +- .../java/cuchaz/enigma/gui/stats/StatsResult.java | 6 +- .../enigma/gui/util/AbstractListCellRenderer.java | 17 +- .../enigma/gui/util/GridBagConstraintsBuilder.java | 2 - .../main/java/cuchaz/enigma/gui/util/GuiUtil.java | 334 ++++----- .../main/java/cuchaz/enigma/gui/util/History.java | 4 +- .../enigma/gui/util/LanguageChangeListener.java | 2 - .../java/cuchaz/enigma/gui/util/LanguageUtil.java | 2 - .../enigma/gui/util/ScaleChangeListener.java | 2 - .../java/cuchaz/enigma/gui/util/ScaleUtil.java | 5 +- .../enigma/gui/util/SingleTreeSelectionModel.java | 7 +- enigma/build.gradle | 74 +- 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 +- enigma/src/test/java/cuchaz/enigma/ConfigTest.java | 32 +- .../cuchaz/enigma/PackageVisibilityIndexTest.java | 43 +- .../src/test/java/cuchaz/enigma/TestDeobfed.java | 54 +- .../test/java/cuchaz/enigma/TestDeobfuscator.java | 30 +- .../test/java/cuchaz/enigma/TestEntryFactory.java | 22 +- .../test/java/cuchaz/enigma/TestInnerClasses.java | 49 +- .../enigma/TestJarIndexConstructorReferences.java | 79 +-- .../cuchaz/enigma/TestJarIndexInheritanceTree.java | 114 ++- .../java/cuchaz/enigma/TestJarIndexLoneClass.java | 69 +- .../java/cuchaz/enigma/TestMethodDescriptor.java | 116 ++- .../java/cuchaz/enigma/TestTokensConstructors.java | 98 +-- .../test/java/cuchaz/enigma/TestTranslator.java | 31 +- .../java/cuchaz/enigma/TestTypeDescriptor.java | 29 +- .../src/test/java/cuchaz/enigma/TokenChecker.java | 40 +- .../src/test/java/cuchaz/enigma/inputs/Keep.java | 18 +- .../enigma/inputs/constructors/BaseClass.java | 19 +- .../cuchaz/enigma/inputs/constructors/Caller.java | 19 +- .../inputs/constructors/DefaultConstructable.java | 18 +- .../enigma/inputs/constructors/SubClass.java | 19 +- .../enigma/inputs/constructors/SubSubClass.java | 19 +- .../enigma/inputs/inheritanceTree/BaseClass.java | 19 +- .../enigma/inputs/inheritanceTree/SubclassA.java | 19 +- .../enigma/inputs/inheritanceTree/SubclassB.java | 19 +- .../inputs/inheritanceTree/SubsubclassAA.java | 19 +- .../enigma/inputs/innerClasses/A_Anonymous.java | 19 +- .../innerClasses/B_AnonymousWithScopeArgs.java | 19 +- .../inputs/innerClasses/C_ConstructorArgs.java | 22 +- .../enigma/inputs/innerClasses/D_Simple.java | 19 +- .../innerClasses/E_AnonymousWithOuterAccess.java | 19 +- .../enigma/inputs/innerClasses/F_ClassTree.java | 22 +- .../cuchaz/enigma/inputs/loneClass/LoneClass.java | 19 +- .../inputs/packageAccess/SamePackageChild.java | 1 - .../packageAccess/sub/OtherPackageChild.java | 1 - .../cuchaz/enigma/inputs/translation/A_Basic.java | 19 +- .../enigma/inputs/translation/B_BaseClass.java | 19 +- .../enigma/inputs/translation/C_SubClass.java | 19 +- .../inputs/translation/D_AnonymousTesting.java | 19 +- .../enigma/inputs/translation/E_Bridges.java | 19 +- .../enigma/inputs/translation/F_ObjectMethods.java | 22 +- .../enigma/inputs/translation/G_OuterClass.java | 27 +- .../enigma/inputs/translation/H_NamelessClass.java | 33 +- .../enigma/inputs/translation/I_Generics.java | 19 +- .../enigma/translation/mapping/TestComments.java | 38 +- .../translation/mapping/TestReadWriteCycle.java | 43 +- .../mapping/TestTinyV2InnerClasses.java | 27 +- .../enigma/translation/mapping/TestV2Main.java | 6 +- .../translation/mapping/serde/recaf/TestRecaf.java | 52 +- 328 files changed, 7514 insertions(+), 6406 deletions(-) create mode 100644 .editorconfig create mode 100644 checkstyle.xml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..308508db --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +[*.{gradle,java,kotlin}] +indent_style = tab +ij_continuation_indent_size = 8 +ij_java_imports_layout = $*, |, java.**, |, javax.**, |, *, |, cuchaz.enigma.** +ij_java_class_count_to_use_import_on_demand = 999 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93a2e1fa..0abdf58e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,10 @@ name: Build -on: [push, pull_request] +on: [ push, pull_request ] jobs: build: strategy: matrix: - java: [17-jdk] + java: [ 17-jdk ] runs-on: ubuntu-20.04 container: image: openjdk:${{ matrix.java }} @@ -13,3 +13,8 @@ jobs: - uses: actions/checkout@v1 - uses: gradle/wrapper-validation-action@v1 - run: ./gradlew build --stacktrace --warning-mode fail + - uses: Juuxel/publish-checkstyle-report@v1 + if: ${{ failure() }} + with: + reports: | + **/build/reports/checkstyle/*.xml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48aacde3..9a4dc97d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,5 @@ name: Release -on: [workflow_dispatch] # Manual trigger +on: [ workflow_dispatch ] # Manual trigger jobs: build: runs-on: ubuntu-20.04 diff --git a/README.md b/README.md index f3ab4c79..b156364d 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,14 @@ A tool for deobfuscation of Java bytecode. Forked from + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/enigma-cli/build.gradle b/enigma-cli/build.gradle index 5b84196e..5281e9e6 100644 --- a/enigma-cli/build.gradle +++ b/enigma-cli/build.gradle @@ -1,10 +1,10 @@ plugins { - id 'application' - id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'application' + id 'com.github.johnrengelman.shadow' version '7.0.0' } dependencies { - implementation project(':enigma') + implementation project(':enigma') } mainClassName = 'cuchaz.enigma.command.Main' @@ -12,9 +12,9 @@ mainClassName = 'cuchaz.enigma.command.Main' jar.manifest.attributes 'Main-Class': mainClassName publishing { - publications { - shadow(MavenPublication) { publication -> - project.shadow.component publication - } - } + publications { + shadow(MavenPublication) { publication -> + project.shadow.component publication + } + } } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java index 75ef225a..922d6688 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/CheckMappingsCommand.java @@ -1,22 +1,21 @@ package cuchaz.enigma.command; +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Collectors; + import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.classprovider.ClasspathClassProvider; import cuchaz.enigma.translation.mapping.EntryMapping; -import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingFormat; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.translation.representation.entry.ClassEntry; -import java.nio.file.Path; -import java.util.Set; -import java.util.stream.Collectors; - public class CheckMappingsCommand extends Command { - public CheckMappingsCommand() { super("checkmappings"); } @@ -55,19 +54,11 @@ public class CheckMappingsCommand extends Command { boolean error = false; for (Set partition : idx.getPackageVisibilityIndex().getPartitions()) { - long packages = partition.stream() - .map(project.getMapper()::deobfuscate) - .map(ClassEntry::getPackageName) - .distinct() - .count(); + long packages = partition.stream().map(project.getMapper()::deobfuscate).map(ClassEntry::getPackageName).distinct().count(); + if (packages > 1) { error = true; - System.err.println("ERROR: Must be in one package:\n" + partition.stream() - .map(project.getMapper()::deobfuscate) - .map(ClassEntry::toString) - .sorted() - .collect(Collectors.joining("\n")) - ); + System.err.println("ERROR: Must be in one package:\n" + partition.stream().map(project.getMapper()::deobfuscate).map(ClassEntry::toString).sorted().collect(Collectors.joining("\n"))); } } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java index 0d71f028..04d49f2f 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/Command.java @@ -1,21 +1,21 @@ package cuchaz.enigma.command; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.google.common.io.MoreFiles; + import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.classprovider.ClasspathClassProvider; import cuchaz.enigma.translation.mapping.EntryMapping; -import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.MappingFormat; +import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import com.google.common.io.MoreFiles; - public abstract class Command { public final String name; @@ -63,15 +63,19 @@ public abstract class Command { if (path == null) { return null; } + File file = new File(path).getAbsoluteFile(); File dir = file.getParentFile(); + if (dir == null) { throw new IllegalArgumentException("Cannot write file: " + path); } + // quick fix to avoid stupid stuff in Gradle code if (!dir.isDirectory()) { dir.mkdirs(); } + return file; } @@ -79,10 +83,13 @@ public abstract class Command { if (path == null) { return null; } + File dir = new File(path).getAbsoluteFile(); + if (!dir.exists()) { throw new IllegalArgumentException("Cannot write to folder: " + dir); } + return dir; } @@ -90,10 +97,13 @@ public abstract class Command { if (path == null) { return null; } + File file = new File(path).getAbsoluteFile(); + if (!file.exists()) { throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); } + return file; } @@ -101,10 +111,13 @@ public abstract class Command { if (path == null) { return null; } + Path file = Paths.get(path).toAbsolutePath(); + if (!Files.exists(file)) { throw new IllegalArgumentException("Cannot find file: " + file.toString()); } + return file; } @@ -116,11 +129,11 @@ public abstract class Command { return null; } } + return args[i]; } public static class ConsoleProgressListener implements ProgressListener { - private static final int ReportTime = 5000; // 5s private int totalWork; @@ -146,6 +159,7 @@ public abstract class Command { System.out.println(String.format("\tProgress: %3d%%", percent)); this.lastReportTime = now; } + if (isLastUpdate) { double elapsedSeconds = (now - this.startTime) / 1000.0; System.out.println(String.format("Finished in %.1f seconds", elapsedSeconds)); diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java index e10fd47e..7e9002d8 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/ComposeMappingsCommand.java @@ -1,42 +1,42 @@ package cuchaz.enigma.command; -import cuchaz.enigma.translation.mapping.MappingOperations; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.MappingOperations; import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.utils.Utils; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - public class ComposeMappingsCommand extends Command { - public ComposeMappingsCommand() { - super("compose-mappings"); - } - - @Override - public String getUsage() { - return " "; - } - - @Override - public boolean isValidArgument(int length) { - return length == 7; - } - - @Override - public void run(String... args) throws IOException, MappingParseException { - MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); - - EntryTree left = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); - EntryTree right = MappingCommandsUtil.read(args[2], Paths.get(args[3]), saveParameters); - EntryTree result = MappingOperations.compose(left, right, args[6].equals("left") || args[6].equals("both"), args[6].equals("right") || args[6].equals("both")); - - Path output = Paths.get(args[5]); - Utils.delete(output); - MappingCommandsUtil.write(result, args[4], output, saveParameters); - } + public ComposeMappingsCommand() { + super("compose-mappings"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 7; + } + + @Override + public void run(String... args) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + + EntryTree left = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); + EntryTree right = MappingCommandsUtil.read(args[2], Paths.get(args[3]), saveParameters); + EntryTree result = MappingOperations.compose(left, right, args[6].equals("left") || args[6].equals("both"), args[6].equals("right") || args[6].equals("both")); + + Path output = Paths.get(args[5]); + Utils.delete(output); + MappingCommandsUtil.write(result, args[4], output, saveParameters); + } } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java index 144d89c5..99b27e1f 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/ConvertMappingsCommand.java @@ -1,39 +1,39 @@ package cuchaz.enigma.command; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.utils.Utils; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - public class ConvertMappingsCommand extends Command { - public ConvertMappingsCommand() { - super("convert-mappings"); - } - - @Override - public String getUsage() { - return " "; - } - - @Override - public boolean isValidArgument(int length) { - return length == 4; - } - - @Override - public void run(String... args) throws IOException, MappingParseException { - MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); - - EntryTree mappings = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); - - Path output = Paths.get(args[3]); - Utils.delete(output); - MappingCommandsUtil.write(mappings, args[2], output, saveParameters); - } + public ConvertMappingsCommand() { + super("convert-mappings"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 4; + } + + @Override + public void run(String... args) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + + EntryTree mappings = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); + + Path output = Paths.get(args[3]); + Utils.delete(output); + MappingCommandsUtil.write(mappings, args[2], output, saveParameters); + } } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java index 12a4e886..020bd979 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/DecompileCommand.java @@ -1,17 +1,16 @@ package cuchaz.enigma.command; +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.Locale; + import cuchaz.enigma.EnigmaProject; -import cuchaz.enigma.ProgressListener; import cuchaz.enigma.EnigmaProject.DecompileErrorStrategy; +import cuchaz.enigma.ProgressListener; import cuchaz.enigma.source.DecompilerService; import cuchaz.enigma.source.Decompilers; -import java.lang.reflect.Field; -import java.nio.file.Path; -import java.util.Locale; - public class DecompileCommand extends Command { - public DecompileCommand() { super("decompile"); } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java index b0d2a7d0..c8e62008 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/DeobfuscateCommand.java @@ -1,12 +1,11 @@ package cuchaz.enigma.command; +import java.nio.file.Path; + import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.ProgressListener; -import java.nio.file.Path; - public class DeobfuscateCommand extends Command { - public DeobfuscateCommand() { super("deobfuscate"); } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java index 0780a965..af24978b 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/InvertMappingsCommand.java @@ -1,41 +1,41 @@ package cuchaz.enigma.command; -import cuchaz.enigma.translation.mapping.MappingOperations; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.MappingOperations; import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.tree.EntryTree; import cuchaz.enigma.utils.Utils; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - public class InvertMappingsCommand extends Command { - public InvertMappingsCommand() { - super("invert-mappings"); - } - - @Override - public String getUsage() { - return " "; - } - - @Override - public boolean isValidArgument(int length) { - return length == 4; - } - - @Override - public void run(String... args) throws IOException, MappingParseException { - MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); - - EntryTree source = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); - EntryTree result = MappingOperations.invert(source); - - Path output = Paths.get(args[3]); - Utils.delete(output); - MappingCommandsUtil.write(result, args[2], output, saveParameters); - } + public InvertMappingsCommand() { + super("invert-mappings"); + } + + @Override + public String getUsage() { + return " "; + } + + @Override + public boolean isValidArgument(int length) { + return length == 4; + } + + @Override + public void run(String... args) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + + EntryTree source = MappingCommandsUtil.read(args[0], Paths.get(args[1]), saveParameters); + EntryTree result = MappingOperations.invert(source); + + Path output = Paths.get(args[3]); + Utils.delete(output); + MappingCommandsUtil.write(result, args[2], output, saveParameters); + } } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java b/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java index 0a4c1b9b..9021ff15 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/Main.java @@ -1,36 +1,39 @@ /******************************************************************************* - * 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.command; -import cuchaz.enigma.Enigma; - import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; -public class Main { +import cuchaz.enigma.Enigma; +public class Main { private static final Map COMMANDS = new LinkedHashMap<>(); public static void main(String... args) throws Exception { try { // process the command - if (args.length < 1) + if (args.length < 1) { throw new IllegalArgumentException("Requires a command"); + } + String command = args[0].toLowerCase(Locale.ROOT); Command cmd = COMMANDS.get(command); - if (cmd == null) + + if (cmd == null) { throw new IllegalArgumentException("Command not recognized: " + command); + } if (!cmd.isValidArgument(args.length - 1)) { throw new CommandHelpException(cmd); @@ -74,6 +77,7 @@ public class Main { private static void register(Command command) { Command old = COMMANDS.put(command.name, command); + if (old != null) { System.err.println("Command " + old + " with name " + command.name + " has been substituted by " + command); } @@ -90,7 +94,6 @@ public class Main { } private static final class CommandHelpException extends IllegalArgumentException { - final Command command; CommandHelpException(Command command) { diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java index 46da89a2..644b08d3 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/MapSpecializedMethodsCommand.java @@ -1,5 +1,10 @@ package cuchaz.enigma.command; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.IndexTreeBuilder; import cuchaz.enigma.analysis.index.BridgeMethodIndex; @@ -18,60 +23,55 @@ import cuchaz.enigma.translation.mapping.tree.HashEntryTree; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.Utils; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; - public class MapSpecializedMethodsCommand extends Command { - public MapSpecializedMethodsCommand() { - super("map-specialized-methods"); - } + public MapSpecializedMethodsCommand() { + super("map-specialized-methods"); + } - @Override - public String getUsage() { - return " "; - } + @Override + public String getUsage() { + return " "; + } - @Override - public boolean isValidArgument(int length) { - return length == 5; - } + @Override + public boolean isValidArgument(int length) { + return length == 5; + } - @Override - public void run(String... args) throws IOException, MappingParseException { - run(Paths.get(args[0]), args[1], Paths.get(args[2]), args[3], Paths.get(args[4])); - } + @Override + public void run(String... args) throws IOException, MappingParseException { + run(Paths.get(args[0]), args[1], Paths.get(args[2]), args[3], Paths.get(args[4])); + } - public static void run(Path jar, String sourceFormat, Path sourcePath, String resultFormat, Path output) throws IOException, MappingParseException { - MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); - EntryTree source = MappingCommandsUtil.read(sourceFormat, sourcePath, saveParameters); - EntryTree result = new HashEntryTree<>(); + public static void run(Path jar, String sourceFormat, Path sourcePath, String resultFormat, Path output) throws IOException, MappingParseException { + MappingSaveParameters saveParameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + EntryTree source = MappingCommandsUtil.read(sourceFormat, sourcePath, saveParameters); + EntryTree result = new HashEntryTree<>(); - JarClassProvider jcp = new JarClassProvider(jar); - JarIndex jarIndex = JarIndex.empty(); - jarIndex.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none()); + JarClassProvider jcp = new JarClassProvider(jar); + JarIndex jarIndex = JarIndex.empty(); + jarIndex.indexJar(jcp.getClassNames(), new CachingClassProvider(jcp), ProgressListener.none()); - BridgeMethodIndex bridgeMethodIndex = jarIndex.getBridgeMethodIndex(); - Translator translator = new MappingTranslator(source, jarIndex.getEntryResolver()); - IndexTreeBuilder indexTreeBuilder = new IndexTreeBuilder(jarIndex); + BridgeMethodIndex bridgeMethodIndex = jarIndex.getBridgeMethodIndex(); + Translator translator = new MappingTranslator(source, jarIndex.getEntryResolver()); + IndexTreeBuilder indexTreeBuilder = new IndexTreeBuilder(jarIndex); - // Copy all non-specialized methods - for (EntryTreeNode node : source) { - if (!(node.getEntry() instanceof MethodEntry) || !bridgeMethodIndex.isSpecializedMethod((MethodEntry) node.getEntry())) { - result.insert(node.getEntry(), node.getValue()); - } - } + // Copy all non-specialized methods + for (EntryTreeNode node : source) { + if (!(node.getEntry() instanceof MethodEntry) || !bridgeMethodIndex.isSpecializedMethod((MethodEntry) node.getEntry())) { + result.insert(node.getEntry(), node.getValue()); + } + } - // Add correct mappings for specialized methods - for (Map.Entry entry : bridgeMethodIndex.getBridgeToSpecialized().entrySet()) { - MethodEntry bridge = entry.getKey(); - MethodEntry specialized = indexTreeBuilder.buildMethodInheritance(translator, entry.getValue()).getMethodEntry(); - String name = translator.translate(bridge).getName(); - result.insert(specialized, new EntryMapping(name)); - } + // Add correct mappings for specialized methods + for (Map.Entry entry : bridgeMethodIndex.getBridgeToSpecialized().entrySet()) { + MethodEntry bridge = entry.getKey(); + MethodEntry specialized = indexTreeBuilder.buildMethodInheritance(translator, entry.getValue()).getMethodEntry(); + String name = translator.translate(bridge).getName(); + result.insert(specialized, new EntryMapping(name)); + } - Utils.delete(output); - MappingCommandsUtil.write(result, resultFormat, output, saveParameters); - } + Utils.delete(output); + MappingCommandsUtil.write(result, resultFormat, output, saveParameters); + } } diff --git a/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java b/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java index d365129b..787625b6 100644 --- a/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java +++ b/enigma-cli/src/main/java/cuchaz/enigma/command/MappingCommandsUtil.java @@ -1,10 +1,14 @@ package cuchaz.enigma.command; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + import cuchaz.enigma.ProgressListener; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.serde.MappingFormat; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; -import cuchaz.enigma.translation.mapping.serde.*; import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsWriter; import cuchaz.enigma.translation.mapping.serde.tiny.TinyMappingsReader; @@ -12,76 +16,77 @@ import cuchaz.enigma.translation.mapping.serde.tiny.TinyMappingsWriter; import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - public final class MappingCommandsUtil { - private MappingCommandsUtil() {} - - public static EntryTree read(String type, Path path, MappingSaveParameters saveParameters) throws MappingParseException, IOException { - if (type.equals("enigma")) { - return (Files.isDirectory(path) ? EnigmaMappingsReader.DIRECTORY : EnigmaMappingsReader.ZIP).read(path, ProgressListener.none(), saveParameters); - } - - if (type.equals("tiny")) { - return TinyMappingsReader.INSTANCE.read(path, ProgressListener.none(), saveParameters); - } - - MappingFormat format = null; - try { - format = MappingFormat.valueOf(type.toUpperCase()); - } catch (IllegalArgumentException ignored) { - if (type.equals("tinyv2")) { - format = MappingFormat.TINY_V2; - } - } - - if (format != null) { - return format.getReader().read(path, ProgressListener.none(), saveParameters); - } - - throw new IllegalArgumentException("no reader for " + type); - } - - public static void write(EntryTree mappings, String type, Path path, MappingSaveParameters saveParameters) { - if (type.equals("enigma")) { - EnigmaMappingsWriter.DIRECTORY.write(mappings, path, ProgressListener.none(), saveParameters); - return; - } - - if (type.startsWith("tinyv2:") || type.startsWith("tiny_v2:")) { - String[] split = type.split(":"); - - if (split.length != 3) { - throw new IllegalArgumentException("specify column names as 'tinyv2:from_namespace:to_namespace'"); - } - - new TinyV2Writer(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); - return; - } - - if (type.startsWith("tiny:")) { - String[] split = type.split(":"); - - if (split.length != 3) { - throw new IllegalArgumentException("specify column names as 'tiny:from_column:to_column'"); - } - - new TinyMappingsWriter(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); - return; - } - - MappingFormat format = null; - try { - format = MappingFormat.valueOf(type.toUpperCase()); - } catch (IllegalArgumentException ignored) {} - - if (format != null) { - format.getWriter().write(mappings, path, ProgressListener.none(), saveParameters); - return; - } - - throw new IllegalArgumentException("no writer for " + type); - } + private MappingCommandsUtil() { + } + + public static EntryTree read(String type, Path path, MappingSaveParameters saveParameters) throws MappingParseException, IOException { + if (type.equals("enigma")) { + return (Files.isDirectory(path) ? EnigmaMappingsReader.DIRECTORY : EnigmaMappingsReader.ZIP).read(path, ProgressListener.none(), saveParameters); + } + + if (type.equals("tiny")) { + return TinyMappingsReader.INSTANCE.read(path, ProgressListener.none(), saveParameters); + } + + MappingFormat format = null; + + try { + format = MappingFormat.valueOf(type.toUpperCase()); + } catch (IllegalArgumentException ignored) { + if (type.equals("tinyv2")) { + format = MappingFormat.TINY_V2; + } + } + + if (format != null) { + return format.getReader().read(path, ProgressListener.none(), saveParameters); + } + + throw new IllegalArgumentException("no reader for " + type); + } + + public static void write(EntryTree mappings, String type, Path path, MappingSaveParameters saveParameters) { + if (type.equals("enigma")) { + EnigmaMappingsWriter.DIRECTORY.write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + if (type.startsWith("tinyv2:") || type.startsWith("tiny_v2:")) { + String[] split = type.split(":"); + + if (split.length != 3) { + throw new IllegalArgumentException("specify column names as 'tinyv2:from_namespace:to_namespace'"); + } + + new TinyV2Writer(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + if (type.startsWith("tiny:")) { + String[] split = type.split(":"); + + if (split.length != 3) { + throw new IllegalArgumentException("specify column names as 'tiny:from_column:to_column'"); + } + + new TinyMappingsWriter(split[1], split[2]).write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + MappingFormat format = null; + + try { + format = MappingFormat.valueOf(type.toUpperCase()); + } catch (IllegalArgumentException ignored) { + // ignored + } + + if (format != null) { + format.getWriter().write(mappings, path, ProgressListener.none(), saveParameters); + return; + } + + throw new IllegalArgumentException("no writer for " + type); + } } diff --git a/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java b/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java index a29bba40..8cfa49e7 100644 --- a/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java +++ b/enigma-cli/src/test/java/cuchaz/enigma/command/CheckMappingsCommandTest.java @@ -1,21 +1,19 @@ package cuchaz.enigma.command; -import org.junit.Test; - import java.io.File; +import org.junit.Test; + public class CheckMappingsCommandTest { private static final String PACKAGE_ACCESS = "../enigma/build/test-obf/packageAccess.jar"; @Test(expected = IllegalStateException.class) public void testWrong() throws Exception { - new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + - "/packageAccess/wrongMappings").getAbsolutePath()); + new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + "/packageAccess/wrongMappings").getAbsolutePath()); } @Test public void testRight() throws Exception { - new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + - "/packageAccess/correctMappings").getAbsolutePath()); + new CheckMappingsCommand().run(new File(PACKAGE_ACCESS).getAbsolutePath(), new File("src/test/resources" + "/packageAccess/correctMappings").getAbsolutePath()); } } diff --git a/enigma-server/build.gradle b/enigma-server/build.gradle index 27645589..873adb90 100644 --- a/enigma-server/build.gradle +++ b/enigma-server/build.gradle @@ -1,10 +1,10 @@ plugins { - id 'application' + id 'application' } dependencies { - implementation project(':enigma') - implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' + implementation project(':enigma') + implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' } mainClassName = 'cuchaz.enigma.network.DedicatedEnigmaServer' diff --git a/enigma-server/docs/protocol.md b/enigma-server/docs/protocol.md index 83ef4c01..f642e138 100644 --- a/enigma-server/docs/protocol.md +++ b/enigma-server/docs/protocol.md @@ -1,4 +1,5 @@ # Enigma protocol + Enigma uses TCP sockets for communication. Data is sent in each direction as a continuous stream, with packets being concatenated one after the other. @@ -10,6 +11,7 @@ use the same modified UTF format as in `DataOutputStream`, I repeat, the normal Strings, see below. ## Login protocol + ``` Client Server | | @@ -22,6 +24,7 @@ Client Server | ConfirmChange | | >>>>>>>>>>>>> | ``` + 1. On connect, the client sends a login packet to the server. This allows the server to test the validity of the client, as well as allowing the client to declare metadata about itself, such as the username. 1. After validating the login packet, the server sends all its mappings to the client, and the client will apply them. @@ -29,15 +32,18 @@ Client Server has received the mappings and is in sync with the server. Once the server receives this packet, the client will be allowed to modify mappings. -The server will not accept any other packets from the client until this entire exchange has been completed. +The server will not accept any other packets from the client until this entire exchange has been completed. ## Kicking clients + When the server kicks a client, it may optionally send a `Kick` packet immediately before closing the connection, which contains the reason why the client was kicked (so the client can display it to the user). This is not required though - the server may simply terminate the connection. ## Changing mappings + This section uses the example of renaming, but the same pattern applies to all mapping changes. + ``` Client A Server Client B | | | @@ -66,19 +72,23 @@ Client A Server Client B server will unlock that mapping for that client and allow them to make changes again. ## Packets + ```c struct Packet { unsigned short packet_id; data[]; // depends on packet_id } ``` + The IDs for client-to-server packets are as follows: + - 0: `Login` - 1: `ConfirmChange` - 6: `Message` - 7: `EntryChange` The IDs for server-to-client packets are as follows: + - 0: `Kick` - 1: `SyncMappings` - 6: `Message` @@ -86,17 +96,20 @@ The IDs for server-to-client packets are as follows: - 8: `EntryChange` ### The utf struct + ```c struct utf { unsigned short length; byte data[length]; } ``` + - `length`: The number of bytes in the UTF-8 encoding of the string. Note, this may not be the same as the number of - Unicode characters in the string. -- `data`: A standard UTF-8 encoded byte array representing the string. + Unicode characters in the string. +- `data`: A standard UTF-8 encoded byte array representing the string. ### The Entry struct + ```c enum EntryType { ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3; @@ -121,9 +134,10 @@ struct Entry { } } ``` + - `type`: The type of entry this is. One of `ENTRY_CLASS`, `ENTRY_FIELD`, `ENTRY_METHOD` or `ENTRY_LOCAL_VAR`. - `parent`: The parent entry. Only class entries may have no parent. fields, methods and inner classes must have their - containing class as their parent. Local variables have a method as a parent. + containing class as their parent. Local variables have a method as a parent. - `name`: The class/field/method/variable name. - `javadoc`: The javadoc of an entry, if present. - `descriptor`: The field/method descriptor. @@ -131,6 +145,7 @@ struct Entry { - `parameter`: Whether the local variable is a parameter. ### The Message struct + ```c enum MessageType { MESSAGE_CHAT = 0, @@ -176,8 +191,9 @@ struct Message { } data; }; ``` + - `type`: The type of message this is. One of `MESSAGE_CHAT`, `MESSAGE_CONNECT`, `MESSAGE_DISCONNECT`, - `MESSAGE_EDIT_DOCS`, `MESSAGE_MARK_DEOBF`, `MESSAGE_REMOVE_MAPPING`, `MESSAGE_RENAME`. + `MESSAGE_EDIT_DOCS`, `MESSAGE_MARK_DEOBF`, `MESSAGE_REMOVE_MAPPING`, `MESSAGE_RENAME`. - `chat`: Chat message. Use in case `type` is `MESSAGE_CHAT` - `connect`: Sent when a user connects. Use in case `type` is `MESSAGE_CONNECT` - `disconnect`: Sent when a user disconnects. Use in case `type` is `MESSAGE_DISCONNECT` @@ -191,6 +207,7 @@ struct Message { - `new_name`: The new name for the entry. ### The entry_change struct + ```c typedef enum tristate_change { TRISTATE_CHANGE_UNCHANGED = 0, @@ -224,6 +241,7 @@ struct entry_change { } } ``` + - `entry`: The entry this change gets applied to. - `flags`: See definition of `entry_change_flags`. - `deobf_name`: The new deobfuscated name, if deobf_name_change == TRISTATE_CHANGE_SET @@ -231,6 +249,7 @@ struct entry_change { - `access_modifiers`: The new access modifier, if access_change == TRISTATE_CHANGE_SET (otherwise 0) ### Login (client-to-server) + ```c struct LoginC2SPacket { unsigned short protocol_version; @@ -240,47 +259,57 @@ struct LoginC2SPacket { utf username; } ``` + - `protocol_version`: the version of the protocol. If the version does not match on the server, then the client will be - kicked immediately. Currently always equal to 0. + kicked immediately. Currently always equal to 0. - `checksum`: the SHA-1 hash of the JAR file the client has open. If this does not match the SHA-1 hash of the JAR file - the server has open, the client will be kicked. + the server has open, the client will be kicked. - `password`: the password needed to log into the server. Note that each `char` is 2 bytes, as per the Java data type. - If this password is incorrect, the client will be kicked. + If this password is incorrect, the client will be kicked. - `username`: the username of the user logging in. If the username is not unique, the client will be kicked. ### ConfirmChange (client-to-server) + ```c struct ConfirmChangeC2SPacket { unsigned short sync_id; } ``` + - `sync_id`: the sync ID to confirm. ### Message (client-to-server) + ```c struct MessageC2SPacket { utf message; } ``` + - `message`: The text message the user sent. ### EntryChange (client-to-server) + ```c struct EntryChangeC2SPacket { entry_change change; } ``` + - `change`: The change to apply. ### Kick (server-to-client) + ```c struct KickS2CPacket { utf reason; } ``` + - `reason`: the reason for the kick, may or may not be a translation key for the client to display to the user. ### SyncMappings (server-to-client) + ```c struct SyncMappingsS2CPacket { int num_roots; @@ -296,6 +325,7 @@ struct MappingNode { } typedef { Entry but without the has_parent or parent fields } NoParentEntry; ``` + - `roots`: The root mapping nodes, containing all the entries without parents. - `obf_entry`: The value of a node, containing the obfuscated name and descriptor of the entry. - `name`: The deobfuscated name of the entry, if it exists, otherwise the empty string. @@ -303,6 +333,7 @@ typedef { Entry but without the has_parent or parent fields } NoParentEntry; - `children`: The children of this node ### Message (server-to-client) + ```c struct MessageS2CPacket { Message message; @@ -310,6 +341,7 @@ struct MessageS2CPacket { ``` ### UserList (server-to-client) + ```c struct UserListS2CPacket { unsigned short len; @@ -318,11 +350,13 @@ struct UserListS2CPacket { ``` ### EntryChange (server-to-client) + ```c struct EntryChangeS2CPacket { uint16_t sync_id; entry_change change; } ``` + - `sync_id`: The sync ID of the change for locking purposes. - `change`: The change to apply. \ No newline at end of file diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java b/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java index a651fe84..8fcd4372 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java @@ -1,22 +1,22 @@ package cuchaz.enigma.network; +import java.util.List; + +import cuchaz.enigma.network.packet.Packet; import cuchaz.enigma.translation.mapping.EntryChange; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import cuchaz.enigma.network.packet.Packet; - -import java.util.List; public interface ClientPacketHandler { - void openMappings(EntryTree mappings); + void openMappings(EntryTree mappings); - boolean applyChangeFromServer(EntryChange change); + boolean applyChangeFromServer(EntryChange change); - void disconnectIfConnected(String reason); + void disconnectIfConnected(String reason); - void sendPacket(Packet packet); + void sendPacket(Packet packet); - void addMessage(Message message); + void addMessage(Message message); - void updateUserList(List users); + void updateUserList(List users); } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java index 41f08342..eb22a506 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java @@ -1,17 +1,5 @@ package cuchaz.enigma.network; -import com.google.common.io.MoreFiles; -import cuchaz.enigma.*; -import cuchaz.enigma.classprovider.ClasspathClassProvider; -import cuchaz.enigma.translation.mapping.serde.MappingParseException; -import cuchaz.enigma.translation.mapping.EntryRemapper; -import cuchaz.enigma.translation.mapping.serde.MappingFormat; -import cuchaz.enigma.utils.Utils; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import joptsimple.OptionSpec; -import joptsimple.ValueConverter; - import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; @@ -22,24 +10,30 @@ import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -public class DedicatedEnigmaServer extends EnigmaServer { +import com.google.common.io.MoreFiles; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import joptsimple.ValueConverter; + +import cuchaz.enigma.Enigma; +import cuchaz.enigma.EnigmaProfile; +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.classprovider.ClasspathClassProvider; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.translation.mapping.serde.MappingFormat; +import cuchaz.enigma.translation.mapping.serde.MappingParseException; +import cuchaz.enigma.utils.Utils; +public class DedicatedEnigmaServer extends EnigmaServer { private final EnigmaProfile profile; private final MappingFormat mappingFormat; private final Path mappingsFile; private final PrintWriter log; private BlockingQueue tasks = new LinkedBlockingDeque<>(); - public DedicatedEnigmaServer( - byte[] jarChecksum, - char[] password, - EnigmaProfile profile, - MappingFormat mappingFormat, - Path mappingsFile, - PrintWriter log, - EntryRemapper mappings, - int port - ) { + public DedicatedEnigmaServer(byte[] jarChecksum, char[] password, EnigmaProfile profile, MappingFormat mappingFormat, Path mappingsFile, PrintWriter log, EntryRemapper mappings, int port) { super(jarChecksum, password, mappings, port); this.profile = profile; this.mappingFormat = mappingFormat; @@ -61,33 +55,17 @@ public class DedicatedEnigmaServer extends EnigmaServer { public static void main(String[] args) { OptionParser parser = new OptionParser(); - OptionSpec jarOpt = parser.accepts("jar", "Jar file to open at startup") - .withRequiredArg() - .required() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec jarOpt = parser.accepts("jar", "Jar file to open at startup").withRequiredArg().required().withValuesConvertedBy(PathConverter.INSTANCE); - OptionSpec mappingsOpt = parser.accepts("mappings", "Mappings file to open at startup") - .withRequiredArg() - .required() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec mappingsOpt = parser.accepts("mappings", "Mappings file to open at startup").withRequiredArg().required().withValuesConvertedBy(PathConverter.INSTANCE); - OptionSpec profileOpt = parser.accepts("profile", "Profile json to apply at startup") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec profileOpt = parser.accepts("profile", "Profile json to apply at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE); - OptionSpec portOpt = parser.accepts("port", "Port to run the server on") - .withOptionalArg() - .ofType(Integer.class) - .defaultsTo(EnigmaServer.DEFAULT_PORT); + OptionSpec portOpt = parser.accepts("port", "Port to run the server on").withOptionalArg().ofType(Integer.class).defaultsTo(EnigmaServer.DEFAULT_PORT); - OptionSpec passwordOpt = parser.accepts("password", "The password to join the server") - .withRequiredArg() - .defaultsTo(""); + OptionSpec passwordOpt = parser.accepts("password", "The password to join the server").withRequiredArg().defaultsTo(""); - OptionSpec logFileOpt = parser.accepts("log", "The log file to write to") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE) - .defaultsTo(Paths.get("log.txt")); + OptionSpec logFileOpt = parser.accepts("log", "The log file to write to").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE).defaultsTo(Paths.get("log.txt")); OptionSet parsedArgs = parser.parse(args); Path jar = parsedArgs.valueOf(jarOpt); @@ -95,14 +73,17 @@ public class DedicatedEnigmaServer extends EnigmaServer { Path profileFile = parsedArgs.valueOf(profileOpt); int port = parsedArgs.valueOf(portOpt); char[] password = parsedArgs.valueOf(passwordOpt).toCharArray(); + if (password.length > EnigmaServer.MAX_PASSWORD_LENGTH) { System.err.println("Password too long, must be at most " + EnigmaServer.MAX_PASSWORD_LENGTH + " characters"); System.exit(1); } + Path logFile = parsedArgs.valueOf(logFileOpt); System.out.println("Starting Enigma server"); DedicatedEnigmaServer server; + try { byte[] checksum = Utils.zipSha1(parsedArgs.valueOf(jarOpt)); @@ -113,10 +94,12 @@ public class DedicatedEnigmaServer extends EnigmaServer { MappingFormat mappingFormat = MappingFormat.ENIGMA_DIRECTORY; EntryRemapper mappings; + if (!Files.exists(mappingsFile)) { mappings = EntryRemapper.empty(project.getJarIndex()); } else { System.out.println("Reading mappings..."); + if (Files.isDirectory(mappingsFile)) { mappingFormat = MappingFormat.ENIGMA_DIRECTORY; } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsFile))) { @@ -124,6 +107,7 @@ public class DedicatedEnigmaServer extends EnigmaServer { } else { mappingFormat = MappingFormat.ENIGMA_FILE; } + mappings = EntryRemapper.mapped(project.getJarIndex(), mappingFormat.read(mappingsFile, ProgressListener.none(), profile.getMappingSaveParameters())); } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java index 71bd011c..69e4f5ef 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java @@ -1,15 +1,20 @@ package cuchaz.enigma.network; -import cuchaz.enigma.network.packet.Packet; -import cuchaz.enigma.network.packet.PacketRegistry; - -import javax.swing.*; -import java.io.*; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; import java.net.Socket; import java.net.SocketException; -public class EnigmaClient { +import javax.swing.SwingUtilities; +import cuchaz.enigma.network.packet.Packet; +import cuchaz.enigma.network.packet.PacketRegistry; + +public class EnigmaClient { private final ClientPacketHandler controller; private final String ip; @@ -29,17 +34,22 @@ public class EnigmaClient { Thread thread = new Thread(() -> { try { DataInput input = new DataInputStream(socket.getInputStream()); + while (true) { int packetId; + try { packetId = input.readUnsignedByte(); } catch (EOFException | SocketException e) { break; } + Packet packet = PacketRegistry.createS2CPacket(packetId); + if (packet == null) { throw new IOException("Received invalid packet id " + packetId); } + packet.read(input); SwingUtilities.invokeLater(() -> packet.handle(controller)); } @@ -47,6 +57,7 @@ public class EnigmaClient { controller.disconnectIfConnected(e.toString()); return; } + controller.disconnectIfConnected("Disconnected"); }); thread.setName("Client I/O thread"); @@ -65,7 +76,6 @@ public class EnigmaClient { } } - public void sendPacket(Packet packet) { try { output.writeByte(PacketRegistry.getC2SId(packet)); @@ -74,5 +84,4 @@ public class EnigmaClient { controller.disconnectIfConnected(e.toString()); } } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java index 1ce359b6..8872735d 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java @@ -1,20 +1,35 @@ package cuchaz.enigma.network; -import java.io.*; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; -import java.util.*; +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.Set; import java.util.concurrent.CopyOnWriteArrayList; -import cuchaz.enigma.network.packet.*; +import cuchaz.enigma.network.packet.EntryChangeS2CPacket; +import cuchaz.enigma.network.packet.KickS2CPacket; +import cuchaz.enigma.network.packet.MessageS2CPacket; +import cuchaz.enigma.network.packet.Packet; +import cuchaz.enigma.network.packet.PacketRegistry; +import cuchaz.enigma.network.packet.UserListS2CPacket; import cuchaz.enigma.translation.mapping.EntryChange; import cuchaz.enigma.translation.mapping.EntryMapping; import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.translation.representation.entry.Entry; public abstract class EnigmaServer { - // https://discordapp.com/channels/507304429255393322/566418023372816394/700292322918793347 public static final int DEFAULT_PORT = 34712; public static final int PROTOCOL_VERSION = 1; @@ -71,17 +86,22 @@ public abstract class EnigmaServer { Thread thread = new Thread(() -> { try { DataInput input = new DataInputStream(client.getInputStream()); + while (true) { int packetId; + try { packetId = input.readUnsignedByte(); } catch (EOFException | SocketException e) { break; } + Packet packet = PacketRegistry.createC2SPacket(packetId); + if (packet == null) { throw new IOException("Received invalid packet id " + packetId); } + packet.read(input); runOnThread(() -> packet.handle(new ServerPacketHandler(client, this))); } @@ -90,6 +110,7 @@ public abstract class EnigmaServer { e.printStackTrace(); return; } + kick(client, "disconnect.disconnected"); }); thread.setName("Server I/O thread #" + (nextIoId++)); @@ -103,6 +124,7 @@ public abstract class EnigmaServer { for (Socket client : clients) { kick(client, "disconnect.server_closed"); } + try { socket.close(); } catch (IOException e) { @@ -114,7 +136,9 @@ public abstract class EnigmaServer { } public void kick(Socket client, String reason) { - if (!clients.remove(client)) return; + if (!clients.remove(client)) { + return; + } sendPacket(client, new KickS2CPacket(reason)); @@ -123,6 +147,7 @@ public abstract class EnigmaServer { return list.isEmpty(); }); String username = usernames.remove(client); + try { client.close(); } catch (IOException e) { @@ -134,6 +159,7 @@ public abstract class EnigmaServer { System.out.println("Kicked " + username + " because " + reason); sendMessage(Message.disconnect(username)); } + sendUsernamePacket(); } @@ -159,6 +185,7 @@ public abstract class EnigmaServer { public void sendPacket(Socket client, Packet packet) { if (!client.isClosed()) { int packetId = PacketRegistry.getS2CId(packet); + try { DataOutput output = new DataOutputStream(client.getOutputStream()); output.writeByte(packetId); @@ -192,9 +219,11 @@ public abstract class EnigmaServer { } Integer syncId = syncIds.get(entry); + if (syncId == null) { return true; } + Set clients = clientsNeedingConfirmation.get(syncId); return clients == null || !clients.contains(client); } @@ -202,14 +231,18 @@ public abstract class EnigmaServer { public int lockEntry(Socket exception, Entry entry) { int syncId = nextSyncId; nextSyncId++; + // sync id is sent as an unsigned short, can't have more than 65536 if (nextSyncId == 65536) { nextSyncId = DUMMY_SYNC_ID + 1; } + Integer oldSyncId = syncIds.get(entry); + if (oldSyncId != null) { clientsNeedingConfirmation.remove(oldSyncId); } + syncIds.put(entry, syncId); inverseSyncIds.put(syncId, entry); Set clients = new HashSet<>(this.clients); @@ -224,8 +257,10 @@ public abstract class EnigmaServer { } Set clients = clientsNeedingConfirmation.get(syncId); + if (clients != null) { clients.remove(client); + if (clients.isEmpty()) { clientsNeedingConfirmation.remove(syncId); syncIds.remove(inverseSyncIds.remove(syncId)); @@ -236,6 +271,7 @@ public abstract class EnigmaServer { public void sendCorrectMapping(Socket client, Entry entry, boolean refreshClassTree) { EntryMapping oldMapping = mappings.getDeobfMapping(entry); String oldName = oldMapping.targetName(); + if (oldName == null) { sendPacket(client, new EntryChangeS2CPacket(DUMMY_SYNC_ID, EntryChange.modify(entry).clearDeobfName())); } else { @@ -269,5 +305,4 @@ public abstract class EnigmaServer { log(String.format("[MSG] %s", message.translate())); sendToAll(new MessageS2CPacket(message)); } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java index 21c6825b..99e4e99e 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java @@ -1,8 +1,8 @@ package cuchaz.enigma.network; -import cuchaz.enigma.translation.mapping.EntryRemapper; +import javax.swing.SwingUtilities; -import javax.swing.*; +import cuchaz.enigma.translation.mapping.EntryRemapper; public class IntegratedEnigmaServer extends EnigmaServer { public IntegratedEnigmaServer(byte[] jarChecksum, char[] password, EntryRemapper mappings, int port) { diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/Message.java b/enigma-server/src/main/java/cuchaz/enigma/network/Message.java index c1578387..d49e60cb 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/Message.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/Message.java @@ -10,9 +10,8 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.utils.I18n; public abstract class Message { - public final String user; - + public static Chat chat(String user, String message) { return new Chat(user, message); } @@ -47,34 +46,37 @@ public abstract class Message { public static Message read(DataInput input) throws IOException { byte typeId = input.readByte(); + if (typeId < 0 || typeId >= Type.values().length) { throw new IOException(String.format("Invalid message type ID %d", typeId)); } + Type type = Type.values()[typeId]; String user = input.readUTF(); + switch (type) { - case CHAT: - String message = input.readUTF(); - return chat(user, message); - case CONNECT: - return connect(user); - case DISCONNECT: - return disconnect(user); - case EDIT_DOCS: - Entry entry = PacketHelper.readEntry(input); - return editDocs(user, entry); - case MARK_DEOBF: - entry = PacketHelper.readEntry(input); - return markDeobf(user, entry); - case REMOVE_MAPPING: - entry = PacketHelper.readEntry(input); - return removeMapping(user, entry); - case RENAME: - entry = PacketHelper.readEntry(input); - String newName = input.readUTF(); - return rename(user, entry, newName); - default: - throw new IllegalStateException("unreachable"); + case CHAT: + String message = input.readUTF(); + return chat(user, message); + case CONNECT: + return connect(user); + case DISCONNECT: + return disconnect(user); + case EDIT_DOCS: + Entry entry = PacketHelper.readEntry(input); + return editDocs(user, entry); + case MARK_DEOBF: + entry = PacketHelper.readEntry(input); + return markDeobf(user, entry); + case REMOVE_MAPPING: + entry = PacketHelper.readEntry(input); + return removeMapping(user, entry); + case RENAME: + entry = PacketHelper.readEntry(input); + String newName = input.readUTF(); + return rename(user, entry, newName); + default: + throw new IllegalStateException("unreachable"); } } @@ -89,8 +91,14 @@ public abstract class Message { @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; + } + Message message = (Message) o; return Objects.equals(user, message.user); } @@ -111,7 +119,6 @@ public abstract class Message { } public static final class Chat extends Message { - public final String message; private Chat(String user, String message) { @@ -137,9 +144,18 @@ public abstract class Message { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + if (!super.equals(o)) { + return false; + } + Chat chat = (Chat) o; return Objects.equals(message, chat.message); } @@ -153,11 +169,9 @@ public abstract class Message { public String toString() { return String.format("Message.Chat { user: '%s', message: '%s' }", user, message); } - } public static final class Connect extends Message { - private Connect(String user) { super(user); } @@ -176,11 +190,9 @@ public abstract class Message { public String toString() { return String.format("Message.Connect { user: '%s' }", user); } - } public static final class Disconnect extends Message { - private Disconnect(String user) { super(user); } @@ -199,11 +211,9 @@ public abstract class Message { public String toString() { return String.format("Message.Disconnect { user: '%s' }", user); } - } - public static final class EditDocs extends Message { - + public static final class EditDocs extends Message { public final Entry entry; private EditDocs(String user, Entry entry) { @@ -229,9 +239,18 @@ public abstract class Message { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + if (!super.equals(o)) { + return false; + } + EditDocs editDocs = (EditDocs) o; return Objects.equals(entry, editDocs.entry); } @@ -245,11 +264,9 @@ public abstract class Message { public String toString() { return String.format("Message.EditDocs { user: '%s', entry: %s }", user, entry); } - } public static final class MarkDeobf extends Message { - public final Entry entry; private MarkDeobf(String user, Entry entry) { @@ -275,9 +292,18 @@ public abstract class Message { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + if (!super.equals(o)) { + return false; + } + MarkDeobf markDeobf = (MarkDeobf) o; return Objects.equals(entry, markDeobf.entry); } @@ -291,11 +317,9 @@ public abstract class Message { public String toString() { return String.format("Message.MarkDeobf { user: '%s', entry: %s }", user, entry); } - } public static final class RemoveMapping extends Message { - public final Entry entry; private RemoveMapping(String user, Entry entry) { @@ -321,9 +345,18 @@ public abstract class Message { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + if (!super.equals(o)) { + return false; + } + RemoveMapping that = (RemoveMapping) o; return Objects.equals(entry, that.entry); } @@ -337,11 +370,9 @@ public abstract class Message { public String toString() { return String.format("Message.RemoveMapping { user: '%s', entry: %s }", user, entry); } - } public static final class Rename extends Message { - public final Entry entry; public final String newName; @@ -370,12 +401,20 @@ public abstract class Message { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + if (!super.equals(o)) { + return false; + } + Rename rename = (Rename) o; - return Objects.equals(entry, rename.entry) && - Objects.equals(newName, rename.newName); + return Objects.equals(entry, rename.entry) && Objects.equals(newName, rename.newName); } @Override @@ -387,7 +426,5 @@ public abstract class Message { public String toString() { return String.format("Message.Rename { user: '%s', entry: %s, newName: '%s' }", user, entry, newName); } - } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java b/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java index 45e07508..a8a10296 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java @@ -5,7 +5,6 @@ import java.util.Objects; import javax.annotation.Nullable; public class ServerAddress { - public final String address; public final int port; @@ -16,11 +15,26 @@ public class ServerAddress { @Nullable public static ServerAddress of(String address, int port) { - if (port < 0 || port > 65535) return null; - if (address == null) return null; - if (address.equals("")) return null; - if (!address.matches("[a-zA-Z0-9.:-]+")) return null; - if (address.startsWith("-") || address.endsWith("-")) return null; + if (port < 0 || port > 65535) { + return null; + } + + if (address == null) { + return null; + } + + if (address.equals("")) { + return null; + } + + if (!address.matches("[a-zA-Z0-9.:-]+")) { + return null; + } + + if (address.startsWith("-") || address.endsWith("-")) { + return null; + } + return new ServerAddress(address, port); } @@ -28,6 +42,7 @@ public class ServerAddress { public static ServerAddress from(String s, int defaultPort) { String address; int idx = s.indexOf(']'); + if (s.startsWith("[") && idx != -1) { address = s.substring(1, idx); s = s.substring(idx + 1); @@ -41,10 +56,12 @@ public class ServerAddress { } int port; + if (s.isEmpty()) { port = defaultPort; } else if (s.startsWith(":")) { s = s.substring(1); + try { port = Integer.parseInt(s); } catch (NumberFormatException e) { @@ -53,16 +70,22 @@ public class ServerAddress { } else { return null; } + return ServerAddress.of(address, port); } @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; + } + ServerAddress that = (ServerAddress) o; - return port == that.port && - Objects.equals(address, that.address); + return port == that.port && Objects.equals(address, that.address); } @Override @@ -74,5 +97,4 @@ public class ServerAddress { public String toString() { return String.format("ServerAddress { address: '%s', port: %d }", address, port); } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java b/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java index 86185536..5b5b0e66 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java @@ -3,7 +3,6 @@ package cuchaz.enigma.network; import java.net.Socket; public class ServerPacketHandler { - private final Socket client; private final EnigmaServer server; diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java index 78ef9645..ab4f5a16 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java @@ -1,11 +1,11 @@ package cuchaz.enigma.network.packet; -import cuchaz.enigma.network.ServerPacketHandler; - import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import cuchaz.enigma.network.ServerPacketHandler; + public class ConfirmChangeC2SPacket implements Packet { private int syncId; diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java index b97877c6..6a7ffe99 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java @@ -12,7 +12,6 @@ import cuchaz.enigma.utils.validation.PrintValidatable; import cuchaz.enigma.utils.validation.ValidationContext; public class EntryChangeC2SPacket implements Packet { - private EntryChange change; EntryChangeC2SPacket() { @@ -62,5 +61,4 @@ public class EntryChangeC2SPacket implements Packet { handler.getServer().sendMessage(Message.editDocs(handler.getServer().getUsername(handler.getClient()), this.change.getTarget())); } } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java index a237b916..8e4688e3 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java @@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler; import cuchaz.enigma.translation.mapping.EntryChange; public class EntryChangeS2CPacket implements Packet { - private int syncId; private EntryChange change; @@ -38,5 +37,4 @@ public class EntryChangeS2CPacket implements Packet { handler.sendPacket(new ConfirmChangeC2SPacket(this.syncId)); } } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java index 9a112a80..bd238dc1 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java @@ -1,11 +1,11 @@ package cuchaz.enigma.network.packet; -import cuchaz.enigma.network.ClientPacketHandler; - import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import cuchaz.enigma.network.ClientPacketHandler; + public class KickS2CPacket implements Packet { private String reason; diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java index da0f44a5..e93c1d4d 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java @@ -1,14 +1,14 @@ package cuchaz.enigma.network.packet; -import cuchaz.enigma.network.EnigmaServer; -import cuchaz.enigma.network.ServerPacketHandler; -import cuchaz.enigma.network.Message; - import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Arrays; +import cuchaz.enigma.network.EnigmaServer; +import cuchaz.enigma.network.Message; +import cuchaz.enigma.network.ServerPacketHandler; + public class LoginC2SPacket implements Packet { private byte[] jarChecksum; private char[] password; @@ -28,12 +28,15 @@ public class LoginC2SPacket implements Packet { if (input.readUnsignedShort() != EnigmaServer.PROTOCOL_VERSION) { throw new IOException("Mismatching protocol"); } + this.jarChecksum = new byte[EnigmaServer.CHECKSUM_SIZE]; input.readFully(jarChecksum); this.password = new char[input.readUnsignedByte()]; + for (int i = 0; i < password.length; i++) { password[i] = input.readChar(); } + this.username = PacketHelper.readString(input); } @@ -42,9 +45,11 @@ public class LoginC2SPacket implements Packet { output.writeShort(EnigmaServer.PROTOCOL_VERSION); output.write(jarChecksum); output.writeByte(password.length); + for (char c : password) { output.writeChar(c); } + PacketHelper.writeString(output, username); } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java index 3bc09e79..b0610b04 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java @@ -4,11 +4,10 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import cuchaz.enigma.network.ServerPacketHandler; import cuchaz.enigma.network.Message; +import cuchaz.enigma.network.ServerPacketHandler; public class MessageC2SPacket implements Packet { - private String message; MessageC2SPacket() { @@ -31,9 +30,9 @@ public class MessageC2SPacket implements Packet { @Override public void handle(ServerPacketHandler handler) { String message = this.message.trim(); + if (!message.isEmpty()) { handler.getServer().sendMessage(Message.chat(handler.getServer().getUsername(handler.getClient()), message)); } } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java index 2b07968d..9833eebc 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java @@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler; import cuchaz.enigma.network.Message; public class MessageS2CPacket implements Packet { - private Message message; MessageS2CPacket() { @@ -32,5 +31,4 @@ public class MessageS2CPacket implements Packet { public void handle(ClientPacketHandler handler) { handler.addMessage(message); } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java index 2f16dfb9..15054e7b 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java @@ -5,11 +5,9 @@ import java.io.DataOutput; import java.io.IOException; public interface Packet { - void read(DataInput input) throws IOException; void write(DataOutput output) throws IOException; void handle(H handler); - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java index 2649cdc4..ce767c3c 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java @@ -9,11 +9,14 @@ import cuchaz.enigma.translation.mapping.AccessModifier; import cuchaz.enigma.translation.mapping.EntryChange; 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.TristateChange; public class PacketHelper { - private static final int ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3; private static final int MAX_STRING_LENGTH = 65535; @@ -31,45 +34,46 @@ public class PacketHelper { String name = readString(input); String javadocs = null; + if (input.readBoolean()) { javadocs = readString(input); } switch (type) { - case ENTRY_CLASS: { - if (parent != null && !(parent instanceof ClassEntry)) { - throw new IOException("Class requires class parent"); - } - - return new ClassEntry((ClassEntry) parent, name, javadocs); + case ENTRY_CLASS: { + if (parent != null && !(parent instanceof ClassEntry)) { + throw new IOException("Class requires class parent"); } - case ENTRY_FIELD: { - if (!(parent instanceof ClassEntry parentClass)) { - throw new IOException("Field requires class parent"); - } - TypeDescriptor desc = new TypeDescriptor(readString(input)); - return new FieldEntry(parentClass, name, desc, javadocs); + return new ClassEntry((ClassEntry) parent, name, javadocs); + } + case ENTRY_FIELD: { + if (!(parent instanceof ClassEntry parentClass)) { + throw new IOException("Field requires class parent"); } - case ENTRY_METHOD: { - if (!(parent instanceof ClassEntry parentClass)) { - throw new IOException("Method requires class parent"); - } - MethodDescriptor desc = new MethodDescriptor(readString(input)); - return new MethodEntry(parentClass, name, desc, javadocs); + TypeDescriptor desc = new TypeDescriptor(readString(input)); + return new FieldEntry(parentClass, name, desc, javadocs); + } + case ENTRY_METHOD: { + if (!(parent instanceof ClassEntry parentClass)) { + throw new IOException("Method requires class parent"); } - case ENTRY_LOCAL_VAR: { - if (!(parent instanceof MethodEntry parentMethod)) { - throw new IOException("Local variable requires method parent"); - } - - int index = input.readUnsignedShort(); - boolean parameter = input.readBoolean(); - return new LocalVariableEntry(parentMethod, index, name, parameter, javadocs); + + MethodDescriptor desc = new MethodDescriptor(readString(input)); + return new MethodEntry(parentClass, name, desc, javadocs); + } + case ENTRY_LOCAL_VAR: { + if (!(parent instanceof MethodEntry parentMethod)) { + throw new IOException("Local variable requires method parent"); } - default: - throw new IOException("Received unknown entry type " + type); + + int index = input.readUnsignedShort(); + boolean parameter = input.readBoolean(); + return new LocalVariableEntry(parentMethod, index, name, parameter, javadocs); + } + default: + throw new IOException("Received unknown entry type " + type); } } @@ -94,6 +98,7 @@ public class PacketHelper { // parent if (includeParent) { output.writeBoolean(entry.getParent() != null); + if (entry.getParent() != null) { writeEntry(output, entry.getParent(), true); } @@ -104,6 +109,7 @@ public class PacketHelper { // javadocs output.writeBoolean(entry.getJavadocs() != null); + if (entry.getJavadocs() != null) { writeString(output, entry.getJavadocs()); } @@ -129,9 +135,11 @@ public class PacketHelper { public static void writeString(DataOutput output, String str) throws IOException { byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + if (bytes.length > MAX_STRING_LENGTH) { throw new IOException("String too long, was " + bytes.length + " bytes, max " + MAX_STRING_LENGTH + " allowed"); } + output.writeShort(bytes.length); output.write(bytes); } @@ -146,30 +154,30 @@ public class PacketHelper { TristateChange.Type javadocT = TristateChange.Type.values()[flags >> 4 & 0x3]; switch (deobfNameT) { - case RESET: - change = change.clearDeobfName(); - break; - case SET: - change = change.withDeobfName(readString(input)); - break; + case RESET: + change = change.clearDeobfName(); + break; + case SET: + change = change.withDeobfName(readString(input)); + break; } switch (accessT) { - case RESET: - change = change.clearAccess(); - break; - case SET: - change = change.withAccess(AccessModifier.values()[flags >> 6 & 0x3]); - break; + case RESET: + change = change.clearAccess(); + break; + case SET: + change = change.withAccess(AccessModifier.values()[flags >> 6 & 0x3]); + break; } switch (javadocT) { - case RESET: - change = change.clearJavadoc(); - break; - case SET: - change = change.withJavadoc(readString(input)); - break; + case RESET: + change = change.clearJavadoc(); + break; + case SET: + change = change.withJavadoc(readString(input)); + break; } return change; @@ -177,9 +185,7 @@ public class PacketHelper { public static void writeEntryChange(DataOutput output, EntryChange change) throws IOException { writeEntry(output, change.getTarget()); - int flags = change.getDeobfName().getType().ordinal() | - change.getAccess().getType().ordinal() << 2 | - change.getJavadoc().getType().ordinal() << 4; + int flags = change.getDeobfName().getType().ordinal() | change.getAccess().getType().ordinal() << 2 | change.getJavadoc().getType().ordinal() << 4; if (change.getAccess().isSet()) { flags |= change.getAccess().getNewValue().ordinal() << 6; @@ -195,5 +201,4 @@ public class PacketHelper { writeString(output, change.getJavadoc().getNewValue()); } } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java index 59999ccc..49d56fd8 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java @@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler; import cuchaz.enigma.network.ServerPacketHandler; public class PacketRegistry { - private static final Map>, Integer> c2sPacketIds = new HashMap<>(); private static final Map>> c2sPacketCreators = new HashMap<>(); private static final Map>, Integer> s2cPacketIds = new HashMap<>(); @@ -54,5 +53,4 @@ public class PacketRegistry { Supplier> creator = s2cPacketCreators.get(id); return creator == null ? null : creator.get(); } - } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java index 6d9c0bcb..7e50938e 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java @@ -28,6 +28,7 @@ public class SyncMappingsS2CPacket implements Packet { public void read(DataInput input) throws IOException { mappings = new HashEntryTree<>(); int size = input.readInt(); + for (int i = 0; i < size; i++) { readEntryTreeNode(input, null); } @@ -40,6 +41,7 @@ public class SyncMappingsS2CPacket implements Packet { EntryMapping mapping = new EntryMapping(!name.isEmpty() ? name : null, !javadoc.isEmpty() ? javadoc : null); mappings.insert(entry, mapping); int size = input.readUnsignedShort(); + for (int i = 0; i < size; i++) { readEntryTreeNode(input, entry); } @@ -49,6 +51,7 @@ public class SyncMappingsS2CPacket implements Packet { public void write(DataOutput output) throws IOException { List> roots = mappings.getRootNodes().toList(); output.writeInt(roots.size()); + for (EntryTreeNode node : roots) { writeEntryTreeNode(output, node); } @@ -57,12 +60,16 @@ public class SyncMappingsS2CPacket implements Packet { private static void writeEntryTreeNode(DataOutput output, EntryTreeNode node) throws IOException { PacketHelper.writeEntry(output, node.getEntry(), false); EntryMapping value = node.getValue(); - if (value == null) value = EntryMapping.DEFAULT; + + if (value == null) { + value = EntryMapping.DEFAULT; + } PacketHelper.writeString(output, value.targetName() != null ? value.targetName() : ""); PacketHelper.writeString(output, value.javadoc() != null ? value.javadoc() : ""); Collection> children = node.getChildNodes(); output.writeShort(children.size()); + for (EntryTreeNode child : children) { writeEntryTreeNode(output, child); } diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java index b4a277a4..baac2b35 100644 --- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java +++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java @@ -1,15 +1,14 @@ package cuchaz.enigma.network.packet; -import cuchaz.enigma.network.ClientPacketHandler; - import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.List; -public class UserListS2CPacket implements Packet { +import cuchaz.enigma.network.ClientPacketHandler; +public class UserListS2CPacket implements Packet { private List users; UserListS2CPacket() { @@ -23,6 +22,7 @@ public class UserListS2CPacket implements Packet { public void read(DataInput input) throws IOException { int len = input.readUnsignedShort(); users = new ArrayList<>(len); + for (int i = 0; i < len; i++) { users.add(input.readUTF()); } @@ -31,6 +31,7 @@ public class UserListS2CPacket implements Packet { @Override public void write(DataOutput output) throws IOException { output.writeShort(users.size()); + for (String user : users) { PacketHelper.writeString(output, user); } @@ -40,5 +41,4 @@ public class UserListS2CPacket implements Packet { public void handle(ClientPacketHandler handler) { handler.updateUserList(users); } - } diff --git a/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java b/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java index 3765f7a5..c2920e02 100644 --- a/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java +++ b/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java @@ -1,12 +1,11 @@ package cuchaz.enigma.network; -import org.junit.Test; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -public class ServerAddressTest { +import org.junit.Test; +public class ServerAddressTest { @Test public void validAddresses() { assertEquals(ServerAddress.of("127.0.0.1", 22), ServerAddress.from("127.0.0.1", 22)); @@ -24,5 +23,4 @@ public class ServerAddressTest { assertNull(ServerAddress.from("127.0.0.1:100000000", 22)); assertNull(ServerAddress.from("127.0.0.1:lmao", 22)); } - } diff --git a/enigma-swing/build.gradle b/enigma-swing/build.gradle index d783e950..a4a13d88 100644 --- a/enigma-swing/build.gradle +++ b/enigma-swing/build.gradle @@ -1,18 +1,18 @@ plugins { - id 'application' - id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'application' + id 'com.github.johnrengelman.shadow' version '7.0.0' } dependencies { - implementation project(':enigma') - implementation project(':enigma-server') + implementation project(':enigma') + implementation project(':enigma-server') - implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' - implementation 'com.formdev:flatlaf:1.6.1' - implementation 'com.formdev:flatlaf-extras:1.6.1' // for SVG icons - implementation 'de.sciss:syntaxpane:1.2.0' - implementation 'com.github.lukeu:swing-dpi:0.9' - implementation 'org.drjekyll:fontchooser:2.5.2' + implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' + implementation 'com.formdev:flatlaf:1.6.1' + implementation 'com.formdev:flatlaf-extras:1.6.1' // for SVG icons + implementation 'de.sciss:syntaxpane:1.2.0' + implementation 'com.github.lukeu:swing-dpi:0.9' + implementation 'org.drjekyll:fontchooser:2.5.2' } mainClassName = 'cuchaz.enigma.gui.Main' @@ -20,9 +20,9 @@ mainClassName = 'cuchaz.enigma.gui.Main' jar.manifest.attributes 'Main-Class': mainClassName publishing { - publications { - shadow(MavenPublication) { publication -> - publication.from components.java - } - } + publications { + shadow(MavenPublication) { publication -> + publication.from components.java + } + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java index af105dbd..a213a592 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/BrowserCaret.java @@ -1,20 +1,19 @@ /******************************************************************************* - * 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.gui; import javax.swing.text.DefaultCaret; public class BrowserCaret extends DefaultCaret { - @Override public boolean isSelectionVisible() { return true; @@ -24,5 +23,4 @@ public class BrowserCaret extends DefaultCaret { public boolean isVisible() { return true; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index b7d3c4ef..4b9fa595 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -1,35 +1,43 @@ /******************************************************************************* - * 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.gui; -import cuchaz.enigma.gui.node.ClassSelectorClassNode; -import cuchaz.enigma.gui.util.GuiUtil; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import cuchaz.enigma.utils.validation.ValidationContext; - -import javax.swing.*; -import javax.swing.event.CellEditorListener; -import javax.swing.event.ChangeEvent; -import javax.swing.tree.*; -import java.awt.*; +import java.awt.Component; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.EventObject; import java.util.List; -import java.util.*; -public class ClassSelector extends JTree { +import javax.swing.JTree; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellEditor; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import cuchaz.enigma.gui.node.ClassSelectorClassNode; +import cuchaz.enigma.gui.util.GuiUtil; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.utils.validation.ValidationContext; +public class ClassSelector extends JTree { public static final Comparator DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); private final Comparator comparator; @@ -56,6 +64,7 @@ public class ClassSelector extends JTree { if (selectionListener != null && event.getClickCount() == 2) { // get the selected node TreePath path = getSelectionPath(); + if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode node) { selectionListener.onSelectClass(node.getObfEntry()); } @@ -121,23 +130,31 @@ public class ClassSelector extends JTree { TreePath path = getSelectionPath(); Object realPath = path.getLastPathComponent(); + if (realPath instanceof DefaultMutableTreeNode node && data != null) { TreeNode parentNode = node.getParent(); - if (parentNode == null) + + if (parentNode == null) { return; + } + boolean allowEdit = true; + for (int i = 0; i < parentNode.getChildCount(); i++) { TreeNode childNode = parentNode.getChildAt(i); + if (childNode != null && childNode.toString().equals(data) && childNode != node) { allowEdit = false; break; } } + if (allowEdit && renameSelectionListener != null) { Object prevData = node.getUserObject(); Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; gui.validateImmediateAction(vc -> { renameSelectionListener.onSelectionRename(vc, node.getUserObject(), objectData, node); + if (vc.canProceed()) { node.setUserObject(objectData); // Make sure that it's modified } else { @@ -206,15 +223,19 @@ public class ClassSelector extends JTree { public List getExpansionState() { List state = new ArrayList<>(); int rowCount = getRowCount(); + for (int i = 0; i < rowCount; i++) { TreePath path = getPathForRow(i); + if (isPathSelected(path)) { state.add(new StateEntry(State.SELECTED, path)); } + if (isExpanded(path)) { state.add(new StateEntry(State.EXPANDED, path)); } } + return state; } @@ -223,8 +244,8 @@ public class ClassSelector extends JTree { for (StateEntry entry : expansionState) { switch (entry.state) { - case SELECTED -> addSelectionPath(entry.path); - case EXPANDED -> expandPath(entry.path); + case SELECTED -> addSelectionPath(entry.path); + case EXPANDED -> expandPath(entry.path); } } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java index 1e8d6cf0..6028609f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EditableType.java @@ -2,7 +2,11 @@ package cuchaz.enigma.gui; import javax.annotation.Nullable; -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; public enum EditableType { CLASS, @@ -10,8 +14,7 @@ public enum EditableType { FIELD, PARAMETER, LOCAL_VARIABLE, - JAVADOC, - ; + JAVADOC; @Nullable public static EditableType fromEntry(Entry entry) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java index c912be3a..0af8f9c1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaQuickFindDialog.java @@ -1,16 +1,22 @@ package cuchaz.enigma.gui; -import de.sciss.syntaxpane.actions.DocumentSearchData; -import de.sciss.syntaxpane.actions.gui.QuickFindDialog; - -import javax.swing.*; -import javax.swing.text.JTextComponent; -import java.awt.*; +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.stream.IntStream; import java.util.stream.Stream; +import javax.swing.JButton; +import javax.swing.JTextField; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; +import javax.swing.text.JTextComponent; + +import de.sciss.syntaxpane.actions.DocumentSearchData; +import de.sciss.syntaxpane.actions.gui.QuickFindDialog; + public class EnigmaQuickFindDialog extends QuickFindDialog { public EnigmaQuickFindDialog(JTextComponent target) { super(target, DocumentSearchData.getFromEditor(target)); @@ -22,6 +28,7 @@ public class EnigmaQuickFindDialog extends QuickFindDialog { @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); + if (e.getKeyCode() == KeyEvent.VK_ENTER) { JToolBar toolBar = getToolBar(); boolean next = !e.isShiftDown(); @@ -78,13 +85,10 @@ public class EnigmaQuickFindDialog extends QuickFindDialog { } private static Stream components(Container container) { - return IntStream.range(0, container.getComponentCount()) - .mapToObj(container::getComponent); + return IntStream.range(0, container.getComponentCount()).mapToObj(container::getComponent); } private static Stream components(Container container, Class type) { - return components(container) - .filter(type::isInstance) - .map(type::cast); + return components(container).filter(type::isInstance).map(type::cast); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java index 43745dd9..b81782eb 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/EnigmaSyntaxKit.java @@ -10,7 +10,6 @@ import de.sciss.syntaxpane.util.Configuration; import cuchaz.enigma.gui.config.UiConfig; public class EnigmaSyntaxKit extends JavaSyntaxKit { - private static Configuration configuration = null; @Override @@ -18,23 +17,20 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit { if (configuration == null) { initConfig(DefaultSyntaxKit.getConfig(JavaSyntaxKit.class)); } + return configuration; } public void initConfig(Configuration baseConfig) { configuration = flattenConfiguration(baseConfig, EnigmaSyntaxKit.class); - // Remove all actions except a select few because they disregard the - // editable state of the editor, or at least are useless anyway because - // they would try editing the file. - // Also includes the Action.insert-date action which is written in - // Javascript and causes the editor to freeze on first load for a short - // time. - configuration.keySet().removeIf(s -> s.startsWith("Action.") && - !(s.startsWith("Action.find") || - s.startsWith("Action.goto-line") || - s.startsWith("Action.jump-to-pair") || - s.startsWith("Action.quick-find"))); + // Remove all actions except a select few because they disregard the + // editable state of the editor, or at least are useless anyway because + // they would try editing the file. + // Also includes the Action.insert-date action which is written in + // Javascript and causes the editor to freeze on first load for a short + // time. + configuration.keySet().removeIf(s -> s.startsWith("Action.") && !(s.startsWith("Action.find") || s.startsWith("Action.goto-line") || s.startsWith("Action.jump-to-pair") || s.startsWith("Action.quick-find"))); // See de.sciss.syntaxpane.TokenType configuration.put("Style.KEYWORD", String.format("%d, 0", UiConfig.getHighlightColor().getRGB())); @@ -59,27 +55,28 @@ public class EnigmaSyntaxKit extends JavaSyntaxKit { Font editorFont = UiConfig.activeUseCustomFonts() ? UiConfig.getEditorFont() : UiConfig.getFallbackEditorFont(); configuration.put("DefaultFont", UiConfig.encodeFont(editorFont)); - } + } + + /** + * Creates a new configuration from the passed configuration so that it has + * no parents and all its values are on the same level. This is needed since + * there is no way to remove map entries from parent configurations. + * + * @param source the configuration to flatten + * @param configClass the class for the new configuration + * @return a new configuration + */ + private static Configuration flattenConfiguration(Configuration source, Class configClass) { + Configuration config = new Configuration(configClass, null); - /** - * Creates a new configuration from the passed configuration so that it has - * no parents and all its values are on the same level. This is needed since - * there is no way to remove map entries from parent configurations. - * - * @param source the configuration to flatten - * @param configClass the class for the new configuration - * @return a new configuration - */ - private static Configuration flattenConfiguration(Configuration source, Class configClass) { - Configuration config = new Configuration(configClass, null); - for (String p : source.stringPropertyNames()) { - config.put(p, source.getString(p)); - } - return config; + for (String p : source.stringPropertyNames()) { + config.put(p, source.getString(p)); + } + + return config; } public static void invalidate() { configuration = null; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java index 6246192c..76d18598 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ExceptionIgnorer.java @@ -1,28 +1,27 @@ /******************************************************************************* - * 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.gui; public class ExceptionIgnorer { - public static boolean shouldIgnore(Throwable t) { - // is this that pesky concurrent access bug in the highlight painter system? // (ancient ui code is ancient) if (t instanceof ArrayIndexOutOfBoundsException) { StackTraceElement[] stackTrace = t.getStackTrace(); - if (stackTrace.length > 1) { + if (stackTrace.length > 1) { // does this stack frame match javax.swing.text.DefaultHighlighter.paint*() ? StackTraceElement frame = stackTrace[1]; + if (frame.getClassName().equals("javax.swing.text.DefaultHighlighter") && frame.getMethodName().startsWith("paint")) { return true; } @@ -31,5 +30,4 @@ public class ExceptionIgnorer { return false; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index 5a1e3d8b..b3117ce2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.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.gui; @@ -17,7 +17,6 @@ import java.awt.Point; import java.awt.event.ActionEvent; import java.nio.file.Path; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -25,12 +24,23 @@ import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.Nullable; -import javax.swing.*; +import javax.swing.AbstractAction; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - -import com.google.common.collect.Lists; import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProfile; @@ -39,8 +49,19 @@ import cuchaz.enigma.gui.config.Themes; import cuchaz.enigma.gui.config.UiConfig; import cuchaz.enigma.gui.dialog.JavadocDialog; import cuchaz.enigma.gui.dialog.SearchDialog; -import cuchaz.enigma.gui.elements.*; -import cuchaz.enigma.gui.panels.*; +import cuchaz.enigma.gui.elements.CallsTree; +import cuchaz.enigma.gui.elements.CollapsibleTabbedPane; +import cuchaz.enigma.gui.elements.EditorTabbedPane; +import cuchaz.enigma.gui.elements.ImplementationsTree; +import cuchaz.enigma.gui.elements.InheritanceTree; +import cuchaz.enigma.gui.elements.MainWindow; +import cuchaz.enigma.gui.elements.MenuBar; +import cuchaz.enigma.gui.elements.ValidatableUi; +import cuchaz.enigma.gui.panels.DeobfPanel; +import cuchaz.enigma.gui.panels.EditorPanel; +import cuchaz.enigma.gui.panels.IdentifierPanel; +import cuchaz.enigma.gui.panels.ObfPanel; +import cuchaz.enigma.gui.panels.StructurePanel; import cuchaz.enigma.gui.renderer.MessageListCellRenderer; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.LanguageUtil; @@ -57,7 +78,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.ValidationContext; public class Gui { - private final MainWindow mainWindow = new MainWindow(Enigma.NAME); private final GuiController controller; @@ -179,6 +199,7 @@ public class Gui { // restore state int[] layout = UiConfig.getLayout(); + if (layout.length >= 4) { this.splitClasses.setDividerLocation(layout[0]); this.splitCenter.setDividerLocation(layout[1]); @@ -200,6 +221,7 @@ public class Gui { frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); Point windowPos = UiConfig.getWindowPos("Main Window", null); + if (windowPos != null) { frame.setLocation(windowPos); } else { @@ -327,18 +349,26 @@ public class Gui { public void startDocChange(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null || !this.isEditable(EditableType.JAVADOC)) return; + + if (cursorReference == null || !this.isEditable(EditableType.JAVADOC)) { + return; + } + JavadocDialog.show(mainWindow.frame(), getController(), cursorReference); } public void startRename(EditorPanel editor, String text) { - if (editor != this.editorTabbedPane.getActiveEditor()) return; + if (editor != this.editorTabbedPane.getActiveEditor()) { + return; + } infoPanel.startRenaming(text); } public void startRename(EditorPanel editor) { - if (editor != this.editorTabbedPane.getActiveEditor()) return; + if (editor != this.editorTabbedPane.getActiveEditor()) { + return; + } infoPanel.startRenaming(); } @@ -349,7 +379,10 @@ public class Gui { public void showInheritance(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } this.inheritanceTree.display(cursorReference.entry); tabs.setSelectedIndex(1); @@ -357,7 +390,10 @@ public class Gui { public void showImplementations(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } this.implementationsTree.display(cursorReference.entry); tabs.setSelectedIndex(2); @@ -365,7 +401,10 @@ public class Gui { public void showCalls(EditorPanel editor, boolean recurse) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } this.callsTree.showCalls(cursorReference.entry, recurse); tabs.setSelectedIndex(3); @@ -373,7 +412,10 @@ public class Gui { public void toggleMapping(EditorPanel editor) { EntryReference, Entry> cursorReference = editor.getCursorReference(); - if (cursorReference == null) return; + + if (cursorReference == null) { + return; + } Entry obfEntry = cursorReference.entry; toggleMappingFromEntry(obfEntry); @@ -388,14 +430,15 @@ public class Gui { } public void showDiscardDiag(Function callback, String... options) { - int response = JOptionPane.showOptionDialog(this.mainWindow.frame(), I18n.translate("prompt.close.summary"), I18n.translate("prompt.close.title"), JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, null, options, options[2]); + int response = JOptionPane.showOptionDialog(this.mainWindow.frame(), I18n.translate("prompt.close.summary"), I18n.translate("prompt.close.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]); callback.apply(response); } public CompletableFuture saveMapping() { - if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.mainWindow.frame()) == JFileChooser.APPROVE_OPTION) + if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.mainWindow.frame()) == JFileChooser.APPROVE_OPTION) { return this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); + } + return CompletableFuture.completedFuture(null); } @@ -421,16 +464,13 @@ public class Gui { private void exit() { UiConfig.setWindowPos("Main Window", this.mainWindow.frame().getLocationOnScreen()); UiConfig.setWindowSize("Main Window", this.mainWindow.frame().getSize()); - UiConfig.setLayout( - this.splitClasses.getDividerLocation(), - this.splitCenter.getDividerLocation(), - this.splitRight.getDividerLocation(), - this.logSplit.getDividerLocation()); + UiConfig.setLayout(this.splitClasses.getDividerLocation(), this.splitCenter.getDividerLocation(), this.splitRight.getDividerLocation(), this.logSplit.getDividerLocation()); UiConfig.save(); if (searchDialog != null) { searchDialog.dispose(); } + this.mainWindow.frame().dispose(); System.exit(0); } @@ -452,6 +492,7 @@ public class Gui { onRenameFromClassTree(vc, prevDataChild, dataChild, node); } + node.setUserObject(data); // Ob package will never be modified, just reload deob view this.deobfPanel.deobfClasses.reload(); @@ -462,11 +503,7 @@ public class Gui { // fast enough for now EntryRemapper mapper = this.controller.project.getMapper(); ClassEntry deobf = (ClassEntry) prevData; - ClassEntry obf = mapper.getObfToDeobf().getAllEntries() - .filter(e -> e instanceof ClassEntry) - .map(e -> (ClassEntry) e) - .filter(e -> mapper.deobfuscate(e).equals(deobf)) - .findAny().orElse(deobf); + ClassEntry obf = mapper.getObfToDeobf().getAllEntries().filter(e -> e instanceof ClassEntry).map(e -> (ClassEntry) e).filter(e -> mapper.deobfuscate(e).equals(deobf)).findAny().orElse(deobf); this.controller.applyChange(vc, EntryChange.modify(obf).withDeobfName(((ClassEntry) data).getFullName())); } else { @@ -493,16 +530,12 @@ public class Gui { this.obfPanel.obfClasses.removeEntry(classEntry); this.deobfPanel.deobfClasses.reload(); this.obfPanel.obfClasses.reload(); - } - // Deob -> ob - else if (!isOldOb) { + } else if (!isOldOb) { // Deob -> ob this.obfPanel.obfClasses.moveClassIn(classEntry); this.deobfPanel.deobfClasses.removeEntry(classEntry); this.deobfPanel.deobfClasses.reload(); this.obfPanel.obfClasses.reload(); - } - // Local move - else if (isOldOb) { + } else if (isOldOb) { // Local move this.obfPanel.obfClasses.moveClassIn(classEntry); this.obfPanel.obfClasses.reload(); } else { @@ -526,6 +559,7 @@ public class Gui { if (searchDialog == null) { searchDialog = new SearchDialog(this); } + return searchDialog; } @@ -549,9 +583,11 @@ public class Gui { private void sendMessage() { String text = chatBox.getText().trim(); + if (!text.isEmpty()) { getController().sendPacket(new MessageC2SPacket(text)); } + chatBox.setText(""); } @@ -617,16 +653,17 @@ public class Gui { public boolean validateImmediateAction(Consumer op) { ValidationContext vc = new ValidationContext(); op.accept(vc); + if (!vc.canProceed()) { List messages = vc.getMessages(); String text = ValidatableUi.formatMessages(messages); JOptionPane.showMessageDialog(this.getFrame(), text, String.format("%d message(s)", messages.size()), JOptionPane.ERROR_MESSAGE); } + return vc.canProceed(); } public boolean isEditable(EditableType t) { return this.editableTypes.contains(t); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index 47a854f9..0eb9a167 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.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.gui; @@ -32,7 +32,17 @@ import com.google.common.collect.Lists; import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProfile; import cuchaz.enigma.EnigmaProject; -import cuchaz.enigma.analysis.*; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.ClassReferenceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.IndexTreeBuilder; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.MethodReferenceTreeNode; +import cuchaz.enigma.analysis.StructureTreeNode; +import cuchaz.enigma.analysis.StructureTreeOptions; import cuchaz.enigma.api.service.ObfuscationTestService; import cuchaz.enigma.classhandle.ClassHandle; import cuchaz.enigma.classhandle.ClassHandleProvider; @@ -44,7 +54,12 @@ import cuchaz.enigma.gui.newabstraction.EntryValidation; import cuchaz.enigma.gui.stats.StatsGenerator; import cuchaz.enigma.gui.stats.StatsMember; import cuchaz.enigma.gui.util.History; -import cuchaz.enigma.network.*; +import cuchaz.enigma.network.ClientPacketHandler; +import cuchaz.enigma.network.EnigmaClient; +import cuchaz.enigma.network.EnigmaServer; +import cuchaz.enigma.network.IntegratedEnigmaServer; +import cuchaz.enigma.network.Message; +import cuchaz.enigma.network.ServerPacketHandler; import cuchaz.enigma.network.packet.EntryChangeC2SPacket; import cuchaz.enigma.network.packet.LoginC2SPacket; import cuchaz.enigma.network.packet.Packet; @@ -54,7 +69,12 @@ import cuchaz.enigma.source.SourceIndex; import cuchaz.enigma.source.Token; import cuchaz.enigma.translation.TranslateResult; import cuchaz.enigma.translation.Translator; -import cuchaz.enigma.translation.mapping.*; +import cuchaz.enigma.translation.mapping.EntryChange; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.translation.mapping.EntryUtil; +import cuchaz.enigma.translation.mapping.MappingDelta; +import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.mapping.serde.MappingFormat; import cuchaz.enigma.translation.mapping.serde.MappingParseException; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; @@ -90,9 +110,7 @@ public class GuiController implements ClientPacketHandler { public GuiController(Gui gui, EnigmaProfile profile) { this.gui = gui; - this.enigma = Enigma.builder() - .setProfile(profile) - .build(); + this.enigma = Enigma.builder().setProfile(profile).build(); } public boolean isDirty() { @@ -121,7 +139,9 @@ public class GuiController implements ClientPacketHandler { } public CompletableFuture openMappings(MappingFormat format, Path path) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } gui.setMappingsFile(path); @@ -145,7 +165,9 @@ public class GuiController implements ClientPacketHandler { @Override public void openMappings(EntryTree mappings) { - if (project == null) return; + if (project == null) { + return; + } project.setMappings(mappings); refreshClasses(); @@ -168,7 +190,9 @@ public class GuiController implements ClientPacketHandler { * @return the future of saving */ public CompletableFuture saveMappings(Path path, MappingFormat format) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { EntryRemapper mapper = project.getMapper(); @@ -189,7 +213,9 @@ public class GuiController implements ClientPacketHandler { } public void closeMappings() { - if (project == null) return; + if (project == null) { + return; + } project.setMappings(null); @@ -202,9 +228,11 @@ public class GuiController implements ClientPacketHandler { Path jarPath = this.project.getJarPath(); MappingFormat loadedMappingFormat = this.loadedMappingFormat; Path loadedMappingPath = this.loadedMappingPath; + if (jarPath != null) { this.closeJar(); CompletableFuture f = this.openJar(jarPath); + if (loadedMappingFormat != null && loadedMappingPath != null) { f.whenComplete((v, t) -> this.openMappings(loadedMappingFormat, loadedMappingPath)); } @@ -214,6 +242,7 @@ public class GuiController implements ClientPacketHandler { public void reloadMappings() { MappingFormat loadedMappingFormat = this.loadedMappingFormat; Path loadedMappingPath = this.loadedMappingPath; + if (loadedMappingFormat != null && loadedMappingPath != null) { this.closeMappings(); this.openMappings(loadedMappingFormat, loadedMappingPath); @@ -221,29 +250,34 @@ public class GuiController implements ClientPacketHandler { } public CompletableFuture dropMappings() { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> project.dropMappings(progress)); } public CompletableFuture exportSource(final Path path) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { EnigmaProject.JarExport jar = project.exportRemappedJar(progress); - jar.decompileStream(progress, chp.getDecompilerService(), EnigmaProject.DecompileErrorStrategy.TRACE_AS_SOURCE) - .forEach(source -> { - try { - source.writeTo(source.resolvePath(path)); - } catch (IOException e) { - e.printStackTrace(); - } - }); + jar.decompileStream(progress, chp.getDecompilerService(), EnigmaProject.DecompileErrorStrategy.TRACE_AS_SOURCE).forEach(source -> { + try { + source.writeTo(source.resolvePath(path)); + } catch (IOException e) { + e.printStackTrace(); + } + }); }); } public CompletableFuture exportJar(final Path path) { - if (project == null) return CompletableFuture.completedFuture(null); + if (project == null) { + return CompletableFuture.completedFuture(null); + } return ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { EnigmaProject.JarExport jar = project.exportRemappedJar(progress); @@ -269,20 +303,14 @@ public class GuiController implements ClientPacketHandler { } try { - return tokenHandle.getSource().get() - .map(DecompiledClassSource::getIndex) - .map(index -> new ReadableToken( - index.getLineNumber(token.start), - index.getColumnNumber(token.start), - index.getColumnNumber(token.end))) - .unwrapOr(null); + return tokenHandle.getSource().get().map(DecompiledClassSource::getIndex).map(index -> new ReadableToken(index.getLineNumber(token.start), index.getColumnNumber(token.start), index.getColumnNumber(token.end))).unwrapOr(null); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } /** - * Navigates to the declaration with respect to navigation history + * Navigates to the declaration with respect to navigation history. * * @param entry the entry whose declaration will be navigated to */ @@ -290,11 +318,12 @@ public class GuiController implements ClientPacketHandler { if (entry == null) { throw new IllegalArgumentException("Entry cannot be null!"); } + openReference(EntryReference.declaration(entry, entry.getName())); } /** - * Navigates to the reference with respect to navigation history + * Navigates to the reference with respect to navigation history. * * @param reference the reference */ @@ -302,6 +331,7 @@ public class GuiController implements ClientPacketHandler { if (reference == null) { throw new IllegalArgumentException("Reference cannot be null!"); } + if (this.referenceHistory == null) { this.referenceHistory = new History<>(reference); } else { @@ -317,11 +347,7 @@ public class GuiController implements ClientPacketHandler { EntryRemapper mapper = this.project.getMapper(); SourceIndex index = source.getIndex(); - return mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_CLOSEST) - .stream() - .flatMap(r -> index.getReferenceTokens(r).stream()) - .sorted() - .toList(); + return mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_CLOSEST).stream().flatMap(r -> index.getReferenceTokens(r).stream()).sorted().toList(); } public void openPreviousReference() { @@ -349,6 +375,7 @@ public class GuiController implements ClientPacketHandler { // entry is not in the jar. Ignore it return; } + openDeclaration(entry); } @@ -356,12 +383,15 @@ public class GuiController implements ClientPacketHandler { if (!project.isRenamable(reference.getLocationClassEntry())) { return; } + openReference(reference); } public void refreshClasses() { - if (project == null) return; - + if (project == null) { + return; + } + List obfClasses = Lists.newArrayList(); List deobfClasses = Lists.newArrayList(); this.addSeparatedClasses(obfClasses, deobfClasses); @@ -373,8 +403,7 @@ public class GuiController implements ClientPacketHandler { EntryRemapper mapper = project.getMapper(); Collection classes = project.getJarIndex().getEntryIndex().getClasses(); - Stream visibleClasses = classes.stream() - .filter(entry -> !entry.isInnerClass()); + Stream visibleClasses = classes.stream().filter(entry -> !entry.isInnerClass()); visibleClasses.forEach(entry -> { if (gui.isSingleClassTree()) { @@ -428,12 +457,15 @@ public class GuiController implements ClientPacketHandler { public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) { Translator translator = project.getMapper().getDeobfuscator(); List rootNodes = indexTreeBuilder.buildMethodImplementations(translator, entry); + if (rootNodes.isEmpty()) { return null; } + if (rootNodes.size() > 1) { System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one."); } + return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry); } @@ -481,13 +513,20 @@ public class GuiController implements ClientPacketHandler { public void applyChange(ValidationContext vc, EntryChange change) { this.applyChange0(vc, change); gui.showStructure(gui.getActiveEditor()); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } + this.sendPacket(new EntryChangeC2SPacket(change)); } private void applyChange0(ValidationContext vc, EntryChange change) { validateChange(vc, change); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } Entry target = change.getTarget(); EntryMapping prev = this.project.getMapper().getDeobfMapping(target); @@ -506,6 +545,7 @@ public class GuiController implements ClientPacketHandler { if (!Objects.equals(prev.javadoc(), mapping.javadoc())) { this.chp.invalidateJavadoc(target.getTopLevelClass()); } + gui.showStructure(gui.getActiveEditor()); } @@ -517,10 +557,7 @@ public class GuiController implements ClientPacketHandler { File statsFile = File.createTempFile("stats", ".html"); try (FileWriter w = new FileWriter(statsFile)) { - w.write( - Utils.readResourceToString("/stats.html") - .replace("/*data*/", data) - ); + w.write(Utils.readResourceToString("/stats.html").replace("/*data*/", data)); } Desktop.getDesktop().open(statsFile); @@ -573,15 +610,18 @@ public class GuiController implements ClientPacketHandler { if (client != null) { client.disconnect(); } + if (server != null) { server.stop(); } + client = null; server = null; SwingUtilities.invokeLater(() -> { if (reason != null) { JOptionPane.showMessageDialog(gui.getFrame(), I18n.translate(reason), I18n.translate("disconnect.disconnected"), JOptionPane.INFORMATION_MESSAGE); } + gui.setConnectionState(ConnectionState.NOT_CONNECTED); }); } @@ -602,5 +642,4 @@ public class GuiController implements ClientPacketHandler { public void updateUserList(List users) { gui.setUserList(users); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java index 1172a393..56f43859 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Main.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.gui; @@ -20,7 +20,11 @@ import java.util.List; import java.util.Set; import com.google.common.io.MoreFiles; -import joptsimple.*; +import joptsimple.OptionException; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import joptsimple.ValueConverter; import cuchaz.enigma.EnigmaProfile; import cuchaz.enigma.gui.config.Themes; @@ -30,21 +34,14 @@ import cuchaz.enigma.translation.mapping.serde.MappingFormat; import cuchaz.enigma.utils.I18n; public class Main { - public static void main(String[] args) throws IOException { OptionParser parser = new OptionParser(); - OptionSpec jar = parser.accepts("jar", "Jar file to open at startup") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec jar = parser.accepts("jar", "Jar file to open at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE); - OptionSpec mappings = parser.accepts("mappings", "Mappings file to open at startup") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec mappings = parser.accepts("mappings", "Mappings file to open at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE); - OptionSpec profile = parser.accepts("profile", "Profile json to apply at startup") - .withRequiredArg() - .withValuesConvertedBy(PathConverter.INSTANCE); + OptionSpec profile = parser.accepts("profile", "Profile json to apply at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE); parser.acceptsAll(List.of("edit-all", "e"), "Enable editing everything"); parser.acceptsAll(List.of("no-edit-all", "E"), "Disable editing everything"); @@ -78,26 +75,26 @@ public class Main { for (OptionSpec spec : options.specs()) { for (String s : spec.options()) { switch (s) { - case "edit-all" -> editables.addAll(List.of(EditableType.values())); - case "no-edit-all" -> editables.clear(); - case "edit-classes" -> editables.add(EditableType.CLASS); - case "no-edit-classes" -> editables.remove(EditableType.CLASS); - case "edit-methods" -> editables.add(EditableType.METHOD); - case "no-edit-methods" -> editables.remove(EditableType.METHOD); - case "edit-fields" -> editables.add(EditableType.FIELD); - case "no-edit-fields" -> editables.remove(EditableType.FIELD); - case "edit-parameters" -> editables.add(EditableType.PARAMETER); - case "no-edit-parameters" -> editables.remove(EditableType.PARAMETER); - case "edit-locals" -> { - editables.add(EditableType.LOCAL_VARIABLE); - System.err.println("warning: --edit-locals has no effect as local variables are currently not editable"); - } - case "no-edit-locals" -> { - editables.remove(EditableType.LOCAL_VARIABLE); - System.err.println("warning: --no-edit-locals has no effect as local variables are currently not editable"); - } - case "edit-javadocs" -> editables.add(EditableType.JAVADOC); - case "no-edit-javadocs" -> editables.remove(EditableType.JAVADOC); + case "edit-all" -> editables.addAll(List.of(EditableType.values())); + case "no-edit-all" -> editables.clear(); + case "edit-classes" -> editables.add(EditableType.CLASS); + case "no-edit-classes" -> editables.remove(EditableType.CLASS); + case "edit-methods" -> editables.add(EditableType.METHOD); + case "no-edit-methods" -> editables.remove(EditableType.METHOD); + case "edit-fields" -> editables.add(EditableType.FIELD); + case "no-edit-fields" -> editables.remove(EditableType.FIELD); + case "edit-parameters" -> editables.add(EditableType.PARAMETER); + case "no-edit-parameters" -> editables.remove(EditableType.PARAMETER); + case "edit-locals" -> { + editables.add(EditableType.LOCAL_VARIABLE); + System.err.println("warning: --edit-locals has no effect as local variables are currently not editable"); + } + case "no-edit-locals" -> { + editables.remove(EditableType.LOCAL_VARIABLE); + System.err.println("warning: --no-edit-locals has no effect as local variables are currently not editable"); + } + case "edit-javadocs" -> editables.add(EditableType.JAVADOC); + case "no-edit-javadocs" -> editables.remove(EditableType.JAVADOC); } } } @@ -110,7 +107,7 @@ public class Main { Gui gui = new Gui(parsedProfile, editables); GuiController controller = gui.getController(); - + if (options.has("single-class-tree")) { gui.setSingleClassTree(true); } @@ -120,6 +117,7 @@ public class Main { CrashDialog.init(gui.getFrame()); Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { t.printStackTrace(System.err); + if (!ExceptionIgnorer.shouldIgnore(t)) { CrashDialog.show(t); } @@ -128,19 +126,19 @@ public class Main { if (options.has(jar)) { Path jarPath = options.valueOf(jar); - controller.openJar(jarPath) - .whenComplete((v, t) -> { - if (options.has(mappings)) { - Path mappingsPath = options.valueOf(mappings); - if (Files.isDirectory(mappingsPath)) { - controller.openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsPath); - } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsPath))) { - controller.openMappings(MappingFormat.ENIGMA_ZIP, mappingsPath); - } else { - controller.openMappings(MappingFormat.ENIGMA_FILE, mappingsPath); - } - } - }); + controller.openJar(jarPath).whenComplete((v, t) -> { + if (options.has(mappings)) { + Path mappingsPath = options.valueOf(mappings); + + if (Files.isDirectory(mappingsPath)) { + controller.openMappings(MappingFormat.ENIGMA_DIRECTORY, mappingsPath); + } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsPath))) { + controller.openMappings(MappingFormat.ENIGMA_ZIP, mappingsPath); + } else { + controller.openMappings(MappingFormat.ENIGMA_FILE, mappingsPath); + } + } + }); } } catch (OptionException e) { System.out.println("Invalid arguments: " + e.getMessage()); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java index 309f9106..c4541fc6 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/NestedPackages.java @@ -1,21 +1,22 @@ package cuchaz.enigma.gui; -import cuchaz.enigma.gui.node.ClassSelectorClassNode; -import cuchaz.enigma.gui.node.ClassSelectorPackageNode; -import cuchaz.enigma.translation.mapping.EntryRemapper; -import cuchaz.enigma.translation.representation.entry.ClassEntry; +import java.util.Collection; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -public class NestedPackages { +import cuchaz.enigma.gui.node.ClassSelectorClassNode; +import cuchaz.enigma.gui.node.ClassSelectorPackageNode; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +public class NestedPackages { private final DefaultMutableTreeNode root = new DefaultMutableTreeNode(); private final Map packageToNode = new HashMap<>(); private final Map classToNode = new HashMap<>(); @@ -42,20 +43,20 @@ public class NestedPackages { return 0; }; - for (var entry : entries) { + for (ClassEntry entry : entries) { addEntry(entry); } } public void addEntry(ClassEntry entry) { - var translated = remapper.deobfuscate(entry); + ClassEntry translated = remapper.deobfuscate(entry); var me = new ClassSelectorClassNode(entry, translated); classToNode.put(entry, me); insert(getPackage(translated.getPackageName()), me); } public DefaultMutableTreeNode getPackage(String packageName) { - var node = packageToNode.get(packageName); + DefaultMutableTreeNode node = packageToNode.get(packageName); if (packageName == null) { return root; @@ -75,7 +76,7 @@ public class NestedPackages { } public TreePath getPackagePath(String packageName) { - var node = packageToNode.getOrDefault(packageName, root); + DefaultMutableTreeNode node = packageToNode.getOrDefault(packageName, root); return new TreePath(node.getPath()); } @@ -84,15 +85,15 @@ public class NestedPackages { } public void removeClassNode(ClassEntry entry) { - var node = classToNode.remove(entry); + ClassSelectorClassNode node = classToNode.remove(entry); if (node != null) { node.removeFromParent(); // remove dangling packages - var packageNode = packageToNode.get(entry.getPackageName()); + DefaultMutableTreeNode packageNode = packageToNode.get(entry.getPackageName()); while (packageNode != null && packageNode.getChildCount() == 0) { - var theNode = packageNode; + DefaultMutableTreeNode theNode = packageNode; packageNode = (DefaultMutableTreeNode) packageNode.getParent(); theNode.removeFromParent(); @@ -108,8 +109,8 @@ public class NestedPackages { } private void insert(DefaultMutableTreeNode parent, MutableTreeNode child) { - var index = 0; - var children = parent.children(); + int index = 0; + Enumeration children = parent.children(); while (children.hasMoreElements()) { if (comparator.compare(children.nextElement(), child) < 0) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java index b7fa2eba..ff80e17f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/QuickFindAction.java @@ -1,10 +1,11 @@ package cuchaz.enigma.gui; -import de.sciss.syntaxpane.SyntaxDocument; -import de.sciss.syntaxpane.actions.DefaultSyntaxAction; +import java.awt.event.ActionEvent; import javax.swing.text.JTextComponent; -import java.awt.event.ActionEvent; + +import de.sciss.syntaxpane.SyntaxDocument; +import de.sciss.syntaxpane.actions.DefaultSyntaxAction; public final class QuickFindAction extends DefaultSyntaxAction { public QuickFindAction() { @@ -26,6 +27,7 @@ public final class QuickFindAction extends DefaultSyntaxAction { public static Data get(JTextComponent target) { Object o = target.getDocument().getProperty(KEY); + if (o instanceof Data) { return (Data) o; } @@ -39,6 +41,7 @@ public final class QuickFindAction extends DefaultSyntaxAction { if (findDialog == null) { findDialog = new EnigmaQuickFindDialog(target); } + findDialog.showFor(target); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java index 3e4b30cd..eac11ed4 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ReadableToken.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.gui; public class ReadableToken { - public int line; public int startColumn; public int endColumn; diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java index 4ef04428..000793e6 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/TokenListCellRenderer.java @@ -1,23 +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.gui; -import cuchaz.enigma.source.Token; +import java.awt.Component; -import javax.swing.*; -import java.awt.*; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; -public class TokenListCellRenderer implements ListCellRenderer { +import cuchaz.enigma.source.Token; +public class TokenListCellRenderer implements ListCellRenderer { private GuiController controller; private DefaultListCellRenderer defaultRenderer; @@ -32,5 +35,4 @@ public class TokenListCellRenderer implements ListCellRenderer { label.setText(this.controller.getReadableToken(token).toString()); return label; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java index cec3fa1e..2088aac2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/LookAndFeel.java @@ -40,11 +40,11 @@ public enum LookAndFeel { try { switch (this) { - case NONE -> UIManager.setLookAndFeel(NONE_LAF); - case DEFAULT -> UIManager.setLookAndFeel(new FlatLightLaf()); - case METAL -> UIManager.setLookAndFeel(new MetalLookAndFeel()); - case DARCULA -> UIManager.setLookAndFeel(new FlatDarkLaf()); - case SYSTEM -> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + case NONE -> UIManager.setLookAndFeel(NONE_LAF); + case DEFAULT -> UIManager.setLookAndFeel(new FlatLightLaf()); + case METAL -> UIManager.setLookAndFeel(new MetalLookAndFeel()); + case DARCULA -> UIManager.setLookAndFeel(new FlatDarkLaf()); + case SYSTEM -> UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } } catch (Exception e) { throw new Error("Failed to set global look and feel", e); @@ -66,5 +66,4 @@ public enum LookAndFeel { int b = (int) (0.3 * c.getRed() + 0.59 * c.getGreen() + 0.11 * c.getBlue()); return b < 85; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java index 4439cb8a..eaf20e7f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/NetConfig.java @@ -4,7 +4,6 @@ import cuchaz.enigma.config.ConfigContainer; import cuchaz.enigma.network.EnigmaServer; public final class NetConfig { - private NetConfig() { } @@ -53,5 +52,4 @@ public final class NetConfig { public static void setServerPort(int port) { cfg.data().section("Server").setInt("Port", port); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java index 660d2313..2e84991b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/OldConfigImporter.java @@ -5,7 +5,6 @@ import java.awt.Font; import cuchaz.enigma.gui.config.legacy.Config; public final class OldConfigImporter { - private OldConfigImporter() { } @@ -13,14 +12,15 @@ public final class OldConfigImporter { public static void doImport() { if (Config.CONFIG_FILE.exists()) { Config config = new Config(); + if (config.editorFont != null) { UiConfig.setEditorFont(Font.decode(config.editorFont)); } + UiConfig.setLanguage(config.language); UiConfig.setLookAndFeel(config.lookAndFeel); UiConfig.setScaleFactor(config.scaleFactor); UiConfig.setDecompiler(config.decompiler); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java index 839a5cbc..e2db9682 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/Themes.java @@ -16,7 +16,6 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.source.RenamableTokenType; public class Themes { - private static final Set listeners = new HashSet<>(); // Calling this after the UI is initialized (e.g. when the user changes @@ -87,11 +86,8 @@ public class Themes { } public static ImmutableMap getBoxHighlightPainters() { - return ImmutableMap.of( - RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(UiConfig.getObfuscatedColor(), UiConfig.getObfuscatedOutlineColor()), - RenamableTokenType.PROPOSED, BoxHighlightPainter.create(UiConfig.getProposedColor(), UiConfig.getProposedOutlineColor()), - RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(UiConfig.getDeobfuscatedColor(), UiConfig.getDeobfuscatedOutlineColor()) - ); + return ImmutableMap.of(RenamableTokenType.OBFUSCATED, BoxHighlightPainter.create(UiConfig.getObfuscatedColor(), UiConfig.getObfuscatedOutlineColor()), RenamableTokenType.PROPOSED, BoxHighlightPainter.create(UiConfig.getProposedColor(), UiConfig.getProposedOutlineColor()), + RenamableTokenType.DEOBFUSCATED, BoxHighlightPainter.create(UiConfig.getDeobfuscatedColor(), UiConfig.getDeobfuscatedOutlineColor())); } public static void addListener(ThemeChangeListener listener) { @@ -101,5 +97,4 @@ public class Themes { public static void removeListener(ThemeChangeListener listener) { listeners.remove(listener); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java index 8a10acec..cdf27cac 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/UiConfig.java @@ -1,6 +1,10 @@ package cuchaz.enigma.gui.config; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.Toolkit; import java.util.Optional; import java.util.OptionalInt; @@ -10,7 +14,6 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public final class UiConfig { - private UiConfig() { } @@ -82,11 +85,11 @@ public final class UiConfig { * @return an integer array composed of these 4 dimensions */ public static int[] getLayout() { - return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[] { -1, -1, -1, -1 }); + return swing.data().section("Main Window").getIntArray("Layout").orElseGet(() -> new int[]{-1, -1, -1, -1}); } public static void setLayout(int leftV, int left, int right, int rightH) { - swing.data().section("Main Window").setIntArray("Layout", new int[] { leftV, left, right, rightH }); + swing.data().section("Main Window").setIntArray("Layout", new int[]{leftV, left, right, rightH}); } public static LookAndFeel getLookAndFeel() { @@ -287,6 +290,7 @@ public final class UiConfig { ConfigSection section = swing.data().section(window); OptionalInt width = section.getInt(String.format("Width %s", screenSize.width)); OptionalInt height = section.getInt(String.format("Height %s", screenSize.height)); + if (width.isPresent() && height.isPresent()) { return new Dimension(width.getAsInt(), height.getAsInt()); } else { @@ -306,6 +310,7 @@ public final class UiConfig { ConfigSection section = swing.data().section(window); OptionalInt x = section.getInt(String.format("X %s", screenSize.width)); OptionalInt y = section.getInt(String.format("Y %s", screenSize.height)); + if (x.isPresent() && y.isPresent()) { int ix = x.getAsInt(); int iy = y.getAsInt(); @@ -354,6 +359,7 @@ public final class UiConfig { public static void setLookAndFeelDefaults(LookAndFeel laf, boolean isDark) { ConfigSection s = swing.data().section("Themes").section(laf.name()).section("Colors"); + if (!isDark) { // Defaults found here: https://github.com/Sciss/SyntaxPane/blob/122da367ff7a5d31627a70c62a48a9f0f4f85a0a/src/main/resources/de/sciss/syntaxpane/defaultsyntaxkit/config.properties#L139 s.setIfAbsentRgbColor("Line Numbers Foreground", 0x333300); @@ -412,5 +418,4 @@ public final class UiConfig { s.setIfAbsentRgbColor("Text", 0xF8F8F2); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java index 1265750f..0e8f7da2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/config/legacy/Config.java @@ -6,7 +6,15 @@ import java.lang.reflect.Type; import java.nio.charset.Charset; import com.google.common.io.Files; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.InstanceCreator; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import cuchaz.enigma.gui.config.Decompiler; import cuchaz.enigma.utils.I18n; @@ -28,7 +36,7 @@ public class Config { } Color baseColor = new Color(rgb); - return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int)(255 * alpha)); + return new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), (int) (255 * alpha)); } } @@ -73,12 +81,7 @@ public class Config { public Decompiler decompiler = Decompiler.CFR; public Config() { - gson = new GsonBuilder() - .registerTypeAdapter(Integer.class, new IntSerializer()) - .registerTypeAdapter(Integer.class, new IntDeserializer()) - .registerTypeAdapter(Config.class, (InstanceCreator) type -> this) - .setPrettyPrinting() - .create(); + gson = new GsonBuilder().registerTypeAdapter(Integer.class, new IntSerializer()).registerTypeAdapter(Integer.class, new IntDeserializer()).registerTypeAdapter(Config.class, (InstanceCreator) type -> this).setPrettyPrinting().create(); this.loadConfig(); } @@ -105,5 +108,4 @@ public class Config { return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java index f8922e64..c2211209 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.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.gui.dialog; @@ -15,7 +15,11 @@ import java.awt.Container; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.WindowConstants; import cuchaz.enigma.Enigma; import cuchaz.enigma.gui.util.GridBagConstraintsBuilder; @@ -23,16 +27,12 @@ import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.utils.I18n; public class AboutDialog { - public static void show(JFrame parent) { JDialog frame = new JDialog(parent, String.format(I18n.translate("menu.help.about.title"), Enigma.NAME), true); Container pane = frame.getContentPane(); pane.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2) - .weight(1.0, 0.0) - .anchor(GridBagConstraints.WEST); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST); JLabel title = new JLabel(Enigma.NAME); title.setFont(title.getFont().deriveFont(title.getFont().getSize2D() * 1.5f)); @@ -52,5 +52,4 @@ public class AboutDialog { frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); frame.setVisible(true); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java index c9ca8090..76232c44 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/AbstractDialog.java @@ -1,6 +1,12 @@ package cuchaz.enigma.gui.dialog; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.util.List; import javax.swing.JButton; @@ -15,7 +21,6 @@ import cuchaz.enigma.utils.Pair; import cuchaz.enigma.utils.validation.ValidationContext; public abstract class AbstractDialog extends JDialog { - protected final ValidationContext vc = new ValidationContext(); private boolean actionConfirm = false; @@ -38,6 +43,7 @@ public abstract class AbstractDialog extends JDialog { inputContainer.add(label, cb.pos(0, i).weightX(0.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.NONE).build()); inputContainer.add(component, cb.pos(1, i).weightX(1.0).anchor(GridBagConstraints.LINE_END).fill(GridBagConstraints.HORIZONTAL).build()); } + contentPane.add(inputContainer, BorderLayout.CENTER); Container buttonContainer = new JPanel(new FlowLayout(FlowLayout.RIGHT, ScaleUtil.scale(4), ScaleUtil.scale(4))); JButton connectButton = new JButton(I18n.translate(confirmAction)); @@ -57,6 +63,7 @@ public abstract class AbstractDialog extends JDialog { protected void confirm() { vc.reset(); validateInputs(); + if (vc.canProceed()) { actionConfirm = true; setVisible(false); @@ -74,5 +81,4 @@ public abstract class AbstractDialog extends JDialog { public void validateInputs() { } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java index df65473c..51948b56 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ChangeDialog.java @@ -14,7 +14,6 @@ import javax.swing.JPanel; import cuchaz.enigma.utils.I18n; public class ChangeDialog { - public static void show(Window parent) { // init frame JDialog frame = new JDialog(parent, I18n.translate("menu.view.change.title"), Dialog.DEFAULT_MODALITY_TYPE); @@ -48,5 +47,4 @@ public class ChangeDialog { frame.setLocationRelativeTo(parent); frame.setVisible(true); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java index 2486dfe1..1c2bd4c1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ConnectToServerDialog.java @@ -20,7 +20,6 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.StandardValidation; public class ConnectToServerDialog extends AbstractDialog { - private JTextField usernameField; private ValidatableTextField ipField; private JPasswordField passwordField; @@ -45,16 +44,13 @@ public class ConnectToServerDialog extends AbstractDialog { ipField.addActionListener(event -> confirm()); passwordField.addActionListener(event -> confirm()); - return Arrays.asList( - new Pair<>("prompt.connect.username", usernameField), - new Pair<>("prompt.connect.address", ipField), - new Pair<>("prompt.password", passwordField) - ); + return Arrays.asList(new Pair<>("prompt.connect.username", usernameField), new Pair<>("prompt.connect.address", ipField), new Pair<>("prompt.password", passwordField)); } @Override public void validateInputs() { vc.setActiveElement(ipField); + if (StandardValidation.notBlank(vc, ipField.getText())) { if (ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT) == null) { vc.raise(Message.INVALID_IP); @@ -63,16 +59,18 @@ public class ConnectToServerDialog extends AbstractDialog { } public Result getResult() { - if (!isActionConfirm()) return null; + if (!isActionConfirm()) { + return null; + } + vc.reset(); validateInputs(); - if (!vc.canProceed()) return null; - return new Result( - usernameField.getText(), - ipField.getText(), - Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), - passwordField.getPassword() - ); + + if (!vc.canProceed()) { + return null; + } + + return new Result(usernameField.getText(), ipField.getText(), Objects.requireNonNull(ServerAddress.from(ipField.getText(), EnigmaServer.DEFAULT_PORT)), passwordField.getPassword()); } public static Result show(Frame parent) { @@ -114,5 +112,4 @@ public class ConnectToServerDialog extends AbstractDialog { return password; } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java index c2a93fa5..a84e9775 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java @@ -1,31 +1,42 @@ /******************************************************************************* - * 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.gui.dialog; +import java.awt.BorderLayout; +import java.awt.Container; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.WindowConstants; + import cuchaz.enigma.Enigma; import cuchaz.enigma.gui.util.GuiUtil; -import cuchaz.enigma.utils.I18n; import cuchaz.enigma.gui.util.ScaleUtil; - -import javax.swing.*; -import java.awt.*; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.FileWriter; -import java.io.File; -import java.io.IOException; +import cuchaz.enigma.utils.I18n; public class CrashDialog { - private static CrashDialog instance = null; private JFrame frame; @@ -53,6 +64,7 @@ public class CrashDialog { exportButton.addActionListener(event -> { JFileChooser chooser = new JFileChooser(); chooser.setSelectedFile(new File("enigma_crash.log")); + if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { try { File file = chooser.getSelectedFile(); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java index 07daf6dc..35999e28 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/CreateServerDialog.java @@ -16,7 +16,6 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.StandardValidation; public class CreateServerDialog extends AbstractDialog { - private ValidatableTextField portField; private ValidatablePasswordField passwordField; @@ -38,10 +37,7 @@ public class CreateServerDialog extends AbstractDialog { portField.addActionListener(event -> confirm()); passwordField.addActionListener(event -> confirm()); - return Arrays.asList( - new Pair<>("prompt.create_server.port", portField), - new Pair<>("prompt.password", passwordField) - ); + return Arrays.asList(new Pair<>("prompt.create_server.port", portField), new Pair<>("prompt.password", passwordField)); } @Override @@ -49,20 +45,25 @@ public class CreateServerDialog extends AbstractDialog { vc.setActiveElement(portField); StandardValidation.isIntInRange(vc, portField.getText(), 0, 65535); vc.setActiveElement(passwordField); + if (passwordField.getPassword().length > EnigmaServer.MAX_PASSWORD_LENGTH) { vc.raise(Message.FIELD_LENGTH_OUT_OF_RANGE, EnigmaServer.MAX_PASSWORD_LENGTH); } } public Result getResult() { - if (!isActionConfirm()) return null; + if (!isActionConfirm()) { + return null; + } + vc.reset(); validateInputs(); - if (!vc.canProceed()) return null; - return new Result( - Integer.parseInt(portField.getText()), - passwordField.getPassword() - ); + + if (!vc.canProceed()) { + return null; + } + + return new Result(Integer.parseInt(portField.getText()), passwordField.getPassword()); } public static Result show(Frame parent) { @@ -92,5 +93,4 @@ public class CreateServerDialog extends AbstractDialog { return password; } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java index 4e02a666..f0bae17b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/FontDialog.java @@ -1,6 +1,11 @@ package cuchaz.enigma.gui.dialog; -import java.awt.*; +import java.awt.Component; +import java.awt.Container; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.util.List; import javax.swing.JButton; @@ -16,20 +21,9 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public class FontDialog extends JDialog { + private static final List CATEGORIES = List.of("Default", "Default 2", "Small", "Editor"); - private static final List CATEGORIES = List.of( - "Default", - "Default 2", - "Small", - "Editor" - ); - - private static final List CATEGORY_TEXTS = List.of( - "fonts.cat.default", - "fonts.cat.default2", - "fonts.cat.small", - "fonts.cat.editor" - ); + private static final List CATEGORY_TEXTS = List.of("fonts.cat.default", "fonts.cat.default2", "fonts.cat.small", "fonts.cat.editor"); private final JList entries = new JList<>(CATEGORY_TEXTS.stream().map(I18n::translate).toArray(String[]::new)); private final FontChooser chooser = new FontChooser(Font.decode(Font.DIALOG)); @@ -55,8 +49,7 @@ public class FontDialog extends JDialog { Container contentPane = this.getContentPane(); contentPane.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2); contentPane.add(this.entries, cb.pos(0, 0).weight(0.0, 1.0).fill(GridBagConstraints.BOTH).build()); contentPane.add(this.chooser, cb.pos(1, 0).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).size(2, 1).build()); @@ -77,6 +70,7 @@ public class FontDialog extends JDialog { private void categoryChanged() { this.updateUiState(); int selectedIndex = this.entries.getSelectedIndex(); + if (selectedIndex != -1) { this.chooser.setSelectedFont(this.fonts[selectedIndex]); } @@ -84,6 +78,7 @@ public class FontDialog extends JDialog { private void selectedFontChanged() { int selectedIndex = this.entries.getSelectedIndex(); + if (selectedIndex != -1) { this.fonts[selectedIndex] = this.chooser.getSelectedFont(); } @@ -98,6 +93,7 @@ public class FontDialog extends JDialog { for (int i = 0; i < CATEGORIES.size(); i++) { UiConfig.setFont(CATEGORIES.get(i), this.fonts[i]); } + UiConfig.setUseCustomFonts(this.customCheckBox.isSelected()); UiConfig.save(); ChangeDialog.show(this); @@ -118,8 +114,8 @@ public class FontDialog extends JDialog { for (Component component : ((Container) self).getComponents()) { recursiveSetEnabled(component, enabled); } + self.setEnabled(enabled); } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java index 9470e11c..d6e544d0 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/JavadocDialog.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.gui.dialog; @@ -18,7 +18,15 @@ import java.awt.FlowLayout; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuBar; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.WindowConstants; import javax.swing.text.html.HTML; import com.google.common.base.Strings; @@ -35,7 +43,6 @@ import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.validation.ValidationContext; public class JavadocDialog { - private final JDialog ui; private final GuiController controller; private final Entry entry; @@ -62,19 +69,21 @@ public class JavadocDialog { @Override public void keyPressed(KeyEvent event) { switch (event.getKeyCode()) { - case KeyEvent.VK_ENTER: - if (event.isControlDown()) { - doSave(); - if (vc.canProceed()) { - close(); - } + case KeyEvent.VK_ENTER: + if (event.isControlDown()) { + doSave(); + + if (vc.canProceed()) { + close(); } - break; - case KeyEvent.VK_ESCAPE: - close(); - break; - default: - break; + } + + break; + case KeyEvent.VK_ESCAPE: + close(); + break; + default: + break; } } }); @@ -108,6 +117,7 @@ public class JavadocDialog { } else { tagText = tag.getText() + " " + text.getSelectedText(); } + text.replaceSelection(tagText); } else { text.insert(tagText, text.getCaretPosition()); @@ -116,6 +126,7 @@ public class JavadocDialog { if (tag.isInline()) { text.setCaretPosition(text.getCaretPosition() - 1); } + text.grabFocus(); }); tagsMenu.add(tagButton); @@ -124,9 +135,11 @@ public class JavadocDialog { // add html tags JComboBox htmlList = new JComboBox(); htmlList.setPreferredSize(new Dimension()); + for (HTML.Tag htmlTag : HTML.getAllTags()) { htmlList.addItem(htmlTag.toString()); } + htmlList.addActionListener(action -> { String tagText = "<" + htmlList.getSelectedItem().toString() + ">"; text.insert(tagText, text.getCaretPosition()); @@ -146,9 +159,17 @@ public class JavadocDialog { public void doSave() { vc.reset(); validate(); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } + save(); - if (!vc.canProceed()) return; + + if (!vc.canProceed()) { + return; + } + close(); } @@ -189,7 +210,7 @@ public class JavadocDialog { private boolean inline; - private JavadocTag(boolean inline) { + JavadocTag(boolean inline) { this.inline = inline; } @@ -201,5 +222,4 @@ public class JavadocDialog { return this.inline; } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java index d76ddea9..3beae21c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.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.gui.dialog; @@ -17,7 +17,12 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.concurrent.CompletableFuture; -import javax.swing.*; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; import cuchaz.enigma.Enigma; import cuchaz.enigma.ProgressListener; @@ -27,7 +32,6 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public class ProgressDialog implements ProgressListener, AutoCloseable { - private final JDialog dialog; private final JLabel labelTitle = new JLabel(); private final JLabel labelText = GuiUtil.unboldLabel(new JLabel()); @@ -39,11 +43,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable { Container pane = this.dialog.getContentPane(); pane.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2) - .anchor(GridBagConstraints.WEST) - .fill(GridBagConstraints.BOTH) - .weight(1.0, 0.0); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).anchor(GridBagConstraints.WEST).fill(GridBagConstraints.BOTH).weight(1.0, 0.0); pane.add(this.labelTitle, cb.pos(0, 0).build()); pane.add(this.labelText, cb.pos(0, 1).build()); @@ -121,6 +121,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable { public void step(int numDone, String message) { SwingUtilities.invokeLater(() -> { this.labelText.setText(message); + if (numDone != -1) { this.progress.setValue(numDone); this.progress.setIndeterminate(false); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java index e65b661b..7814dd81 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.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.gui.dialog; @@ -15,17 +15,36 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; -import java.awt.event.*; -import java.util.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import javax.swing.*; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.GuiController; +import cuchaz.enigma.gui.search.SearchEntry; +import cuchaz.enigma.gui.search.SearchUtil; import cuchaz.enigma.gui.util.AbstractListCellRenderer; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.ScaleUtil; @@ -34,11 +53,8 @@ import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.translation.representation.entry.ParentedEntry; import cuchaz.enigma.utils.I18n; -import cuchaz.enigma.gui.search.SearchEntry; -import cuchaz.enigma.gui.search.SearchUtil; public class SearchDialog { - private final JTextField searchField; private DefaultListModel classListModel; private final JList classList; @@ -60,7 +76,6 @@ public class SearchDialog { searchField = new JTextField(); searchField.getDocument().addDocumentListener(new DocumentListener() { - @Override public void insertUpdate(DocumentEvent e) { updateList(); @@ -75,7 +90,6 @@ public class SearchDialog { public void changedUpdate(DocumentEvent e) { updateList(); } - }); searchField.addKeyListener(new KeyAdapter() { @Override @@ -143,23 +157,9 @@ public class SearchDialog { final EntryIndex entryIndex = parent.getController().project.getJarIndex().getEntryIndex(); switch (type) { - case CLASS -> entryIndex.getClasses().parallelStream() - .filter(e -> !e.isInnerClass()) - .map(e -> SearchEntryImpl.from(e, parent.getController())) - .map(SearchUtil.Entry::from) - .sequential() - .forEach(su::add); - case METHOD -> entryIndex.getMethods().parallelStream() - .filter(e -> !e.isConstructor() && !entryIndex.getMethodAccess(e).isSynthetic()) - .map(e -> SearchEntryImpl.from(e, parent.getController())) - .map(SearchUtil.Entry::from) - .sequential() - .forEach(su::add); - case FIELD -> entryIndex.getFields().parallelStream() - .map(e -> SearchEntryImpl.from(e, parent.getController())) - .map(SearchUtil.Entry::from) - .sequential() - .forEach(su::add); + case CLASS -> entryIndex.getClasses().parallelStream().filter(e -> !e.isInnerClass()).map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add); + case METHOD -> entryIndex.getMethods().parallelStream().filter(e -> !e.isConstructor() && !entryIndex.getMethodAccess(e).isSynthetic()).map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add); + case FIELD -> entryIndex.getFields().parallelStream().map(e -> SearchEntryImpl.from(e, parent.getController())).map(SearchUtil.Entry::from).sequential().forEach(su::add); } updateList(); @@ -172,6 +172,7 @@ public class SearchDialog { private void openSelected() { SearchEntryImpl selectedValue = classList.getSelectedValue(); + if (selectedValue != null) { openEntry(selectedValue); } @@ -181,6 +182,7 @@ public class SearchDialog { close(); su.hit(e); parent.getController().navigateTo(e.obf); + if (e.obf instanceof ClassEntry) { if (e.deobf != null) { parent.getDeobfPanel().deobfClasses.setSelectionClass((ClassEntry) e.deobf); @@ -202,7 +204,9 @@ public class SearchDialog { // Updates the list of class names private void updateList() { - if (currentSearch != null) currentSearch.stop(); + if (currentSearch != null) { + currentSearch.stop(); + } DefaultListModel classListModel = new DefaultListModel<>(); this.classListModel = classListModel; @@ -210,7 +214,9 @@ public class SearchDialog { // handle these search result like minecraft scheduled tasks to prevent // flooding swing buttons inputs etc with tons of (possibly outdated) invocations - record Order(int idx, SearchEntryImpl e) {} + record Order(int idx, SearchEntryImpl e) { + } + Queue queue = new ConcurrentLinkedQueue<>(); Runnable updater = new Runnable() { @Override @@ -221,8 +227,9 @@ public class SearchDialog { // too large count may increase delay for key and input handling, etc. int count = 100; + while (count > 0 && !queue.isEmpty()) { - var o = queue.remove(); + Order o = queue.remove(); classListModel.insertElementAt(o.e, o.idx); count--; } @@ -240,7 +247,6 @@ public class SearchDialog { } private static final class SearchEntryImpl implements SearchEntry { - public final ParentedEntry obf; public final ParentedEntry deobf; @@ -270,10 +276,13 @@ public class SearchDialog { public static SearchEntryImpl from(ParentedEntry e, GuiController controller) { ParentedEntry deobf = controller.project.getMapper().deobfuscate(e); - if (deobf.equals(e)) deobf = null; + + if (deobf.equals(e)) { + deobf = null; + } + return new SearchEntryImpl(e, deobf); } - } private static final class ListCellRendererImpl extends AbstractListCellRenderer { @@ -281,7 +290,7 @@ public class SearchDialog { private final JLabel mainName; private final JLabel secondaryName; - public ListCellRendererImpl(Gui gui) { + ListCellRendererImpl(Gui gui) { this.setLayout(new BorderLayout()); this.gui = gui; @@ -316,7 +325,6 @@ public class SearchDialog { mainName.setIcon(GuiUtil.FIELD_ICON); } } - } public enum Type { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java index 0398093b..1ab66ef2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/dialog/StatsDialog.java @@ -4,10 +4,19 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.config.UiConfig; @@ -19,14 +28,15 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.I18n; public class StatsDialog { - public static void show(Gui gui) { ProgressDialog.runOffThread(gui.getFrame(), listener -> { final StatsGenerator statsGenerator = new StatsGenerator(gui.getController().project); final Map results = new HashMap<>(); + for (StatsMember member : StatsMember.values()) { results.put(member, statsGenerator.generate(listener, Collections.singleton(member), "", false)); } + SwingUtilities.invokeLater(() -> show(gui, results)); }); } @@ -111,12 +121,7 @@ public class StatsDialog { private static void generateStats(Gui gui, Map checkboxes, String topLevelPackage, boolean includeSynthetic) { // get members from selected checkboxes - Set includedMembers = checkboxes - .entrySet() - .stream() - .filter(entry -> entry.getValue().isSelected()) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()); + Set includedMembers = checkboxes.entrySet().stream().filter(entry -> entry.getValue().isSelected()).map(Map.Entry::getKey).collect(Collectors.toSet()); // checks if a project is open if (gui.getController().project != null) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java index 39aa212f..3f1625de 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/AbstractInheritanceTree.java @@ -7,7 +7,11 @@ import javax.annotation.Nullable; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; -import javax.swing.tree.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; import cuchaz.enigma.analysis.ClassInheritanceTreeNode; import cuchaz.enigma.analysis.MethodInheritanceTreeNode; @@ -40,11 +44,13 @@ public abstract class AbstractInheritanceTree { if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { // get the selected node TreePath path = tree.getSelectionPath(); + if (path == null) { return; } Object node = path.getLastPathComponent(); + if (node instanceof ClassInheritanceTreeNode classNode) { gui.getController().navigateTo(new ClassEntry(classNode.getObfClassName())); } else if (node instanceof MethodInheritanceTreeNode methodNode) { @@ -72,7 +78,6 @@ public abstract class AbstractInheritanceTree { } public void retranslateUi() { - } @Nullable diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java index c92534f0..56711880 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CallsTree.java @@ -5,7 +5,12 @@ import java.awt.event.MouseEvent; import java.util.Collection; import java.util.Vector; -import javax.swing.*; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTree; +import javax.swing.ListSelectionModel; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -47,12 +52,7 @@ public class CallsTree { this.tokens.setPreferredSize(ScaleUtil.getDimension(0, 200)); this.tokens.setMinimumSize(ScaleUtil.getDimension(0, 200)); - JSplitPane contentPane = new JSplitPane( - JSplitPane.VERTICAL_SPLIT, - true, - new JScrollPane(this.callsTree), - new JScrollPane(this.tokens) - ); + JSplitPane contentPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, new JScrollPane(this.callsTree), new JScrollPane(this.tokens)); contentPane.setResizeWeight(1); // let the top side take all the slack contentPane.resetToPreferredSizes(); @@ -109,6 +109,7 @@ public class CallsTree { private void onTokenClicked(MouseEvent event) { if (event.getClickCount() == 2) { Token selected = this.tokens.getSelectedValue(); + if (selected != null) { this.gui.openClass(this.gui.getController().getTokenHandle().getRef()).navigateToToken(selected); } @@ -116,7 +117,6 @@ public class CallsTree { } public void retranslateUi() { - } public JPanel getPanel() { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java index fb497b11..e05ab458 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/CollapsibleTabbedPane.java @@ -5,7 +5,6 @@ import java.awt.event.MouseEvent; import javax.swing.JTabbedPane; public class CollapsibleTabbedPane extends JTabbedPane { - public CollapsibleTabbedPane() { } @@ -20,9 +19,14 @@ public class CollapsibleTabbedPane extends JTabbedPane { @Override protected void processMouseEvent(MouseEvent e) { int id = e.getID(); + if (id == MouseEvent.MOUSE_PRESSED) { - if (!isEnabled()) return; + if (!isEnabled()) { + return; + } + int tabIndex = getUI().tabForCoordinate(this, e.getX(), e.getY()); + if (tabIndex >= 0 && isEnabledAt(tabIndex)) { if (tabIndex == getSelectedIndex()) { if (isFocusOwner() && isRequestFocusEnabled()) { @@ -30,11 +34,12 @@ public class CollapsibleTabbedPane extends JTabbedPane { } else { setSelectedIndex(-1); } + return; } } } + super.processMouseEvent(e); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java index 9a6ea098..301ae7f1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ConvertingTextField.java @@ -1,7 +1,12 @@ package cuchaz.enigma.gui.elements; import java.awt.GridLayout; -import java.awt.event.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.HashSet; import java.util.Set; @@ -21,7 +26,6 @@ import cuchaz.enigma.utils.validation.Validatable; * A label that converts into an editable text field when you click it. */ public class ConvertingTextField implements Validatable { - private final JPanel ui; private final ValidatableTextField textField; private final JLabel label; @@ -69,7 +73,9 @@ public class ConvertingTextField implements Validatable { } public void startEditing() { - if (this.editing || !this.editable) return; + if (this.editing || !this.editable) { + return; + } this.ui.removeAll(); this.ui.add(this.textField); @@ -82,9 +88,13 @@ public class ConvertingTextField implements Validatable { } public void stopEditing(boolean abort) { - if (!editing) return; + if (!editing) { + return; + } - if (!listeners.stream().allMatch(l -> l.tryStopEditing(this, abort))) return; + if (!listeners.stream().allMatch(l -> l.tryStopEditing(this, abort))) { + return; + } if (abort) { this.textField.setText(this.label.getText()); @@ -107,7 +117,9 @@ public class ConvertingTextField implements Validatable { } public void setEditText(String text) { - if (!editing) return; + if (!editing) { + return; + } this.textField.setText(text); } @@ -122,22 +134,29 @@ public class ConvertingTextField implements Validatable { } public void selectAll() { - if (!editing) return; + if (!editing) { + return; + } this.textField.selectAll(); } public void selectSubstring(int startIndex) { - if (!editing) return; + if (!editing) { + return; + } Document doc = this.textField.getDocument(); + if (doc != null) { this.selectSubstring(startIndex, doc.getLength()); } } public void selectSubstring(int startIndex, int endIndex) { - if (!editing) return; + if (!editing) { + return; + } this.textField.select(startIndex, endIndex); } @@ -155,7 +174,10 @@ public class ConvertingTextField implements Validatable { } public boolean hasChanges() { - if (!editing) return false; + if (!editing) { + return false; + } + return !this.textField.getText().equals(this.label.getText()); } @@ -180,5 +202,4 @@ public class ConvertingTextField implements Validatable { public JPanel getUi() { return ui; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java index 0b44881f..bcc6dc6f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/DeobfPanelPopupMenu.java @@ -9,55 +9,54 @@ import cuchaz.enigma.gui.panels.DeobfPanel; import cuchaz.enigma.utils.I18n; public class DeobfPanelPopupMenu { - - private final JPopupMenu ui; - private final JMenuItem renamePackage = new JMenuItem(); - private final JMenuItem renameClass = new JMenuItem(); - private final JMenuItem expandAll = new JMenuItem(); - private final JMenuItem collapseAll = new JMenuItem(); - - public DeobfPanelPopupMenu(DeobfPanel panel) { - this.ui = new JPopupMenu(); - - this.ui.add(this.renamePackage); - this.ui.add(this.renameClass); - this.ui.addSeparator(); - this.ui.add(this.expandAll); - this.ui.add(this.collapseAll); - - ClassSelector deobfClasses = panel.deobfClasses; - - this.renamePackage.addActionListener(a -> { - TreePath path; - - if (deobfClasses.getSelectedClass() != null) { - // Rename parent package if selected path is a class - path = deobfClasses.getSelectionPath().getParentPath(); - } else { - // Rename selected path if it's already a package - path = deobfClasses.getSelectionPath(); - } - - deobfClasses.getUI().startEditingAtPath(deobfClasses, path); - }); - this.renameClass.addActionListener(a -> deobfClasses.getUI().startEditingAtPath(deobfClasses, deobfClasses.getSelectionPath())); - this.expandAll.addActionListener(a -> deobfClasses.expandAll()); - this.collapseAll.addActionListener(a -> deobfClasses.collapseAll()); - - this.retranslateUi(); - } - - public void show(ClassSelector deobfClasses, int x, int y) { - // Only enable rename class if selected path is a class - this.renameClass.setEnabled(deobfClasses.getSelectedClass() != null); - - this.ui.show(deobfClasses, x, y); - } - - public void retranslateUi() { - this.renamePackage.setText(I18n.translate("popup_menu.deobf_panel.rename_package")); - this.renameClass.setText(I18n.translate("popup_menu.deobf_panel.rename_class")); - this.expandAll.setText(I18n.translate("popup_menu.deobf_panel.expand_all")); - this.collapseAll.setText(I18n.translate("popup_menu.deobf_panel.collapse_all")); - } + private final JPopupMenu ui; + private final JMenuItem renamePackage = new JMenuItem(); + private final JMenuItem renameClass = new JMenuItem(); + private final JMenuItem expandAll = new JMenuItem(); + private final JMenuItem collapseAll = new JMenuItem(); + + public DeobfPanelPopupMenu(DeobfPanel panel) { + this.ui = new JPopupMenu(); + + this.ui.add(this.renamePackage); + this.ui.add(this.renameClass); + this.ui.addSeparator(); + this.ui.add(this.expandAll); + this.ui.add(this.collapseAll); + + ClassSelector deobfClasses = panel.deobfClasses; + + this.renamePackage.addActionListener(a -> { + TreePath path; + + if (deobfClasses.getSelectedClass() != null) { + // Rename parent package if selected path is a class + path = deobfClasses.getSelectionPath().getParentPath(); + } else { + // Rename selected path if it's already a package + path = deobfClasses.getSelectionPath(); + } + + deobfClasses.getUI().startEditingAtPath(deobfClasses, path); + }); + this.renameClass.addActionListener(a -> deobfClasses.getUI().startEditingAtPath(deobfClasses, deobfClasses.getSelectionPath())); + this.expandAll.addActionListener(a -> deobfClasses.expandAll()); + this.collapseAll.addActionListener(a -> deobfClasses.collapseAll()); + + this.retranslateUi(); + } + + public void show(ClassSelector deobfClasses, int x, int y) { + // Only enable rename class if selected path is a class + this.renameClass.setEnabled(deobfClasses.getSelectedClass() != null); + + this.ui.show(deobfClasses, x, y); + } + + public void retranslateUi() { + this.renamePackage.setText(I18n.translate("popup_menu.deobf_panel.rename_package")); + this.renameClass.setText(I18n.translate("popup_menu.deobf_panel.rename_class")); + this.expandAll.setText(I18n.translate("popup_menu.deobf_panel.expand_all")); + this.collapseAll.setText(I18n.translate("popup_menu.deobf_panel.collapse_all")); + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java index 2ce6ed93..d128bf5c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorPopupMenu.java @@ -19,7 +19,6 @@ import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; public class EditorPopupMenu { - private final JPopupMenu ui = new JPopupMenu(); private final JMenuItem renameItem = new JMenuItem(); @@ -105,39 +104,41 @@ public class EditorPopupMenu { public boolean handleKeyEvent(KeyEvent event) { if (event.isControlDown()) { switch (event.getKeyCode()) { - case KeyEvent.VK_I: - this.showInheritanceItem.doClick(); - return true; - case KeyEvent.VK_M: - this.showImplementationsItem.doClick(); - return true; - case KeyEvent.VK_N: - this.openEntryItem.doClick(); - return true; - case KeyEvent.VK_P: - this.openPreviousItem.doClick(); - return true; - case KeyEvent.VK_E: - this.openNextItem.doClick(); - return true; - case KeyEvent.VK_C: - if (event.isShiftDown()) { - this.showCallsSpecificItem.doClick(); - } else { - this.showCallsItem.doClick(); - } - return true; - case KeyEvent.VK_O: - this.toggleMappingItem.doClick(); - return true; - case KeyEvent.VK_R: - this.renameItem.doClick(); - return true; - case KeyEvent.VK_D: - this.editJavadocItem.doClick(); - return true; + case KeyEvent.VK_I: + this.showInheritanceItem.doClick(); + return true; + case KeyEvent.VK_M: + this.showImplementationsItem.doClick(); + return true; + case KeyEvent.VK_N: + this.openEntryItem.doClick(); + return true; + case KeyEvent.VK_P: + this.openPreviousItem.doClick(); + return true; + case KeyEvent.VK_E: + this.openNextItem.doClick(); + return true; + case KeyEvent.VK_C: + if (event.isShiftDown()) { + this.showCallsSpecificItem.doClick(); + } else { + this.showCallsItem.doClick(); + } + + return true; + case KeyEvent.VK_O: + this.toggleMappingItem.doClick(); + return true; + case KeyEvent.VK_R: + this.renameItem.doClick(); + return true; + case KeyEvent.VK_D: + this.editJavadocItem.doClick(); + return true; } } + return false; } @@ -191,5 +192,4 @@ public class EditorPopupMenu { public JPopupMenu getUi() { return ui; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java index 0b4926eb..93854818 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabPopupMenu.java @@ -11,7 +11,6 @@ import cuchaz.enigma.gui.panels.EditorPanel; import cuchaz.enigma.utils.I18n; public class EditorTabPopupMenu { - private final JPopupMenu ui; private final JMenuItem close; private final JMenuItem closeAll; diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java index ff0bba3f..7a6290ea 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/EditorTabbedPane.java @@ -39,7 +39,11 @@ public class EditorTabbedPane { public EditorPanel openClass(ClassEntry entry) { EditorPanel editorPanel = this.editors.computeIfAbsent(entry, e -> { ClassHandle ch = this.gui.getController().getClassHandleProvider().openClass(entry); - if (ch == null) return null; + + if (ch == null) { + return null; + } + EditorPanel ed = new EditorPanel(this.gui); ed.setup(); ed.setClassHandle(ch); @@ -125,7 +129,10 @@ public class EditorTabbedPane { int index = this.openFiles.indexOfComponent(ed.getUi()); for (int i = this.openFiles.getTabCount() - 1; i >= 0; i--) { - if (i == index) continue; + if (i == index) { + continue; + } + closeEditor(EditorPanel.byUi(this.openFiles.getComponentAt(i))); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java index 533d1b30..9e632d9d 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/JMultiLineToolTip.java @@ -12,13 +12,12 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolTipUI; /** - * Implements a multi line tooltip for GUI components + * Implements a multi line tooltip for GUI components. * Copied from http://www.codeguru.com/java/articles/122.shtml * * @author Zafir Anjum */ public class JMultiLineToolTip extends JToolTip { - private static final long serialVersionUID = 7813662474312183098L; public JMultiLineToolTip() { @@ -52,10 +51,9 @@ public class JMultiLineToolTip extends JToolTip { } /** - * UI for multi line tool tip + * UI for multi line tool tip. */ class MultiLineToolTipUI extends BasicToolTipUI { - static MultiLineToolTipUI sharedInstance = new MultiLineToolTipUI(); Font smallFont; static JToolTip tip; @@ -67,7 +65,7 @@ class MultiLineToolTipUI extends BasicToolTipUI { return sharedInstance; } - public MultiLineToolTipUI() { + MultiLineToolTipUI() { super(); } @@ -93,7 +91,11 @@ class MultiLineToolTipUI extends BasicToolTipUI { public Dimension getPreferredSize(JComponent c) { String tipText = ((JToolTip) c).getTipText(); - if (tipText == null) return new Dimension(0, 0); + + if (tipText == null) { + return new Dimension(0, 0); + } + textArea = new JTextArea(tipText); rendererPane.removeAll(); rendererPane.add(textArea); @@ -112,8 +114,9 @@ class MultiLineToolTipUI extends BasicToolTipUI { d.width = width; d.height++; textArea.setSize(d); - } else + } else { textArea.setLineWrap(false); + } Dimension dim = textArea.getPreferredSize(); @@ -129,4 +132,4 @@ class MultiLineToolTipUI extends BasicToolTipUI { public Dimension getMaximumSize(JComponent c) { return getPreferredSize(c); } -} \ No newline at end of file +} diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index eeb52ccf..24a69b65 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java @@ -12,7 +12,14 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; -import javax.swing.*; +import javax.swing.ButtonGroup; +import javax.swing.JFileChooser; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; import cuchaz.enigma.gui.ConnectionState; import cuchaz.enigma.gui.Gui; @@ -20,7 +27,13 @@ import cuchaz.enigma.gui.config.Decompiler; import cuchaz.enigma.gui.config.LookAndFeel; import cuchaz.enigma.gui.config.NetConfig; import cuchaz.enigma.gui.config.UiConfig; -import cuchaz.enigma.gui.dialog.*; +import cuchaz.enigma.gui.dialog.AboutDialog; +import cuchaz.enigma.gui.dialog.ChangeDialog; +import cuchaz.enigma.gui.dialog.ConnectToServerDialog; +import cuchaz.enigma.gui.dialog.CreateServerDialog; +import cuchaz.enigma.gui.dialog.FontDialog; +import cuchaz.enigma.gui.dialog.SearchDialog; +import cuchaz.enigma.gui.dialog.StatsDialog; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.gui.util.LanguageUtil; import cuchaz.enigma.gui.util.ScaleUtil; @@ -29,7 +42,6 @@ import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.Pair; public class MenuBar { - private final JMenu fileMenu = new JMenu(); private final JMenuItem jarOpenItem = new JMenuItem(); private final JMenuItem jarCloseItem = new JMenuItem(); @@ -221,13 +233,16 @@ public class MenuBar { } File file = d.getSelectedFile(); + // checks if the file name is not empty if (file != null) { Path path = file.toPath(); + // checks if the file name corresponds to an existing file if (Files.exists(path)) { this.gui.getController().openJar(path); } + UiConfig.setLastSelectedDir(d.getCurrentDirectory().getAbsolutePath()); } } @@ -241,8 +256,10 @@ public class MenuBar { this.gui.showDiscardDiag((response -> { if (response == JOptionPane.YES_OPTION) { this.gui.saveMapping().thenRun(then); - } else if (response == JOptionPane.NO_OPTION) + } else if (response == JOptionPane.NO_OPTION) { then.run(); + } + return null; }), I18n.translate("prompt.close.save"), I18n.translate("prompt.close.discard"), I18n.translate("prompt.cancel")); } else { @@ -264,6 +281,7 @@ public class MenuBar { private void onExportSourceClicked() { this.gui.exportSourceFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); + if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { UiConfig.setLastSelectedDir(this.gui.exportSourceFileChooser.getCurrentDirectory().toString()); this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile().toPath()); @@ -287,29 +305,35 @@ public class MenuBar { } private void onCustomScaleClicked() { - String answer = (String) JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("menu.view.scale.custom.title"), I18n.translate("menu.view.scale.custom.title"), - JOptionPane.QUESTION_MESSAGE, null, null, Float.toString(UiConfig.getScaleFactor() * 100)); - if (answer == null) return; + String answer = (String) JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("menu.view.scale.custom.title"), I18n.translate("menu.view.scale.custom.title"), JOptionPane.QUESTION_MESSAGE, null, null, Float.toString(UiConfig.getScaleFactor() * 100)); + + if (answer == null) { + return; + } + float newScale = 1.0f; + try { newScale = Float.parseFloat(answer) / 100f; } catch (NumberFormatException ignored) { + // ignored } + ScaleUtil.setScaleFactor(newScale); ChangeDialog.show(this.gui.getFrame()); } private void onFontClicked(Gui gui) { -// FontDialog fd = new FontDialog(gui.getFrame(), "Choose Font", true); -// fd.setLocationRelativeTo(gui.getFrame()); -// fd.setSelectedFont(UiConfig.getEditorFont()); -// fd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); -// fd.setVisible(true); -// -// if (!fd.isCancelSelected()) { -// UiConfig.setEditorFont(fd.getSelectedFont()); -// UiConfig.save(); -// } + // FontDialog fd = new FontDialog(gui.getFrame(), "Choose Font", true); + // fd.setLocationRelativeTo(gui.getFrame()); + // fd.setSelectedFont(UiConfig.getEditorFont()); + // fd.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + // fd.setVisible(true); + // + // if (!fd.isCancelSelected()) { + // UiConfig.setEditorFont(fd.getSelectedFont()); + // UiConfig.save(); + // } FontDialog.display(gui.getFrame()); } @@ -324,11 +348,15 @@ public class MenuBar { this.gui.getController().disconnectIfConnected(null); return; } + ConnectToServerDialog.Result result = ConnectToServerDialog.show(this.gui.getFrame()); + if (result == null) { return; } + this.gui.getController().disconnectIfConnected(null); + try { this.gui.getController().createClient(result.getUsername(), result.getAddress().address, result.getAddress().port, result.getPassword()); NetConfig.setUsername(result.getUsername()); @@ -339,6 +367,7 @@ public class MenuBar { JOptionPane.showMessageDialog(this.gui.getFrame(), e.toString(), I18n.translate("menu.collab.connect.error"), JOptionPane.ERROR_MESSAGE); this.gui.getController().disconnectIfConnected(null); } + Arrays.fill(result.getPassword(), (char) 0); } @@ -347,11 +376,15 @@ public class MenuBar { this.gui.getController().disconnectIfConnected(null); return; } + CreateServerDialog.Result result = CreateServerDialog.show(this.gui.getFrame()); + if (result == null) { return; } + this.gui.getController().disconnectIfConnected(null); + try { this.gui.getController().createServer(result.getPort(), result.getPassword()); NetConfig.setServerPort(result.getPort()); @@ -373,6 +406,7 @@ public class MenuBar { JMenuItem item = new JMenuItem(I18n.translate("mapping_format." + format.name().toLowerCase(Locale.ROOT))); item.addActionListener(event -> { gui.enigmaMappingsFileChooser.setCurrentDirectory(new File(UiConfig.getLastSelectedDir())); + if (gui.enigmaMappingsFileChooser.showOpenDialog(gui.getFrame()) == JFileChooser.APPROVE_OPTION) { File selectedFile = gui.enigmaMappingsFileChooser.getSelectedFile(); gui.getController().openMappings(format, selectedFile.toPath()); @@ -411,9 +445,11 @@ public class MenuBar { for (Decompiler decompiler : Decompiler.values()) { JRadioButtonMenuItem decompilerButton = new JRadioButtonMenuItem(decompiler.name); decompilerGroup.add(decompilerButton); + if (decompiler.equals(UiConfig.getDecompiler())) { decompilerButton.setSelected(true); } + decompilerButton.addActionListener(event -> { gui.getController().setDecompiler(decompiler.service); @@ -426,12 +462,15 @@ public class MenuBar { private static void prepareThemesMenu(JMenu themesMenu, Gui gui) { ButtonGroup themeGroup = new ButtonGroup(); + for (LookAndFeel lookAndFeel : LookAndFeel.values()) { JRadioButtonMenuItem themeButton = new JRadioButtonMenuItem(I18n.translate("menu.view.themes." + lookAndFeel.name().toLowerCase(Locale.ROOT))); themeGroup.add(themeButton); + if (lookAndFeel.equals(UiConfig.getLookAndFeel())) { themeButton.setSelected(true); } + themeButton.addActionListener(_e -> { UiConfig.setLookAndFeel(lookAndFeel); UiConfig.save(); @@ -443,12 +482,15 @@ public class MenuBar { private static void prepareLanguagesMenu(JMenu languagesMenu) { ButtonGroup languageGroup = new ButtonGroup(); + for (String lang : I18n.getAvailableLanguages()) { JRadioButtonMenuItem languageButton = new JRadioButtonMenuItem(I18n.getLanguageName(lang)); languageGroup.add(languageButton); + if (lang.equals(UiConfig.getLanguage())) { languageButton.setSelected(true); } + languageButton.addActionListener(event -> { UiConfig.setLanguage(lang); I18n.setLanguage(lang); @@ -461,25 +503,25 @@ public class MenuBar { private static void prepareScaleMenu(JMenu scaleMenu, Gui gui) { ButtonGroup scaleGroup = new ButtonGroup(); - Map scaleButtons = IntStream.of(100, 125, 150, 175, 200) - .mapToObj(scaleFactor -> { - float realScaleFactor = scaleFactor / 100f; - JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor)); - menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor)); - menuItem.addActionListener(event -> ChangeDialog.show(gui.getFrame())); - scaleGroup.add(menuItem); - scaleMenu.add(menuItem); - return new Pair<>(realScaleFactor, menuItem); - }) - .collect(Collectors.toMap(x -> x.a, x -> x.b)); + Map scaleButtons = IntStream.of(100, 125, 150, 175, 200).mapToObj(scaleFactor -> { + float realScaleFactor = scaleFactor / 100f; + JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(String.format("%d%%", scaleFactor)); + menuItem.addActionListener(event -> ScaleUtil.setScaleFactor(realScaleFactor)); + menuItem.addActionListener(event -> ChangeDialog.show(gui.getFrame())); + scaleGroup.add(menuItem); + scaleMenu.add(menuItem); + return new Pair<>(realScaleFactor, menuItem); + }).collect(Collectors.toMap(x -> x.a, x -> x.b)); JRadioButtonMenuItem currentScaleButton = scaleButtons.get(UiConfig.getScaleFactor()); + if (currentScaleButton != null) { currentScaleButton.setSelected(true); } ScaleUtil.addListener((newScale, _oldScale) -> { JRadioButtonMenuItem mi = scaleButtons.get(newScale); + if (mi != null) { mi.setSelected(true); } else { @@ -487,5 +529,4 @@ public class MenuBar { } }); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java index 0c667c00..48404a1c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/StatusBar.java @@ -54,7 +54,7 @@ public class StatusBar { * * @param message the message to display * @param timeout the timeout in milliseconds to wait until clearing the - * message; if 0, the message is not automatically cleared + * message; if 0, the message is not automatically cleared */ public void showMessage(String message, int timeout) { this.timer.stop(); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java index 02e1bc39..4329cae0 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatablePasswordField.java @@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.Validatable; public class ValidatablePasswordField extends JPasswordField implements Validatable { - private List messages = new ArrayList<>(); private String tooltipText = null; @@ -92,5 +91,4 @@ public class ValidatablePasswordField extends JPasswordField implements Validata super.paint(g); ValidatableUi.drawMarker(this, g, messages); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java index 7d1f8665..2d5e2295 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextArea.java @@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.Validatable; public class ValidatableTextArea extends JTextArea implements Validatable { - private List messages = new ArrayList<>(); private String tooltipText = null; @@ -96,5 +95,4 @@ public class ValidatableTextArea extends JTextArea implements Validatable { super.paint(g); ValidatableUi.drawMarker(this, g, messages); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java index c114dc17..be658d59 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableTextField.java @@ -14,7 +14,6 @@ import cuchaz.enigma.utils.validation.ParameterizedMessage; import cuchaz.enigma.utils.validation.Validatable; public class ValidatableTextField extends JTextField implements Validatable { - private List messages = new ArrayList<>(); private String tooltipText = null; @@ -92,5 +91,4 @@ public class ValidatableTextField extends JTextField implements Validatable { super.paint(g); ValidatableUi.drawMarker(this, g, messages); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java index 5df63486..b8b8431b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ValidatableUi.java @@ -13,26 +13,29 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.utils.validation.ParameterizedMessage; public final class ValidatableUi { - private ValidatableUi() { } public static String getTooltipText(String tooltipText, List messages) { List strings = new ArrayList<>(); + if (tooltipText != null) { strings.add(tooltipText); } + if (!messages.isEmpty()) { strings.add("Error(s): "); messages.forEach(msg -> { strings.add(String.format(" - %s", msg.getText())); String longDesc = msg.getLongText(); + if (!longDesc.isEmpty()) { Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add); } }); } + if (strings.isEmpty()) { return null; } else { @@ -49,11 +52,13 @@ public final class ValidatableUi { messages.forEach(msg -> { strings.add(String.format(" - %s", msg.getText())); String longDesc = msg.getLongText(); + if (!longDesc.isEmpty()) { Arrays.stream(longDesc.split("\n")).map(s -> String.format(" %s", s)).forEach(strings::add); } }); } + if (strings.isEmpty()) { return null; } else { @@ -63,6 +68,7 @@ public final class ValidatableUi { public static void drawMarker(Component self, Graphics g, List messages) { Color color = ValidatableUi.getMarkerColor(messages); + if (color != null) { g.setColor(color); int x1 = self.getWidth() - ScaleUtil.scale(8) - 1; @@ -75,33 +81,32 @@ public final class ValidatableUi { @Nullable public static Color getMarkerColor(List messages) { - int level = messages.stream() - .mapToInt(ValidatableUi::getMessageLevel) - .max().orElse(0); + int level = messages.stream().mapToInt(ValidatableUi::getMessageLevel).max().orElse(0); switch (level) { - case 0: - return null; - case 1: - return Color.BLUE; - case 2: - return Color.ORANGE; - case 3: - return Color.RED; + case 0: + return null; + case 1: + return Color.BLUE; + case 2: + return Color.ORANGE; + case 3: + return Color.RED; } + throw new IllegalStateException("unreachable"); } private static int getMessageLevel(ParameterizedMessage message) { switch (message.message.type) { - case INFO: - return 1; - case WARNING: - return 2; - case ERROR: - return 3; + case INFO: + return 1; + case WARNING: + return 2; + case ERROR: + return 3; } + throw new IllegalStateException("unreachable"); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java index 6e17fec1..d9ec95c2 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ConvertingTextFieldListener.java @@ -3,7 +3,6 @@ package cuchaz.enigma.gui.events; import cuchaz.enigma.gui.elements.ConvertingTextField; public interface ConvertingTextFieldListener { - default void onStartEditing(ConvertingTextField field) { } @@ -13,5 +12,4 @@ public interface ConvertingTextFieldListener { default void onStopEditing(ConvertingTextField field, boolean abort) { } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java index 48c9ec4b..1651abf0 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/EditorActionListener.java @@ -7,7 +7,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.Entry; public interface EditorActionListener { - default void onCursorReferenceChanged(EditorPanel editor, EntryReference, Entry> ref) { } @@ -16,5 +15,4 @@ public interface EditorActionListener { default void onTitleChanged(EditorPanel editor, String title) { } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java index 10d7ce1c..e2054b29 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/events/ThemeChangeListener.java @@ -7,7 +7,5 @@ import cuchaz.enigma.gui.highlight.BoxHighlightPainter; import cuchaz.enigma.source.RenamableTokenType; public interface ThemeChangeListener { - void onThemeChanged(LookAndFeel lookAndFeel, Map boxHighlightPainters); - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java index 2d8d76a7..a97b3779 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.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.gui.highlight; @@ -67,5 +67,4 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter { g.setColor(this.borderColor); g.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java index 22d64201..a807802d 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java @@ -1,17 +1,21 @@ /******************************************************************************* - * 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.gui.highlight; -import java.awt.*; +import java.awt.BasicStroke; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; @@ -19,7 +23,6 @@ import javax.swing.text.JTextComponent; import cuchaz.enigma.gui.config.UiConfig; public class SelectionHighlightPainter implements Highlighter.HighlightPainter { - public static final SelectionHighlightPainter INSTANCE = new SelectionHighlightPainter(); @Override @@ -31,5 +34,4 @@ public class SelectionHighlightPainter implements Highlighter.HighlightPainter { g2d.setStroke(new BasicStroke(2.0f)); g2d.drawRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, 4, 4); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java index 898529a4..1dcdeabf 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/newabstraction/EntryValidation.java @@ -6,17 +6,16 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.ValidationContext; public class EntryValidation { - public static boolean validateJavadoc(ValidationContext vc, String javadoc) { if (javadoc.contains("*/")) { vc.raise(Message.ILLEGAL_DOC_COMMENT_END); return false; } + return true; } public static boolean validateRename(ValidationContext vc, EnigmaProject p, Entry entry, String newName) { return p.getMapper().getValidator().validateRename(vc, entry, newName); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index 922f8f24..f931a93f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java @@ -1,22 +1,21 @@ /******************************************************************************* - * 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.gui.node; -import cuchaz.enigma.translation.representation.entry.ClassEntry; - import javax.swing.tree.DefaultMutableTreeNode; -public class ClassSelectorClassNode extends DefaultMutableTreeNode { +import cuchaz.enigma.translation.representation.entry.ClassEntry; +public class ClassSelectorClassNode extends DefaultMutableTreeNode { private final ClassEntry obfEntry; private ClassEntry classEntry; @@ -57,12 +56,17 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { @Override public void setUserObject(Object userObject) { String packageName = ""; - if (classEntry.getPackageName() != null) + + if (classEntry.getPackageName() != null) { packageName = classEntry.getPackageName() + "/"; - if (userObject instanceof String) + } + + if (userObject instanceof String) { this.classEntry = new ClassEntry(packageName + userObject); - else if (userObject instanceof ClassEntry) + } else if (userObject instanceof ClassEntry) { this.classEntry = (ClassEntry) userObject; + } + super.setUserObject(classEntry); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java index c1c7d387..dfcbd8c1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java @@ -1,22 +1,21 @@ /******************************************************************************* - * 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.gui.node; -import cuchaz.enigma.translation.representation.entry.ClassEntry; - import javax.swing.tree.DefaultMutableTreeNode; -public class ClassSelectorPackageNode extends DefaultMutableTreeNode { +import cuchaz.enigma.translation.representation.entry.ClassEntry; +public class ClassSelectorPackageNode extends DefaultMutableTreeNode { private String packageName; public ClassSelectorPackageNode(String packageName) { @@ -34,8 +33,10 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode { @Override public void setUserObject(Object userObject) { - if (userObject instanceof String) + if (userObject instanceof String) { this.packageName = (String) userObject; + } + super.setUserObject(userObject); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java index fe5c8578..dca714dd 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ClosableTabTitlePane.java @@ -9,12 +9,17 @@ import java.awt.event.MouseEvent; import javax.accessibility.AccessibleContext; import javax.annotation.Nullable; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeListener; public class ClosableTabTitlePane { - private final JPanel ui; private final JButton closeButton; private final JLabel label; @@ -66,19 +71,7 @@ public class ClosableTabTitlePane { if (parent != null) { Point pt = new Point(e.getXOnScreen(), e.getYOnScreen()); SwingUtilities.convertPointFromScreen(pt, parent); - MouseEvent e1 = new MouseEvent( - parent, - e.getID(), - e.getWhen(), - e.getModifiersEx(), - (int) pt.getX(), - (int) pt.getY(), - e.getXOnScreen(), - e.getYOnScreen(), - e.getClickCount(), - e.isPopupTrigger(), - e.getButton() - ); + MouseEvent e1 = new MouseEvent(parent, e.getID(), e.getWhen(), e.getModifiersEx(), (int) pt.getX(), (int) pt.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton()); parent.dispatchEvent(e1); } } @@ -91,11 +84,13 @@ public class ClosableTabTitlePane { if (this.parent != null) { pane.removeChangeListener(cachedChangeListener); } + if (pane != null) { updateState(pane); cachedChangeListener = e -> updateState(pane); pane.addChangeListener(cachedChangeListener); } + this.parent = pane; } @@ -123,11 +118,12 @@ public class ClosableTabTitlePane { public static ClosableTabTitlePane byUi(Component c) { if (c instanceof JComponent) { Object prop = ((JComponent) c).getClientProperty(ClosableTabTitlePane.class); + if (prop instanceof ClosableTabTitlePane) { return (ClosableTabTitlePane) prop; } } + return null; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java index 10fc5e1a..5d1d0f2b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/DeobfPanel.java @@ -15,7 +15,6 @@ import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.utils.I18n; public class DeobfPanel extends JPanel { - public final ClassSelector deobfClasses; private final JLabel title = new JLabel(); @@ -44,6 +43,7 @@ public class DeobfPanel extends JPanel { if (SwingUtilities.isRightMouseButton(e)) { deobfClasses.setSelectionRow(deobfClasses.getClosestRowForLocation(e.getX(), e.getY())); int i = deobfClasses.getRowForPath(deobfClasses.getSelectionPath()); + if (i != -1) { deobfPanelPopupMenu.show(deobfClasses, e.getX(), e.getY()); } @@ -54,5 +54,4 @@ public class DeobfPanel extends JPanel { this.title.setText(I18n.translate(gui.isSingleClassTree() ? "info_panel.classes" : "info_panel.classes.deobfuscated")); this.deobfPanelPopupMenu.retranslateUi(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java index f4b190bc..cb74ceca 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java @@ -1,14 +1,35 @@ package cuchaz.enigma.gui.panels; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import javax.annotation.Nullable; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Highlighter.HighlightPainter; @@ -46,7 +67,6 @@ import cuchaz.enigma.utils.I18n; import cuchaz.enigma.utils.Result; public class EditorPanel { - private final JPanel ui = new JPanel(); private final JEditorPane editor = new JEditorPane(); private final JScrollPane editorScrollPane = new JScrollPane(this.editor); @@ -123,18 +143,19 @@ public class EditorPanel { @Override public void mouseReleased(MouseEvent e) { switch (e.getButton()) { - case MouseEvent.BUTTON3: // Right click - EditorPanel.this.editor.setCaretPosition(EditorPanel.this.editor.viewToModel(e.getPoint())); - break; + case MouseEvent.BUTTON3: // Right click + EditorPanel.this.editor.setCaretPosition(EditorPanel.this.editor.viewToModel(e.getPoint())); + break; - case 4: // Back navigation - gui.getController().openPreviousReference(); - break; + case 4: // Back navigation + gui.getController().openPreviousReference(); + break; - case 5: // Forward navigation - gui.getController().openNextReference(); - break; + case 5: // Forward navigation + gui.getController().openNextReference(); + break; } + EditorPanel.this.mouseIsPressed = false; } }); @@ -143,31 +164,36 @@ public class EditorPanel { public void keyPressed(KeyEvent event) { if (event.isControlDown()) { EditorPanel.this.shouldNavigateOnClick = false; - if (EditorPanel.this.popupMenu.handleKeyEvent(event)) return; + + if (EditorPanel.this.popupMenu.handleKeyEvent(event)) { + return; + } + switch (event.getKeyCode()) { - case KeyEvent.VK_F5: - if (EditorPanel.this.classHandle != null) { - EditorPanel.this.classHandle.invalidate(); - } - break; - - case KeyEvent.VK_F: - // prevent navigating on click when quick find activated - break; - - case KeyEvent.VK_ADD: - case KeyEvent.VK_EQUALS: - case KeyEvent.VK_PLUS: - offsetEditorZoom(2); - break; - case KeyEvent.VK_SUBTRACT: - case KeyEvent.VK_MINUS: - offsetEditorZoom(-2); - break; - - default: - EditorPanel.this.shouldNavigateOnClick = true; // CTRL - break; + case KeyEvent.VK_F5: + if (EditorPanel.this.classHandle != null) { + EditorPanel.this.classHandle.invalidate(); + } + + break; + + case KeyEvent.VK_F: + // prevent navigating on click when quick find activated + break; + + case KeyEvent.VK_ADD: + case KeyEvent.VK_EQUALS: + case KeyEvent.VK_PLUS: + offsetEditorZoom(2); + break; + case KeyEvent.VK_SUBTRACT: + case KeyEvent.VK_MINUS: + offsetEditorZoom(-2); + break; + + default: + EditorPanel.this.shouldNavigateOnClick = true; // CTRL + break; } } } @@ -175,8 +201,14 @@ public class EditorPanel { @Override public void keyTyped(KeyEvent event) { EntryReference, Entry> ref = EditorPanel.this.getCursorReference(); - if (ref == null) return; - if (!EditorPanel.this.controller.project.isRenamable(ref)) return; + + if (ref == null) { + return; + } + + if (!EditorPanel.this.controller.project.isRenamable(ref)) { + return; + } if (!event.isControlDown() && !event.isAltDown() && Character.isJavaIdentifierPart(event.getKeyChar())) { EnigmaProject project = gui.getController().project; @@ -184,8 +216,10 @@ public class EditorPanel { Entry entry = reference.getNameableEntry(); String name = String.valueOf(event.getKeyChar()); + if (entry instanceof ClassEntry && ((ClassEntry) entry).getParent() == null) { String packageName = ((ClassEntry) entry).getPackageName(); + if (packageName != null) { name = packageName + "/" + name; } @@ -207,12 +241,14 @@ public class EditorPanel { if ((this.editorLaf == null || this.editorLaf != laf)) { this.editor.updateUI(); this.editor.setBackground(UiConfig.getEditorBackgroundColor()); + if (this.editorLaf != null) { this.classHandle.invalidateMapped(); } this.editorLaf = laf; } + this.boxHighlightPainters = boxHighlightPainters; }; @@ -223,19 +259,23 @@ public class EditorPanel { public static EditorPanel byUi(Component ui) { if (ui instanceof JComponent) { Object prop = ((JComponent) ui).getClientProperty(EditorPanel.class); + if (prop instanceof EditorPanel) { return (EditorPanel) prop; } } + return null; } public void setClassHandle(ClassHandle handle) { ClassEntry old = null; + if (this.classHandle != null) { old = this.classHandle.getRef(); this.classHandle.close(); } + setClassHandle0(old, handle); } @@ -299,53 +339,61 @@ public class EditorPanel { } else { this.displayError(res.unwrapErr()); } + this.nextReference = null; }); } public void displayError(ClassHandleError t) { this.setDisplayMode(DisplayMode.ERRORED); + String str = switch (t.type) { - case DECOMPILE -> "editor.decompile_error"; - case REMAP -> "editor.remap_error"; + case DECOMPILE -> "editor.decompile_error"; + case REMAP -> "editor.remap_error"; }; + this.errorLabel.setText(I18n.translate(str)); this.errorTextArea.setText(t.getStackTrace()); this.errorTextArea.setCaretPosition(0); } public void setDisplayMode(DisplayMode mode) { - if (this.mode == mode) return; + if (this.mode == mode) { + return; + } + this.ui.removeAll(); + switch (mode) { - case INACTIVE: - break; - case IN_PROGRESS: { - // make progress bar start from the left every time - this.decompilingProgressBar.setIndeterminate(false); - this.decompilingProgressBar.setIndeterminate(true); - - this.ui.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2); - this.ui.add(this.decompilingLabel, cb.pos(0, 0).anchor(GridBagConstraints.SOUTH).build()); - this.ui.add(this.decompilingProgressBar, cb.pos(0, 1).anchor(GridBagConstraints.NORTH).build()); - break; - } - case SUCCESS: { - this.ui.setLayout(new GridLayout(1, 1, 0, 0)); - this.ui.add(this.editorScrollPane); - break; - } - case ERRORED: { - this.ui.setLayout(new GridBagLayout()); - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST); - this.ui.add(this.errorLabel, cb.pos(0, 0).build()); - this.ui.add(new JSeparator(JSeparator.HORIZONTAL), cb.pos(0, 1).fill(GridBagConstraints.HORIZONTAL).build()); - this.ui.add(this.errorScrollPane, cb.pos(0, 2).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).build()); - this.ui.add(this.retryButton, cb.pos(0, 3).weight(0.0, 0.0).anchor(GridBagConstraints.EAST).build()); - break; - } + case INACTIVE: + break; + case IN_PROGRESS: { + // make progress bar start from the left every time + this.decompilingProgressBar.setIndeterminate(false); + this.decompilingProgressBar.setIndeterminate(true); + + this.ui.setLayout(new GridBagLayout()); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2); + this.ui.add(this.decompilingLabel, cb.pos(0, 0).anchor(GridBagConstraints.SOUTH).build()); + this.ui.add(this.decompilingProgressBar, cb.pos(0, 1).anchor(GridBagConstraints.NORTH).build()); + break; + } + case SUCCESS: { + this.ui.setLayout(new GridLayout(1, 1, 0, 0)); + this.ui.add(this.editorScrollPane); + break; + } + case ERRORED: { + this.ui.setLayout(new GridBagLayout()); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).weight(1.0, 0.0).anchor(GridBagConstraints.WEST); + this.ui.add(this.errorLabel, cb.pos(0, 0).build()); + this.ui.add(new JSeparator(JSeparator.HORIZONTAL), cb.pos(0, 1).fill(GridBagConstraints.HORIZONTAL).build()); + this.ui.add(this.errorScrollPane, cb.pos(0, 2).weight(1.0, 1.0).fill(GridBagConstraints.BOTH).build()); + this.ui.add(this.retryButton, cb.pos(0, 3).weight(0.0, 0.0).anchor(GridBagConstraints.EAST).build()); + break; + } } + this.ui.validate(); this.ui.repaint(); this.mode = mode; @@ -353,6 +401,7 @@ public class EditorPanel { public void offsetEditorZoom(int zoomAmount) { int newResult = this.fontSize + zoomAmount; + if (newResult > 8 && newResult < 72) { this.fontSize = newResult; this.editor.setFont(ScaleUtil.getFont(this.editor.getFont().getFontName(), Font.PLAIN, this.fontSize)); @@ -365,8 +414,13 @@ public class EditorPanel { } public void onCaretMove(int pos, boolean fromClick) { - if (this.settingSource) return; - if (this.controller.project == null) return; + if (this.settingSource) { + return; + } + + if (this.controller.project == null) { + return; + } EntryRemapper mapper = this.controller.project.getMapper(); Token token = getToken(pos); @@ -378,10 +432,12 @@ public class EditorPanel { if (referenceEntry != null && this.shouldNavigateOnClick && fromClick) { this.shouldNavigateOnClick = false; Entry navigationEntry = referenceEntry; + if (this.cursorReference.context == null) { EntryResolver resolver = mapper.getObfResolver(); navigationEntry = resolver.resolveFirstEntry(referenceEntry, ResolutionStrategy.RESOLVE_ROOT); } + this.controller.navigateTo(navigationEntry); } } @@ -398,6 +454,7 @@ public class EditorPanel { if (this.source == null) { return null; } + return this.source.getIndex().getReferenceToken(pos); } @@ -406,16 +463,22 @@ public class EditorPanel { if (this.source == null) { return null; } + return this.source.getIndex().getReference(token); } public void setSource(DecompiledClassSource source) { this.setDisplayMode(DisplayMode.SUCCESS); - if (source == null) return; + + if (source == null) { + return; + } + try { this.settingSource = true; int newCaretPos = 0; + if (this.source != null && this.source.getEntry().equals(source.getEntry())) { int caretPos = this.editor.getCaretPosition(); @@ -441,9 +504,11 @@ public class EditorPanel { this.source = source; this.editor.getHighlighter().removeAllHighlights(); this.editor.setText(source.toString()); + if (this.source != null) { this.editor.setCaretPosition(newCaretPos); } + setHighlightedTokens(source.getHighlightedTokens()); setCursorReference(getReference(getToken(this.editor.getCaretPosition()))); } finally { @@ -515,10 +580,16 @@ public class EditorPanel { * @param reference */ private void showReference0(EntryReference, Entry> reference) { - if (this.source == null) return; - if (reference == null) return; + if (this.source == null) { + return; + } + + if (reference == null) { + return; + } List tokens = this.controller.getTokensForReference(this.source, reference); + if (tokens.isEmpty()) { // DEBUG System.err.println(String.format("WARNING: no tokens found for %s in %s", reference, this.classHandle.getRef())); @@ -531,6 +602,7 @@ public class EditorPanel { if (token == null) { throw new IllegalArgumentException("Token cannot be null!"); } + navigateToToken(token, SelectionHighlightPainter.INSTANCE); } @@ -546,9 +618,11 @@ public class EditorPanel { // make sure the token is visible in the scroll window Rectangle start = this.editor.modelToView(token.start); Rectangle end = this.editor.modelToView(token.end); + if (start == null || end == null) { return; } + Rectangle show = start.union(end); show.grow(start.width * 10, start.height * 6); SwingUtilities.invokeLater(() -> this.editor.scrollRectToVisible(show)); @@ -625,5 +699,4 @@ public class EditorPanel { SUCCESS, ERRORED, } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java index e71894db..7b75f1a8 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java @@ -25,12 +25,15 @@ import cuchaz.enigma.gui.util.ScaleUtil; import cuchaz.enigma.translation.mapping.AccessModifier; import cuchaz.enigma.translation.mapping.EntryChange; import cuchaz.enigma.translation.mapping.EntryMapping; -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; import cuchaz.enigma.utils.validation.ValidationContext; public class IdentifierPanel { - private final Gui gui; private final JPanel ui = new JPanel(); @@ -57,7 +60,9 @@ public class IdentifierPanel { } public boolean startRenaming() { - if (this.nameField == null) return false; + if (this.nameField == null) { + return false; + } this.nameField.startEditing(); @@ -65,7 +70,9 @@ public class IdentifierPanel { } public boolean startRenaming(String text) { - if (this.nameField == null) return false; + if (this.nameField == null) { + return false; + } this.nameField.startEditing(); this.nameField.setEditText(text); @@ -84,6 +91,7 @@ public class IdentifierPanel { TableHelper th = new TableHelper(this.ui, this.entry, this.gui); th.begin(); + if (this.entry == null) { this.ui.setEnabled(false); } else { @@ -102,8 +110,10 @@ public class IdentifierPanel { th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.FIELD, this::onModifierChanged); } else if (deobfEntry instanceof MethodEntry) { MethodEntry me = (MethodEntry) deobfEntry; + if (me.isConstructor()) { ClassEntry ce = me.getParent(); + if (ce != null) { String name = ce.isInnerClass() ? ce.getName() : ce.getFullName(); this.nameField = th.addRenameTextField(EditableType.CLASS, name); @@ -112,6 +122,7 @@ public class IdentifierPanel { this.nameField = th.addRenameTextField(EditableType.METHOD, me.getName()); th.addStringRow(I18n.translate("info_panel.identifier.class"), me.getParent().getFullName()); } + th.addCopiableStringRow(I18n.translate("info_panel.identifier.method_descriptor"), me.getDesc().toString()); th.addModifierRow(I18n.translate("info_panel.identifier.modifier"), EditableType.METHOD, this::onModifierChanged); } else if (deobfEntry instanceof LocalVariableEntry) { @@ -132,6 +143,7 @@ public class IdentifierPanel { throw new IllegalStateException("unreachable"); } } + th.end(); if (this.nameField != null) { @@ -139,6 +151,7 @@ public class IdentifierPanel { @Override public void onStartEditing(ConvertingTextField field) { int i = field.getText().lastIndexOf('/'); + if (i != -1) { field.selectSubstring(i + 1); } @@ -146,7 +159,10 @@ public class IdentifierPanel { @Override public boolean tryStopEditing(ConvertingTextField field, boolean abort) { - if (abort) return true; + if (abort) { + return true; + } + vc.reset(); vc.setActiveElement(field); validateRename(field.getText()); @@ -162,6 +178,7 @@ public class IdentifierPanel { } EditorPanel e = gui.getActiveEditor(); + if (e != null) { e.getEditor().requestFocusInWindow(); } @@ -192,13 +209,12 @@ public class IdentifierPanel { } private static final class TableHelper { - private final Container c; private final Entry e; private final Gui gui; private int row; - public TableHelper(Container c, Entry e, Gui gui) { + TableHelper(Container c, Entry e, Gui gui) { this.c = c; this.e = e; this.gui = gui; @@ -210,9 +226,7 @@ public class IdentifierPanel { } public void addRow(Component c1, Component c2) { - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create() - .insets(2) - .anchor(GridBagConstraints.WEST); + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(2).anchor(GridBagConstraints.WEST); c.add(c1, cb.pos(0, this.row).build()); c.add(c2, cb.pos(1, this.row).weightX(1.0).fill(GridBagConstraints.HORIZONTAL).build()); @@ -239,12 +253,12 @@ public class IdentifierPanel { } public ConvertingTextField addRenameTextField(EditableType type, String c2) { - String description = switch(type) { - case CLASS -> I18n.translate("info_panel.identifier.class"); - case METHOD -> I18n.translate("info_panel.identifier.method"); - case FIELD -> I18n.translate("info_panel.identifier.field"); - case PARAMETER, LOCAL_VARIABLE -> I18n.translate("info_panel.identifier.variable"); - default -> throw new IllegalStateException("Unexpected value: " + type); + String description = switch (type) { + case CLASS -> I18n.translate("info_panel.identifier.class"); + case METHOD -> I18n.translate("info_panel.identifier.method"); + case FIELD -> I18n.translate("info_panel.identifier.field"); + case PARAMETER, LOCAL_VARIABLE -> I18n.translate("info_panel.identifier.variable"); + default -> throw new IllegalStateException("Unexpected value: " + type); }; if (this.gui.getController().project.isRenamable(e)) { @@ -296,7 +310,5 @@ public class IdentifierPanel { // Add an empty panel with y-weight=1 so that all the other elements get placed at the top edge c.add(new JPanel(), GridBagConstraintsBuilder.create().pos(0, row).weight(0.0, 1.0).build()); } - } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java index 7783843d..f82e6663 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/ObfPanel.java @@ -13,7 +13,6 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.utils.I18n; public class ObfPanel extends JPanel { - public final ClassSelector obfClasses; private final JLabel title = new JLabel(); @@ -25,9 +24,11 @@ public class ObfPanel extends JPanel { Comparator obfClassComparator = (a, b) -> { String aname = a.getFullName(); String bname = b.getFullName(); + if (aname.length() != bname.length()) { return aname.length() - bname.length(); } + return aname.compareTo(bname); }; @@ -45,5 +46,4 @@ public class ObfPanel extends JPanel { public void retranslateUi() { this.title.setText(I18n.translate("info_panel.classes.obfuscated")); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java index ccded45c..571c638f 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java @@ -1,9 +1,16 @@ package cuchaz.enigma.gui.panels; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.awt.event.MouseEvent; -import javax.swing.*; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; @@ -23,144 +30,144 @@ import cuchaz.enigma.translation.representation.entry.ParentedEntry; import cuchaz.enigma.utils.I18n; public class StructurePanel { - private final Gui gui; - - private final JPanel panel = new JPanel(new BorderLayout()); - - private final JPanel optionsPanel; - - private final JLabel obfuscationVisibilityLabel = new JLabel(); - private final JLabel documentationVisibilityLabel = new JLabel(); - private final JLabel sortingOrderLabel = new JLabel(); - - private final JComboBox obfuscationVisibility; - private final JComboBox documentationVisibility; - private final JComboBox sortingOrder; - - private final JTree structureTree; - - public StructurePanel(Gui gui) { - this.gui = gui; - - this.optionsPanel = new JPanel(new GridBagLayout()); - this.optionsPanel.setVisible(false); - - GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(5).fill(GridBagConstraints.HORIZONTAL); - - this.optionsPanel.add(this.obfuscationVisibilityLabel, cb.pos(0, 0).build()); - this.obfuscationVisibility = new JComboBox<>(StructureTreeOptions.ObfuscationVisibility.values()); - this.obfuscationVisibility.setRenderer(new StructureOptionListCellRenderer()); - this.obfuscationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor())); - this.optionsPanel.add(this.obfuscationVisibility, cb.pos(1, 0).build()); - - this.optionsPanel.add(this.documentationVisibilityLabel, cb.pos(0, 1).build()); - this.documentationVisibility = new JComboBox<>(StructureTreeOptions.DocumentationVisibility.values()); - this.documentationVisibility.setRenderer(new StructureOptionListCellRenderer()); - this.documentationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor())); - this.optionsPanel.add(this.documentationVisibility, cb.pos(1, 1).build()); - - this.optionsPanel.add(this.sortingOrderLabel, cb.pos(0, 2).build()); - this.sortingOrder = new JComboBox<>(StructureTreeOptions.SortingOrder.values()); - this.sortingOrder.setRenderer(new StructureOptionListCellRenderer()); - this.sortingOrder.addActionListener(event -> this.showStructure(gui.getActiveEditor())); - this.optionsPanel.add(this.sortingOrder, cb.pos(1, 2).build()); - - this.structureTree = new JTree(); - this.structureTree.setModel(null); - this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui)); - this.structureTree.setSelectionModel(new SingleTreeSelectionModel()); - this.structureTree.setShowsRootHandles(true); - this.structureTree.addMouseListener(GuiUtil.onMouseClick(this::onClick)); - - this.retranslateUi(); - - this.panel.add(this.optionsPanel, BorderLayout.NORTH); - this.panel.add(new JScrollPane(this.structureTree)); - } - - public void showStructure(EditorPanel editor) { - structureTree.setModel(null); - - if (editor == null) { - this.optionsPanel.setVisible(false); - return; - } - - ClassEntry classEntry = editor.getClassHandle().getRef(); - if (classEntry == null) return; - - this.optionsPanel.setVisible(true); - - // get the class structure - StructureTreeNode node = this.gui.getController().getClassStructure(classEntry, this.getOptions()); - - // show the tree at the root - TreePath path = GuiUtil.getPathToRoot(node); - structureTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); - structureTree.expandPath(path); - structureTree.setSelectionRow(structureTree.getRowForPath(path)); - } - - private void onClick(MouseEvent event) { - if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { - // get the selected node - TreePath path = structureTree.getSelectionPath(); - if (path == null) { - return; - } - - Object node = path.getLastPathComponent(); - - if (node instanceof StructureTreeNode) { - this.gui.getController().navigateTo(((StructureTreeNode) node).getEntry()); - } - } - } - - /** - * Creates and returns the options of this structure panel. - */ - private StructureTreeOptions getOptions() { - return new StructureTreeOptions( - (StructureTreeOptions.ObfuscationVisibility) this.obfuscationVisibility.getSelectedItem(), - (StructureTreeOptions.DocumentationVisibility) this.documentationVisibility.getSelectedItem(), - (StructureTreeOptions.SortingOrder) this.sortingOrder.getSelectedItem() - ); - } - - public void retranslateUi() { - this.obfuscationVisibilityLabel.setText(I18n.translate("structure.options.obfuscation")); - this.documentationVisibilityLabel.setText(I18n.translate("structure.options.documentation")); - this.sortingOrderLabel.setText(I18n.translate("structure.options.sorting")); - } - - public JPanel getPanel() { - return this.panel; - } - - private static class StructureTreeCellRenderer extends DefaultTreeCellRenderer { - private final Gui gui; - - StructureTreeCellRenderer(Gui gui) { - this.gui = gui; - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); - ParentedEntry entry = ((StructureTreeNode) value).getEntry(); - - if (entry instanceof ClassEntry classEntry) { - this.setIcon(GuiUtil.getClassIcon(gui, classEntry)); - } else if (entry instanceof MethodEntry methodEntry) { - this.setIcon(GuiUtil.getMethodIcon(methodEntry)); - } else if (entry instanceof FieldEntry) { - this.setIcon(GuiUtil.FIELD_ICON); - } - - this.setText("" + ((StructureTreeNode) value).toHtml()); - - return c; - } - } + private final Gui gui; + + private final JPanel panel = new JPanel(new BorderLayout()); + + private final JPanel optionsPanel; + + private final JLabel obfuscationVisibilityLabel = new JLabel(); + private final JLabel documentationVisibilityLabel = new JLabel(); + private final JLabel sortingOrderLabel = new JLabel(); + + private final JComboBox obfuscationVisibility; + private final JComboBox documentationVisibility; + private final JComboBox sortingOrder; + + private final JTree structureTree; + + public StructurePanel(Gui gui) { + this.gui = gui; + + this.optionsPanel = new JPanel(new GridBagLayout()); + this.optionsPanel.setVisible(false); + + GridBagConstraintsBuilder cb = GridBagConstraintsBuilder.create().insets(5).fill(GridBagConstraints.HORIZONTAL); + + this.optionsPanel.add(this.obfuscationVisibilityLabel, cb.pos(0, 0).build()); + this.obfuscationVisibility = new JComboBox<>(StructureTreeOptions.ObfuscationVisibility.values()); + this.obfuscationVisibility.setRenderer(new StructureOptionListCellRenderer()); + this.obfuscationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor())); + this.optionsPanel.add(this.obfuscationVisibility, cb.pos(1, 0).build()); + + this.optionsPanel.add(this.documentationVisibilityLabel, cb.pos(0, 1).build()); + this.documentationVisibility = new JComboBox<>(StructureTreeOptions.DocumentationVisibility.values()); + this.documentationVisibility.setRenderer(new StructureOptionListCellRenderer()); + this.documentationVisibility.addActionListener(event -> this.showStructure(gui.getActiveEditor())); + this.optionsPanel.add(this.documentationVisibility, cb.pos(1, 1).build()); + + this.optionsPanel.add(this.sortingOrderLabel, cb.pos(0, 2).build()); + this.sortingOrder = new JComboBox<>(StructureTreeOptions.SortingOrder.values()); + this.sortingOrder.setRenderer(new StructureOptionListCellRenderer()); + this.sortingOrder.addActionListener(event -> this.showStructure(gui.getActiveEditor())); + this.optionsPanel.add(this.sortingOrder, cb.pos(1, 2).build()); + + this.structureTree = new JTree(); + this.structureTree.setModel(null); + this.structureTree.setCellRenderer(new StructureTreeCellRenderer(gui)); + this.structureTree.setSelectionModel(new SingleTreeSelectionModel()); + this.structureTree.setShowsRootHandles(true); + this.structureTree.addMouseListener(GuiUtil.onMouseClick(this::onClick)); + + this.retranslateUi(); + + this.panel.add(this.optionsPanel, BorderLayout.NORTH); + this.panel.add(new JScrollPane(this.structureTree)); + } + + public void showStructure(EditorPanel editor) { + structureTree.setModel(null); + + if (editor == null) { + this.optionsPanel.setVisible(false); + return; + } + + ClassEntry classEntry = editor.getClassHandle().getRef(); + + if (classEntry == null) { + return; + } + + this.optionsPanel.setVisible(true); + + // get the class structure + StructureTreeNode node = this.gui.getController().getClassStructure(classEntry, this.getOptions()); + + // show the tree at the root + TreePath path = GuiUtil.getPathToRoot(node); + structureTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); + structureTree.expandPath(path); + structureTree.setSelectionRow(structureTree.getRowForPath(path)); + } + + private void onClick(MouseEvent event) { + if (event.getClickCount() >= 2 && event.getButton() == MouseEvent.BUTTON1) { + // get the selected node + TreePath path = structureTree.getSelectionPath(); + + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + + if (node instanceof StructureTreeNode) { + this.gui.getController().navigateTo(((StructureTreeNode) node).getEntry()); + } + } + } + + /** + * Creates and returns the options of this structure panel. + */ + private StructureTreeOptions getOptions() { + return new StructureTreeOptions((StructureTreeOptions.ObfuscationVisibility) this.obfuscationVisibility.getSelectedItem(), (StructureTreeOptions.DocumentationVisibility) this.documentationVisibility.getSelectedItem(), (StructureTreeOptions.SortingOrder) this.sortingOrder.getSelectedItem()); + } + + public void retranslateUi() { + this.obfuscationVisibilityLabel.setText(I18n.translate("structure.options.obfuscation")); + this.documentationVisibilityLabel.setText(I18n.translate("structure.options.documentation")); + this.sortingOrderLabel.setText(I18n.translate("structure.options.sorting")); + } + + public JPanel getPanel() { + return this.panel; + } + + private static class StructureTreeCellRenderer extends DefaultTreeCellRenderer { + private final Gui gui; + + StructureTreeCellRenderer(Gui gui) { + this.gui = gui; + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); + ParentedEntry entry = ((StructureTreeNode) value).getEntry(); + + if (entry instanceof ClassEntry classEntry) { + this.setIcon(GuiUtil.getClassIcon(gui, classEntry)); + } else if (entry instanceof MethodEntry methodEntry) { + this.setIcon(GuiUtil.getMethodIcon(methodEntry)); + } else if (entry instanceof FieldEntry) { + this.setIcon(GuiUtil.FIELD_ICON); + } + + this.setText("" + ((StructureTreeNode) value).toHtml()); + + return c; + } + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java index 0aa6510c..3791a1ec 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/CallsTreeCellRenderer.java @@ -1,45 +1,51 @@ package cuchaz.enigma.gui.renderer; -import cuchaz.enigma.analysis.*; +import java.awt.Component; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + +import cuchaz.enigma.analysis.ClassReferenceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodReferenceTreeNode; +import cuchaz.enigma.analysis.ReferenceTreeNode; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.config.UiConfig; import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import javax.swing.*; -import javax.swing.tree.DefaultTreeCellRenderer; -import java.awt.*; - public class CallsTreeCellRenderer extends DefaultTreeCellRenderer { - private final Gui gui; - - public CallsTreeCellRenderer(Gui gui) { - this.gui = gui; - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - EntryReference reference = ((ReferenceTreeNode) value).getReference(); - - this.setForeground(UiConfig.getTextColor()); - - // if the node represents the method calling the entry - if (reference != null) { - if (reference.context instanceof MethodEntry) { - this.setIcon(GuiUtil.getMethodIcon((MethodEntry) reference.context)); - } - // if the node represents the called entry - } else { - if (value instanceof ClassReferenceTreeNode node) { - this.setIcon(GuiUtil.getClassIcon(this.gui, node.getEntry())); - } else if (value instanceof MethodReferenceTreeNode node) { - this.setIcon(GuiUtil.getMethodIcon(node.getEntry())); - } else if (value instanceof FieldReferenceTreeNode) { - this.setIcon(GuiUtil.FIELD_ICON); - } - } - - return c; - } + private final Gui gui; + + public CallsTreeCellRenderer(Gui gui) { + this.gui = gui; + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + EntryReference reference = ((ReferenceTreeNode) value).getReference(); + + this.setForeground(UiConfig.getTextColor()); + + // if the node represents the method calling the entry + if (reference != null) { + if (reference.context instanceof MethodEntry) { + this.setIcon(GuiUtil.getMethodIcon((MethodEntry) reference.context)); + } + + // if the node represents the called entry + } else { + if (value instanceof ClassReferenceTreeNode node) { + this.setIcon(GuiUtil.getClassIcon(this.gui, node.getEntry())); + } else if (value instanceof MethodReferenceTreeNode node) { + this.setIcon(GuiUtil.getMethodIcon(node.getEntry())); + } else if (value instanceof FieldReferenceTreeNode) { + this.setIcon(GuiUtil.FIELD_ICON); + } + } + + return c; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java index 7bf39005..b4126c02 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/ImplementationsTreeCellRenderer.java @@ -1,34 +1,35 @@ package cuchaz.enigma.gui.renderer; +import java.awt.Component; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + import cuchaz.enigma.analysis.ClassImplementationsTreeNode; import cuchaz.enigma.analysis.MethodImplementationsTreeNode; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.config.UiConfig; import cuchaz.enigma.gui.util.GuiUtil; -import javax.swing.*; -import javax.swing.tree.DefaultTreeCellRenderer; -import java.awt.*; - public class ImplementationsTreeCellRenderer extends DefaultTreeCellRenderer { - private final Gui gui; + private final Gui gui; - public ImplementationsTreeCellRenderer(Gui gui) { - this.gui = gui; - } + public ImplementationsTreeCellRenderer(Gui gui) { + this.gui = gui; + } - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - this.setForeground(UiConfig.getTextColor()); + this.setForeground(UiConfig.getTextColor()); - if (value instanceof ClassImplementationsTreeNode node) { - this.setIcon(GuiUtil.getClassIcon(this.gui, node.getClassEntry())); - } else if (value instanceof MethodImplementationsTreeNode node) { - this.setIcon(GuiUtil.getMethodIcon(node.getMethodEntry())); - } + if (value instanceof ClassImplementationsTreeNode node) { + this.setIcon(GuiUtil.getClassIcon(this.gui, node.getClassEntry())); + } else if (value instanceof MethodImplementationsTreeNode node) { + this.setIcon(GuiUtil.getMethodIcon(node.getMethodEntry())); + } - return c; - } + return c; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java index a1025531..04bf0f93 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/InheritanceTreeCellRenderer.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.gui.renderer; @@ -37,6 +37,7 @@ public class InheritanceTreeCellRenderer extends DefaultTreeCellRenderer { if (!(value instanceof MethodInheritanceTreeNode node) || node.isImplemented()) { ret.setForeground(UiConfig.getTextColor()); ret.setFont(ret.getFont().deriveFont(Font.PLAIN)); + if (value instanceof ClassInheritanceTreeNode) { this.setIcon(GuiUtil.getClassIcon(this.gui, ((ClassInheritanceTreeNode) value).getClassEntry())); } else if (value instanceof MethodInheritanceTreeNode) { diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java index b6ae0c50..123990e4 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/MessageListCellRenderer.java @@ -10,15 +10,15 @@ import cuchaz.enigma.network.Message; // For now, just render the translated text. // TODO: Icons or something later? public class MessageListCellRenderer extends DefaultListCellRenderer { - @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); Message message = (Message) value; + if (message != null) { setText(message.translate()); } + return this; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java index f9a1cae6..09cdc9bd 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/renderer/StructureOptionListCellRenderer.java @@ -1,21 +1,22 @@ package cuchaz.enigma.gui.renderer; +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JList; + import cuchaz.enigma.analysis.StructureTreeOptions; import cuchaz.enigma.utils.I18n; -import javax.swing.*; -import java.awt.*; - public class StructureOptionListCellRenderer extends DefaultListCellRenderer { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - - if (value instanceof StructureTreeOptions.Option option) { - this.setText(I18n.translate(option.getTranslationKey())); - } + if (value instanceof StructureTreeOptions.Option option) { + this.setText(I18n.translate(option.getTranslationKey())); + } - return c; - } + return c; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java index 91727c38..93507bcd 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchEntry.java @@ -3,7 +3,6 @@ package cuchaz.enigma.gui.search; import java.util.List; public interface SearchEntry { - List getSearchableNames(); /** @@ -13,5 +12,4 @@ public interface SearchEntry { * @return a unique identifier for this search entry */ String getIdentifier(); - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java index a3b35faa..c8212ce5 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/search/SearchUtil.java @@ -1,6 +1,14 @@ package cuchaz.enigma.gui.search; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; @@ -14,7 +22,6 @@ import java.util.stream.Stream; import cuchaz.enigma.utils.Pair; public class SearchUtil { - private final Map> entries = new HashMap<>(); private final Map hitCount = new HashMap<>(); private final Executor searchExecutor = Executors.newWorkStealingPool(); @@ -45,12 +52,7 @@ public class SearchUtil { } public Stream search(String term) { - return entries.values().parallelStream() - .map(e -> new Pair<>(e, e.getScore(term, hitCount.getOrDefault(e.searchEntry.getIdentifier(), 0)))) - .filter(e -> e.b > 0) - .sorted(Comparator.comparingDouble(o -> -o.b)) - .map(e -> e.a.searchEntry) - .sequential(); + return entries.values().parallelStream().map(e -> new Pair<>(e, e.getScore(term, hitCount.getOrDefault(e.searchEntry.getIdentifier(), 0)))).filter(e -> e.b > 0).sorted(Comparator.comparingDouble(o -> -o.b)).map(e -> e.a.searchEntry).sequential(); } public SearchControl asyncSearch(String term, SearchResultConsumer consumer) { @@ -61,21 +63,36 @@ public class SearchUtil { AtomicInteger size = new AtomicInteger(); AtomicBoolean control = new AtomicBoolean(false); AtomicInteger elapsed = new AtomicInteger(); + for (Entry value : entries.values()) { searchExecutor.execute(() -> { try { - if (control.get()) return; + if (control.get()) { + return; + } + float score = value.getScore(term, hitCount.getOrDefault(value.searchEntry.getIdentifier(), 0)); - if (score <= 0) return; + + if (score <= 0) { + return; + } + score = -score; // sort descending + try { scoresLock.lock(); - if (control.get()) return; + + if (control.get()) { + return; + } + int dataSize = size.getAndIncrement(); int index = Arrays.binarySearch(scores, 0, dataSize, score); + if (index < 0) { index = ~index; } + System.arraycopy(scores, index, scores, index + 1, dataSize - index); scores[index] = score; consumer.add(index, value.searchEntry); @@ -113,7 +130,6 @@ public class SearchUtil { } public static final class Entry { - public final T searchEntry; private final String[][] components; @@ -124,9 +140,7 @@ public class SearchUtil { public float getScore(String term, int hits) { String ucTerm = term.toUpperCase(Locale.ROOT); - float maxScore = (float) Arrays.stream(components) - .mapToDouble(name -> getScoreFor(ucTerm, name)) - .max().orElse(0.0); + float maxScore = (float) Arrays.stream(components).mapToDouble(name -> getScoreFor(ucTerm, name)).max().orElse(0.0); return maxScore * (hits + 1); } @@ -156,17 +170,20 @@ public class SearchUtil { String component = name[componentIndex]; float posMultiplier = (name.length - componentIndex) * 0.3f; Map newSnapshots = new HashMap<>(); + for (Map.Entry snapshot : snapshots.entrySet()) { String remaining = snapshot.getKey(); float score = snapshot.getValue(); component = component.toUpperCase(Locale.ROOT); int l = compareEqualLength(remaining, component); + for (int i = 1; i <= l; i++) { float baseScore = scorePerChar * i; float chainBonus = (i - 1) * 0.5f; merge(newSnapshots, Collections.singletonMap(remaining.substring(i), score + baseScore * posMultiplier + chainBonus), Math::max); } } + merge(snapshots, newSnapshots, Math::max); } @@ -180,24 +197,24 @@ public class SearchUtil { } public static Entry from(T e) { - String[][] components = e.getSearchableNames().parallelStream() - .map(Entry::wordwiseSplit) - .toArray(String[][]::new); + String[][] components = e.getSearchableNames().parallelStream().map(Entry::wordwiseSplit).toArray(String[][]::new); return new Entry<>(e, components); } private static int compareEqualLength(String s1, String s2) { int len = 0; + while (len < s1.length() && len < s2.length() && s1.charAt(len) == s2.charAt(len)) { len += 1; } + return len; } /** * Splits the given input into components, trying to detect word parts. - *

- * Example of how words get split (using | as seperator): + * + *

Example of how words get split (using | as seperator): *

MinecraftClientGame -> Minecraft|Client|Game

*

HTTPInputStream -> HTTP|Input|Stream

*

class_932 -> class|_|932

@@ -210,46 +227,57 @@ public class SearchUtil { */ private static String[] wordwiseSplit(String input) { List list = new ArrayList<>(); + while (!input.isEmpty()) { int take; + if (Character.isLetter(input.charAt(0))) { if (input.length() == 1) { take = 1; } else { boolean nextSegmentIsUppercase = Character.isUpperCase(input.charAt(0)) && Character.isUpperCase(input.charAt(1)); + if (nextSegmentIsUppercase) { int nextLowercase = 1; + while (Character.isUpperCase(input.charAt(nextLowercase))) { nextLowercase += 1; + if (nextLowercase == input.length()) { nextLowercase += 1; break; } } + take = nextLowercase - 1; } else { int nextUppercase = 1; + while (nextUppercase < input.length() && Character.isLowerCase(input.charAt(nextUppercase))) { nextUppercase += 1; } + take = nextUppercase; } } } else if (Character.isDigit(input.charAt(0))) { int nextNonNum = 1; + while (nextNonNum < input.length() && Character.isLetter(input.charAt(nextNonNum)) && !Character.isLowerCase(input.charAt(nextNonNum))) { nextNonNum += 1; } + take = nextNonNum; } else { take = 1; } + list.add(input.substring(0, take)); input = input.substring(take); } + return list.toArray(new String[0]); } - } @FunctionalInterface @@ -264,5 +292,4 @@ public class SearchUtil { float getProgress(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java index 20d6a0eb..99b5572b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java @@ -1,5 +1,10 @@ package cuchaz.enigma.gui.stats; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.analysis.index.EntryIndex; @@ -7,109 +12,112 @@ import cuchaz.enigma.translation.mapping.EntryRemapper; import cuchaz.enigma.translation.mapping.EntryResolver; import cuchaz.enigma.translation.mapping.ResolutionStrategy; 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.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; import cuchaz.enigma.utils.I18n; -import java.util.*; - public class StatsGenerator { - private final EnigmaProject project; - private final EntryIndex entryIndex; - private final EntryRemapper mapper; - private final EntryResolver entryResolver; - - public StatsGenerator(EnigmaProject project) { - this.project = project; - this.entryIndex = project.getJarIndex().getEntryIndex(); - this.mapper = project.getMapper(); - this.entryResolver = project.getJarIndex().getEntryResolver(); - } - - public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean includeSynthetic) { - includedMembers = EnumSet.copyOf(includedMembers); - int totalWork = 0; - int totalMappable = 0; - - if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { - totalWork += entryIndex.getMethods().size(); - } - - if (includedMembers.contains(StatsMember.FIELDS)) { - totalWork += entryIndex.getFields().size(); - } - - if (includedMembers.contains(StatsMember.CLASSES)) { - totalWork += entryIndex.getClasses().size(); - } - - progress.init(totalWork, I18n.translate("progress.stats")); - - Map counts = new HashMap<>(); - - int numDone = 0; - if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { - for (MethodEntry method : entryIndex.getMethods()) { - progress.step(numDone++, I18n.translate("type.methods")); - MethodEntry root = entryResolver - .resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT) - .stream() - .findFirst() - .orElseThrow(AssertionError::new); - - if (root == method) { - if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { - update(counts, method); - totalMappable ++; - } - - if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { - int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; - for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { - update(counts, new LocalVariableEntry(method, index, "", true,null)); - index += argument.getSize(); - totalMappable ++; - } - } - } - } - } - - if (includedMembers.contains(StatsMember.FIELDS)) { - for (FieldEntry field : entryIndex.getFields()) { - progress.step(numDone++, I18n.translate("type.fields")); - if (!((FieldDefEntry)field).getAccess().isSynthetic()) { - update(counts, field); - totalMappable ++; - } - } - } - - if (includedMembers.contains(StatsMember.CLASSES)) { - for (ClassEntry clazz : entryIndex.getClasses()) { - progress.step(numDone++, I18n.translate("type.classes")); - update(counts, clazz); - totalMappable ++; - } - } - - progress.step(-1, I18n.translate("progress.stats.data")); - - StatsResult.Tree tree = new StatsResult.Tree<>(); - - for (Map.Entry entry : counts.entrySet()) { - if (entry.getKey().startsWith(topLevelPackage)) { - tree.getNode(entry.getKey()).value = entry.getValue(); - } - } - - tree.collapse(tree.root); - return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree); - } - - private void update(Map counts, Entry entry) { - if (project.isObfuscated(entry)) { - String parent = mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); - counts.put(parent, counts.getOrDefault(parent, 0) + 1); - } - } + private final EnigmaProject project; + private final EntryIndex entryIndex; + private final EntryRemapper mapper; + private final EntryResolver entryResolver; + + public StatsGenerator(EnigmaProject project) { + this.project = project; + this.entryIndex = project.getJarIndex().getEntryIndex(); + this.mapper = project.getMapper(); + this.entryResolver = project.getJarIndex().getEntryResolver(); + } + + public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean includeSynthetic) { + includedMembers = EnumSet.copyOf(includedMembers); + int totalWork = 0; + int totalMappable = 0; + + if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { + totalWork += entryIndex.getMethods().size(); + } + + if (includedMembers.contains(StatsMember.FIELDS)) { + totalWork += entryIndex.getFields().size(); + } + + if (includedMembers.contains(StatsMember.CLASSES)) { + totalWork += entryIndex.getClasses().size(); + } + + progress.init(totalWork, I18n.translate("progress.stats")); + + Map counts = new HashMap<>(); + + int numDone = 0; + + if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { + for (MethodEntry method : entryIndex.getMethods()) { + progress.step(numDone++, I18n.translate("type.methods")); + MethodEntry root = entryResolver.resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT).stream().findFirst().orElseThrow(AssertionError::new); + + if (root == method) { + if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { + update(counts, method); + totalMappable++; + } + + if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { + int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; + + for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { + update(counts, new LocalVariableEntry(method, index, "", true, null)); + index += argument.getSize(); + totalMappable++; + } + } + } + } + } + + if (includedMembers.contains(StatsMember.FIELDS)) { + for (FieldEntry field : entryIndex.getFields()) { + progress.step(numDone++, I18n.translate("type.fields")); + + if (!((FieldDefEntry) field).getAccess().isSynthetic()) { + update(counts, field); + totalMappable++; + } + } + } + + if (includedMembers.contains(StatsMember.CLASSES)) { + for (ClassEntry clazz : entryIndex.getClasses()) { + progress.step(numDone++, I18n.translate("type.classes")); + update(counts, clazz); + totalMappable++; + } + } + + progress.step(-1, I18n.translate("progress.stats.data")); + + StatsResult.Tree tree = new StatsResult.Tree<>(); + + for (Map.Entry entry : counts.entrySet()) { + if (entry.getKey().startsWith(topLevelPackage)) { + tree.getNode(entry.getKey()).value = entry.getValue(); + } + } + + tree.collapse(tree.root); + return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree); + } + + private void update(Map counts, Entry entry) { + if (project.isObfuscated(entry)) { + String parent = mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); + counts.put(parent, counts.getOrDefault(parent, 0) + 1); + } + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java index 0e2452fa..0037ab5b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsMember.java @@ -1,8 +1,8 @@ package cuchaz.enigma.gui.stats; public enum StatsMember { - CLASSES, - METHODS, - FIELDS, - PARAMETERS + CLASSES, + METHODS, + FIELDS, + PARAMETERS } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java index 0a71a647..12726c0b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java @@ -1,14 +1,13 @@ package cuchaz.enigma.gui.stats; -import com.google.gson.GsonBuilder; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -public final class StatsResult { +import com.google.gson.GsonBuilder; +public final class StatsResult { private final int total; private final int unmapped; private final Tree tree; @@ -101,5 +100,4 @@ public final class StatsResult { } } } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java index 612e3e92..f8ce36db 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/AbstractListCellRenderer.java @@ -3,11 +3,16 @@ package cuchaz.enigma.gui.util; import java.awt.Component; import java.awt.event.MouseEvent; -import javax.swing.*; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.border.Border; public abstract class AbstractListCellRenderer extends JPanel implements ListCellRenderer { - private static final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1); private Border noFocusBorder; @@ -21,22 +26,27 @@ public abstract class AbstractListCellRenderer extends JPanel implements List Border border = UIManager.getLookAndFeel().getDefaults().getBorder("List.List.cellNoFocusBorder"); noFocusBorder = border != null ? border : NO_FOCUS_BORDER; } + return noFocusBorder; } protected Border getBorder(boolean isSelected, boolean cellHasFocus) { Border b = null; + if (cellHasFocus) { UIDefaults defaults = UIManager.getLookAndFeel().getDefaults(); + if (isSelected) { b = defaults.getBorder("List.focusSelectedCellHighlightBorder"); } + if (b == null) { b = defaults.getBorder("List.focusCellHighlightBorder"); } } else { b = getNoFocusBorder(); } + return b; } @@ -68,10 +78,11 @@ public abstract class AbstractListCellRenderer extends JPanel implements List @Override public String getToolTipText(MouseEvent event) { Component c = getComponentAt(event.getPoint()); + if (c instanceof JComponent) { return ((JComponent) c).getToolTipText(); } + return getToolTipText(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java index 6a756867..2d8aa73e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GridBagConstraintsBuilder.java @@ -3,7 +3,6 @@ package cuchaz.enigma.gui.util; import java.awt.GridBagConstraints; public final class GridBagConstraintsBuilder { - private final GridBagConstraints inner; private GridBagConstraintsBuilder(GridBagConstraints inner) { @@ -136,5 +135,4 @@ public final class GridBagConstraintsBuilder { public GridBagConstraints build() { return (GridBagConstraints) this.inner.clone(); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index 3b8ecbc5..d0784243 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java @@ -1,8 +1,17 @@ package cuchaz.enigma.gui.util; -import java.awt.*; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; -import java.awt.event.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; import java.awt.font.TextAttribute; import java.io.IOException; import java.net.URI; @@ -13,7 +22,14 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.function.Consumer; -import javax.swing.*; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JToolTip; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.Timer; +import javax.swing.ToolTipManager; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -28,160 +44,160 @@ import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.Os; public class GuiUtil { - public static final Icon CLASS_ICON = loadIcon("class"); - public static final Icon INTERFACE_ICON = loadIcon("interface"); - public static final Icon ENUM_ICON = loadIcon("enum"); - public static final Icon ANNOTATION_ICON = loadIcon("annotation"); - public static final Icon RECORD_ICON = loadIcon("record"); - public static final Icon METHOD_ICON = loadIcon("method"); - public static final Icon FIELD_ICON = loadIcon("field"); - public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor"); - - public static void openUrl(String url) { - try { - switch (Os.getOs()) { - case LINUX: - new ProcessBuilder("/usr/bin/env", "xdg-open", url).start(); - break; - default: - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - desktop.browse(new URI(url)); - } - } - } catch (IOException ex) { - throw new RuntimeException(ex); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException(ex); - } - } - - public static JLabel unboldLabel(JLabel label) { - Font font = label.getFont(); - label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); - return label; - } - - /** - * Puts the provided {@code text} in the system clipboard. - */ - public static void copyToClipboard(String text) { - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null); - } - - public static void showPopup(JComponent component, String text, int x, int y) { - // from https://stackoverflow.com/questions/39955015/java-swing-show-tooltip-as-a-message-dialog - JToolTip tooltip = new JToolTip(); - tooltip.setTipText(text); - Popup p = PopupFactory.getSharedInstance().getPopup(component, tooltip, x + 10, y); - p.show(); - Timer t = new Timer(1000, e -> p.hide()); - t.setRepeats(false); - t.start(); - } - - public static void showToolTipNow(JComponent component) { - // HACKHACK: trick the tooltip manager into showing the tooltip right now - ToolTipManager manager = ToolTipManager.sharedInstance(); - int oldDelay = manager.getInitialDelay(); - manager.setInitialDelay(0); - manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); - manager.setInitialDelay(oldDelay); - } - - public static JLabel createLink(String text, Runnable action) { - JLabel link = new JLabel(text); - link.setForeground(Color.BLUE.darker()); - link.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - @SuppressWarnings("unchecked") - Map attributes = (Map) link.getFont().getAttributes(); - attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); - link.setFont(link.getFont().deriveFont(attributes)); - link.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - action.run(); - } - }); - return link; - } - - public static Icon loadIcon(String name) { - String path = "icons/" + name + ".svg"; - - // Do an eager check for a missing icon since FlatSVGIcon does it later at render time - if (GuiUtil.class.getResource('/' + path) == null) { - throw new NoSuchElementException("Missing icon: '" + name + "' at " + path); - } - - // Note: the width and height are scaled automatically because the FlatLaf UI scale - // is set in LookAndFeel.setGlobalLAF() - return new FlatSVGIcon(path, 16, 16, GuiUtil.class.getClassLoader()); - } - - public static Icon getClassIcon(Gui gui, ClassEntry entry) { - EntryIndex entryIndex = gui.getController().project.getJarIndex().getEntryIndex(); - AccessFlags access = entryIndex.getClassAccess(entry); - - if (access != null) { - if (access.isAnnotation()) { - return ANNOTATION_ICON; - } else if (access.isInterface()) { - return INTERFACE_ICON; - } else if (access.isEnum()) { - return ENUM_ICON; - } else if (entryIndex.getDefinition(entry).isRecord()) { - return RECORD_ICON; - } - } - - return CLASS_ICON; - } - - public static Icon getMethodIcon(MethodEntry entry) { - if (entry.isConstructor()) { - return CONSTRUCTOR_ICON; - } - return METHOD_ICON; - } - - public static TreePath getPathToRoot(TreeNode node) { - List nodes = Lists.newArrayList(); - TreeNode n = node; - - do { - nodes.add(n); - n = n.getParent(); - } while (n != null); - - Collections.reverse(nodes); - return new TreePath(nodes.toArray()); - } - - public static MouseListener onMouseClick(Consumer op) { - return new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - op.accept(e); - } - }; - } - - public static MouseListener onMousePress(Consumer op) { - return new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - op.accept(e); - } - }; - } - - public static WindowListener onWindowClose(Consumer op) { - return new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - op.accept(e); - } - }; - } + public static final Icon CLASS_ICON = loadIcon("class"); + public static final Icon INTERFACE_ICON = loadIcon("interface"); + public static final Icon ENUM_ICON = loadIcon("enum"); + public static final Icon ANNOTATION_ICON = loadIcon("annotation"); + public static final Icon RECORD_ICON = loadIcon("record"); + public static final Icon METHOD_ICON = loadIcon("method"); + public static final Icon FIELD_ICON = loadIcon("field"); + public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor"); + + public static void openUrl(String url) { + try { + switch (Os.getOs()) { + case LINUX: + new ProcessBuilder("/usr/bin/env", "xdg-open", url).start(); + break; + default: + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + desktop.browse(new URI(url)); + } + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException(ex); + } + } + + public static JLabel unboldLabel(JLabel label) { + Font font = label.getFont(); + label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD)); + return label; + } + + /** + * Puts the provided {@code text} in the system clipboard. + */ + public static void copyToClipboard(String text) { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null); + } + + public static void showPopup(JComponent component, String text, int x, int y) { + // from https://stackoverflow.com/questions/39955015/java-swing-show-tooltip-as-a-message-dialog + JToolTip tooltip = new JToolTip(); + tooltip.setTipText(text); + Popup p = PopupFactory.getSharedInstance().getPopup(component, tooltip, x + 10, y); + p.show(); + Timer t = new Timer(1000, e -> p.hide()); + t.setRepeats(false); + t.start(); + } + + public static void showToolTipNow(JComponent component) { + // HACKHACK: trick the tooltip manager into showing the tooltip right now + ToolTipManager manager = ToolTipManager.sharedInstance(); + int oldDelay = manager.getInitialDelay(); + manager.setInitialDelay(0); + manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); + manager.setInitialDelay(oldDelay); + } + + public static JLabel createLink(String text, Runnable action) { + JLabel link = new JLabel(text); + link.setForeground(Color.BLUE.darker()); + link.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + @SuppressWarnings("unchecked") Map attributes = (Map) link.getFont().getAttributes(); + attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + link.setFont(link.getFont().deriveFont(attributes)); + link.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + action.run(); + } + }); + return link; + } + + public static Icon loadIcon(String name) { + String path = "icons/" + name + ".svg"; + + // Do an eager check for a missing icon since FlatSVGIcon does it later at render time + if (GuiUtil.class.getResource('/' + path) == null) { + throw new NoSuchElementException("Missing icon: '" + name + "' at " + path); + } + + // Note: the width and height are scaled automatically because the FlatLaf UI scale + // is set in LookAndFeel.setGlobalLAF() + return new FlatSVGIcon(path, 16, 16, GuiUtil.class.getClassLoader()); + } + + public static Icon getClassIcon(Gui gui, ClassEntry entry) { + EntryIndex entryIndex = gui.getController().project.getJarIndex().getEntryIndex(); + AccessFlags access = entryIndex.getClassAccess(entry); + + if (access != null) { + if (access.isAnnotation()) { + return ANNOTATION_ICON; + } else if (access.isInterface()) { + return INTERFACE_ICON; + } else if (access.isEnum()) { + return ENUM_ICON; + } else if (entryIndex.getDefinition(entry).isRecord()) { + return RECORD_ICON; + } + } + + return CLASS_ICON; + } + + public static Icon getMethodIcon(MethodEntry entry) { + if (entry.isConstructor()) { + return CONSTRUCTOR_ICON; + } + + return METHOD_ICON; + } + + public static TreePath getPathToRoot(TreeNode node) { + List nodes = Lists.newArrayList(); + TreeNode n = node; + + do { + nodes.add(n); + n = n.getParent(); + } while (n != null); + + Collections.reverse(nodes); + return new TreePath(nodes.toArray()); + } + + public static MouseListener onMouseClick(Consumer op) { + return new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + op.accept(e); + } + }; + } + + public static MouseListener onMousePress(Consumer op) { + return new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + op.accept(e); + } + }; + } + + public static WindowListener onWindowClose(Consumer op) { + return new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + op.accept(e); + } + }; + } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java index b1286998..f1a8a7a9 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/History.java @@ -1,9 +1,9 @@ package cuchaz.enigma.gui.util; -import com.google.common.collect.Queues; - import java.util.Deque; +import com.google.common.collect.Queues; + public class History { private final Deque previous = Queues.newArrayDeque(); private final Deque next = Queues.newArrayDeque(); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java index 9f53a44f..818e112b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageChangeListener.java @@ -1,11 +1,9 @@ package cuchaz.enigma.gui.util; public interface LanguageChangeListener { - void retranslateUi(); default boolean isValid() { return true; } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java index d3e63763..30a91809 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/LanguageUtil.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; public final class LanguageUtil { - private static final List listeners = new ArrayList<>(); public LanguageUtil() { @@ -21,5 +20,4 @@ public final class LanguageUtil { public static void dispatchLanguageChange() { listeners.forEach(LanguageChangeListener::retranslateUi); } - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java index d045c6d5..243f26fc 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleChangeListener.java @@ -2,7 +2,5 @@ package cuchaz.enigma.gui.util; @FunctionalInterface public interface ScaleChangeListener { - void onScaleChanged(float scale, float oldScale); - } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java index 28e37693..bc587fa7 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/ScaleUtil.java @@ -21,7 +21,6 @@ import de.sciss.syntaxpane.DefaultSyntaxKit; import cuchaz.enigma.gui.config.UiConfig; public class ScaleUtil { - private static List listeners = new ArrayList<>(); public static void setScaleFactor(float scaleFactor) { @@ -110,15 +109,19 @@ public class ScaleUtil { private static BasicTweaker createTweakerForCurrentLook(float dpiScaling) { String testString = UIManager.getLookAndFeel().getName().toLowerCase(); + if (testString.contains("windows")) { return new WindowsTweaker(dpiScaling, testString.contains("classic")); } + if (testString.contains("metal")) { return new MetalTweaker(dpiScaling); } + if (testString.contains("nimbus")) { return new NimbusTweaker(dpiScaling); } + return new BasicTweaker(dpiScaling); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java index 8915264b..9d967b8c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/SingleTreeSelectionModel.java @@ -4,8 +4,7 @@ import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreeSelectionModel; public class SingleTreeSelectionModel extends DefaultTreeSelectionModel { - - public SingleTreeSelectionModel() { - this.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - } + public SingleTreeSelectionModel() { + this.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + } } diff --git a/enigma/build.gradle b/enigma/build.gradle index b4a40629..13fb6b50 100644 --- a/enigma/build.gradle +++ b/enigma/build.gradle @@ -1,19 +1,19 @@ configurations { - proGuard + proGuard } dependencies { - implementation 'org.ow2.asm:asm:9.3' - implementation 'org.ow2.asm:asm-commons:9.3' - implementation 'org.ow2.asm:asm-tree:9.3' - implementation 'org.ow2.asm:asm-util:9.3' + implementation 'org.ow2.asm:asm:9.3' + implementation 'org.ow2.asm:asm-commons:9.3' + implementation 'org.ow2.asm:asm-tree:9.3' + implementation 'org.ow2.asm:asm-util:9.3' - implementation 'net.fabricmc:procyon-fabric-compilertools:0.5.35.13' - implementation 'net.fabricmc:cfr:0.1.0' + implementation 'net.fabricmc:procyon-fabric-compilertools:0.5.35.13' + implementation 'net.fabricmc:cfr:0.1.0' - proGuard 'com.guardsquare:proguard-base:7.2.0-beta2' + proGuard 'com.guardsquare:proguard-base:7.2.0-beta2' - testImplementation 'com.google.jimfs:jimfs:1.2' + testImplementation 'com.google.jimfs:jimfs:1.2' } // Generate "version.txt" file @@ -21,11 +21,11 @@ dependencies { ext.genOutputDir = file("$buildDir/generated-resources") task generateVersionFile { - ext.outputFile = file("$genOutputDir/version.txt") - outputs.file(outputFile) - doLast { - outputFile.text = "${project.version}" - } + ext.outputFile = file("$genOutputDir/version.txt") + outputs.file(outputFile) + doLast { + outputFile.text = "${project.version}" + } } sourceSets.main.output.dir genOutputDir, builtBy: generateVersionFile @@ -36,29 +36,29 @@ def libraryJarsArg = "/jmods" // If your test fails for class file version problem with proguard, run gradle with -Dorg.gradle.java.home="" flag file('src/test/java/cuchaz/enigma/inputs').listFiles().each { theFile -> - if (theFile.directory) { - task("${theFile.name}TestJar", type: Jar) { - from(sourceSets.test.output) { - include "cuchaz/enigma/inputs/$theFile.name/**/*.class" - include 'cuchaz/enigma/inputs/Keep.class' - } - - archiveFileName = theFile.name + '.jar' - destinationDirectory = file('build/test-inputs') - } - - task("${theFile.name}TestObf", type: JavaExec, - dependsOn: "${theFile.name}TestJar") { - mainClass = 'proguard.ProGuard' - classpath configurations.proGuard - - args '@src/test/resources/proguard-test.conf', '-injars', file('build/test-inputs/' + - "${theFile.name}.jar"), '-libraryjars', libraryJarsArg, - '-outjars', file('build/test-obf/' + "${theFile.name}.jar") - } - - test.dependsOn "${theFile.name}TestObf" - } + if (theFile.directory) { + task("${theFile.name}TestJar", type: Jar) { + from(sourceSets.test.output) { + include "cuchaz/enigma/inputs/$theFile.name/**/*.class" + include 'cuchaz/enigma/inputs/Keep.class' + } + + archiveFileName = theFile.name + '.jar' + destinationDirectory = file('build/test-inputs') + } + + task("${theFile.name}TestObf", type: JavaExec, + dependsOn: "${theFile.name}TestJar") { + mainClass = 'proguard.ProGuard' + classpath configurations.proGuard + + args '@src/test/resources/proguard-test.conf', '-injars', file('build/test-inputs/' + + "${theFile.name}.jar"), '-libraryjars', libraryJarsArg, + '-outjars', file('build/test-obf/' + "${theFile.name}.jar") + } + + test.dependsOn "${theFile.name}TestObf" + } } test.dependsOn 'translationTestObf' diff --git a/enigma/src/main/java/cuchaz/enigma/Enigma.java b/enigma/src/main/java/cuchaz/enigma/Enigma.java index a37f074b..696a848d 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 daf27274..f95bf1e3 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 4f50f2ff..15d5e980 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 df3b7bba..bbdc6846 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 82ca6692..cc7d121f 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 013c52f5..45dac2c1 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 0fc44ca6..8ef28d9f 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 788c5347..24da23c5 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 01424120..c76dca7b 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 281b05ff..9c542814 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 c93ac53f..cc511f39 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 ec8f3237..44a768ea 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 0c2dfd77..3043577b 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 a624b7c5..2ca1dfd3 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 4633ace8..83275da6 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 455456f0..2afeed9e 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 81171038..8dc7fe68 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 68038615..fc58c6d0 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 5b19d189..4dcb834a 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 ce23cb6d..8e0afd59 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 aea76180..b3ba8965 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 cfc80b4a..e2e5084f 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 a4b1aac9..26093c3c 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 bb992b73..0e4cdcfa 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 f9cb23ce..e697182f 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 efea83d4..97fec47b 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 1ab2abdf..1c60db96 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 6c26282e..60864ba5 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 f17e7c98..8726fb56 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 64de5f37..b400a66c 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 b6797c21..332a9674 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 358828f0..e2cb6b12 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 5417531c..3ed6d338 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 4c357db1..4c40868e 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 341cfce4..891fe9d1 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 cfd8fbee..dc399e53 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 2b750eac..51b21a67 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 cb843ad4..d105e4c6 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 0b2ca9ae..66c8490b 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 28fc199c..d026f159 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 a82df1b0..932c1235 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 06fd22bb..f7de0f88 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 6cab22c7..49350f6b 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 326197db..2a1643e9 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 20f847ae..ce6b23f8 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 229d18a8..f18be672 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 47f5eb86..eaba6df2 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 6e4a665a..6eec0f36 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 e9472fa9..224093f8 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 865464cf..6856540f 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 c614b0a8..900a0c8b 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 36236a8d..604bf499 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 cb9cbc2e..fd078a2d 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 b3f3d0c7..6d9d304a 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 3e7bf6d4..fba7da3b 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 dccb5858..a1e3e55c 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 12d7ec4e..53743143 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 61fea4ea..22be2e02 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 5f371a58..9475d743 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 938a7362..b31dcc43 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 638498f7..a87fc98c 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 643ea7a6..0e3244db 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 6ecce7c5..fe458055 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 971252e2..e9d928e9 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 b5f80063..f12c3875 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 f6c68e98..17547706 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 7d729abb..5c14234c 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 f32d918c..179eeceb 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 97d2969b..6461d20e 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 4364b408..bcb54552 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 2f3fcf2b..f1586ee9 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 cd7b2aa2..55312364 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 fb44ef9a..70b43ac3 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 14fd1684..93cc64f2 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 2fae61a6..dade7c6c 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 bc3d0725..3ba112ff 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 5b5b70e8..a3f878b8 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 f6eeb159..174431f6 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 8fc8e82a..12dca73e 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 dad505f7..56450c77 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 dc36865a..6f87895b 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 1e5beb1e..c48aba5b 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 b8c087b9..defd2511 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 34d95fa5..bc7d5a25 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 8accfc7c..3edc06c8 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 32bb72f4..204351ec 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 cf0376f3..679b168f 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 d3ddaab6..234834b7 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 dec75ff4..92d5cfcd 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 51e5d86d..833ea294 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 5ab16c85..33a38fee 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 37830535..99660141 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 7061bfa3..af6fbc95 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 bb26235b..0cf4acd2 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 2ecb30be..0725ebb8 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 66c2f9ec..ed581972 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 28364f73..7d4755d0 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 f57dd900..62337847 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 b5ec8552..4cd79b94 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 e1a32533..b398d412 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 e916bf35..72a8fd48 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 0268834d..ed5dba59 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 9dd0c3aa..39e68259 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 582076c1..f9a10600 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 e3aeb205..06e8d762 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 00168ba7..8dc56599 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 250851ce..765c1f2a 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 2c037484..3b756c66 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 5d39e3d2..21c78cfd 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 065e5c3e..5f42373c 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 33247fa6..dc8055b7 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 2eab55fd..19975721 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 441949c6..f118e64a 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 062c8775..3be80481 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 7c8f6cc6..5f466bba 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 9d04b976..6f5d7d6e 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 2f01375a..4fdfdcba 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 68a8dbb2..5c273ad3 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 61dbe93a..1f8b7558 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 5d7a7893..cd00ef77 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 034fb416..fc550d4d 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 483e4e40..ce54feae 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 aa29ff60..df65b3c8 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 92758475..4621efef 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 f3c66dfb..e08c8673 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 1f785e10..972d1807 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 dc3246de..61bc41da 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 c4005688..959d2d8f 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 255fa5fb..b943cc84 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 eb26ea99..254b3318 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 0992d342..2902373d 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 0a990bd5..3ddaf81f 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 e8480a26..24204f89 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 13c7cd48..0854699d 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 998c9442..571488cc 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 33b9797d..a8278fc7 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 6a1b82f0..fd53522d 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 ab5a422f..cb2faf06 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 b0adb2c1..be5f0b38 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 956f32ca..9615ca8e 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 0efb6a99..492d72e7 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 db940118..c1592a43 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 c151de4e..ac36a483 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 1cf1a832..d22188b2 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 30ef706e..c6a4ab73 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 ab9c2d17..6fc3f0a0 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 267bc11a..ff5ffa39 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 7d34b020..6732a59b 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 26e5b27a..95512026 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 b493c041..eaa93609 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 bf02ceff..10752aca 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 dcaabd58..354418ae 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 864154cc..2b4ed371 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 ad4e9368..081c9410 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 6bcdbde6..b7e67f2b 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 9ad58679..afcbf625 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 5067d7e3..8a7a9ed9 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 871b59d7..123c9b64 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 765ee084..39e8c410 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 0ecb9fb3..416e8a05 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(); } - } diff --git a/enigma/src/test/java/cuchaz/enigma/ConfigTest.java b/enigma/src/test/java/cuchaz/enigma/ConfigTest.java index a44f0375..95689f82 100644 --- a/enigma/src/test/java/cuchaz/enigma/ConfigTest.java +++ b/enigma/src/test/java/cuchaz/enigma/ConfigTest.java @@ -1,13 +1,12 @@ package cuchaz.enigma; +import static org.junit.Assert.assertEquals; + import org.junit.Test; import cuchaz.enigma.config.ConfigContainer; -import static org.junit.Assert.assertEquals; - public class ConfigTest { - @Test public void serialize() { ConfigContainer cc = new ConfigContainer(); @@ -17,15 +16,7 @@ public class ConfigTest { cc.data().section("a").section("b").section("c").setInt("c", 5); cc.data().section("a").section("b").section("c").setDouble("d", 3.5); cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456); - assertEquals("a=a\n" + - "\n" + - "[a][b][c]\n" + - "a=abcd\n" + - "b=true\n" + - "c=5\n" + - "d=3.5\n" + - "e=#123456\n", - cc.serialize()); + assertEquals("a=a\n" + "\n" + "[a][b][c]\n" + "a=abcd\n" + "b=true\n" + "c=5\n" + "d=3.5\n" + "e=#123456\n", cc.serialize()); } @Test @@ -37,14 +28,7 @@ public class ConfigTest { cc.data().section("a").section("b").section("c").setInt("c", 5); cc.data().section("a").section("b").section("c").setDouble("d", 3.5); cc.data().section("a").section("b").section("c").setRgbColor("e", 0x123456); - assertEquals(ConfigContainer.parse("a=a\n" + - "\n" + - "[a][b][c]\n" + - "a=abcd\n" + - "b=true\n" + - "c=5\n" + - "d=3.5\n" + - "e=#123456\n").data(), cc.data()); + assertEquals(ConfigContainer.parse("a=a\n" + "\n" + "[a][b][c]\n" + "a=abcd\n" + "b=true\n" + "c=5\n" + "d=3.5\n" + "e=#123456\n").data(), cc.data()); } @Test @@ -52,12 +36,10 @@ public class ConfigTest { ConfigContainer cc = new ConfigContainer(); String thing = "\\[],\\,./'\"`~!@#$%^&*()_+-=|}{\n\\\\\r\b\u0000\uffff\u1234"; cc.data().section(thing).setString(thing, thing); - cc.data().section(thing).setArray("arr", new String[] { thing, thing, thing, thing }); + cc.data().section(thing).setArray("arr", new String[]{thing, thing, thing, thing}); assertEquals( - "[\\\\[\\],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234]\n" + - "\\\\\\[],\\\\,./'\"`~!@#$%^&*()_+-\\=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234=\\\\[],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n" + - "arr=\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n", + "[\\\\[\\],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234]\n" + "\\\\\\[],\\\\,./'\"`~!@#$%^&*()_+-\\=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234=\\\\[],\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n" + "arr=\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234,\\\\\\\\[]\\\\,\\\\\\\\\\\\,./'\"`~!@#$%^&*()_+-=|}{\\n\\\\\\\\\\\\\\\\\\u000d\\u0008\\u0000\\uffff\\u1234\n", cc.serialize()); ConfigContainer cc1 = ConfigContainer.parse(cc.serialize()); @@ -77,10 +59,8 @@ public class ConfigTest { assertEquals("", ConfigContainer.parse("[").serialize()); assertEquals("[a]\na=b\nc=d\n", ConfigContainer.parse("[a]\na=b\n[\nc=d").serialize()); - // not technically syntax errors but never something that gets generated assertEquals("", ConfigContainer.parse("[a]").serialize()); assertEquals("", ConfigContainer.parse("[a]\n[b]").serialize()); } - } diff --git a/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java b/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java index 12515358..1678f59e 100644 --- a/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java +++ b/enigma/src/test/java/cuchaz/enigma/PackageVisibilityIndexTest.java @@ -1,30 +1,31 @@ /******************************************************************************* - * 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; -import cuchaz.enigma.analysis.index.JarIndex; -import cuchaz.enigma.analysis.index.PackageVisibilityIndex; -import cuchaz.enigma.classprovider.JarClassProvider; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import org.junit.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; - import static cuchaz.enigma.TestEntryFactory.newClass; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Test; + +import cuchaz.enigma.analysis.index.JarIndex; +import cuchaz.enigma.analysis.index.PackageVisibilityIndex; +import cuchaz.enigma.classprovider.JarClassProvider; +import cuchaz.enigma.translation.representation.entry.ClassEntry; + public class PackageVisibilityIndexTest { public static final Path JAR = Paths.get("build/test-obf/packageAccess.jar"); private static final ClassEntry KEEP = newClass("cuchaz/enigma/inputs/Keep"); @@ -46,10 +47,6 @@ public class PackageVisibilityIndexTest { PackageVisibilityIndex visibilityIndex = jarIndex.getPackageVisibilityIndex(); assertThat(visibilityIndex.getPartition(BASE), containsInAnyOrder(BASE, SAME_PACKAGE_CHILD, SAME_PACKAGE_CHILD_INNER)); System.out.println(visibilityIndex.getPartitions()); - assertThat(visibilityIndex.getPartitions(), containsInAnyOrder( - containsInAnyOrder(BASE, SAME_PACKAGE_CHILD, SAME_PACKAGE_CHILD_INNER), - containsInAnyOrder(OTHER_PACKAGE_CHILD, OTHER_PACKAGE_CHILD_INNER), - contains(KEEP) - )); + assertThat(visibilityIndex.getPartitions(), containsInAnyOrder(containsInAnyOrder(BASE, SAME_PACKAGE_CHILD, SAME_PACKAGE_CHILD_INNER), containsInAnyOrder(OTHER_PACKAGE_CHILD, OTHER_PACKAGE_CHILD_INNER), contains(KEEP))); } } diff --git a/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java b/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java index 2584d581..b19aa77e 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestDeobfed.java +++ b/enigma/src/test/java/cuchaz/enigma/TestDeobfed.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; +import static cuchaz.enigma.TestEntryFactory.newClass; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; + import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -23,10 +27,6 @@ import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.Decompilers; import cuchaz.enigma.source.SourceSettings; -import static cuchaz.enigma.TestEntryFactory.newClass; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; - public class TestDeobfed { public static final Path OBF = Paths.get("build/test-obf/translation.jar"); public static final Path DEOBF = Paths.get("build/test-deobf/translation.jar"); @@ -45,31 +45,9 @@ public class TestDeobfed { @Test public void obfEntries() { - assertThat(deobfProject.getJarIndex().getEntryIndex().getClasses(), containsInAnyOrder( - newClass("cuchaz/enigma/inputs/Keep"), - newClass("a"), - newClass("b"), - newClass("c"), - newClass("d"), - newClass("d$1"), - newClass("e"), - newClass("f"), - newClass("g"), - newClass("g$a"), - newClass("g$a$a"), - newClass("g$b"), - newClass("g$b$a"), - newClass("h"), - newClass("h$a"), - newClass("h$a$a"), - newClass("h$b"), - newClass("h$b$a"), - newClass("h$b$a$a"), - newClass("h$b$a$b"), - newClass("i"), - newClass("i$a"), - newClass("i$b") - )); + assertThat(deobfProject.getJarIndex().getEntryIndex().getClasses(), + containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), newClass("a"), newClass("b"), newClass("c"), newClass("d"), newClass("d$1"), newClass("e"), newClass("f"), newClass("g"), newClass("g$a"), newClass("g$a$a"), newClass("g$b"), newClass("g$b$a"), newClass("h"), + newClass("h$a"), newClass("h$a$a"), newClass("h$b"), newClass("h$b$a"), newClass("h$b$a$a"), newClass("h$b$a$b"), newClass("i"), newClass("i$a"), newClass("i$b"))); } @Test diff --git a/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java index 38940ca1..587494e6 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java +++ b/enigma/src/test/java/cuchaz/enigma/TestDeobfuscator.java @@ -1,24 +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; +import java.io.IOException; +import java.nio.file.Paths; + +import org.junit.Test; + import cuchaz.enigma.classprovider.ClasspathClassProvider; import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.Decompilers; import cuchaz.enigma.source.SourceSettings; -import org.junit.Test; - -import java.io.IOException; -import java.nio.file.Paths; public class TestDeobfuscator { private EnigmaProject openProject() throws IOException { @@ -27,8 +28,7 @@ public class TestDeobfuscator { } @Test - public void loadJar() - throws Exception { + public void loadJar() throws Exception { openProject(); } diff --git a/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java b/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java index 9e1425a2..833e217f 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.java +++ b/enigma/src/test/java/cuchaz/enigma/TestEntryFactory.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; import cuchaz.enigma.analysis.EntryReference; -import cuchaz.enigma.translation.representation.*; +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; public class TestEntryFactory { - public static ClassEntry newClass(String name) { return new ClassEntry(name); } diff --git a/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java b/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java index 6b609941..46086e56 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java +++ b/enigma/src/test/java/cuchaz/enigma/TestInnerClasses.java @@ -1,16 +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; +import static cuchaz.enigma.TestEntryFactory.newClass; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Test; + import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.classprovider.CachingClassProvider; import cuchaz.enigma.classprovider.JarClassProvider; @@ -18,17 +27,8 @@ import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.Decompilers; import cuchaz.enigma.source.SourceSettings; import cuchaz.enigma.translation.representation.entry.ClassEntry; -import org.junit.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import static cuchaz.enigma.TestEntryFactory.newClass; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; public class TestInnerClasses { - private static final ClassEntry SimpleOuter = newClass("d"); private static final ClassEntry SimpleInner = newClass("d$a"); private static final ClassEntry ConstructorArgsOuter = newClass("c"); @@ -61,26 +61,19 @@ public class TestInnerClasses { @Test public void classTree() { - // root level assertThat(index.getEntryIndex().hasClass(ClassTreeRoot), is(true)); // level 1 - ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() - + "$" + ClassTreeLevel1.getSimpleName()); + ClassEntry fullClassEntry = new ClassEntry(ClassTreeRoot.getName() + "$" + ClassTreeLevel1.getSimpleName()); assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true)); // level 2 - fullClassEntry = new ClassEntry(ClassTreeRoot.getName() - + "$" + ClassTreeLevel1.getSimpleName() - + "$" + ClassTreeLevel2.getSimpleName()); + fullClassEntry = new ClassEntry(ClassTreeRoot.getName() + "$" + ClassTreeLevel1.getSimpleName() + "$" + ClassTreeLevel2.getSimpleName()); assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true)); // level 3 - fullClassEntry = new ClassEntry(ClassTreeRoot.getName() - + "$" + ClassTreeLevel1.getSimpleName() - + "$" + ClassTreeLevel2.getSimpleName() - + "$" + ClassTreeLevel3.getSimpleName()); + fullClassEntry = new ClassEntry(ClassTreeRoot.getName() + "$" + ClassTreeLevel1.getSimpleName() + "$" + ClassTreeLevel2.getSimpleName() + "$" + ClassTreeLevel3.getSimpleName()); assertThat(index.getEntryIndex().hasClass(fullClassEntry), is(true)); } diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java index 0790193d..05565b61 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java +++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexConstructorReferences.java @@ -1,16 +1,30 @@ /******************************************************************************* - * 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; +import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; +import static cuchaz.enigma.TestEntryFactory.newClass; +import static cuchaz.enigma.TestEntryFactory.newMethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; + +import org.junit.Test; + import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.classprovider.CachingClassProvider; @@ -18,18 +32,8 @@ import cuchaz.enigma.classprovider.JarClassProvider; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.MethodDefEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; -import org.junit.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; - -import static cuchaz.enigma.TestEntryFactory.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; public class TestJarIndexConstructorReferences { - public static final Path JAR = Paths.get("build/test-obf/constructors.jar"); private JarIndex index; @@ -47,8 +51,7 @@ public class TestJarIndexConstructorReferences { @Test public void obfEntries() { - assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, - subClass, subsubClass, defaultClass, callerClass)); + assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClass, subsubClass, defaultClass, callerClass)); } @Test @@ -56,50 +59,36 @@ public class TestJarIndexConstructorReferences { public void baseDefault() { MethodEntry source = newMethod(baseClass, "", "()V"); Collection> references = index.getReferenceIndex().getReferencesToMethod(source); - assertThat(references, containsInAnyOrder( - newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), - newBehaviorReferenceByMethod(source, subClass.getName(), "", "()V"), - newBehaviorReferenceByMethod(source, subClass.getName(), "", "(III)V") - )); + assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "a", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "", "(III)V"))); } @Test @SuppressWarnings("unchecked") public void baseInt() { MethodEntry source = newMethod(baseClass, "", "(I)V"); - assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( - newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V") - )); + assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "b", "()V"))); } @Test @SuppressWarnings("unchecked") public void subDefault() { MethodEntry source = newMethod(subClass, "", "()V"); - assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( - newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"), - newBehaviorReferenceByMethod(source, subClass.getName(), "", "(I)V") - )); + assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "c", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "", "(I)V"))); } @Test @SuppressWarnings("unchecked") public void subInt() { MethodEntry source = newMethod(subClass, "", "(I)V"); - assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( - newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"), - newBehaviorReferenceByMethod(source, subClass.getName(), "", "(II)V"), - newBehaviorReferenceByMethod(source, subsubClass.getName(), "", "(I)V") - )); + assertThat(index.getReferenceIndex().getReferencesToMethod(source), + containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "d", "()V"), newBehaviorReferenceByMethod(source, subClass.getName(), "", "(II)V"), newBehaviorReferenceByMethod(source, subsubClass.getName(), "", "(I)V"))); } @Test @SuppressWarnings("unchecked") public void subIntInt() { MethodEntry source = newMethod(subClass, "", "(II)V"); - assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( - newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V") - )); + assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "e", "()V"))); } @Test @@ -112,17 +101,13 @@ public class TestJarIndexConstructorReferences { @SuppressWarnings("unchecked") public void subsubInt() { MethodEntry source = newMethod(subsubClass, "", "(I)V"); - assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( - newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V") - )); + assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "f", "()V"))); } @Test @SuppressWarnings("unchecked") public void defaultConstructable() { MethodEntry source = newMethod(defaultClass, "", "()V"); - assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder( - newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V") - )); + assertThat(index.getReferenceIndex().getReferencesToMethod(source), containsInAnyOrder(newBehaviorReferenceByMethod(source, callerClass.getName(), "g", "()V"))); } } diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java index a9045f9c..3f6f1511 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java +++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexInheritanceTree.java @@ -1,16 +1,34 @@ /******************************************************************************* - * 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; +import static cuchaz.enigma.TestEntryFactory.newClass; +import static cuchaz.enigma.TestEntryFactory.newField; +import static cuchaz.enigma.TestEntryFactory.newMethod; +import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByMethod; +import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; + +import org.junit.Test; +import org.objectweb.asm.Opcodes; + import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.analysis.index.InheritanceIndex; @@ -24,19 +42,8 @@ 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 org.junit.Test; -import org.objectweb.asm.Opcodes; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; - -import static cuchaz.enigma.TestEntryFactory.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; public class TestJarIndexInheritanceTree { - public static final Path JAR = Paths.get("build/test-obf/inheritanceTree.jar"); private JarIndex index; @@ -55,21 +62,17 @@ public class TestJarIndexInheritanceTree { @Test public void obfEntries() { - assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder( - newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB - )); + assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), baseClass, subClassA, subClassAA, subClassB)); } @Test public void translationIndex() { - InheritanceIndex index = this.index.getInheritanceIndex(); // base class assertThat(index.getParents(baseClass), is(empty())); assertThat(index.getAncestors(baseClass), is(empty())); - assertThat(index.getChildren(baseClass), containsInAnyOrder(subClassA, subClassB - )); + assertThat(index.getChildren(baseClass), containsInAnyOrder(subClassA, subClassB)); // subclass a assertThat(index.getParents(subClassA), contains(baseClass)); @@ -95,41 +98,22 @@ public class TestJarIndexInheritanceTree { @Test public void relatedMethodImplementations() { - Collection entries; EntryResolver resolver = new IndexEntryResolver(index); // getName() entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()Ljava/lang/String;")); - assertThat(entries, containsInAnyOrder( - newMethod(baseClass, "a", "()Ljava/lang/String;"), - newMethod(subClassAA, "a", "()Ljava/lang/String;") - )); + assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()Ljava/lang/String;"), newMethod(subClassAA, "a", "()Ljava/lang/String;"))); entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()Ljava/lang/String;")); - assertThat(entries, containsInAnyOrder( - newMethod(baseClass, "a", "()Ljava/lang/String;"), - newMethod(subClassAA, "a", "()Ljava/lang/String;") - )); + assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()Ljava/lang/String;"), newMethod(subClassAA, "a", "()Ljava/lang/String;"))); // doBaseThings() entries = resolver.resolveEquivalentMethods(newMethod(baseClass, "a", "()V")); - assertThat(entries, containsInAnyOrder( - newMethod(baseClass, "a", "()V"), - newMethod(subClassAA, "a", "()V"), - newMethod(subClassB, "a", "()V") - )); + assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()V"), newMethod(subClassAA, "a", "()V"), newMethod(subClassB, "a", "()V"))); entries = resolver.resolveEquivalentMethods(newMethod(subClassAA, "a", "()V")); - assertThat(entries, containsInAnyOrder( - newMethod(baseClass, "a", "()V"), - newMethod(subClassAA, "a", "()V"), - newMethod(subClassB, "a", "()V") - )); + assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()V"), newMethod(subClassAA, "a", "()V"), newMethod(subClassB, "a", "()V"))); entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "a", "()V")); - assertThat(entries, containsInAnyOrder( - newMethod(baseClass, "a", "()V"), - newMethod(subClassAA, "a", "()V"), - newMethod(subClassB, "a", "()V") - )); + assertThat(entries, containsInAnyOrder(newMethod(baseClass, "a", "()V"), newMethod(subClassAA, "a", "()V"), newMethod(subClassB, "a", "()V"))); // doBThings entries = resolver.resolveEquivalentMethods(newMethod(subClassB, "b", "()V")); @@ -143,55 +127,38 @@ public class TestJarIndexInheritanceTree { // name references = index.getReferenceIndex().getReferencesToField(nameField); - assertThat(references, containsInAnyOrder( - newFieldReferenceByMethod(nameField, baseClass.getName(), "", "(Ljava/lang/String;)V"), - newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;") - )); + assertThat(references, containsInAnyOrder(newFieldReferenceByMethod(nameField, baseClass.getName(), "", "(Ljava/lang/String;)V"), newFieldReferenceByMethod(nameField, baseClass.getName(), "a", "()Ljava/lang/String;"))); // numThings references = index.getReferenceIndex().getReferencesToField(numThingsField); - assertThat(references, containsInAnyOrder( - newFieldReferenceByMethod(numThingsField, subClassB.getName(), "", "()V"), - newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V") - )); + assertThat(references, containsInAnyOrder(newFieldReferenceByMethod(numThingsField, subClassB.getName(), "", "()V"), newFieldReferenceByMethod(numThingsField, subClassB.getName(), "b", "()V"))); } @Test @SuppressWarnings("unchecked") public void behaviorReferences() { - MethodEntry source; Collection> references; // baseClass constructor source = newMethod(baseClass, "", "(Ljava/lang/String;)V"); references = index.getReferenceIndex().getReferencesToMethod(source); - assertThat(references, containsInAnyOrder( - newBehaviorReferenceByMethod(source, subClassA.getName(), "", "(Ljava/lang/String;)V"), - newBehaviorReferenceByMethod(source, subClassB.getName(), "", "()V") - )); + assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassA.getName(), "", "(Ljava/lang/String;)V"), newBehaviorReferenceByMethod(source, subClassB.getName(), "", "()V"))); // subClassA constructor source = newMethod(subClassA, "", "(Ljava/lang/String;)V"); references = index.getReferenceIndex().getReferencesToMethod(source); - assertThat(references, containsInAnyOrder( - newBehaviorReferenceByMethod(source, subClassAA.getName(), "", "()V") - )); + assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassAA.getName(), "", "()V"))); // baseClass.getName() source = newMethod(baseClass, "a", "()Ljava/lang/String;"); references = index.getReferenceIndex().getReferencesToMethod(source); - assertThat(references, containsInAnyOrder( - newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), - newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V") - )); + assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()Ljava/lang/String;"), newBehaviorReferenceByMethod(source, subClassB.getName(), "a", "()V"))); // subclassAA.getName() source = newMethod(subClassAA, "a", "()Ljava/lang/String;"); references = index.getReferenceIndex().getReferencesToMethod(source); - assertThat(references, containsInAnyOrder( - newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V") - )); + assertThat(references, containsInAnyOrder(newBehaviorReferenceByMethod(source, subClassAA.getName(), "a", "()V"))); } @Test @@ -225,6 +192,5 @@ public class TestJarIndexInheritanceTree { assertThat(entryIndex.hasMethod(newMethod(subClassA, "b", "()V")), is(false)); assertThat(entryIndex.hasMethod(newMethod(subClassAA, "b", "()V")), is(false)); assertThat(entryIndex.hasMethod(newMethod(subClassB, "b", "()V")), is(true)); - } } diff --git a/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java b/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java index 6e3755ca..dcbe95f3 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java +++ b/enigma/src/test/java/cuchaz/enigma/TestJarIndexLoneClass.java @@ -1,17 +1,41 @@ /******************************************************************************* - * 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; -import cuchaz.enigma.analysis.*; +import static cuchaz.enigma.TestEntryFactory.newClass; +import static cuchaz.enigma.TestEntryFactory.newField; +import static cuchaz.enigma.TestEntryFactory.newFieldReferenceByMethod; +import static cuchaz.enigma.TestEntryFactory.newMethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.List; + +import org.junit.Test; + +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.IndexTreeBuilder; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.analysis.index.InheritanceIndex; import cuchaz.enigma.analysis.index.JarIndex; @@ -23,19 +47,8 @@ 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 org.junit.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; -import java.util.List; - -import static cuchaz.enigma.TestEntryFactory.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; public class TestJarIndexLoneClass { - public static final Path JAR = Paths.get("build/test-obf/loneClass.jar"); private JarIndex index; @@ -47,10 +60,7 @@ public class TestJarIndexLoneClass { @Test public void obfEntries() { - assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder( - newClass("cuchaz/enigma/inputs/Keep"), - newClass("a") - )); + assertThat(index.getEntryIndex().getClasses(), containsInAnyOrder(newClass("cuchaz/enigma/inputs/Keep"), newClass("a"))); } @Test @@ -112,9 +122,7 @@ public class TestJarIndexLoneClass { @Test public void relatedMethodImplementations() { Collection entries = index.getEntryResolver().resolveEquivalentMethods(newMethod("a", "a", "()Ljava/lang/String;")); - assertThat(entries, containsInAnyOrder( - newMethod("a", "a", "()Ljava/lang/String;") - )); + assertThat(entries, containsInAnyOrder(newMethod("a", "a", "()Ljava/lang/String;"))); } @Test @@ -122,10 +130,7 @@ public class TestJarIndexLoneClass { public void fieldReferences() { FieldEntry source = newField("a", "a", "Ljava/lang/String;"); Collection> references = index.getReferenceIndex().getReferencesToField(source); - assertThat(references, containsInAnyOrder( - newFieldReferenceByMethod(source, "a", "", "(Ljava/lang/String;)V"), - newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;") - )); + assertThat(references, containsInAnyOrder(newFieldReferenceByMethod(source, "a", "", "(Ljava/lang/String;)V"), newFieldReferenceByMethod(source, "a", "a", "()Ljava/lang/String;"))); } @Test diff --git a/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java b/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java index a73880dd..918466b4 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java +++ b/enigma/src/test/java/cuchaz/enigma/TestMethodDescriptor.java @@ -1,25 +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; -import cuchaz.enigma.translation.representation.MethodDescriptor; -import cuchaz.enigma.translation.representation.TypeDescriptor; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import cuchaz.enigma.translation.representation.MethodDescriptor; +import cuchaz.enigma.translation.representation.TypeDescriptor; public class TestMethodDescriptor { - @Test public void easiest() { final MethodDescriptor sig = new MethodDescriptor("()V"); @@ -31,26 +34,19 @@ public class TestMethodDescriptor { public void primitives() { { final MethodDescriptor sig = new MethodDescriptor("(I)V"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("I") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); } + { final MethodDescriptor sig = new MethodDescriptor("(I)I"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("I") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("I"))); } + { final MethodDescriptor sig = new MethodDescriptor("(IBCJ)Z"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("I"), - new TypeDescriptor("B"), - new TypeDescriptor("C"), - new TypeDescriptor("J") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("B"), new TypeDescriptor("C"), new TypeDescriptor("J"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z"))); } } @@ -63,20 +59,16 @@ public class TestMethodDescriptor { assertThat(sig.getArgumentDescs().get(0), is(new TypeDescriptor("[LFoo;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); } + { final MethodDescriptor sig = new MethodDescriptor("(LFoo;)LBar;"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("LFoo;") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LFoo;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;"))); } + { final MethodDescriptor sig = new MethodDescriptor("(LFoo;LMoo;LZoo;)LBar;"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("LFoo;"), - new TypeDescriptor("LMoo;"), - new TypeDescriptor("LZoo;") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LFoo;"), new TypeDescriptor("LMoo;"), new TypeDescriptor("LZoo;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LBar;"))); } } @@ -85,25 +77,19 @@ public class TestMethodDescriptor { public void arrays() { { final MethodDescriptor sig = new MethodDescriptor("([I)V"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("[I") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[I"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); } + { final MethodDescriptor sig = new MethodDescriptor("([I)[J"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("[I") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[I"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[J"))); } + { final MethodDescriptor sig = new MethodDescriptor("([I[Z[F)[D"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("[I"), - new TypeDescriptor("[Z"), - new TypeDescriptor("[F") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[I"), new TypeDescriptor("[Z"), new TypeDescriptor("[F"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[D"))); } } @@ -112,20 +98,13 @@ public class TestMethodDescriptor { public void mixed() { { final MethodDescriptor sig = new MethodDescriptor("(I[JLFoo;)Z"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("I"), - new TypeDescriptor("[J"), - new TypeDescriptor("LFoo;") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("[J"), new TypeDescriptor("LFoo;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("Z"))); } + { final MethodDescriptor sig = new MethodDescriptor("(III)[LFoo;"); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("I"), - new TypeDescriptor("I"), - new TypeDescriptor("I") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("I"), new TypeDescriptor("I"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[LFoo;"))); } } @@ -138,42 +117,37 @@ public class TestMethodDescriptor { assertThat(sig.getArgumentDescs(), is(empty())); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); } + { final MethodDescriptor oldSig = new MethodDescriptor("(IJLFoo;)V"); final MethodDescriptor sig = oldSig.remap(s -> null); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("I"), - new TypeDescriptor("J"), - new TypeDescriptor("LFoo;") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("I"), new TypeDescriptor("J"), new TypeDescriptor("LFoo;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("V"))); } + { final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;"); final MethodDescriptor sig = oldSig.remap(s -> { if (s.equals("Foo")) { return "Bar"; } + return null; }); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("LBar;"), - new TypeDescriptor("LBar;") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LBar;"), new TypeDescriptor("LBar;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LMoo;"))); } + { final MethodDescriptor oldSig = new MethodDescriptor("(LFoo;LBar;)LMoo;"); final MethodDescriptor sig = oldSig.remap(s -> { if (s.equals("Moo")) { return "Cow"; } + return null; }); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("LFoo;"), - new TypeDescriptor("LBar;") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("LFoo;"), new TypeDescriptor("LBar;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("LCow;"))); } } @@ -188,18 +162,16 @@ public class TestMethodDescriptor { } else if (s.equals("Bar")) { return "Beer"; } + return null; }); - assertThat(sig.getArgumentDescs(), contains( - new TypeDescriptor("[LFood;") - )); + assertThat(sig.getArgumentDescs(), contains(new TypeDescriptor("[LFood;"))); assertThat(sig.getReturnDesc(), is(new TypeDescriptor("[[[LBeer;"))); } } @Test public void equals() { - // base assertThat(new MethodDescriptor("()V"), is(new MethodDescriptor("()V"))); diff --git a/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java b/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java index a5e8367b..8017ab61 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java +++ b/enigma/src/test/java/cuchaz/enigma/TestTokensConstructors.java @@ -1,31 +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; -import cuchaz.enigma.translation.representation.entry.MethodEntry; -import org.junit.Ignore; -import org.junit.Test; - -import java.nio.file.Paths; - import static cuchaz.enigma.TestEntryFactory.newBehaviorReferenceByMethod; import static cuchaz.enigma.TestEntryFactory.newMethod; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; -public class TestTokensConstructors extends TokenChecker { +import java.nio.file.Paths; + +import org.junit.Ignore; +import org.junit.Test; + +import cuchaz.enigma.translation.representation.entry.MethodEntry; - public TestTokensConstructors() - throws Exception { +public class TestTokensConstructors extends TokenChecker { + public TestTokensConstructors() throws Exception { super(Paths.get("build/test-obf/constructors.jar")); } @@ -57,17 +59,10 @@ public class TestTokensConstructors extends TokenChecker { @Ignore // TODO needs fixing, broke when compiling against J16 public void baseDefaultReferences() { MethodEntry source = newMethod("a", "", "()V"); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "a", "()V")), - containsInAnyOrder("a") - ); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "", "()V")), - is(empty()) // implicit call, not decompiled to token + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "a", "()V")), containsInAnyOrder("a")); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "", "()V")), is(empty()) // implicit call, not decompiled to token ); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "", "(III)V")), - is(empty()) // implicit call, not decompiled to token + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "", "(III)V")), is(empty()) // implicit call, not decompiled to token ); } @@ -75,71 +70,44 @@ public class TestTokensConstructors extends TokenChecker { @Ignore // TODO needs fixing, broke when compiling against J16 public void baseIntReferences() { MethodEntry source = newMethod("a", "", "(I)V"); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "b", "()V")), - containsInAnyOrder("a") - ); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "b", "()V")), containsInAnyOrder("a")); } @Test @Ignore // TODO needs fixing, broke when compiling against J16 public void subDefaultReferences() { MethodEntry source = newMethod("d", "", "()V"); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "c", "()V")), - containsInAnyOrder("d") - ); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "", "(I)V")), - containsInAnyOrder("this") - ); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "c", "()V")), containsInAnyOrder("d")); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "", "(I)V")), containsInAnyOrder("this")); } @Test @Ignore // TODO needs fixing, broke when compiling against J16 public void subIntReferences() { MethodEntry source = newMethod("d", "", "(I)V"); - assertThat(getReferenceTokens( - newBehaviorReferenceByMethod(source, "b", "d", "()V")), - containsInAnyOrder("d") - ); - assertThat(getReferenceTokens( - newBehaviorReferenceByMethod(source, "d", "", "(II)V")), - containsInAnyOrder("this") - ); - assertThat(getReferenceTokens( - newBehaviorReferenceByMethod(source, "e", "", "(I)V")), - containsInAnyOrder("super") - ); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "d", "()V")), containsInAnyOrder("d")); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "d", "", "(II)V")), containsInAnyOrder("this")); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "e", "", "(I)V")), containsInAnyOrder("super")); } @Test @Ignore // TODO needs fixing, broke when compiling against J16 public void subIntIntReferences() { MethodEntry source = newMethod("d", "", "(II)V"); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "e", "()V")), - containsInAnyOrder("d") - ); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "e", "()V")), containsInAnyOrder("d")); } @Test @Ignore // TODO needs fixing, broke when compiling against J16 public void subsubIntReferences() { MethodEntry source = newMethod("e", "", "(I)V"); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "f", "()V")), - containsInAnyOrder("e") - ); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "f", "()V")), containsInAnyOrder("e")); } @Test @Ignore // TODO needs fixing, broke when compiling against J16 public void defaultConstructableReferences() { MethodEntry source = newMethod("c", "", "()V"); - assertThat( - getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "g", "()V")), - containsInAnyOrder("c") - ); + assertThat(getReferenceTokens(newBehaviorReferenceByMethod(source, "b", "g", "()V")), containsInAnyOrder("c")); } } diff --git a/enigma/src/test/java/cuchaz/enigma/TestTranslator.java b/enigma/src/test/java/cuchaz/enigma/TestTranslator.java index a420afe1..93b70176 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestTranslator.java +++ b/enigma/src/test/java/cuchaz/enigma/TestTranslator.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; -import cuchaz.enigma.translation.representation.entry.Entry; +import static cuchaz.enigma.TestEntryFactory.newClass; +import static cuchaz.enigma.TestEntryFactory.newField; +import static cuchaz.enigma.TestEntryFactory.newMethod; + import org.junit.BeforeClass; import org.junit.Test; -import static cuchaz.enigma.TestEntryFactory.*; +import cuchaz.enigma.translation.representation.entry.Entry; public class TestTranslator { - @BeforeClass - public static void beforeClass() - throws Exception { + public static void beforeClass() throws Exception { //TODO FIx //deobfuscator = new Enigma(new JarFile("build/test-obf/translation.jar")); //try (InputStream in = TestTranslator.class.getResourceAsStream("/cuchaz/enigma/resources/translation.mappings")) { @@ -94,7 +95,6 @@ public class TestTranslator { @Test public void innerClasses() { - // classes assertMapping(newClass("g"), newClass("deobf/G_OuterClass")); assertMapping(newClass("g$a"), newClass("deobf/G_OuterClass$A_InnerClass")); @@ -120,7 +120,6 @@ public class TestTranslator { @Test public void testGenerics() { - // classes assertMapping(newClass("i"), newClass("deobf/I_Generics")); assertMapping(newClass("i$a"), newClass("deobf/I_Generics$A_Type")); diff --git a/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java b/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java index b9ebe559..280dadc9 100644 --- a/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java +++ b/enigma/src/test/java/cuchaz/enigma/TestTypeDescriptor.java @@ -1,26 +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; -import cuchaz.enigma.translation.representation.TypeDescriptor; -import org.junit.Test; - import static cuchaz.enigma.TestEntryFactory.newClass; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -public class TestTypeDescriptor { +import org.junit.Test; +import cuchaz.enigma.translation.representation.TypeDescriptor; + +public class TestTypeDescriptor { @Test public void isVoid() { assertThat(new TypeDescriptor("V").isVoid(), is(true)); @@ -161,6 +161,7 @@ public class TestTypeDescriptor { assertThat(TypeDescriptor.parseFirst("LFoo;LFoo;"), is(answer)); assertThat(TypeDescriptor.parseFirst("LFoo;[LFoo;"), is(answer)); } + { final String answer = "Ljava/lang/String;"; assertThat(TypeDescriptor.parseFirst("Ljava/lang/String;"), is(answer)); @@ -182,6 +183,7 @@ public class TestTypeDescriptor { assertThat(TypeDescriptor.parseFirst("[I[I"), is(answer)); assertThat(TypeDescriptor.parseFirst("[ILFoo;"), is(answer)); } + { final String answer = "[[I"; assertThat(TypeDescriptor.parseFirst("[[I"), is(answer)); @@ -190,6 +192,7 @@ public class TestTypeDescriptor { assertThat(TypeDescriptor.parseFirst("[[I[I"), is(answer)); assertThat(TypeDescriptor.parseFirst("[[ILFoo;"), is(answer)); } + { final String answer = "[LFoo;"; assertThat(TypeDescriptor.parseFirst("[LFoo;"), is(answer)); diff --git a/enigma/src/test/java/cuchaz/enigma/TokenChecker.java b/enigma/src/test/java/cuchaz/enigma/TokenChecker.java index fb3a8da5..5f510a42 100644 --- a/enigma/src/test/java/cuchaz/enigma/TokenChecker.java +++ b/enigma/src/test/java/cuchaz/enigma/TokenChecker.java @@ -1,28 +1,34 @@ /******************************************************************************* - * 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; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; + import com.google.common.collect.Lists; + import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.classprovider.CachingClassProvider; import cuchaz.enigma.classprovider.JarClassProvider; -import cuchaz.enigma.source.*; +import cuchaz.enigma.source.Decompiler; +import cuchaz.enigma.source.Decompilers; +import cuchaz.enigma.source.Source; +import cuchaz.enigma.source.SourceIndex; +import cuchaz.enigma.source.SourceSettings; +import cuchaz.enigma.source.Token; import cuchaz.enigma.translation.representation.entry.Entry; -import java.io.IOException; -import java.nio.file.Path; -import java.util.Collection; -import java.util.List; - public class TokenChecker { private final Decompiler decompiler; @@ -41,9 +47,11 @@ public class TokenChecker { // get the token value Token token = index.getDeclarationToken(entry); + if (token == null) { return null; } + return string.substring(token.start, token.end); } @@ -56,9 +64,11 @@ public class TokenChecker { // get the token values List values = Lists.newArrayList(); + for (Token token : index.getReferenceTokens((EntryReference, Entry>) reference)) { values.add(string.substring(token.start, token.end)); } + return values; } } diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/Keep.java b/enigma/src/test/java/cuchaz/enigma/inputs/Keep.java index 4dbe8e2f..46d5380a 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/Keep.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/Keep.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.inputs; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java index f07e1f8b..a82db77d 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/BaseClass.java @@ -1,19 +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.inputs.constructors; // a public class BaseClass { - // ()V public BaseClass() { System.out.println("Default constructor"); diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java index 71439fd1..e81df1bd 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/Caller.java @@ -1,19 +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.inputs.constructors; // b public class Caller { - // a()V public void callBaseDefault() { // a.()V diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java index c3d41705..55aa767c 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/DefaultConstructable.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.inputs.constructors; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java index bc56b3b2..1a90eba7 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubClass.java @@ -1,19 +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.inputs.constructors; // d extends a public class SubClass extends BaseClass { - // ()V public SubClass() { // a.()V diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java index 87b69d32..ec5ac444 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/constructors/SubSubClass.java @@ -1,19 +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.inputs.constructors; // e extends d public class SubSubClass extends SubClass { - // (I)V public SubSubClass(int i) { // c.(I)V diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java index b9c4929c..87e849fd 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/BaseClass.java @@ -1,19 +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.inputs.inheritanceTree; // a public abstract class BaseClass { - // a private String name; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java index 50e963c0..ba8c1b78 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassA.java @@ -1,19 +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.inputs.inheritanceTree; // b extends a public abstract class SubclassA extends BaseClass { - // (Ljava/lang/String;)V protected SubclassA(String name) { // call to a.(Ljava/lang/String)V diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java index d0dd664d..cfc696a8 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubclassB.java @@ -1,19 +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.inputs.inheritanceTree; // c extends a public class SubclassB extends BaseClass { - // a private int numThings; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java index c5845702..d3bb62ee 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/inheritanceTree/SubsubclassAA.java @@ -1,19 +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.inputs.inheritanceTree; // d extends b public class SubsubclassAA extends SubclassA { - protected SubsubclassAA() { // call to b.(Ljava/lang/String;)V super("AA"); diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java index f652d875..515205a3 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/A_Anonymous.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.inputs.innerClasses; public class A_Anonymous { - public void foo() { Runnable runnable = new Runnable() { @Override diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java index d1b7601f..6ec27ac6 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/B_AnonymousWithScopeArgs.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.inputs.innerClasses; public class B_AnonymousWithScopeArgs { - public static void foo(final D_Simple arg) { System.out.println(new Object() { @Override diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java index 94061faa..223c424c 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/C_ConstructorArgs.java @@ -1,19 +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.inputs.innerClasses; @SuppressWarnings("unused") public class C_ConstructorArgs { - Inner i; public void foo() { @@ -21,10 +20,9 @@ public class C_ConstructorArgs { } class Inner { - private int a; - public Inner(int a) { + Inner(int a) { this.a = a; } } diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java index 71b3a6d8..f401d5f1 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/D_Simple.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.inputs.innerClasses; public class D_Simple { - class Inner { // nothing to do } diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java index 976ec426..0056bc60 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/E_AnonymousWithOuterAccess.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.inputs.innerClasses; public class E_AnonymousWithOuterAccess { - // reproduction of error case documented at: // https://bitbucket.org/cuchaz/enigma/issue/61/stackoverflowerror-when-deobfuscating diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java index b1de3c9a..b2e9e2d1 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/innerClasses/F_ClassTree.java @@ -1,28 +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.inputs.innerClasses; public class F_ClassTree { - public class Level1 { - public int f1; public class Level2 { - public int f2; public class Level3 { - public int f3; } } diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java index ddc4e319..e50d37fd 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/loneClass/LoneClass.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.inputs.loneClass; public class LoneClass { - private String name; public LoneClass(String name) { diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java index cf0f6574..aa8de19a 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/SamePackageChild.java @@ -1,7 +1,6 @@ package cuchaz.enigma.inputs.packageAccess; public class SamePackageChild extends Base { - class Inner { final int value; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java index 19fb19c2..5bcb763c 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/packageAccess/sub/OtherPackageChild.java @@ -3,7 +3,6 @@ package cuchaz.enigma.inputs.packageAccess.sub; import cuchaz.enigma.inputs.packageAccess.Base; public class OtherPackageChild extends Base { - class Inner { final int value; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java index 26f3718c..b411e0af 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/A_Basic.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.inputs.translation; public class A_Basic { - public int one; public float two; public String three; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java index fd7f6e7e..d5e9a255 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/B_BaseClass.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.inputs.translation; public class B_BaseClass { - public int f1; public char f2; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java index 9d74e443..fd9e217c 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/C_SubClass.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.inputs.translation; public class C_SubClass extends B_BaseClass { - public char f2; // shadows B_BaseClass.f2 public int f3; public int f4; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java index 99c83bbf..56cccc7d 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/D_AnonymousTesting.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.inputs.translation; @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.List; public class D_AnonymousTesting { - public List getObjs() { List objs = new ArrayList(); objs.add(new Object() { diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java index 0b8cf2a5..f44bdbff 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/E_Bridges.java @@ -1,20 +1,19 @@ /******************************************************************************* - * 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.inputs.translation; import java.util.Iterator; public class E_Bridges implements Iterator { - @Override public boolean hasNext() { return false; diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java index 8a92792a..ac1d7b5f 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/F_ObjectMethods.java @@ -1,21 +1,19 @@ /******************************************************************************* - * 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.inputs.translation; @SuppressWarnings("FinalizeCalledExplicitly") public class F_ObjectMethods { - - public void callEmAll() - throws Throwable { + public void callEmAll() throws Throwable { clone(); equals(this); finalize(); diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java index a1e6a85c..4d992358 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/G_OuterClass.java @@ -1,30 +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.inputs.translation; public class G_OuterClass { - public class A_InnerClass { - public int f1; public String f2; - public void m1() {} + public void m1() { + } public class A_InnerInnerClass { - public int f3; - public void m2() {} + public void m2() { + } } } diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java index 013c55ae..d996dc89 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/H_NamelessClass.java @@ -1,30 +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.inputs.translation; public class H_NamelessClass { - public class A_InnerClass { - public int f1; public String f2; - public void m1() {} + public void m1() { + } public class A_InnerInnerClass { - public int f3; - public void m2() {} + public void m2() { + } } } @@ -32,9 +31,11 @@ public class H_NamelessClass { public class A_NamedInnerClass { public int f4; - public class A_AnotherInnerClass {} + public class A_AnotherInnerClass { + } - public class B_YetAnotherInnerClass {} + public class B_YetAnotherInnerClass { + } } } } diff --git a/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java b/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java index fd2ebdd5..9a9048c7 100644 --- a/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.java +++ b/enigma/src/test/java/cuchaz/enigma/inputs/translation/I_Generics.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.inputs.translation; @@ -15,7 +15,6 @@ import java.util.List; import java.util.Map; public class I_Generics { - public List f1; public List f2; public Map f3; diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java index e8319430..15ec44e4 100644 --- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java +++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestComments.java @@ -5,6 +5,8 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; +import org.junit.Test; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; import cuchaz.enigma.translation.mapping.serde.MappingParseException; @@ -12,28 +14,24 @@ import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import org.junit.Test; public class TestComments { - private static Path DIRECTORY; - - static { - try { - DIRECTORY = Paths.get(TestTinyV2InnerClasses.class.getResource("/comments/").toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } + private static Path DIRECTORY; - @Test - public void testParseAndWrite() throws IOException, MappingParseException { - ProgressListener progressListener = ProgressListener.none(); - MappingSaveParameters params = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); - EntryTree mappings = EnigmaMappingsReader.DIRECTORY.read( - DIRECTORY, progressListener, params); + static { + try { + DIRECTORY = Paths.get(TestTinyV2InnerClasses.class.getResource("/comments/").toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } - new TinyV2Writer("intermediary", "named") - .write(mappings, DIRECTORY.resolve("convertedtiny.tiny"), progressListener, params); - } + @Test + public void testParseAndWrite() throws IOException, MappingParseException { + ProgressListener progressListener = ProgressListener.none(); + MappingSaveParameters params = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); + EntryTree mappings = EnigmaMappingsReader.DIRECTORY.read(DIRECTORY, progressListener, params); -} \ No newline at end of file + new TinyV2Writer("intermediary", "named").write(mappings, DIRECTORY.resolve("convertedtiny.tiny"), progressListener, params); + } +} diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java index 510dd3cd..681fd3f2 100644 --- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java +++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestReadWriteCycle.java @@ -1,5 +1,11 @@ package cuchaz.enigma.translation.mapping; +import java.io.File; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; import cuchaz.enigma.translation.mapping.serde.MappingFormat; @@ -12,46 +18,25 @@ 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.Pair; -import org.junit.Assert; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; /** * Tests that a MappingFormat can write out a fixed set of mappings and read them back without losing any information. * Javadoc skipped for Tiny (v1) as it doesn't support them. */ public class TestReadWriteCycle { - private final MappingSaveParameters parameters = new MappingSaveParameters(MappingFileNameFormat.BY_DEOBF); - private final Pair testClazz = new Pair<>( - new ClassEntry("a/b/c"), - new EntryMapping("alpha/beta/charlie", "this is a test class") - ); + private final Pair testClazz = new Pair<>(new ClassEntry("a/b/c"), new EntryMapping("alpha/beta/charlie", "this is a test class")); - private final Pair testField1 = new Pair<>( - FieldEntry.parse("a/b/c", "field1", "I"), - new EntryMapping("mapped1", "this is field 1") - ); + private final Pair testField1 = new Pair<>(FieldEntry.parse("a/b/c", "field1", "I"), new EntryMapping("mapped1", "this is field 1")); - private final Pair testField2 = new Pair<>( - FieldEntry.parse("a/b/c", "field2", "I"), - new EntryMapping("mapped2", "this is field 2") - ); + private final Pair testField2 = new Pair<>(FieldEntry.parse("a/b/c", "field2", "I"), new EntryMapping("mapped2", "this is field 2")); - private final Pair testMethod1 = new Pair<>( - MethodEntry.parse("a/b/c", "method1", "()V"), - new EntryMapping("mapped3", "this is method1") - ); + private final Pair testMethod1 = new Pair<>(MethodEntry.parse("a/b/c", "method1", "()V"), new EntryMapping("mapped3", "this is method1")); - private final Pair testMethod2 = new Pair<>( - MethodEntry.parse("a/b/c", "method2", "()V"), - new EntryMapping("mapped4", "this is method 2") - ); + private final Pair testMethod2 = new Pair<>(MethodEntry.parse("a/b/c", "method2", "()V"), new EntryMapping("mapped4", "this is method 2")); - private void insertMapping(EntryTree mappings, Pair, EntryMapping> mappingPair){ + private void insertMapping(EntryTree mappings, Pair, EntryMapping> mappingPair) { mappings.insert(mappingPair.a, mappingPair.b); } @@ -71,8 +56,8 @@ public class TestReadWriteCycle { Assert.assertTrue("Test mapping insertion failed: testMethod2", testMappings.contains(testMethod2.a)); File tempFile = File.createTempFile("readWriteCycle", tmpNameSuffix); - tempFile.delete();//remove the auto created file - + //remove the auto created file + tempFile.delete(); mappingFormat.write(testMappings, tempFile.toPath(), ProgressListener.none(), parameters); Assert.assertTrue("Written file not created", tempFile.exists()); diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java index 60c70b73..659ac53d 100644 --- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java +++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestTinyV2InnerClasses.java @@ -1,25 +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.translation.mapping; +import java.nio.file.Path; +import java.nio.file.Paths; + import cuchaz.enigma.Enigma; import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.ProgressListener; import cuchaz.enigma.classprovider.ClasspathClassProvider; import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; -import java.nio.file.Path; -import java.nio.file.Paths; - public final class TestTinyV2InnerClasses { private Path jar; private Path mappings; @@ -29,10 +29,9 @@ public final class TestTinyV2InnerClasses { mappings = Paths.get(TestTinyV2InnerClasses.class.getResource("/tinyV2InnerClasses/").toURI()); } -// @Test + // @Test public void testMappings() throws Exception { EnigmaProject project = Enigma.create().openJar(jar, new ClasspathClassProvider(), ProgressListener.none()); project.setMappings(EnigmaMappingsReader.DIRECTORY.read(mappings, ProgressListener.none(), project.getEnigma().getProfile().getMappingSaveParameters())); - } } diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java index 6e4d7b99..cc08b852 100644 --- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java +++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/TestV2Main.java @@ -1,5 +1,8 @@ package cuchaz.enigma.translation.mapping; +import java.nio.file.Path; +import java.nio.file.Paths; + import cuchaz.enigma.ProgressListener; import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat; import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; @@ -7,9 +10,6 @@ import cuchaz.enigma.translation.mapping.serde.enigma.EnigmaMappingsReader; import cuchaz.enigma.translation.mapping.serde.tinyv2.TinyV2Writer; import cuchaz.enigma.translation.mapping.tree.EntryTree; -import java.nio.file.Path; -import java.nio.file.Paths; - public final class TestV2Main { public static void main(String... args) throws Exception { Path path = Paths.get(TestV2Main.class.getResource("/tinyV2InnerClasses/").toURI()); diff --git a/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java b/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java index 1026f576..bd1ec20a 100644 --- a/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java +++ b/enigma/src/test/java/cuchaz/enigma/translation/mapping/serde/recaf/TestRecaf.java @@ -1,11 +1,6 @@ package cuchaz.enigma.translation.mapping.serde.recaf; -import com.google.common.collect.Sets; -import com.google.common.jimfs.Jimfs; -import cuchaz.enigma.ProgressListener; -import cuchaz.enigma.translation.mapping.EntryMapping; -import cuchaz.enigma.translation.mapping.tree.EntryTree; -import org.junit.Test; +import static org.junit.Assert.assertEquals; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -15,32 +10,37 @@ import java.nio.file.Path; import java.util.HashSet; import java.util.Set; -import static org.junit.Assert.assertEquals; +import com.google.common.collect.Sets; +import com.google.common.jimfs.Jimfs; +import org.junit.Test; -public class TestRecaf { +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.tree.EntryTree; - @Test - public void testIntegrity() throws Exception { - Set contents; - try (InputStream in = getClass().getResourceAsStream("/recaf.mappings")) { - contents = Sets.newHashSet(new String(in.readAllBytes(), StandardCharsets.UTF_8).split("\\R")); - } +public class TestRecaf { + @Test + public void testIntegrity() throws Exception { + Set contents; - try (FileSystem fs = Jimfs.newFileSystem()) { + try (InputStream in = getClass().getResourceAsStream("/recaf.mappings")) { + contents = Sets.newHashSet(new String(in.readAllBytes(), StandardCharsets.UTF_8).split("\\R")); + } - Path path = fs.getPath("recaf.mappings"); - Files.writeString(path, String.join("\n", contents)); + try (FileSystem fs = Jimfs.newFileSystem()) { + Path path = fs.getPath("recaf.mappings"); + Files.writeString(path, String.join("\n", contents)); - RecafMappingsWriter writer = RecafMappingsWriter.INSTANCE; - RecafMappingsReader reader = RecafMappingsReader.INSTANCE; + RecafMappingsWriter writer = RecafMappingsWriter.INSTANCE; + RecafMappingsReader reader = RecafMappingsReader.INSTANCE; - EntryTree mappings = reader.read(path, ProgressListener.none(), null); - writer.write(mappings, path, ProgressListener.none(), null); + EntryTree mappings = reader.read(path, ProgressListener.none(), null); + writer.write(mappings, path, ProgressListener.none(), null); - reader.read(path, ProgressListener.none(), null); - Set newContents = new HashSet<>(Files.readAllLines(path)); + reader.read(path, ProgressListener.none(), null); + Set newContents = new HashSet<>(Files.readAllLines(path)); - assertEquals(contents, newContents); - } - } + assertEquals(contents, newContents); + } + } } -- cgit v1.2.3