4 Replies Latest reply on Feb 23, 2006 5:00 AM by Adrian Brock

    Changing method signatures

    Adrian Brock Master

       


      On Tue, 2006-02-21 at 20:16, Shigeru Chiba wrote:
      Hi Adrian,
      >
      > These methods you added might be dangerous since constant pool
      > entries can be shared among different entities. So changing
      > MethodRef (rarely shared) would be OK but changing Utf8Info is not
      > recommended. So I would like to remove all these methods or at least
      > setUtf8Info(). I recommend creating another Utf8Info and remove
      > unused entries at the end.
      >
      > I will release 3.1 soon. Please reply as soon as possible.
      >
      > Thanks,
      >
      > Chiba
      >
      > On 02/21/2006, at 22:30, Adrian Brock wrote:
      >
      > > User: adrian
      > > Date: 06/02/21 08:30:24
      > >
      > > Modified: src/main/javassist/bytecode ConstPool.java
      > > Log:
      > > Add the ability to change the signature of a constant pool
      > > methodref entry.
      > >
      > > This is useful for JDK5 to 1.4 mapping, e.g.
      > > java.lang.String.clone()Ljava.lang.String; ->
      > > java.lang.String.clone()Ljava.lang.Object;
      >
      >


      I think set setUtf8Info can be private.
      I need the other methods to handle the following case
      otherwise you get a NoSuchMethodError running the code in JDK1.4
      after compiling with JDK5

      public class MyClass
      {
       MyClass getSomething();
      }
      
      public class AnotherClass extends MyClass
      {
       AnotherClass getSomething();
      }
      


      I need to change the signature of AnotherClass::getSomething() back to
       MyClass getSomething();
      


      Changing every reference to this method is what I want to do! ;-)

        • 1. Re: Changing method signatures
          Adrian Brock Master

          I've backed out this change.

          It doesn't work in the case where MyClass is an interface.
          I get verification errors. :-)
          e.g. trying to do MyInterface.toString()

          So I need a different approach.

          • 2. Re: Changing method signatures
            Scott Stark Master

            So what is this new jdk5 behavior, the compiler selects an overloaded method based on the return type? The bytecode is showing two methods for the AnotherClass.getSomething() method, but I don't see how one could use the reflection api to select one or the other. I'll have to try this.

            [starksm@banshee9100 src]$ cat AnotherClass.java ISomething.java MyClass.java
            public class AnotherClass extends MyClass implements ISomething
            {
             public AnotherClass getSomething()
             {
             return this;
             }
             public ISomething getISomething()
             {
             return this;
             }
            }
            
            public interface ISomething
            {
             ISomething getISomething();
            }
            
            public class MyClass
            {
             MyClass getSomething()
             {
             return this;
             }
            }
            


            [starksm@banshee9100 src]$ javap -c -verbose AnotherClass
            Compiled from "AnotherClass.java"
            public class AnotherClass extends MyClass implements ISomething
             SourceFile: "AnotherClass.java"
             minor version: 0
             major version: 49
             Constant pool:
            const #1 = Method #4.#17; // MyClass."<init>":()V
            const #2 = Method #3.#18; // AnotherClass.getSomething:()LAnotherClass;
            const #3 = class #19; // AnotherClass
            const #4 = class #20; // MyClass
            const #5 = class #21; // ISomething
            const #6 = Asciz <init>;
            const #7 = Asciz ()V;
            const #8 = Asciz Code;
            const #9 = Asciz LineNumberTable;
            const #10 = Asciz getSomething;
            const #11 = Asciz ()LAnotherClass;;
            const #12 = Asciz getISomething;
            const #13 = Asciz ()LISomething;;
            const #14 = Asciz ()LMyClass;;
            const #15 = Asciz SourceFile;
            const #16 = Asciz AnotherClass.java;
            const #17 = NameAndType #6:#7;// "<init>":()V
            const #18 = NameAndType #10:#11;// getSomething:()LAnotherClass;
            const #19 = Asciz AnotherClass;
            const #20 = Asciz MyClass;
            const #21 = Asciz ISomething;
            
            {
            public AnotherClass();
             Code:
             Stack=1, Locals=1, Args_size=1
             0: aload_0
             1: invokespecial #1; //Method MyClass."<init>":()V
             4: return
             LineNumberTable:
             line 1: 0
            
            public AnotherClass getSomething();
             Code:
             Stack=1, Locals=1, Args_size=1
             0: aload_0
             1: areturn
             LineNumberTable:
             line 5: 0
            
            public ISomething getISomething();
             Code:
             Stack=1, Locals=1, Args_size=1
             0: aload_0
             1: areturn
             LineNumberTable:
             line 9: 0
            
            public MyClass getSomething();
             Code:
             Stack=1, Locals=1, Args_size=1
             0: aload_0
             1: invokevirtual #2; //Method getSomething:()LAnotherClass;
             4: areturn
             LineNumberTable:
             line 1: 0
            
            }
            



            • 3. Re: Changing method signatures
              Scott Stark Master

              So I don't get it. Reflect will return a "public volatile lang.reflect.MyClass lang.reflect.AnotherClass.getSomething()" (don't know what a volatile method is), but it cannot be explicitly selected, and is not selected based on return type by the compiler:

              public class TestOverload
              {
               static void callBothGetSomething(AnotherClass ac)
               {
               MyClass mc = ac.getSomething();
               AnotherClass mc2 = ac.getSomething();
               }
               public static void main(String[] args) throws Exception
               {
               System.out.println("+++ AnotherClass.class.getMethods():");
               Method[] methods = AnotherClass.class.getMethods();
               for(Method m : methods)
               {
               System.out.println(m);
               }
               System.out.println("+++ AnotherClass.class.getMethod(getSomething):");
               Class[] sig = {};
               Method getSomething = AnotherClass.class.getMethod("getSomething", sig);
               System.out.println(getSomething);
               }
              }
              


              Output:
              +++ AnotherClass.class.getMethods():
              public volatile lang.reflect.MyClass lang.reflect.AnotherClass.getSomething()
              public lang.reflect.AnotherClass lang.reflect.AnotherClass.getSomething()
              public lang.reflect.ISomething lang.reflect.AnotherClass.getISomething()
              public native int java.lang.Object.hashCode()
              public final native java.lang.Class java.lang.Object.getClass()
              public final void java.lang.Object.wait() throws java.lang.InterruptedException
              public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
              public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
              public boolean java.lang.Object.equals(java.lang.Object)
              public final native void java.lang.Object.notify()
              public final native void java.lang.Object.notifyAll()
              public java.lang.String java.lang.Object.toString()
              +++ AnotherClass.class.getMethod(getSomething):
              public lang.reflect.AnotherClass lang.reflect.AnotherClass.getSomething()
              


              javap of the callBothGetSomething method only shows "public lang.reflect.AnotherClass lang.reflect.AnotherClass.getSomething()" being selected:

              static void callBothGetSomething(lang.reflect.AnotherClass);
               Code:
               0: aload_0
               1: invokevirtual #2; //Method lang/reflect/AnotherClass.getSomething:()Ll
              ang/reflect/AnotherClass;
               4: astore_1
               5: aload_0
               6: invokevirtual #2; //Method lang/reflect/AnotherClass.getSomething:()Ll
              ang/reflect/AnotherClass;
               9: astore_2
               10: return
              



              • 4. Re: Changing method signatures
                Adrian Brock Master

                I think we'll have to rewrite the method invocation to be something like:

                result = (DerivedClassReturnValue) ((DefiningSuperClass) derivedClassInstance).doSomething();
                


                The volatile flag on the method, probably just tells the JDK5 runtime that the return type
                has been changed?