IDEA-245329: Handle CONSTANT_Module and CONSTANT_Package

PR #1406

GitOrigin-RevId: 501d3b66e790316f8ab52606ea4cba41665213c0
master
leonardosnt 4 years ago committed by intellij-monorepo-bot
parent 78d932a0cb
commit f259b38c72
  1. 6
      src/org/jetbrains/java/decompiler/code/CodeConstants.java
  2. 117
      src/org/jetbrains/java/decompiler/main/ClassWriter.java
  3. 6
      src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java
  4. 227
      src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java
  5. 4
      src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java
  6. 4
      src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java
  7. 1
      test/org/jetbrains/java/decompiler/SingleClassesTest.java
  8. BIN
      testData/classes/java9/module-info.class
  9. 13
      testData/results/module-info.dec
  10. 15
      testData/src/java9/sample.module/module-info.java
  11. 3
      testData/src/java9/sample.module/test/TestService.java
  12. 3
      testData/src/java9/sample.module/test/TestServiceImpl.java
  13. 3
      testData/src/java9/sample.module/test2/TestClass.java

@ -1,4 +1,4 @@
// Copyright 2000-2019 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.code; package org.jetbrains.java.decompiler.code;
@SuppressWarnings({"unused", "SpellCheckingInspection"}) @SuppressWarnings({"unused", "SpellCheckingInspection"})
@ -64,6 +64,7 @@ public interface CodeConstants {
int ACC_STATIC = 0x0008; int ACC_STATIC = 0x0008;
int ACC_FINAL = 0x0010; int ACC_FINAL = 0x0010;
int ACC_SYNCHRONIZED = 0x0020; int ACC_SYNCHRONIZED = 0x0020;
int ACC_OPEN = 0x0020;
int ACC_NATIVE = 0x0100; int ACC_NATIVE = 0x0100;
int ACC_ABSTRACT = 0x0400; int ACC_ABSTRACT = 0x0400;
int ACC_STRICT = 0x0800; int ACC_STRICT = 0x0800;
@ -75,6 +76,7 @@ public interface CodeConstants {
int ACC_ANNOTATION = 0x2000; int ACC_ANNOTATION = 0x2000;
int ACC_ENUM = 0x4000; int ACC_ENUM = 0x4000;
int ACC_MANDATED = 0x8000; int ACC_MANDATED = 0x8000;
int ACC_MODULE = 0x8000;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// CLASS FLAGS // CLASS FLAGS
@ -112,6 +114,8 @@ public interface CodeConstants {
int CONSTANT_MethodHandle = 15; int CONSTANT_MethodHandle = 15;
int CONSTANT_MethodType = 16; int CONSTANT_MethodType = 16;
int CONSTANT_InvokeDynamic = 18; int CONSTANT_InvokeDynamic = 18;
int CONSTANT_Module = 19;
int CONSTANT_Package = 20;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// MethodHandle reference_kind values // MethodHandle reference_kind values

@ -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; package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -249,6 +249,12 @@ public class ClassWriter {
} }
} }
boolean isModuleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
if (isModuleInfo) {
writeModuleInfoBody(buffer, cl);
}
buffer.appendIndent(indent).append('}'); buffer.appendIndent(indent).append('}');
if (node.type != ClassNode.CLASS_ANONYMOUS) { if (node.type != ClassNode.CLASS_ANONYMOUS) {
@ -277,6 +283,99 @@ public class ClassWriter {
return false; return false;
} }
private void writeModuleInfoBody(TextBuffer buffer, StructClass cl) {
StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
for (StructModuleAttribute.RequiresEntry requires : moduleAttribute.requires) {
String moduleName = requires.moduleName.replace('/', '.');
buffer.appendIndent(1)
.append("requires ")
.append(moduleName)
.append(';')
.appendLineSeparator();
}
for (StructModuleAttribute.ExportsEntry exports : moduleAttribute.exports) {
String packageName = exports.packageName.replace('/', '.');
buffer.appendIndent(1).append("exports ").append(packageName);
List<String> exportToModules = exports.exportToModules;
if (exportToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
int lastIndex = exportToModules.size() - 1;
for (int i = 0; i < exportToModules.size(); i++) {
String moduleName = exportToModules.get(i).replace('/', '.');
char separator = i == lastIndex ? ';' : ',';
buffer.appendIndent(2)
.append(moduleName)
.append(separator)
.appendLineSeparator();
}
} else {
buffer.append(';').appendLineSeparator();
}
}
for (StructModuleAttribute.OpensEntry opens : moduleAttribute.opens) {
String packageName = opens.packageName.replace('/', '.');
buffer.appendIndent(1).append("opens ").append(packageName);
List<String> opensToModules = opens.opensToModules;
if (opensToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
int lastIndex = opensToModules.size() - 1;
for (int i = 0; i < opensToModules.size(); i++) {
String moduleName = opensToModules.get(i).replace('/', '.');
char separator = i == lastIndex ? ';' : ',';
buffer.appendIndent(2)
.append(moduleName)
.append(separator)
.appendLineSeparator();
}
} else {
buffer.append(';').appendLineSeparator();
}
}
for (String uses : moduleAttribute.uses) {
String className = ExprProcessor.buildJavaClassName(uses);
buffer.appendIndent(1)
.append("uses ")
.append(className)
.append(';')
.appendLineSeparator();
}
for (StructModuleAttribute.ProvidesEntry provides : moduleAttribute.provides) {
String interfaceName = ExprProcessor.buildJavaClassName(provides.interfaceName);
buffer.appendIndent(1)
.append("provides ")
.append(interfaceName)
.append(" with")
.appendLineSeparator();
int lastIndex = provides.implementationNames.size() - 1;
for (int i = 0; i < provides.implementationNames.size(); i++) {
String className = ExprProcessor.buildJavaClassName(provides.implementationNames.get(i));
char separator = i == lastIndex ? ';' : ',';
buffer.appendIndent(2)
.append(className)
.append(separator)
.appendLineSeparator();
}
}
}
private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) { private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {
StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
tracer.setLineNumberTable(table); tracer.setLineNumberTable(table);
@ -299,6 +398,7 @@ 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);
@ -344,11 +444,24 @@ 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 ");
} }
buffer.append(node.simpleName); // Handled above
if (!isModuleInfo) {
buffer.append(node.simpleName);
}
GenericClassDescriptor descriptor = getGenericClassDescriptor(cl); GenericClassDescriptor descriptor = getGenericClassDescriptor(cl);
if (descriptor != null && !descriptor.fparameters.isEmpty()) { if (descriptor != null && !descriptor.fparameters.isEmpty()) {

@ -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.struct.attr; package org.jetbrains.java.decompiler.struct.attr;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
@ -34,6 +34,7 @@ public class StructGeneralAttribute {
public static final Key<StructGeneralAttribute> ATTRIBUTE_DEPRECATED = new Key<>("Deprecated"); public static final Key<StructGeneralAttribute> ATTRIBUTE_DEPRECATED = new Key<>("Deprecated");
public static final Key<StructLineNumberTableAttribute> ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>("LineNumberTable"); public static final Key<StructLineNumberTableAttribute> ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>("LineNumberTable");
public static final Key<StructMethodParametersAttribute> ATTRIBUTE_METHOD_PARAMETERS = new Key<>("MethodParameters"); public static final Key<StructMethodParametersAttribute> ATTRIBUTE_METHOD_PARAMETERS = new Key<>("MethodParameters");
public static final Key<StructModuleAttribute> ATTRIBUTE_MODULE = new Key<>("Module");
public static final Key<StructRecordAttribute> ATTRIBUTE_RECORD = new Key<>("Record"); public static final Key<StructRecordAttribute> ATTRIBUTE_RECORD = new Key<>("Record");
public static class Key<T extends StructGeneralAttribute> { public static class Key<T extends StructGeneralAttribute> {
@ -98,6 +99,9 @@ public class StructGeneralAttribute {
else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) { else if (ATTRIBUTE_METHOD_PARAMETERS.getName().equals(name)) {
attr = new StructMethodParametersAttribute(); attr = new StructMethodParametersAttribute();
} }
else if (ATTRIBUTE_MODULE.getName().equals(name)) {
attr = new StructModuleAttribute();
}
else if (ATTRIBUTE_RECORD.getName().equals(name)) { else if (ATTRIBUTE_RECORD.getName().equals(name)) {
attr = new StructRecordAttribute(); attr = new StructRecordAttribute();
} }

@ -0,0 +1,227 @@
// 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.struct.attr;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StructModuleAttribute extends StructGeneralAttribute {
public String moduleName;
public int moduleFlags;
public String moduleVersion;
public List<RequiresEntry> requires;
public List<ExportsEntry> exports;
public List<OpensEntry> opens;
public List<String> uses;
public List<ProvidesEntry> provides;
@Override
public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {
int moduleNameIndex = data.readUnsignedShort();
this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
this.moduleFlags = data.readUnsignedShort();
int moduleVersionIndex = data.readUnsignedShort();
if (moduleVersionIndex != 0) {
moduleVersion = pool.getPrimitiveConstant(moduleVersionIndex).getString();
}
this.requires = readRequires(data, pool);
this.exports = readExports(data, pool);
this.opens = readOpens(data, pool);
this.uses = readUses(data, pool);
this.provides = readProvides(data, pool);
}
public List<RequiresEntry> readRequires(DataInputFullStream data, ConstantPool pool) throws IOException {
int requiresCount = data.readUnsignedShort();
if (requiresCount <= 0) {
return Collections.emptyList();
}
List<RequiresEntry> requires = new ArrayList<>(requiresCount);
for (int i = 0; i < requiresCount; i++) {
int moduleNameIndex = data.readUnsignedShort();
int moduleFlags = data.readUnsignedShort();
int versionIndex = data.readUnsignedShort();
String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString();
requires.add(new RequiresEntry(moduleName, moduleFlags, version));
}
return requires;
}
private List<ExportsEntry> readExports(DataInputFullStream data, ConstantPool pool) throws IOException {
int exportsCount = data.readUnsignedShort();
if (exportsCount <= 0) {
return Collections.emptyList();
}
List<ExportsEntry> exports = new ArrayList<>(exportsCount);
for (int i = 0; i < exportsCount; i++) {
int packageNameIndex = data.readUnsignedShort();
int exportsFlags = data.readUnsignedShort();
int exportsToCount = data.readUnsignedShort();
List<String> exportsToModules;
if (exportsToCount > 0) {
exportsToModules = new ArrayList<>(exportsToCount);
for (int j = 0; j < exportsToCount; j++) {
int moduleNameIndex = data.readUnsignedShort();
String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
exportsToModules.add(moduleName);
}
} else {
exportsToModules = Collections.emptyList();
}
String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();
exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules));
}
return exports;
}
private List<OpensEntry> readOpens(DataInputFullStream data, ConstantPool pool) throws IOException {
int opensCount = data.readUnsignedShort();
if (opensCount <= 0) {
return Collections.emptyList();
}
List<OpensEntry> opens = new ArrayList<>(opensCount);
for (int i = 0; i < opensCount; i++) {
int packageNameIndex = data.readUnsignedShort();
int opensFlags = data.readUnsignedShort();
int opensToCount = data.readUnsignedShort();
List<String> opensToModules;
if (opensToCount > 0) {
opensToModules = new ArrayList<>(opensToCount);
for (int j = 0; j < opensToCount; j++) {
int moduleNameIndex = data.readUnsignedShort();
String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();
opensToModules.add(moduleName);
}
} else {
opensToModules = Collections.emptyList();
}
String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();
opens.add(new OpensEntry(packageName, opensFlags, opensToModules));
}
return opens;
}
private List<String> readUses(DataInputFullStream data, ConstantPool pool) throws IOException {
int usesCount = data.readUnsignedShort();
if (usesCount <= 0) {
return Collections.emptyList();
}
List<String> uses = new ArrayList<>(usesCount);
for (int i = 0; i < usesCount; i++) {
int classNameIndex = data.readUnsignedShort();
String className = pool.getPrimitiveConstant(classNameIndex).getString();
uses.add(className);
}
return uses;
}
private List<ProvidesEntry> readProvides(DataInputFullStream data, ConstantPool pool) throws IOException {
int providesCount = data.readUnsignedShort();
if (providesCount <= 0) {
return Collections.emptyList();
}
List<ProvidesEntry> provides = new ArrayList<>(providesCount);
for (int i = 0; i < providesCount; i++) {
int interfaceNameIndex = data.readUnsignedShort();
String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString();
// Always nonzero
int providesWithCount = data.readUnsignedShort();
List<String> implementationNames = new ArrayList<>(providesWithCount);
for (int j = 0; j < providesWithCount; j++) {
int classNameIndex = data.readUnsignedShort();
String className = pool.getPrimitiveConstant(classNameIndex).getString();
implementationNames.add(className);
}
provides.add(new ProvidesEntry(interfaceName, implementationNames));
}
return provides;
}
public static final class RequiresEntry {
public String moduleName;
public int moduleFlags;
public String moduleVersion;
public RequiresEntry(String moduleName, int moduleFlags, String moduleVersion) {
this.moduleName = moduleName;
this.moduleFlags = moduleFlags;
this.moduleVersion = moduleVersion;
}
}
public static final class ExportsEntry {
public String packageName;
public int exportsFlags;
public List<String> exportToModules;
public ExportsEntry(String packageName, int exportsFlags, List<String> exportToModules) {
this.packageName = packageName;
this.exportsFlags = exportsFlags;
this.exportToModules = exportToModules;
}
}
public static final class OpensEntry {
public String packageName;
public int opensFlags;
public List<String> opensToModules;
public OpensEntry(String packageName, int exportsFlags, List<String> exportToModules) {
this.packageName = packageName;
this.opensFlags = exportsFlags;
this.opensToModules = exportToModules;
}
}
public static final class ProvidesEntry {
public String interfaceName;
public List<String> implementationNames;
public ProvidesEntry(String interfaceName, List<String> implementationNames) {
this.interfaceName = interfaceName;
this.implementationNames = implementationNames;
}
}
}

@ -1,4 +1,4 @@
// Copyright 2000-2017 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.struct.consts; package org.jetbrains.java.decompiler.struct.consts;
import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.CodeConstants;
@ -64,6 +64,8 @@ public class ConstantPool implements NewClassNameBuilder {
case CodeConstants.CONSTANT_Class: case CodeConstants.CONSTANT_Class:
case CodeConstants.CONSTANT_String: case CodeConstants.CONSTANT_String:
case CodeConstants.CONSTANT_MethodType: case CodeConstants.CONSTANT_MethodType:
case CodeConstants.CONSTANT_Module:
case CodeConstants.CONSTANT_Package:
pool.add(new PrimitiveConstant(tag, in.readUnsignedShort())); pool.add(new PrimitiveConstant(tag, in.readUnsignedShort()));
nextPass[0].set(i); nextPass[0].set(i);
break; break;

@ -1,4 +1,4 @@
// Copyright 2000-2017 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.struct.consts; package org.jetbrains.java.decompiler.struct.consts;
public class PrimitiveConstant extends PooledConstant { public class PrimitiveConstant extends PooledConstant {
@ -31,7 +31,7 @@ public class PrimitiveConstant extends PooledConstant {
@Override @Override
public void resolveConstant(ConstantPool pool) { public void resolveConstant(ConstantPool pool) {
if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType) { if (type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType || type == CONSTANT_Module || type == CONSTANT_Package) {
value = pool.getPrimitiveConstant(index).getString(); value = pool.getPrimitiveConstant(index).getString();
initConstant(); initConstant();
} }

@ -74,6 +74,7 @@ public class SingleClassesTest {
"pkg/TestShadowingSuperClass"); } "pkg/TestShadowingSuperClass"); }
@Test public void testStringConcat() { doTest("pkg/TestStringConcat"); } @Test public void testStringConcat() { doTest("pkg/TestStringConcat"); }
@Test public void testJava9StringConcat() { doTest("java9/TestJava9StringConcat"); } @Test public void testJava9StringConcat() { doTest("java9/TestJava9StringConcat"); }
@Test public void testJava9ModuleInfo() { doTest("java9/module-info"); }
@Test public void testJava11StringConcat() { doTest("java11/TestJava11StringConcat"); } @Test public void testJava11StringConcat() { doTest("java11/TestJava11StringConcat"); }
@Test public void testMethodReferenceSameName() { doTest("pkg/TestMethodReferenceSameName"); } @Test public void testMethodReferenceSameName() { doTest("pkg/TestMethodReferenceSameName"); }
@Test public void testMethodReferenceLetterClass() { doTest("pkg/TestMethodReferenceLetterClass"); } @Test public void testMethodReferenceLetterClass() { doTest("pkg/TestMethodReferenceLetterClass"); }

@ -0,0 +1,13 @@
module sample.module {
requires java.base;
exports test;
exports test2 to
java.base;
opens test;
opens test2 to
java.base;
uses java.util.spi.ToolProvider;
provides test.TestService with
test.TestServiceImpl;
}

@ -0,0 +1,15 @@
module sample.module {
requires java.base;
uses java.util.spi.ToolProvider;
provides test.TestService with test.TestServiceImpl;
exports test;
exports test2 to java.base;
opens test;
opens test2 to java.base;
}

@ -0,0 +1,3 @@
package test;
public interface TestService {}

@ -0,0 +1,3 @@
package test;
public class TestServiceImpl implements TestService {}

@ -0,0 +1,3 @@
package test2;
public class TestClass {}
Loading…
Cancel
Save