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 7f4d090..0380f41 100644 Binary files a/testData/classes/java9/module-info.class and b/testData/classes/java9/module-info.class differ diff --git a/testData/classes/pkg/package-info.class b/testData/classes/pkg/package-info.class index eee2939..88f6acc 100644 Binary files a/testData/classes/pkg/package-info.class and b/testData/classes/pkg/package-info.class differ diff --git a/testData/results/module-info.dec b/testData/results/module-info.dec index 12d289d..ec5e268 100644 --- a/testData/results/module-info.dec +++ b/testData/results/module-info.dec @@ -1,4 +1,6 @@ -@Deprecated +import pkg.test1.TestModuleAnno; + +@TestModuleAnno("...") module sample.module { requires java.base; @@ -15,4 +17,3 @@ module sample.module { provides sample.pkg1.TestService with sample.pkg1.TestServiceImpl; } - diff --git a/testData/results/package-info.dec b/testData/results/package-info.dec index f19fbea..0ccd581 100644 --- a/testData/results/package-info.dec +++ b/testData/results/package-info.dec @@ -1,2 +1,4 @@ -@jdk.Exported -package pkg; \ No newline at end of file +@PkgAnno("...") +package pkg; + +import ext.PkgAnno; diff --git a/testData/src/ext/PkgAnno.java b/testData/src/ext/PkgAnno.java new file mode 100644 index 0000000..683bd14 --- /dev/null +++ b/testData/src/ext/PkgAnno.java @@ -0,0 +1,8 @@ +package ext; + +import java.lang.annotation.*; + +@Target(ElementType.PACKAGE) +public @interface PkgAnno { + String value(); +} diff --git a/testData/src/java9/sample.module/TestModuleAnno.java b/testData/src/java9/sample.module/TestModuleAnno.java new file mode 100644 index 0000000..fb28aca --- /dev/null +++ b/testData/src/java9/sample.module/TestModuleAnno.java @@ -0,0 +1,8 @@ +package pkg.test1; + +import java.lang.annotation.*; + +@Target(ElementType.MODULE) +public @interface TestModuleAnno { + String value(); +} diff --git a/testData/src/java9/sample.module/module-info.java b/testData/src/java9/sample.module/module-info.java index 82d5266..f301ac5 100644 --- a/testData/src/java9/sample.module/module-info.java +++ b/testData/src/java9/sample.module/module-info.java @@ -1,4 +1,6 @@ -@Deprecated +import pkg.test1.TestModuleAnno; + +@TestModuleAnno("...") module sample.module { requires java.base; diff --git a/testData/src/pkg/package-info.java b/testData/src/pkg/package-info.java index d665652..0ccd581 100644 --- a/testData/src/pkg/package-info.java +++ b/testData/src/pkg/package-info.java @@ -1,2 +1,4 @@ -@jdk.Exported +@PkgAnno("...") package pkg; + +import ext.PkgAnno;