Initial support for CONSTANT_Dynamic constant pool entry type

- Support for CONSTANT_Dynamic constant pool entry type
 - Fail-fast on unknown constant pool entry type

(PR #1560)

GitOrigin-RevId: 53990c2752281268d99807cdcb0a580cae1d7b75
This commit is contained in:
Maxim Degtyarev
2021-11-26 01:41:33 +00:00
committed by intellij-monorepo-bot
parent 7049554673
commit 128ea3bcf8
12 changed files with 246 additions and 7 deletions
@@ -1,4 +1,4 @@
// 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.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. 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;
@SuppressWarnings({"unused", "SpellCheckingInspection"})
@@ -113,6 +113,7 @@ public interface CodeConstants {
int CONSTANT_NameAndType = 12;
int CONSTANT_MethodHandle = 15;
int CONSTANT_MethodType = 16;
int CONSTANT_Dynamic = 17;
int CONSTANT_InvokeDynamic = 18;
int CONSTANT_Module = 19;
int CONSTANT_Package = 20;
@@ -340,4 +341,4 @@ public interface CodeConstants {
String CLINIT_NAME = "<clinit>";
String INIT_NAME = "<init>";
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2020 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. and contributors. 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.interpreter;
import org.jetbrains.java.decompiler.code.CodeConstants;
@@ -8,6 +8,7 @@ import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.DataPoint;
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.ListStack;
@@ -403,6 +404,14 @@ public final class InstructionImpact {
case CodeConstants.CONSTANT_MethodHandle:
stack.push(new VarType(((LinkConstant)constant).descriptor));
break;
case CodeConstants.CONSTANT_Dynamic:
ck = pool.getLinkConstant(instr.operand(0));
FieldDescriptor constDescriptor = FieldDescriptor.parseDescriptor(ck.descriptor);
stack.push(constDescriptor.type);
if (constDescriptor.type.stackSize == 2) {
stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));
}
break;
}
break;
case CodeConstants.opc_aload:
@@ -0,0 +1,10 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. 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;
public class MalformedClassFormatException extends RuntimeException {
public MalformedClassFormatException(String message) {
super(message);
}
}
@@ -1,9 +1,10 @@
// 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.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. 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;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;
import org.jetbrains.java.decompiler.struct.MalformedClassFormatException;
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder;
@@ -78,6 +79,7 @@ public class ConstantPool implements NewClassNameBuilder {
case CodeConstants.CONSTANT_Fieldref:
case CodeConstants.CONSTANT_Methodref:
case CodeConstants.CONSTANT_InterfaceMethodref:
case CodeConstants.CONSTANT_Dynamic:
case CodeConstants.CONSTANT_InvokeDynamic:
pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort()));
nextPass[1].set(i);
@@ -87,6 +89,11 @@ public class ConstantPool implements NewClassNameBuilder {
pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort()));
nextPass[2].set(i);
break;
default:
// Fail-fast on unknown constant pool entry.
// We have no chance to process this class correctly.
throw new MalformedClassFormatException(String.format("Unsupported constant pool entry type %d at index #%d! ", Byte.toUnsignedInt(tag), i));
}
}
@@ -117,6 +124,7 @@ public class ConstantPool implements NewClassNameBuilder {
case CodeConstants.CONSTANT_Methodref:
case CodeConstants.CONSTANT_InterfaceMethodref:
case CodeConstants.CONSTANT_NameAndType:
case CodeConstants.CONSTANT_Dynamic:
case CodeConstants.CONSTANT_InvokeDynamic:
in.discard(4);
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. and contributors. 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;
public class LinkConstant extends PooledConstant {
@@ -49,7 +49,7 @@ public class LinkConstant extends PooledConstant {
descriptor = ref_info.descriptor;
}
else {
if (type != CONSTANT_InvokeDynamic) {
if (type != CONSTANT_InvokeDynamic && type != CONSTANT_Dynamic) {
classname = pool.getPrimitiveConstant(index1).getString();
}
@@ -72,4 +72,4 @@ public class LinkConstant extends PooledConstant {
this.descriptor.equals(cn.descriptor) &&
(this.type != CONSTANT_NameAndType || this.classname.equals(cn.classname));
}
}
}
@@ -3,6 +3,7 @@ package org.jetbrains.java.decompiler;
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.struct.MalformedClassFormatException;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -131,6 +132,12 @@ public class SingleClassesTest {
@Test public void testPackageInfo() { doTest("pkg/package-info"); }
@Test public void testIntVarMerge() { doTest("pkg/TestIntVarMerge"); }
@Test(expected = MalformedClassFormatException.class)
public void testUnsupportedConstantPoolEntry() { doTest("java11/TestUnsupportedConstantPoolEntry"); }
@Test
public void testDynamicConstantPoolEntry() { doTest("java11/TestDynamicConstantPoolEntry"); }
// TODO: fix all below
//@Test public void testSwitchOnStrings() { doTest("pkg/TestSwitchOnStrings");}
//@Test public void testUnionType() { doTest("pkg/TestUnionType"); }
@@ -0,0 +1,18 @@
package java11;
public class TestDynamicConstantPoolEntry {
public static void main(String... var0) {
System.out.println("This class file contains constant pool entry of type CONSTANT_Dynamic, not used in the bytecode.");
}
}
class 'java11/TestDynamicConstantPoolEntry' {
method 'main ([Ljava/lang/String;)V' {
0 4
3 4
5 4
8 5
}
}
Lines mapping:
@@ -0,0 +1,122 @@
/**
*
* This code can be assembled with <a href="https://wiki.openjdk.java.net/display/CodeTools/asmtools">asmtools</a>
* using <code>asmtools jcoder *.jcoder</code> command line.
*
* Test reading constant pool with CONSTANT_Dynamic entry at index #2. Dynamic constant didn't used in the bytecode and thus
* should not affect decompiled code correctness in any way.
*
*/
class java11/TestDynamicConstantPoolEntry {
0xCAFEBABE;
0; // minor version
55; // version
[] { // Constant Pool
; // first element is empty
String #29; // #1
Dynamic 0s #25; // #2
Field #35 #30; // #3
Method #32 #36; // #4
Method #22 #14; // #5
Utf8 "TestDynamicConstantPoolEntry.jcoder"; // #6
Utf8 "()V"; // #7
Method #10 #9; // #8
NameAndType #27 #13; // #9
class #23; // #10
Utf8 "Ljava/lang/Object;"; // #11
Utf8 "<init>"; // #12
Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"; // #13
NameAndType #12 #7; // #14
Utf8 "out"; // #15
Utf8 "java/io/PrintStream"; // #16
MethodHandle 6b #8; // #17
Utf8 "SourceFile"; // #18
class #31; // #19
Utf8 "java/lang/System"; // #20
Utf8 "(Ljava/lang/String;)V"; // #21
class #34; // #22
Utf8 "java/lang/invoke/ConstantBootstraps"; // #23
Utf8 "println"; // #24
NameAndType #27 #11; // #25
Utf8 "Ljava/io/PrintStream;"; // #26
Utf8 "nullConstant"; // #27
Utf8 "BootstrapMethods"; // #28
Utf8 "This class file contains constant pool entry of type CONSTANT_Dynamic, not used in the bytecode."; // #29
NameAndType #15 #26; // #30
Utf8 "java11/TestDynamicConstantPoolEntry"; // #31
class #16; // #32
Utf8 "main"; // #33
Utf8 "java/lang/Object"; // #34
class #20; // #35
NameAndType #24 #21; // #36
Utf8 "([Ljava/lang/String;)V"; // #37
Utf8 "Code"; // #38
} // Constant Pool
0x0021; // access
#19;// this_cpx
#22;// super_cpx
[] { // Interfaces
} // Interfaces
[] { // Fields
} // Fields
[] { // Methods
{ // method
0x0001; // access
#12; // name_index
#7; // descriptor_index
[] { // Attributes
Attr(#38) { // Code
1; // max_stack
1; // max_locals
Bytes[]{
0x2AB70005B1;
}
[] { // Traps
} // end Traps
[] { // Attributes
} // Attributes
} // end Code
} // Attributes
}
;
{ // method
0x0089; // access
#33; // name_index
#37; // descriptor_index
[] { // Attributes
Attr(#38) { // Code
2; // max_stack
1; // max_locals
Bytes[]{
0xB200031201B60004;
0xB1;
}
[] { // Traps
} // end Traps
[] { // Attributes
} // Attributes
} // end Code
} // Attributes
}
} // Methods
[] { // Attributes
Attr(#18) { // SourceFile
#6;
} // end SourceFile
;
Attr(#28) { // BootstrapMethods
[] { // bootstrap_methods
{ // bootstrap_method
#17; // bootstrap_method_ref
[] { // bootstrap_arguments
} // bootstrap_arguments
} // bootstrap_method
}
} // end BootstrapMethods
} // Attributes
} // end class java11/TestDynamicConstantPoolEntry
@@ -0,0 +1,64 @@
/**
*
* This code can be assembled with <a href="https://wiki.openjdk.java.net/display/CodeTools/asmtools">asmtools</a>
* using <code>asmtools jcoder *.jcoder</code> command line.
*
* Test handling constant pool entries of unknown type.
*
* Constant pool entry at index #10 has unknown tag 0xFF, not defined in JVMS.
* We should refuse to process this *.class file as we has no chance to correctly detect constant pool entry size
* and process rest of the class.
*
*/
class java11/TestUnsupportedConstantPoolEntry {
0xCAFEBABE;
0; // minor version
55; // version
[] { // Constant Pool
; // first element is empty
Method #3 #7; // #1
class #8; // #2
class #9; // #3
Utf8 "<init>"; // #4
Utf8 "()V"; // #5
Utf8 "Code"; // #6
NameAndType #4 #5; // #7
Utf8 "java11/TestUnsupportedConstantPoolEntry"; // #8
Utf8 "java/lang/Object"; // #9
0xFF #3; // #10
} // Constant Pool
0x0021; // access
#2;// this_cpx
#3;// super_cpx
[] { // Interfaces
} // Interfaces
[] { // Fields
} // Fields
[] { // Methods
{ // method
0x0001; // access
#4; // name_index
#5; // descriptor_index
[] { // Attributes
Attr(#6) { // Code
1; // max_stack
1; // max_locals
Bytes[]{
0x2AB70001B1;
}
[] { // Traps
} // end Traps
[] { // Attributes
} // Attributes
} // end Code
} // Attributes
}
} // Methods
[] { // Attributes
} // Attributes
} // end class java11/TestUnsupportedConstantPoolEntry