[jboss-jira] [JBoss JIRA] (JASSIST-222) Using insertBeforeBody to insert try/catch block in constructor can generate invalid bytecode for certain Java compilers
Manuel Geffken (JIRA)
issues at jboss.org
Thu May 8 13:11:56 EDT 2014
[ https://issues.jboss.org/browse/JASSIST-222?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12965725#comment-12965725 ]
Manuel Geffken edited comment on JASSIST-222 at 5/8/14 1:11 PM:
----------------------------------------------------------------
A sort of educated guess is that a version of the jikes compiler was used. I've tried some versions of jikes on S.java. Appearantly only versions before 1.18 generate .class files with minor version: 3 and major version: 45. However, both 1.13 and 1.17 do not seem to generate the bytecode sequence that caused the trouble.
was (Author: manuel.geffken):
A sort of educated guess is that a version of the jikes compiler was used. I've tried some versions of jikes on S.java. Appearantly only versions before 1.18 generate .class files with minor version: 3 and major version: 45. However, both 1.13 and 1.17 does not seem to generate the bytecode sequence that caused the trouble.
> Using insertBeforeBody to insert try/catch block in constructor can generate invalid bytecode for certain Java compilers
> ------------------------------------------------------------------------------------------------------------------------
>
> Key: JASSIST-222
> URL: https://issues.jboss.org/browse/JASSIST-222
> Project: Javassist
> Issue Type: Bug
> Affects Versions: 3.18.0-GA
> Environment: Unknown java compiler generating class files with major version 45, minor version 3.
> Mint Linux, JDK 1.7.
> Reporter: Manuel Geffken
> Assignee: Shigeru Chiba
> Priority: Minor
> Attachments: S.class, S.java, S_instr_error.class, S_instr_success.class
>
>
> The class S in the "boyer" benchmark of the Ashes Suite Collection (http://www.sable.mcgill.ca/ashes/) (no original source code available) has the following bytecode according to javap:
> {noformat}
> Classfile ashesSuiteCollection/suites/ashesHardTestSuite/benchmarks/boyer/classes/S.class
> Last modified May 2, 2014; size 154 bytes
> MD5 checksum e79a1481a39d40004bc504cc4f7c2641
> class S
> minor version: 3
> major version: 45
> flags: ACC_SUPER
> Constant pool:
> ...
> {
> protected int $;
> flags: ACC_PROTECTED
> protected S(int);
> flags: ACC_PROTECTED
> Code:
> stack=3, locals=2, args_size=2
> 0: aload_0
> 1: iload_1
> 2: aload_0
> 3: invokespecial #14 // Method java/lang/Object."<init>":()V
> 6: putfield #13 // Field $:I
> 9: return
> }
> {noformat}
> An instrumentation of this class' constructor with a try/catch block as in
> {noformat}
> CtConstructor[] ctors = target.getDeclaredConstructors();
> for (CtConstructor ctor : ctors) {
> StringBuilder headerSB = new StringBuilder();
> headerSB.append("{");
> headerSB.append(" try {");
> headerSB.append(" throw new java.lang.Exception();");
> headerSB.append(" } catch(java.lang.Exception e) {}");
> headerSB.append("}");
> ctor.insertBeforeBody(headerSB.toString());
> }
> {noformat}
> causes an invalid class file that _does not verify_:
> {noformat}
> Classfile ashesSuiteCollection/suites/ashesHardTestSuite/benchmarks/boyer/out_classes/S_instr_error.class
> Last modified May 5, 2014; size 356 bytes
> MD5 checksum d4ef556ee82cee6c4c2960b43d7e71e1
> class S
> minor version: 3
> major version: 45
> flags: ACC_SUPER
> Constant pool:
> ...
> {
> protected int $;
> flags: ACC_PROTECTED
> protected S(int);
> flags: ACC_PROTECTED
> Code:
> stack=5, locals=3, args_size=2
> 0: aload_0
> 1: iload_1
> 2: aload_0
> 3: invokespecial #14 // Method java/lang/Object."<init>":()V
> 6: new #22 // class java/lang/Exception
> 9: dup
> 10: invokespecial #23 // Method java/lang/Exception."<init>":()V
> 13: athrow
> 14: astore_2
> 15: goto 18
> 18: putfield #13 // Field $:I
> 21: return
> Exception table:
> from to target type
> 6 14 14 Class java/lang/Exception
> }
> {noformat}
> The problem is that the operand stack can be empty when the {{putfield}} instruction is reached. The problem seems to be rooted in the fact that the original bytecode sequence assumes that the two entries of the operand stack reach the {{putfield}} instruction. However, this assumption is invalid in the instrumented code.
> In contrast the reconstructed source code (the original source is not available)
> {noformat}
> class S {
> int $;
>
> public S(int i) {
> $ = i;
> }
> }
> {noformat}
> compiled with javac 1.7.0_51 ({{javac -source 1.3 -target 1.1}}) produces
> {noformat}
> Classfile S.class
> Last modified May 5, 2014; size 222 bytes
> MD5 checksum 9700e6458db9d62e640018421a079761
> Compiled from "S.java"
> public class S
> SourceFile: "S.java"
> minor version: 3
> major version: 45
> flags: ACC_SUPER
> Constant pool:
> ...
> {
> int $;
> flags:
> public S(int);
> flags: ACC_PUBLIC
> Code:
> stack=2, locals=2, args_size=2
> 0: aload_0
> 1: invokespecial #1 // Method java/lang/Object."<init>":()V
> 4: aload_0
> 5: iload_1
> 6: putfield #2 // Field $:I
> 9: return
> }
> {noformat}
> This version can be instrumented as shown above without problems. Here the operand of the putfield instruction are pushed on the stack _after_ the {{invokespecial}} instruction.
> {noformat}
> 1: invokespecial #1 // Method java/lang/Object."<init>":()V
> 4: aload_0
> 5: iload_1
> {noformat}
> This sequence seems easier to instrument (after the super call) with code containing a try/catch block.
--
This message was sent by Atlassian JIRA
(v6.2.3#6260)
More information about the jboss-jira
mailing list