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 0000000..308508d --- /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 93a2e1f..0abdf58 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 48aacde..9a4dc97 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 f3ab4c7..b156364 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 5b84196..5281e9e 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 75ef225..922d668 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 0d71f02..04d49f2 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 e10fd47..7e9002d 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 144d89c..99b27e1 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 12a4e88..020bd97 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 b0d2a7d..c8e6200 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 0780a96..af24978 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 0a4c1b9..9021ff1 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 46da89a..644b08d 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 d365129..787625b 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 a29bba4..8cfa49e 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 2764558..873adb9 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 83ef4c0..f642e13 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 a651fe8..8fcd437 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 41f0834..eb22a50 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 71bd011..69e4f5e 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 1ce359b..8872735 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 21c6825..99e4e99 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 c157838..d49e60c 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 45e0750..a8a1029 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 8618553..5b5b0e6 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 78ef964..ab4f5a1 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 b97877c..6a7ffe9 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 a237b91..8e4688e 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 9a112a8..bd238dc 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 da0f44a..e93c1d4 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 3bc09e7..b0610b0 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 2b07968..9833eeb 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 2f16dfb..15054e7 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 2649cdc..ce767c3 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 59999cc..49d56fd 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 6d9c0bc..7e50938 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 b4a277a..baac2b3 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 3765f7a..c2920e0 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 d783e95..a4a13d8 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 af105db..a213a59 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 b7d3c4e..4b9fa59 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 1e8d6cf..6028609 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 c912be3..0af8f9c 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 43745dd..b81782e 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 6246192..76d1859 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 5a1e3d8..b3117ce 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 47a854f..0eb9a16 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 1172a39..56f4385 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 309f910..c4541fc 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 b7fa2eb..ff80e17 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 3e4b30c..eac11ed 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 4ef0442..000793e 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 cec3fa1..2088aac 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 4439cb8..eaf20e7 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 660d231..2e84991 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 839a5cb..e2db968 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 8a10ace..cdf27ca 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 1265750..0e8f7da 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 f8922e6..c221120 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 c9ca809..76232c4 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 df65473..51948b5 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 2486dfe..1c2bd4c 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 c2a93fa..a84e977 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 07daf6d..35999e2 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 4e02a66..f0bae17 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 9470e11..d6e544d 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 d76ddea..3beae21 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 e65b661..7814dd8 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 0398093..1ab66ef 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 39aa212..3f1625d 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 c92534f..5671188 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 fb497b1..e05ab45 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 9a6ea09..301ae7f 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 0b44881..bcc6dc6 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 2ce6ed9..d128bf5 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 0b4926e..9385481 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 ff0bba3..7a6290e 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 533d1b3..9e632d9 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 eeb52cc..24a69b6 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 0c667c0..48404a1 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 02e1bc3..4329cae 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 7d1f866..2d5e229 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 c114dc1..be658d5 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 5df6348..b8b8431 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 6e17fec..d9ec95c 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 48c9ec4..1651abf 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 10d7ce1..e2054b2 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 2d8d76a..a97b377 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 22d6420..a807802 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 898529a..1dcdeab 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 922f8f2..f931a93 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 c1c7d38..dfcbd8c 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 fe5c857..dca714d 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 10fc5e1..5d1d0f2 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 f4b190b..cb74cec 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 e71894d..7b75f1a 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 7783843..f82e666 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 ccded45..571c638 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 0aa6510..3791a1e 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 7bf3900..b4126c0 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 a102553..04bf0f9 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 b6ae0c5..123990e 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 f9a1cae..09cdc9b 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 91727c3..93507bc 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 a3b35fa..c8212ce 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 20d6a0e..99b5572 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 0e2452f..0037ab5 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 0a71a64..12726c0 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 612e3e9..f8ce36d 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 6a75686..2d8aa73 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 3b8ecbc..d078424 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 b128699..f1a8a7a 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 9f53a44..818e112 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 d3e6376..30a9180 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 d045c6d..243f26f 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 28e3769..bc587fa 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 8915264..9d967b8 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 b4a4062..13fb6b5 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 a37f074..696a848 100644 --- a/enigma/src/main/java/cuchaz/enigma/Enigma.java +++ b/enigma/src/main/java/cuchaz/enigma/Enigma.java @@ -1,13 +1,13 @@ /******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    > implements Entry

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

    > implements Entry

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

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

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

    Contributors: +* Jeff Martin - initial API and implementation +******************************************************************************/ package cuchaz.enigma.utils; -import com.google.common.io.CharStreams; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,70 +28,80 @@ import java.util.function.Supplier; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import com.google.common.io.CharStreams; + public class Utils { - public static String readStreamToString(InputStream in) throws IOException { - return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)); - } - - public static String readResourceToString(String path) throws IOException { - InputStream in = Utils.class.getResourceAsStream(path); - if (in == null) { - throw new IllegalArgumentException("Resource not found! " + path); - } - return readStreamToString(in); - } - - public static void delete(Path path) throws IOException { - if (Files.exists(path)) { - for (Path p : Files.walk(path).sorted(Comparator.reverseOrder()).toList()) { - Files.delete(p); - } - } - } - - public static byte[] zipSha1(Path path) throws IOException { - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - // Algorithm guaranteed to be supported - throw new RuntimeException(e); - } - try (ZipFile zip = new ZipFile(path.toFile())) { - List entries = Collections.list(zip.entries()); - // only compare classes (some implementations may not generate directory entries) - entries.removeIf(entry -> !entry.getName().toLowerCase(Locale.ROOT).endsWith(".class")); - // different implementations may add zip entries in a different order - entries.sort(Comparator.comparing(ZipEntry::getName)); - byte[] buffer = new byte[8192]; - for (ZipEntry entry : entries) { - digest.update(entry.getName().getBytes(StandardCharsets.UTF_8)); - try (InputStream in = zip.getInputStream(entry)) { - int n; - while ((n = in.read(buffer)) != -1) { - digest.update(buffer, 0, n); - } - } - } - } - return digest.digest(); - } - - public static void withLock(Lock l, Runnable op) { - try { - l.lock(); - op.run(); - } finally { - l.unlock(); - } - } - - public static R withLock(Lock l, Supplier op) { - try { - l.lock(); - return op.get(); - } finally { - l.unlock(); - } - } + public static String readStreamToString(InputStream in) throws IOException { + return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8)); + } + + public static String readResourceToString(String path) throws IOException { + InputStream in = Utils.class.getResourceAsStream(path); + + if (in == null) { + throw new IllegalArgumentException("Resource not found! " + path); + } + + return readStreamToString(in); + } + + public static void delete(Path path) throws IOException { + if (Files.exists(path)) { + for (Path p : Files.walk(path).sorted(Comparator.reverseOrder()).toList()) { + Files.delete(p); + } + } + } + + public static byte[] zipSha1(Path path) throws IOException { + MessageDigest digest; + + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + // Algorithm guaranteed to be supported + throw new RuntimeException(e); + } + + try (ZipFile zip = new ZipFile(path.toFile())) { + List entries = Collections.list(zip.entries()); + // only compare classes (some implementations may not generate directory entries) + entries.removeIf(entry -> !entry.getName().toLowerCase(Locale.ROOT).endsWith(".class")); + // different implementations may add zip entries in a different order + entries.sort(Comparator.comparing(ZipEntry::getName)); + byte[] buffer = new byte[8192]; + + for (ZipEntry entry : entries) { + digest.update(entry.getName().getBytes(StandardCharsets.UTF_8)); + + try (InputStream in = zip.getInputStream(entry)) { + int n; + + while ((n = in.read(buffer)) != -1) { + digest.update(buffer, 0, n); + } + } + } + } + + return digest.digest(); + } + + public static void withLock(Lock l, Runnable op) { + try { + l.lock(); + op.run(); + } finally { + l.unlock(); + } + } + + public static R withLock(Lock l, Supplier op) { + try { + l.lock(); + return op.get(); + } finally { + l.unlock(); + } + } } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java index 6bcdbde..b7e67f2 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java @@ -3,7 +3,6 @@ package cuchaz.enigma.utils.validation; import cuchaz.enigma.utils.I18n; public class Message { - public static final Message EMPTY_FIELD = create(Type.ERROR, "empty_field"); public static final Message INVALID_IP = create(Type.ERROR, "invalid_ip"); public static final Message NOT_INT = create(Type.ERROR, "not_int"); @@ -46,5 +45,4 @@ public class Message { WARNING, ERROR, } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java index 9ad5867..afcbf62 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ParameterizedMessage.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.Objects; public final class ParameterizedMessage { - public final Message message; private final Object[] params; private final Validatable target; @@ -25,11 +24,15 @@ public final class ParameterizedMessage { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ParameterizedMessage that)) return false; - return Objects.equals(message, that.message) && - Arrays.equals(params, that.params) && - Objects.equals(target, that.target); + if (this == o) { + return true; + } + + if (!(o instanceof ParameterizedMessage that)) { + return false; + } + + return Objects.equals(message, that.message) && Arrays.equals(params, that.params) && Objects.equals(target, that.target); } @Override @@ -43,5 +46,4 @@ public final class ParameterizedMessage { public String toString() { return String.format("ParameterizedMessage { message: %s, params: %s, target: %s }", message, Arrays.toString(params), target); } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java index 5067d7e..8a7a9ed 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java @@ -4,7 +4,6 @@ import java.io.PrintStream; import java.util.Arrays; public class PrintValidatable implements Validatable { - public static final PrintValidatable INSTANCE = new PrintValidatable(); @Override @@ -16,9 +15,9 @@ public class PrintValidatable implements Validatable { String text = message.getText(); String longText = message.getLongText(); String type = switch (message.message.type) { - case INFO -> "info"; - case WARNING -> "warning"; - case ERROR -> "error"; + case INFO -> "info"; + case WARNING -> "warning"; + case ERROR -> "error"; }; w.printf("%s: %s\n", type, text); @@ -30,5 +29,4 @@ public class PrintValidatable implements Validatable { @Override public void clearMessages() { } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java index 871b59d..123c9b6 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/StandardValidation.java @@ -1,17 +1,20 @@ package cuchaz.enigma.utils.validation; public class StandardValidation { - public static boolean notBlank(ValidationContext vc, String value) { if (value.trim().isEmpty()) { vc.raise(Message.EMPTY_FIELD); return false; } + return true; } public static boolean isInt(ValidationContext vc, String value) { - if (!notBlank(vc, value)) return false; + if (!notBlank(vc, value)) { + return false; + } + try { Integer.parseInt(value); return true; @@ -22,13 +25,17 @@ public class StandardValidation { } public static boolean isIntInRange(ValidationContext vc, String value, int min, int max) { - if (!isInt(vc, value)) return false; + if (!isInt(vc, value)) { + return false; + } + int intVal = Integer.parseInt(value); + if (intVal < min || intVal > max) { vc.raise(Message.FIELD_OUT_OF_RANGE_INT, min, max); return false; } + return true; } - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java index 765ee08..39e8c41 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Validatable.java @@ -1,9 +1,7 @@ package cuchaz.enigma.utils.validation; public interface Validatable { - void addMessage(ParameterizedMessage message); void clearMessages(); - } diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java index 0ecb9fb..416e8a0 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java @@ -1,6 +1,10 @@ package cuchaz.enigma.utils.validation; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import javax.annotation.Nullable; @@ -15,7 +19,6 @@ import cuchaz.enigma.utils.validation.Message.Type; * multiple errors and displaying them to the user at the same time. */ public class ValidationContext { - private Validatable activeElement = null; private final Set elements = new HashSet<>(); private final List messages = new ArrayList<>(); @@ -30,6 +33,7 @@ public class ValidationContext { if (v != null) { elements.add(v); } + activeElement = v; } @@ -38,14 +42,16 @@ public class ValidationContext { * that element about the message. * * @param message the message to raise - * @param args the arguments used when formatting the message text + * @param args the arguments used when formatting the message text */ public void raise(Message message, Object... args) { ParameterizedMessage pm = new ParameterizedMessage(message, args, this.activeElement); + if (!this.messages.contains(pm)) { if (activeElement != null) { activeElement.addMessage(pm); } + messages.add(pm); } } @@ -72,6 +78,7 @@ public class ValidationContext { for (ParameterizedMessage message : this.messages) { PrintValidatable.formatMessage(System.err, message); } + throw new IllegalStateException("Errors encountered; cannot continue! Check error log for details."); } } @@ -90,5 +97,4 @@ public class ValidationContext { elements.clear(); messages.clear(); } - } diff --git a/enigma/src/test/java/cuchaz/enigma/ConfigTest.java b/enigma/src/test/java/cuchaz/enigma/ConfigTest.java index a44f037..95689f8 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 1251535..1678f59 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 2584d58..b19aa77 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 38940ca..587494e 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 9e1425a..833e217 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 6b60994..46086e5 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 0790193..05565b6 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 a9045f9..3f6f151 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 6e3755c..dcbe95f 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 a73880d..918466b 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 a5e8367..8017ab6 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 a420afe..93b7017 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 b9ebe55..280dadc 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 fb3a8da..5f510a4 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 4dbe8e2..46d5380 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 f07e1f8..a82db77 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 71439fd..e81df1b 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 c3d4170..55aa767 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 bc56b3b..1a90eba 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 87b69d3..ec5ac44 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 b9c4929..87e849f 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 50e963c..ba8c1b7 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 d0dd664..cfc696a 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 c584570..d3bb62e 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 f652d87..515205a 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 d1b7601..6ec27ac 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 94061fa..223c424 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 71b3a6d..f401d5f 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 976ec42..0056bc6 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 b1de3c9..b2e9e2d 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 ddc4e31..e50d37f 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 cf0f657..aa8de19 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 19fb19c..5bcb763 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 26f3718..b411e0a 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 fd7f6e7..d5e9a25 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 9d74e44..fd9e217 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 99c83bb..56cccc7 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 0b8cf2a..f44bdbf 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 8a92792..ac1d7b5 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 a1e6a85..4d99235 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 013c55a..d996dc8 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 fd2ebdd..9a9048c 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 e831943..15ec44e 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 510dd3c..681fd3f 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 60c70b7..659ac53 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 6e4d7b9..cc08b85 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 1026f57..bd1ec20 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