From 128ea3bcf8c43db13690652d877b9e8950e38509 Mon Sep 17 00:00:00 2001 From: Maxim Degtyarev Date: Thu, 25 Nov 2021 21:35:56 +0100 Subject: [PATCH] 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 --- .../java/decompiler/code/CodeConstants.java | 5 +- .../code/interpreter/InstructionImpact.java | 11 +- .../struct/MalformedClassFormatException.java | 10 ++ .../struct/consts/ConstantPool.java | 10 +- .../struct/consts/LinkConstant.java | 6 +- .../java/decompiler/SingleClassesTest.java | 7 + .../java11/TestDynamicConstantPoolEntry.class | Bin 0 -> 738 bytes .../TestUnsupportedConstantPoolEntry.class | Bin 0 -> 157 bytes .../results/TestDynamicConstantPoolEntry.dec | 18 +++ .../TestUnsupportedConstantPoolEntry.dec | 0 .../TestDynamicConstantPoolEntry.jcoder | 122 ++++++++++++++++++ .../TestUnsupportedConstantPoolEntry.jcoder | 64 +++++++++ 12 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/MalformedClassFormatException.java create mode 100644 plugins/java-decompiler/engine/testData/classes/java11/TestDynamicConstantPoolEntry.class create mode 100644 plugins/java-decompiler/engine/testData/classes/java11/TestUnsupportedConstantPoolEntry.class create mode 100644 plugins/java-decompiler/engine/testData/results/TestDynamicConstantPoolEntry.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestUnsupportedConstantPoolEntry.dec create mode 100644 plugins/java-decompiler/engine/testData/src/java11/TestDynamicConstantPoolEntry.jcoder create mode 100644 plugins/java-decompiler/engine/testData/src/java11/TestUnsupportedConstantPoolEntry.jcoder diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/CodeConstants.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/CodeConstants.java index 2c5cb42f61d9..2cf6f7ad2e1b 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/CodeConstants.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/CodeConstants.java @@ -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 = ""; String INIT_NAME = ""; -} \ No newline at end of file +} diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java index aeafe5c3c333..ff576a802013 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java @@ -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: diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/MalformedClassFormatException.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/MalformedClassFormatException.java new file mode 100644 index 000000000000..d7c18674526d --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/MalformedClassFormatException.java @@ -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); + } + +} diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java index 3280038cb409..0620a7cb2db6 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java @@ -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; diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java index b38800daf4ec..27a0fa14f9eb 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java @@ -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)); } -} \ No newline at end of file +} diff --git a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java index ce2f5b88a320..452f2caad3f4 100644 --- a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -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"); } diff --git a/plugins/java-decompiler/engine/testData/classes/java11/TestDynamicConstantPoolEntry.class b/plugins/java-decompiler/engine/testData/classes/java11/TestDynamicConstantPoolEntry.class new file mode 100644 index 0000000000000000000000000000000000000000..8e69d8a21aa514ab0c9b543e1d07355130481602 GIT binary patch literal 738 zcmaJfgc5Ph4rabpZ^Q&PU^(iTV%s?`&RAS4JN4yCP>s23#Y*4Z|jI=hy4UCBRz zp92SwIPe4bQHXKlw(bQVJoe6;nK$q4pTEC;19*X_^LVgCUw0Z<$3q_+8$MQWgJFFj zt$UkmF;P*^XzPS_d&b1?v`h2Nab$)vWteGq_I>zh1h|Lm9&Rx-x5wgCgt5@a;qKvB zMy|_Hf2p)`uNXeJD`}-q&4~;@N;fjY4?+)PX*afwImwc41=e?|(#O@4UM#Hbb}E>1 zQh)#+(K4B1SUw}7Ot?q&j=D%OS*T;4!S97yy+S&^LXS|0+hS>>CzarXHeGfMS+(wIGrTwYm&4qeY+M|JZgk`#9#Zv<* z0FT~J=x>Q4qd52MC+5Btd3+`C<1*bkg9DB4m}#;fn4LmuXcDl7I_^`f6PRxFKeLU; ABLDyZ literal 0 HcmV?d00001 diff --git a/plugins/java-decompiler/engine/testData/classes/java11/TestUnsupportedConstantPoolEntry.class b/plugins/java-decompiler/engine/testData/classes/java11/TestUnsupportedConstantPoolEntry.class new file mode 100644 index 0000000000000000000000000000000000000000..1d300859edc15d5524169f095dca0c2f7bbafba7 GIT binary patch literal 157 zcmX^0Z`VEs1_pBmZY~C926lD^4t54kMg}&U%)HDJJ4Oa(4b3n{1{UZ1lvExD76w*E z2KB7OvP45e{gBk+lF+>3(t?8gqLS1U=ls0llEl1{fc*R%*SwOVN=60&kV^fW#JqHU r|D>$casmtools + * using asmtools jcoder *.jcoder 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 ""; // #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 diff --git a/plugins/java-decompiler/engine/testData/src/java11/TestUnsupportedConstantPoolEntry.jcoder b/plugins/java-decompiler/engine/testData/src/java11/TestUnsupportedConstantPoolEntry.jcoder new file mode 100644 index 000000000000..24f22fda3fe0 --- /dev/null +++ b/plugins/java-decompiler/engine/testData/src/java11/TestUnsupportedConstantPoolEntry.jcoder @@ -0,0 +1,64 @@ +/** + * + * This code can be assembled with asmtools + * using asmtools jcoder *.jcoder 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 ""; // #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