[jboss-jira] [JBoss JIRA] Commented: (JASSIST-52) VerifyError (inconsistent stack height) through MethodCall#replace when block includes a try/catch block

Shigeru Chiba (JIRA) jira-events at lists.jboss.org
Wed Apr 9 07:36:58 EDT 2008


    [ http://jira.jboss.com/jira/browse/JASSIST-52?page=comments#action_12407866 ] 
            
Shigeru Chiba commented on JASSIST-52:
--------------------------------------

I could reproduce the bug.  The reason is that Javassist cannot replace method calls with
try { $_ = $proceed($$); } catch (Throwable e) {}.


The reason is related to the semantics of Java bytecode.  Suppose that we have a statement:

String s = concat("foo", getName());

This statement is compiled into the following bytecode:

1: push "foo" on the stack
2: call getName (the method pushes the return value on the stack)
3: call concat (the method pops the arguments from the stack and pushes the return value)
4: pop the stack top and stores it into the variable s

Then you replace the call to getName (line 2) with try{...} catch (..) {}.
 
If the try block throws an exception, the JVM pops all the stack entries and makes the stack
empty.  Thus, when the thread comes back from the catch block and reaches line 3, the stack
is empty.  The call to concat cannot obtain the second argument from the stack.   This is why
the verifier throws an exception.

I don't know how to fix this problem.  You will see this problem even if you use other bytecode
engineering tools or if you directly transform bytecode.

Chiba



> VerifyError (inconsistent stack height) through MethodCall#replace when block includes a try/catch block
> --------------------------------------------------------------------------------------------------------
>
>                 Key: JASSIST-52
>                 URL: http://jira.jboss.com/jira/browse/JASSIST-52
>             Project: Javassist
>          Issue Type: Bug
>         Environment: javassist 3.7.1 (from cvs HEAD), jdk 1.6.0_03 on winXP
>            Reporter: Yanic Inghelbrecht
>         Assigned To: Shigeru Chiba
>         Attachments: supplement_to_jassist-52.zip
>
>
> Included is the simplest test case I could find that reproduces the error, it consists of two classes : Client (to be instrumented) and Main (does the instrumentation).
> When run, it produces the following exception trace :
> Exception in thread "main" java.lang.VerifyError: (class: test_verify_error_with_try_catch_in_replace/Client, method: instrumentMe signature: ()V) Inconsistent stack height 0 != 2
> 	at java.lang.Class.getDeclaredConstructors0(Native Method)
> 	at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
> 	at java.lang.Class.getConstructor0(Unknown Source)
> 	at java.lang.Class.newInstance0(Unknown Source)
> 	at java.lang.Class.newInstance(Unknown Source)
> 	at test_verify_error_with_try_catch_in_replace.Main.main(Main.java:30)
> The error disappears  in all of the following cases (so it must be related to them I guess) :
>  - when the try/catch block is removed from the replacement block
>  - when the string concatenation happens without using two local variables
>  - when the string concatenation is removed but the local variables are kept
> This is related (for me at least) to JIRA JAVASSIST 51, where a similar error occurred for NewExpr (and was quickly fixed, thanks again for that!!). 
> Just like issue 51, it is a show stopper for our project since it all ties in with the support for exceptions in the development tool we're creating, so I hope you will again regard it as an urgent issue.
> Thanks in advance.
> Best regards,
> Yanic
> -- source code for client.java
> package test_verify_error_with_try_catch_in_replace;
> public class Client {
> 	public void instrumentMe() throws Exception {
> 		// the error is somehow related to the string concatenation and local variables,
> 		// when the code below is replaced with something else, the error does not occur.
> 		String s1 = "a";
> 		String s2 = s1 + "b";
> 	}
> }
> -- source code for Main.java
> package test_verify_error_with_try_catch_in_replace;
> import java.lang.reflect.Method;
> import javassist.CannotCompileException;
> import javassist.ClassPool;
> import javassist.CtClass;
> import javassist.CtMethod;
> import javassist.Loader;
> import javassist.NotFoundException;
> import javassist.Translator;
> import javassist.expr.ExprEditor;
> import javassist.expr.MethodCall;
> public class Main {
> 	public static void main(String[] args) throws Exception {
> 		ClassPool pool = new ClassPool(true);
> 		Loader cl = new Loader();
> 		try {
> 			cl.addTranslator(pool, new MyTranslator());
> 		} catch (Exception e) {
> 			e.printStackTrace();
> 			return;
> 		}
> 		
> 		// load the Client class
> 		Class clientClass=cl.loadClass("test_verify_error_with_try_catch_in_replace.Client");
> 		
> 		// create an instance
> 		Object client=clientClass.newInstance();
> 		
> 		// execute Client#instrumentMe
> 		Method callMethod=clientClass.getMethod("instrumentMe", new Class[]{});
> 		callMethod.invoke(client, new Object[]{});
> 	}
> 	
> 	static public class MyTranslator extends ExprEditor implements Translator {
> 		public void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException {
> 			CtClass cc = pool.get(classname);
> 			modify(cc);
> 		}
> 		public void modify(CtClass c) throws CannotCompileException {
> 			for (CtMethod m : c.getMethods()) {
> 				m.instrument(this);
> 			}
> 		}
> 		@Override
> 		public void edit(MethodCall mc) throws CannotCompileException {
> 			// simple proceed surrounded by a try/catch block
> 			String block = "{try{$_=$proceed($$);} catch(Throwable t) {}}";
> 			mc.replace(block);
> 		}
> 		public void start(ClassPool pool) throws NotFoundException, CannotCompileException {
> 			// do nothing
> 		}
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        



More information about the jboss-jira mailing list