[jboss-jira] [JBoss JIRA] (JASSIST-210) MethodCall.replace() throws inconsistent stack height in certain scenarios

Shigeru Chiba (JIRA) issues at jboss.org
Wed Jul 23 06:21:30 EDT 2014


    [ https://issues.jboss.org/browse/JASSIST-210?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12927781#comment-12927781 ] 

Shigeru Chiba edited comment on JASSIST-210 at 7/23/14 6:21 AM:
----------------------------------------------------------------

For the same reason I mentioned in https://issues.jboss.org/browse/JASSIST-52
substituting a try-catch block for an argument or an operand in an expression is not possible (as far as I know).
The only workaround is to throw an exception again (or return a value) at the end of the catch clause.

If you execute ref.testMethod1(ref.testMethod2), then

1: ref is pushed onto the stack
2: ref is pushed onto the stack (for calling testMethod2)
3: invoke testMethod2, which push a return value onto the stack
4: pop the two values from the stack and invoke testMethod1

After the translation, 2 and 3 are replaced with a try-catch statement.
If an exception is thrown in the try block, then *all* the values in the stack are popped out.
So the value of ref stored in 1 is also popped out.
That's why even if the catch block pushed some value onto the stack, the JVM reports a verification error.
The value of ref is missing.






was (Author: chiba):
For the same reason I mentioned in This is similar to https://issues.jboss.org/browse/JASSIST-52
substituting a try-catch block for an argument or an operand in an expression is not possible (as far as I know).
The only workaround is to throw an exception again (or return a value) at the end of the catch clause.

If you execute ref.testMethod1(ref.testMethod2), then

1: ref is pushed onto the stack
2: ref is pushed onto the stack (for calling testMethod2)
3: invoke testMethod2, which push a return value onto the stack
4: pop the two values from the stack and invoke testMethod1

After the translation, 2 and 3 are replaced with a try-catch statement.
If an exception is thrown in the try block, then *all* the values in the stack are popped out.
So the value of ref stored in 1 is also popped out.
That's why even if the catch block pushed some value onto the stack, the JVM reports a verification error.
The value of ref is missing.





> MethodCall.replace() throws inconsistent stack height in certain scenarios
> --------------------------------------------------------------------------
>
>                 Key: JASSIST-210
>                 URL: https://issues.jboss.org/browse/JASSIST-210
>             Project: Javassist
>          Issue Type: Bug
>      Security Level: Public(Everyone can see) 
>    Affects Versions: 3.16.1-GA, 3.18.1-GA
>            Reporter: Patson Luk
>            Assignee: Shigeru Chiba
>
> Tested on 3.16.1-GA
> This is similar to https://issues.jboss.org/browse/JASSIST-52, but the try-catch block usage is different (not wrapping the $proceed($$))
> Based on the javassist tutorial,  the substituted code for MethodCall.replace():
> {{Note that the substituted code is not an expression but a statement or a block. It cannot be or contain a try-catch statement.}}
> However in the case that try-catch does not wrap the $_=$proceed($$);
> It seems to work properly. For example:
> ctClass.instrument(new ExprEditor() {
>     public void edit(MethodCall m) throws CannotCompileException {
>         if (m.getMethodName().equals("testMethod2")) {
>             String newBlock = "{ try { System.out.println(\"injected\"); } catch (Exception e) { e.printStackTrace(); } $_ = $proceed($$); }";
>             m.replace(newBlock);
>         }
>     }
> });
> {{which ctClass is class TestReplace, which has code as below:}}
> public class TestReplace {
>     public static void main(String[] args) {
>         RefClass ref = new RefClass();
>         ref.testMethod2();
>     }
> }
> class RefClass {
>     public void testMethod1(Object o) {}
>     public Object testMethod2() { return null; }
> }
> {{running TestReplace with the modified bytecode correctly prints "injected" to the screen. However if I add one more line to the TestReplace's main() method}}
> ref.testMethod1(ref.testMethod2());
> {{It throws exception as below}}
> javassist.bytecode.BadBytecode: inconsistent stack height -1
> javassist.bytecode.stackmap.Tracer.doOpcode(Tracer.java:106)
> *So is try-catch acceptable if it does not wrap the $_=$proceed($$)? If so, is it a bug if it does not work properly for method invocation within another method invocation?*
> Many thanks in advance!



--
This message was sent by Atlassian JIRA
(v6.2.6#6264)


More information about the jboss-jira mailing list