[java decompiler] supporting `package-info`

GitOrigin-RevId: e20e9b802600dec52700065a7a29799c73c1dc49
master
Roman Shevchenko 4 years ago committed by intellij-monorepo-bot
parent 23b6aacfc7
commit e130aa6896
  1. 48
      src/org/jetbrains/java/decompiler/main/ClassWriter.java
  2. 38
      src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
  3. 19
      src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java
  4. 2
      test/org/jetbrains/java/decompiler/SingleClassesTest.java
  5. BIN
      testData/classes/java9/module-info.class
  6. BIN
      testData/classes/pkg/package-info.class
  7. 5
      testData/results/module-info.dec
  8. 4
      testData/results/package-info.dec
  9. 8
      testData/src/ext/PkgAnno.java
  10. 8
      testData/src/java9/sample.module/TestModuleAnno.java
  11. 4
      testData/src/java9/sample.module/module-info.java
  12. 4
      testData/src/pkg/package-info.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('}'); buffer.appendIndent(indent).append('}');
if (node.type != ClassNode.CLASS_ANONYMOUS) { if (node.type != ClassNode.CLASS_ANONYMOUS) {
@ -269,10 +262,10 @@ public class ClassWriter {
DecompilerContext.getLogger().endWriteClass(); DecompilerContext.getLogger().endWriteClass();
} }
@SuppressWarnings("SpellCheckingInspection")
private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) { private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) {
if (cl.getRecordComponents() != null) { if (cl.getRecordComponents() != null) {
String name = mt.getName(), descriptor = mt.getDescriptor(); String name = mt.getName(), descriptor = mt.getDescriptor();
//noinspection SpellCheckingInspection
if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") || if (name.equals("equals") && descriptor.equals("(Ljava/lang/Object;)Z") ||
name.equals("hashCode") && descriptor.equals("()I") || name.equals("hashCode") && descriptor.equals("()I") ||
name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) { name.equals("toString") && descriptor.equals("()Ljava/lang/String;")) {
@ -285,6 +278,30 @@ public class ClassWriter {
return false; 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) { private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) {
boolean newLineNeeded = false; boolean newLineNeeded = false;
@ -368,7 +385,6 @@ public class ClassWriter {
boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0; boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0;
boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0; boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0;
boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0; boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0;
boolean isModuleInfo = (flags & CodeConstants.ACC_MODULE) != 0 && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
if (isDeprecated) { if (isDeprecated) {
appendDeprecation(buffer, indent); appendDeprecation(buffer, indent);
@ -414,24 +430,10 @@ public class ClassWriter {
else if (components != null) { else if (components != null) {
buffer.append("record "); 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 { else {
buffer.append("class "); buffer.append("class ");
} }
// Handled above
if (!isModuleInfo) {
buffer.append(node.simpleName); buffer.append(node.simpleName);
}
GenericClassDescriptor descriptor = getGenericClassDescriptor(cl); GenericClassDescriptor descriptor = getGenericClassDescriptor(cl);
if (descriptor != null && !descriptor.fparameters.isEmpty()) { if (descriptor != null && !descriptor.fparameters.isEmpty()) {

@ -302,15 +302,32 @@ public class ClassesProcessor implements CodeConstants {
return; 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); DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);
try { try {
ImportCollector importCollector = new ImportCollector(root); ImportCollector importCollector = new ImportCollector(root);
DecompilerContext.startClass(importCollector); DecompilerContext.startClass(importCollector);
if (packageInfo) {
ClassWriter.packageInfoToJava(cl, buffer);
importCollector.writeImports(buffer, false);
}
else if (moduleInfo) {
TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
ClassWriter.moduleInfoToJava(cl, moduleBuffer);
importCollector.writeImports(buffer, true);
buffer.append(moduleBuffer);
}
else {
new LambdaProcessor().processClass(root); new LambdaProcessor().processClass(root);
// add simple class names to implicit import // add simple class names to implicit import
addClassnameToImport(root, importCollector); addClassNameToImport(root, importCollector);
// build wrappers for all nested classes (that's where actual processing takes place) // build wrappers for all nested classes (that's where actual processing takes place)
initWrappers(root); initWrappers(root);
@ -322,21 +339,13 @@ public class ClassesProcessor implements CodeConstants {
TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);
new ClassWriter().classToJava(root, classBuffer, 0, null); new ClassWriter().classToJava(root, classBuffer, 0, null);
int index = cl.qualifiedName.lastIndexOf("/"); int index = cl.qualifiedName.lastIndexOf('/');
if (index >= 0) { if (index >= 0) {
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator();
buffer.append("package ");
buffer.append(packageName);
buffer.append(";");
buffer.appendLineSeparator();
buffer.appendLineSeparator();
} }
int import_lines_written = importCollector.writeImports(buffer); importCollector.writeImports(buffer, true);
if (import_lines_written > 0) {
buffer.appendLineSeparator();
}
int offsetLines = buffer.countLines(); int offsetLines = buffer.countLines();
@ -354,6 +363,7 @@ public class ClassesProcessor implements CodeConstants {
} }
} }
} }
}
finally { finally {
destroyWrappers(root); destroyWrappers(root);
DecompilerContext.getLogger().endReadingClass(); DecompilerContext.getLogger().endReadingClass();
@ -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) { if (node.simpleName != null && node.simpleName.length() > 0) {
imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false); imp.getShortName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false);
} }
for (ClassNode nd : node.nested) { for (ClassNode nd : node.nested) {
addClassnameToImport(nd, imp); addClassNameToImport(nd, imp);
} }
} }

@ -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; package org.jetbrains.java.decompiler.main.collectors;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
@ -150,21 +150,14 @@ public class ImportCollector {
return result == null ? shortName : result; return result == null ? shortName : result;
} }
public int writeImports(TextBuffer buffer) { public void writeImports(TextBuffer buffer, boolean addSeparator) {
int importLinesWritten = 0;
List<String> imports = packImports(); List<String> imports = packImports();
for (String line : imports) {
for (String s : imports) { buffer.append("import ").append(line).append(';').appendLineSeparator();
buffer.append("import "); }
buffer.append(s); if (addSeparator && !imports.isEmpty()) {
buffer.append(';');
buffer.appendLineSeparator(); buffer.appendLineSeparator();
importLinesWritten++;
} }
return importLinesWritten;
} }
private List<String> packImports() { private List<String> packImports() {

@ -115,9 +115,9 @@ public class SingleClassesTest {
@Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); } @Test public void testSynchronizedUnprotected() { doTest("pkg/TestSynchronizedUnprotected"); }
@Test public void testInterfaceSuper() { doTest("pkg/TestInterfaceSuper"); } @Test public void testInterfaceSuper() { doTest("pkg/TestInterfaceSuper"); }
@Test public void testFieldSingleAccess() { doTest("pkg/TestFieldSingleAccess"); } @Test public void testFieldSingleAccess() { doTest("pkg/TestFieldSingleAccess"); }
@Test public void testPackageInfo() { doTest("pkg/package-info"); }
// TODO: fix all below // TODO: fix all below
//@Test public void testPackageInfo() { doTest("pkg/package-info"); }
//@Test public void testSwitchOnStrings() { doTest("pkg/TestSwitchOnStrings");} //@Test public void testSwitchOnStrings() { doTest("pkg/TestSwitchOnStrings");}
//@Test public void testUnionType() { doTest("pkg/TestUnionType"); } //@Test public void testUnionType() { doTest("pkg/TestUnionType"); }
//@Test public void testInnerClassConstructor2() { doTest("pkg/TestInner2"); } //@Test public void testInnerClassConstructor2() { doTest("pkg/TestInner2"); }

@ -1,4 +1,6 @@
@Deprecated import pkg.test1.TestModuleAnno;
@TestModuleAnno("...")
module sample.module { module sample.module {
requires java.base; requires java.base;
@ -15,4 +17,3 @@ module sample.module {
provides sample.pkg1.TestService with provides sample.pkg1.TestService with
sample.pkg1.TestServiceImpl; sample.pkg1.TestServiceImpl;
} }

@ -1,2 +1,4 @@
@jdk.Exported @PkgAnno("...")
package pkg; package pkg;
import ext.PkgAnno;

@ -0,0 +1,8 @@
package ext;
import java.lang.annotation.*;
@Target(ElementType.PACKAGE)
public @interface PkgAnno {
String value();
}

@ -0,0 +1,8 @@
package pkg.test1;
import java.lang.annotation.*;
@Target(ElementType.MODULE)
public @interface TestModuleAnno {
String value();
}

@ -1,4 +1,6 @@
@Deprecated import pkg.test1.TestModuleAnno;
@TestModuleAnno("...")
module sample.module { module sample.module {
requires java.base; requires java.base;

@ -1,2 +1,4 @@
@jdk.Exported @PkgAnno("...")
package pkg; package pkg;
import ext.PkgAnno;

Loading…
Cancel
Save