12 Replies Latest reply on Nov 5, 2004 3:53 AM by madman

    CtMethod.setBody(CtMethod, ClassMap) does not work

    madman Newbie

      Hello,

      for a student project I need to modify some classes.
      It's my task to add some new methods and copy the method
      bodies of the old methods to the new ones (different names and
      parameters).

      Everything works fine if I use setBody(java.lang.String) and make
      my new methods simply call the old methods.
      But for performance reasons I need to copy the method bodies
      and if I just replace this part of the code, i get a ClassNotFoundException
      if I want to load my class after modification.

      Can anybody help me, please?
      I don't have any ideas how to solve this problem.

      Thanks

        • 1. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
          Shigeru Chiba Expert

           

          if I just replace this part of the code, i get a ClassNotFoundException
          if I want to load my class after modification.


          How did you replace? Could you explain details?


          • 2. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
            madman Newbie

            Hello,

            in my code I do the following:

            CtMethod[] oldMethods = cc.getDeclaredMethods();
            CtMethod oldMethod = null;
            CtMethod[] newMethods = new CtMethod[oldMethods.length];

            //first I insert the new method signatures
            for (int i = 0; i < oldMethods.length; i++) {

            oldMethod = oldMethods[ i ];
            /*I build the new parameters here*/
            //...

            /*then I define the new Method*/
            CtMethod newMethod = new CtMethod(oldMethod.getReturnType(), oldMethod.getName() + "byMe", newParameters, cc);
            cc.addMethod(newMethod);
            newMethods[ i ] = newMethod;
            }

            /*then I instrument the method bodies of the old methods*/

            /*now I insert the method bodies of the new methods*/
            CtMethod nMethod = null;
            for (int i = 0; i < newMethods.length; i++) {
            nMethod = cc.getDeclaredMethod(newMethods[ i ].getName());
            /* and here is difference: first the fine working version:*/
            /* The helper method buildParameterString just reads the parameters
            from the new method and gives the correct parameters to
            the old method. This must be done because the new method
            has more parameters.*/
            nMethod.setBody("return " + oldMethods[ i ].getName() + "(" + buildParameterString(oldMethods[ i ].getParameterTypes().length) + ");}");

            /*if I replace the above mentioned line with the second version, I get
            a ClassNotFoundException:*/
            nMethod.setBody(cc.getDeclaredMethod(oldMethods[ i ].getName()), null);
            cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
            }

            //end of code

            I also tried to copy directly in the first for(...), but it was the same result.

            I looked at the resulting files with javap, and the modifications are also done
            in the second case. The method bodies are copied. But I can't load them.

            If you need more information, just ask.

            Great thanks that you try to help me.

            • 3. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
              Shigeru Chiba Expert

               

              /* if I replace the above mentioned line with the second version, I get
               a ClassNotFoundException:*/
              nMethod.setBody(cc.getDeclaredMethod(oldMethods[ i ].getName()), null);
              cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);


              So you received a ClassNotFoundException when you loaded this
              modified class. How did you load that class? By a user-defined class
              loader or anything else?

              I also want to know what class was not found. The modified class?
              Another class that the modified class refers to?

              • 4. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                madman Newbie

                Hi,

                I use the normal URLClassLoader in java 1.4.2. In my opinion the URL must be
                correct as I can load it when I use version 1 with the indirection.

                The class not found is the modified one.

                Other classes in the same directory, for example the interface of the
                modified class, which was also modified, are found. I know this because
                I tried to load them seperately in another URLClassLoader with the same
                URL.

                I hope this helps.

                Thanks a lot.

                • 5. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                  Shigeru Chiba Expert

                  One possibility is that URLClassLoader could find
                  the modified class file but failed bytecode verification.
                  (I'm not sure, though.)

                  Can you make sure that you can load the modified
                  class without a URL class loader (with a system
                  class loader)? If you cannot, that means the modified
                  class file is broken for some reason.

                  The modified class file might include two methods with
                  the same name and signature.

                  nMethod.setBody(cc.getDeclaredMethod(oldMethods[ i ].getName()), null);


                  Do you know getDeclaredMethod() randomly chooses
                  one of the methods with the given name if there are
                  multiple methods with the same name but a different
                  signature?

                  I think the code above should be:

                  nMethod.setBody(oldMethods[ i ], null)


                  Hope it helps.

                  • 6. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                    madman Newbie

                    Result is the same with the system classloader. Maybe the classfile is
                    really broken. But why?

                    I`m sure there are no methods with the same name as I tested
                    it with a class with one method. And the methods added by me have
                    more parameters and the name is "oldName" + "byMe".

                    I also tried setBody(oldMethods[ i ], null), but it was the same result
                    again. I also tried to copy the method body before instrumenting the
                    methods (in this case I then have to instrument all bodies), but again I
                    got a ClassNotFoundException.

                    Thanks for your help, I`m really thankful.

                    • 7. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                      Shigeru Chiba Expert

                      Did the system class loader throw ClassNotFoundException?
                      If the class file is broken, it should throw VerifyError.

                      • 8. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                        madman Newbie

                        Hallo,

                        I tried it with the SystemClassLoader again and you were right. It's
                        no ClassNotFoundException, it throws a java.lang.ClassFormatError.

                        Sorry for writing something different here.

                        I hope that helps.

                        • 9. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                          Shigeru Chiba Expert

                          OK, good. What was the detailed message
                          of that ClassFormatError? No message
                          except the exception type name?

                          • 10. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                            madman Newbie

                            Ok, I'll post the whole StackTrace here:

                            Exception in thread "main" java.lang.ClassFormatError: test/AskMe (Arguments can't fit into locals)
                            at java.lang.ClassLoader.defineClass0(Native Method)
                            at java.lang.ClassLoader.defineClass(ClassLoader.java:537)
                            at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
                            at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
                            at java.net.URLClassLoader.access$100(URLClassLoader.java:55)
                            at java.net.URLClassLoader$1.run(URLClassLoader.java:194)
                            at java.security.AccessController.doPrivileged(Native Method)
                            at java.net.URLClassLoader.findClass(URLClassLoader.java:187)
                            at java.lang.ClassLoader.loadClass(ClassLoader.java:289)
                            at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:274)
                            at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
                            at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)
                            at java.lang.ClassLoader.defineClass0(Native Method)
                            at java.lang.ClassLoader.defineClass(ClassLoader.java:537)
                            at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
                            at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
                            at java.net.URLClassLoader.access$100(URLClassLoader.java:55)
                            at java.net.URLClassLoader$1.run(URLClassLoader.java:194)
                            at java.security.AccessController.doPrivileged(Native Method)
                            at java.net.URLClassLoader.findClass(URLClassLoader.java:187)
                            at java.lang.ClassLoader.loadClass(ClassLoader.java:289)
                            at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:274)
                            at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
                            at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)
                            at java.lang.Class.forName0(Native Method)
                            at java.lang.Class.forName(Class.java:141)
                            at testIt.main(testIt.java:8)


                            I use the command ClassLoader.getSystemClassLoader().loadClass("test.AskMe");
                            to load the class. I think, that should be correct so far. If it isn't, please write it and I'll try it again, of course.

                            Thanks for that real quick reply.

                            • 11. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                              Shigeru Chiba Expert

                               

                              Exception in thread "main" java.lang.ClassFormatError: test/AskMe (Arguments can't fit into locals)


                              I see. Unfortunately, the argument to CtMethod#setBody() must
                              be a method that has the exactly same signature. The reason of
                              your problem is that the number of arguments were different
                              between the original method and the new method.

                              Javassist has not supported yet the function for increasing the number
                              of parameters of an existing method. :<

                              • 12. Re: CtMethod.setBody(CtMethod, ClassMap) does not work
                                madman Newbie

                                Ok, I think must go on with the indirection, then.

                                Great thanks for all you postings and your help.