From e130aa68964dea2160594700f67f195a06bcd32c Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 12 Feb 2021 12:30:20 +0100 Subject: [PATCH] [java decompiler] supporting `package-info` GitOrigin-RevId: e20e9b802600dec52700065a7a29799c73c1dc49 --- .../java/decompiler/main/ClassWriter.java | 50 +++++------ .../decompiler/main/ClassesProcessor.java | 80 ++++++++++-------- .../main/collectors/ImportCollector.java | 21 ++--- .../java/decompiler/SingleClassesTest.java | 2 +- testData/classes/java9/module-info.class | Bin 392 -> 417 bytes testData/classes/pkg/package-info.class | Bin 166 -> 186 bytes testData/results/module-info.dec | 5 +- testData/results/package-info.dec | 6 +- testData/src/ext/PkgAnno.java | 8 ++ .../java9/sample.module/TestModuleAnno.java | 8 ++ .../src/java9/sample.module/module-info.java | 4 +- testData/src/pkg/package-info.java | 4 +- 12 files changed, 108 insertions(+), 80 deletions(-) create mode 100644 testData/src/ext/PkgAnno.java create mode 100644 testData/src/java9/sample.module/TestModuleAnno.java diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 05b93c9..3f111a6 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -249,13 +249,6 @@ public class ClassWriter { } } - if (cl.hasModifier(CodeConstants.ACC_MODULE)) { - StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - if (moduleAttribute != null) { - writeModuleInfoBody(buffer, moduleAttribute); - } - } - buffer.appendIndent(indent).append('}'); if (node.type != ClassNode.CLASS_ANONYMOUS) { @@ -269,10 +262,10 @@ public class ClassWriter { DecompilerContext.getLogger().endWriteClass(); } + @SuppressWarnings("SpellCheckingInspection") private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) { if (cl.getRecordComponents() != null) { String name = mt.getName(), descriptor = mt.getDescriptor(); - //noinspection SpellCheckingInspection if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || name.equals("hashCode") && descriptor.equals("()I") || name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { @@ -285,6 +278,30 @@ public class ClassWriter { return false; } + public static void packageInfoToJava(StructClass cl, TextBuffer buffer) { + appendAnnotations(buffer, 0, cl, -1); + + int index = cl.qualifiedName.lastIndexOf('/'); + String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); + buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator(); + } + + public static void moduleInfoToJava(StructClass cl, TextBuffer buffer) { + appendAnnotations(buffer, 0, cl, -1); + + StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + + if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) { + buffer.append("open "); + } + + buffer.append("module ").append(moduleAttribute.moduleName).append(" {").appendLineSeparator(); + + writeModuleInfoBody(buffer, moduleAttribute); + + buffer.append('}').appendLineSeparator(); + } + private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) { boolean newLineNeeded = false; @@ -368,7 +385,6 @@ public class ClassWriter { boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; - boolean isModuleInfo = (flags & CodeConstants.ACC_MODULE) != 0 && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); if (isDeprecated) { appendDeprecation(buffer, indent); @@ -414,24 +430,10 @@ public class ClassWriter { else if (components != null) { buffer.append("record "); } - else if (isModuleInfo) { - StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); - - if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) { - buffer.append("open "); - } - - buffer.append("module "); - buffer.append(moduleAttribute.moduleName); - } else { buffer.append("class "); } - - // Handled above - if (!isModuleInfo) { - buffer.append(node.simpleName); - } + buffer.append(node.simpleName); GenericClassDescriptor descriptor = getGenericClassDescriptor(cl); if (descriptor != null && !descriptor.fparameters.isEmpty()) { diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index d05c839..7bb50aa 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -302,55 +302,65 @@ public class ClassesProcessor implements CodeConstants { return; } + boolean packageInfo = cl.isSynthetic() && "package-info".equals(root.simpleName); + boolean moduleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE); + DecompilerContext.getLogger().startReadingClass(cl.qualifiedName); try { ImportCollector importCollector = new ImportCollector(root); DecompilerContext.startClass(importCollector); - new LambdaProcessor().processClass(root); + if (packageInfo) { + ClassWriter.packageInfoToJava(cl, buffer); - // add simple class names to implicit import - addClassnameToImport(root, importCollector); + importCollector.writeImports(buffer, false); + } + else if (moduleInfo) { + TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); + ClassWriter.moduleInfoToJava(cl, moduleBuffer); - // build wrappers for all nested classes (that's where actual processing takes place) - initWrappers(root); + importCollector.writeImports(buffer, true); - new NestedClassProcessor().processClass(root, root); + buffer.append(moduleBuffer); + } + else { + new LambdaProcessor().processClass(root); - new NestedMemberAccess().propagateMemberAccess(root); + // add simple class names to implicit import + addClassNameToImport(root, importCollector); - TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); - new ClassWriter().classToJava(root, classBuffer, 0, null); + // build wrappers for all nested classes (that's where actual processing takes place) + initWrappers(root); - int index = cl.qualifiedName.lastIndexOf("/"); - if (index >= 0) { - String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); + new NestedClassProcessor().processClass(root, root); - buffer.append("package "); - buffer.append(packageName); - buffer.append(";"); - buffer.appendLineSeparator(); - buffer.appendLineSeparator(); - } + new NestedMemberAccess().propagateMemberAccess(root); - int import_lines_written = importCollector.writeImports(buffer); - if (import_lines_written > 0) { - buffer.appendLineSeparator(); - } + TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); + new ClassWriter().classToJava(root, classBuffer, 0, null); - int offsetLines = buffer.countLines(); + int index = cl.qualifiedName.lastIndexOf('/'); + if (index >= 0) { + String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); + buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator(); + } - buffer.append(classBuffer); + importCollector.writeImports(buffer, true); - if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { - BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper(); - mapper.addTotalOffset(offsetLines); - if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) { - buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping()); - } - if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) { - buffer.appendLineSeparator(); - mapper.dumpMapping(buffer, true); + int offsetLines = buffer.countLines(); + + buffer.append(classBuffer); + + if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { + BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper(); + mapper.addTotalOffset(offsetLines); + if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) { + buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping()); + } + if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) { + buffer.appendLineSeparator(); + mapper.dumpMapping(buffer, true); + } } } } @@ -375,13 +385,13 @@ public class ClassesProcessor implements CodeConstants { } } - private static void addClassnameToImport(ClassNode node, ImportCollector imp) { + private static void addClassNameToImport(ClassNode node, ImportCollector imp) { if (node.simpleName != null && node.simpleName.length() > 0) { imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false); } for (ClassNode nd : node.nested) { - addClassnameToImport(nd, imp); + addClassNameToImport(nd, imp); } } diff --git a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java index 7b8c3b4..647ac7c 100644 --- a/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java +++ b/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.java.decompiler.main.collectors; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; @@ -150,21 +150,14 @@ public class ImportCollector { return result == null ? shortName : result; } - public int writeImports(TextBuffer buffer) { - int importLinesWritten = 0; - + public void writeImports(TextBuffer buffer, boolean addSeparator) { List imports = packImports(); - - for (String s : imports) { - buffer.append("import "); - buffer.append(s); - buffer.append(';'); + for (String line : imports) { + buffer.append("import ").append(line).append(';').appendLineSeparator(); + } + if (addSeparator && !imports.isEmpty()) { buffer.appendLineSeparator(); - - importLinesWritten++; } - - return importLinesWritten; } private List packImports() { @@ -181,4 +174,4 @@ public class ImportCollector { .map(ent -> ent.getValue() + "." + ent.getKey()) .collect(Collectors.toList()); } -} \ No newline at end of file +} diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 7ac11d8..feea5aa 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -115,9 +115,9 @@ public class SingleClassesTest { @Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); } @Test public void testInterfaceSuper() { doTest("pkg/TestInterfaceSuper"); } @Test public void testFieldSingleAccess() { doTest("pkg/TestFieldSingleAccess"); } + @Test public void testPackageInfo() { doTest("pkg/package-info"); } // TODO: fix all below - //@Test public void testPackageInfo() { doTest("pkg/package-info"); } //@Test public void testSwitchOnStrings() { doTest("pkg/TestSwitchOnStrings");} //@Test public void testUnionType() { doTest("pkg/TestUnionType"); } //@Test public void testInnerClassConstructor2() { doTest("pkg/TestInner2"); } diff --git a/testData/classes/java9/module-info.class b/testData/classes/java9/module-info.class index 7f4d09012a0fa34e1c5ed69a6fd48d9276214dc1..0380f41b55f6e340258f1b1a449435fa1567fe77 100644 GIT binary patch delta 229 zcmeBRUdYUQ>ff$?3=9nB43g{&f)ja_>ZOB9^GY&vQ$6#_GK({la#9`h^72a(OEUBG ziWwQCdff$?3=9nB4C3qz{1bVVVkCn~^GY&vQ^PWgGm~;s9rNm^Ye-s z8N__D63Y_xa}x8?^<7d6ic*skOHxy;85!7o^HWN5QiT}=gc$@G88{3L^$heZ^$bNA zghUvG*%?IG8ARC`#3s()=FY~z$iND;kqwBg7}$X%15ktmNOCeTF>rzT+)y!eJ|hDU KP%SSI^8o-l1R!Ss diff --git a/testData/classes/pkg/package-info.class b/testData/classes/pkg/package-info.class index eee29394a7d0983ed45f01bfd0318a0809373ab2..88f6acc440589c3652dddaa35ed6eb1043eead55 100644 GIT binary patch delta 99 zcmZ3+xQkKX)W2Q(7#JAL8MxUQIN2GvCJHE9Ne7kYm1O3odghg77H1~qq&nv1<(DLu zWaj4;Gcxe{q*j#Z2V|#%M6DSaSj!S~N>dpbnDzAZCdN7Pa5FG6umLf9F#`tx%?28a delta 79 zcmdnRxQtQY)W2Q(7#J8#7&zG(*x4C4CJHEPO9qwZm1O3ohGiCKCgr3$=H=y=B$j06 j=M^(D@cCq=Wb3