[java decompiler] supporting `package-info`

GitOrigin-RevId: e20e9b802600dec52700065a7a29799c73c1dc49
master
Roman Shevchenko 3 years ago committed by intellij-monorepo-bot
parent 23b6aacfc7
commit e130aa6896
  1. 50
      src/org/jetbrains/java/decompiler/main/ClassWriter.java
  2. 80
      src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
  3. 21
      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. 6
      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('}');
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()) {

@ -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);
}
}

@ -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<String> 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<String> packImports() {
@ -181,4 +174,4 @@ public class ImportCollector {
.map(ent -> ent.getValue() + "." + ent.getKey())
.collect(Collectors.toList());
}
}
}

@ -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"); }

@ -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;
}

@ -1,2 +1,4 @@
@jdk.Exported
package pkg;
@PkgAnno("...")
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 {
requires java.base;

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

Loading…
Cancel
Save