[jboss-jira] [JBoss JIRA] (JASSIST-222) Using insertBeforeBody to insert try/catch block in constructor can generate invalid bytecode for certain Java compilers
Shigeru Chiba (JIRA)
issues at jboss.org
Thu Nov 20 06:27:40 EST 2014
[ https://issues.jboss.org/browse/JASSIST-222?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Shigeru Chiba closed JASSIST-222.
---------------------------------
Resolution: Won't Fix
OK, I close this issue because it would be impossible to fix this problem.
In the bytecode generated by Jikes, there is not a position where a constructor call has been done
but the execution of the constructor body has not started. As we see in the bytecode, when the constructor
call is executed, the execution of the constructor body has been already started. So there is no position we can safely insert a try-catch statement at somewhere "just after a constructor call".
> 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.3.8#6338)
More information about the jboss-jira
mailing list