8 Replies Latest reply on Feb 25, 2010 8:42 AM by jaikiran

    javassist.bytecode.BadBytecode: unset variable. Please Help Me!

       Hello All,
       
       I am trying to use Javassist with my tomcat based web application. 
       I am getting exceptions when I try to instrument servlets generated from JSPs. Stack Trace attached below.
       
       Instrumentation that I am trying to do:
       
       Create an object and display it using its toString() method. class and method names are 
       com.temp1.tenga.SimpleInstrumenter.prefixMethod (for reference in the below stack trace).
       
       I use the 'instrument' api in java and a java.lang.ClassFileTransformer to modify classes during load time. 
       From the byte array of the class that I receive in my ClassFileTransformer, I create a 
       ByteArrayInputStream use the ClassPool.makeClassIfNew() method to create my CtClass object. 
       As per my understanding JSP classes can be loaded  multiple times apparently with the same class loader  
       (a concept which I am not too familiar with leave alone tomcat implementation details).
       
       After instrumenting a JSP class for the first time, it gets frozen. So if a CtClass is frozen, I detach() it
        and recreate the CtClass using the new byteArrayInputStream using the ClassPool.makeclass() method. 
      -  Is this logic correct?
       Once I recreate the CtClass in the above manner, I make the same instrumentation changes in it as in the 
       first time. 
       
       However, I am getting an exception whenever I try to access an instrumented JSP page. I cannot understand
       why. I know that the exception looks like I am using a local variable without initializing it but I do not
       think that that is the issue. jdk version is 1.6.0_13. Platform is windows
        
       Please help or point me in a proper direction. Thank you very much. Let me know if you need further 
       details.
       
      // First line is a statement that I just printed. Stack Trace starts from the next line:prefixMethod for org.apache.jsp.tempLogin_jsp method name _jspService . signature (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V Modifiers public
      javassist.CannotCompileException: by javassist.bytecode.BadBytecode: unset variable
      at javassist.CtBehavior.insertBefore(CtBehavior.java:724)
      at javassist.CtBehavior.insertBefore(CtBehavior.java:681)
      at com.temp1.tenga.SimpleInstrumenter.prefixMethod(SimpleInstrumenter.java:404)
      at com.temp1.tenga.SimpleInstrumenter.instrument(SimpleInstrumenter.java:255)
      at com.temp1.tenga.Instrument$Logger.transform(Instrument.java:78)
      at sun.instrument.TransformerManager.transform(TransformerManager.java:169)
      at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:365)
      at java.lang.ClassLoader.defineClass1(Native Method)
      at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
      at java.lang.ClassLoader.defineClass(ClassLoader.java:466)
      at org.apache.jasper.servlet.JasperLoader.loadClass(JasperLoader.java:215)
      at org.apache.jasper.servlet.JasperLoader.loadClass(JasperLoader.java:131)
      at org.apache.jasper.JspCompilationContext.load(JspCompilationContext.java:497)
      at org.apache.jasper.servlet.JspServletWrapper.getServlet(JspServletWrapper.java:150)
      at org.apache.jasper.compiler.Compiler.isOutDated(Compiler.java:440)
      at org.apache.jasper.compiler.Compiler.isOutDated(Compiler.java:390)
      at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:471)
      at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:190)
      at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:295)
      at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
      at com.temp.tempAccessFilter.doFilter(tempAccessFilter.java:59)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:213)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.ajp.tomcat4.Ajp13Processor.process(Ajp13Processor.java:457)
      at org.apache.ajp.tomcat4.Ajp13Processor.run(Ajp13Processor.java:576)
      at java.lang.Thread.run(Thread.java:619)
      Caused by: javassist.bytecode.BadBytecode: unset variable
      at javassist.bytecode.stackmap.TypeData.setType(TypeData.java:44)
      at javassist.bytecode.stackmap.Tracer.checkParamTypes(Tracer.java:908)
      at javassist.bytecode.stackmap.Tracer.doInvokeMethod(Tracer.java:813)
      at javassist.bytecode.stackmap.Tracer.doOpcode148_201(Tracer.java:615)
      at javassist.bytecode.stackmap.Tracer.doOpcode(Tracer.java:101)
      at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:161)
      at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:120)
      at javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:96)
      at javassist.bytecode.MethodInfo.rebuildStackMap(MethodInfo.java:404)
      at javassist.bytecode.MethodInfo.rebuildStackMapIf6(MethodInfo.java:389)
      at javassist.CtBehavior.insertBefore(CtBehavior.java:715)
      ... 49 more
      StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exception
      javax.servlet.ServletException: (class: org/apache/jsp/tempLogin_jsp, method: _jspService signature: (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V) Accessing value from uninitialized register 35
      at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:249)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
      at com.temp.tempAccessFilter.doFilter(tempAccessFilter.java:59)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:213)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
      at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
      at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
      at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
      at org.apache.ajp.tomcat4.Ajp13Processor.process(Ajp13Processor.java:457)
      at org.apache.ajp.tomcat4.Ajp13Processor.run(Ajp13Processor.java:576)
      at java.lang.Thread.run(Thread.java:619)
      
        • 1. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!

          prefixmethod() in brief:

           

             protected void prefixMethod(CtMethod myCtM, String methodName) {
             try {
             StringBuffer tempSB = new StringBuffer(100);
             CtField teTLCtF = new CtField(TLocCtC,TETLName,CtC);
             CtC.addField(teTLCtF);
             myCtM.addLocalVariable(localMIName,MICtC);
             Log2File.writeSpl("prefix Method ");
          
             // === PRINTING THE VARIABLE USING TOSTRING() METHOD ===
             tempSB.append("System.out.println(\"MI is \" + " + localMIName + ");");
             // EXCEPTION IS THROWN BY NEXT LINE IN THE INSERTBEFORE() METHOD
             Log2File.writeSpl(tempSB.toString());myCtM.insertBefore(tempSB.toString());tempSB.setLength(0);
          
             // === INITIALIZING THE LOCAL VARIABLE ===
             // MIFQ is a String containing a full class path
             // localMIName is a String representing the local variable name
             tempSB.append("{"+localMIName + " = new " + MIFQ + "(\"" + this.key + "\",\"" + myCtM.getReturnType().getName() + "\");}");
             Log2File.writeSpl(tempSB.toString());myCtM.insertBefore(tempSB.toString());tempSB.setLength(0);
              } catch (CannotCompileException cce) {
               System.out.println("prefixMethod for " + className + " method name " + methodName + " . signature " + myCtM.getSignature() + " Modifiers " + Modifier.toString(myCtM.getModifiers()));
               cce.printStackTrace();
               Log2File.writeErr(cce,"prefixMethod for " + className);
              }
              catch (NotFoundException ntfnde) {
                  System.out.println("prefixMethod for " + className + " method name " + methodName + ". Exception: " + ntfnde);
               ntfnde.printStackTrace();
               Log2File.writeErr(ntfnde,"prefixMethod for " + className);
              }
             }
          
          • 2. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!

            Some more information regarding this error:

             

            1. In prefix method, I am creating a new instance and assigning it to localMIName variable. Later I am just printing the newly created object (with over ridden toString() method). So there are 2 statements here. One to assign new instance to localMIName and another to print localMIName value. IF, these 2 statements are submitted to CtMethod.insertBefore() function as a single block, I get no exception! Mind you, If I add a statement later on to print localMIName in a separate statement (another CtMethod.insertBefore()) call, I still get the exception! What happens to the assigned value? Does it go out of scope?? Why does this happen only in JSP classes?

             

            2. I get this error only in sun java jdk 1.6 releases and not in sun java jdk 1.5 releases. What could be the exact issue? What extra information do i need so that I can also discuss this exception in the Java forums?

             

            Please help

            • 3. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!
              jaikiran

              megalodon wrote:

               

              Some more information regarding this error:

               

              1. In prefix method, I am creating a new instance and assigning it to localMIName variable. Later I am just printing the newly created object (with over ridden toString() method). So there are 2 statements here. One to assign new instance to localMIName and another to print localMIName value. IF, these 2 statements are submitted to CtMethod.insertBefore() function as a single block, I get no exception! Mind you, If I add a statement later on to print localMIName in a separate statement (another CtMethod.insertBefore()) call, I still get the exception! What happens to the assigned value? Does it go out of scope?? Why does this happen only in JSP classes?

               


               

               

              I haven't looked much into the details and can't help much as to what's wrong with your code. But looking at some of your posts here, you seem to be having a hard time trying to debug these javassist runtime issues. Sometime back, there was thread in this forum where a user pointed to a open source library which he had developed as an wrapper to Javassist http://www.fuin.org/srcgen4javassist/index.html . In one of his examples http://www.fuin.org/srcgen4javassist/examples.html he shows how it can be used to generate this debug information from javassist. I haven't personally given it a try, but maybe you might find it useful. You could use that library to wrap to javassist code and print out debug information - thay way you can see the exact code that has been generated.

              1 of 1 people found this helpful
              • 4. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!

                Hello Jaikiran,

                 

                srcgen4javassist looked promising but I'm not sure if it can help me out since:

                 

                1.) I don't think it can work with existing classes as opposed to creating new classes and then printing them out (which will not be of much help).

                2.) I don't know if it will take in bytecode to create its SgClass.

                 

                I am looking into APIs/tools like jreversepro which will decompile classes rather than "wrappers" like srcgen4javassist with seemingly incomplete capabilities/examples.

                 

                Thanks

                -Arvind

                • 5. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!
                  jaikiran

                  Is your insertBefore code, adding some local variable of type some class array? For ex: String[ ]?

                  • 6. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!

                    I am adding the local variable using the CtMethod.addLocalVariable() method. Using insertBefore(), I am just creating a new instance for the local variable and assigning it.

                    It is not an array. Its just an object. It has some fields which are String arrays - not sure if this will be an issue.

                     

                    -Arvind

                    • 7. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!

                      I did a little (largely incomplete) work with respect to exception tracing in Javassist source since I think it gets lost in the insertBefore implementation where the exception is caught and rethrown as a different one. Of course, this is how "I think" the exception is being thrown. We know that the 'unset variable' exception is being thrown from javassist.bytecode.stackmap.TypeData.setType(TypeData.java:44). I have tried to trace its invocation from the insertBefore implementation. I do not understand javassist implementation and it gets a little hairier for me when I get down to javassist.bytecode.stackmap package. Anyway, here goes:

                       

                      javassist.bytecode.stackmap.TypeData.setType(TypeData.java:44) -> throw new BadBytecode("unset variable");
                      ..
                      ..
                      ..
                      javassist.bytecode.stackmap.MapMaker.make(MapMaker.java:84) -> public static StackMapTable make(ClassPool classes, MethodInfo minfo)
                      javassist.bytecode.MethodInfo.rebuildStackMap(MethodInfo.java:404) -> StackMapTable smt = MapMaker.make(pool, this);
                      javassist.CtBehavior.insertBefore(CtBehavior.java:715) -> methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
                      javassist.CtBehavior.insertBefore(CtBehavior.java:724)
                      


                      I will see if I can do a better job later but if this one helps save a little time/offers some insight, thats great. Please help. Thanks

                      • 8. Re: javassist.bytecode.BadBytecode: unset variable. Please Help Me!
                        jaikiran

                        All i can say is - you are on right track. But use an debugger instead. That will help a lot. I would have looked into this a little more, but currently am busy with some other work. Maybe some time during the weekend or later next week, i'll do a quick example test.

                         

                        Here's a hint  - The exception is occuring when it enters a flow which is specifically meant to run for Java 6 compiled classes. And it ends up with an exception at the point where it identifies the "type" as a non-primitive array type (i haven't looked into the details yet). It sounds a bit like the allowArraySyntax thing of Java 6, but i don't see how that's applicable here.