11 Replies Latest reply on Jul 2, 2009 7:20 AM by chiba

    JBossRetro cannot replace final reference to StringBuilder

    kfinkels

      Hi,
      when replacing the StringBuilder with JBossStringBuilder there is a problem with final field referring to StringBuilder - it is not being renamed.
      I have the following code for example:

      button.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e) {
      final StringBuilder builder = new StringBuilder("Hello");
      frame.add(new JLabel(builder.toString()));
      }
      });

      and the manipulated byte code is:

      public void actionPerformed(java.awt.event.ActionEvent e);
      0 new org.jboss.lang.JBossStringBuilder [27]
      3 dup
      4 ldc <String "Hello"> [29]
      .......
      .......
      Local variable table:
      .......
      [pc: 0, pc: 30] local: e index: 1 type: java.awt.event.ActionEvent
      [pc: 10, pc: 30] local: builder index: 2 type: java.lang.StringBuilder

      is there an option to rename class referring from final field???

      10x,
      Keren

        • 1. Re: JBossRetro cannot replace final reference to StringBuild
          pgier

          Can you file a JIRA issue for this?
          https://jira.jboss.org/jira/browse/JBBUILD
          If you create a patch that fixes it, I'd be happy to apply it. If not, it may take a while for it to be fixed.

          • 2. Re: JBossRetro cannot replace final reference to StringBuild

            This looks to be a problem in javassist rather jboss-retro

            The following test class

            public class Test
            {
             public static void main(String[] args) throws Exception
             {
             final StringBuilder s = new StringBuilder("hello");
             System.out.println(s.toString());
             }
            }
            


            When run through the following simple javassist program to change the class names

            import java.io.DataInputStream;
            import java.io.DataOutputStream;
            import java.io.FileInputStream;
            import java.io.FileOutputStream;
            import javassist.bytecode.ClassFile;
            
            public class Rename
            {
             public static void main(String[] args) throws Exception
             {
             DataInputStream dis = new DataInputStream(new FileInputStream("Test.class"));
             ClassFile cf = new ClassFile(dis);
             cf.renameClass("Test", "Test1");
             cf.renameClass("java/lang/StringBuilder", "test/StringBuilder");
             DataOutputStream dos = new DataOutputStream(new FileOutputStream("Test1.class"));
             cf.write(dos);
             }
            }
            


            Isn't renaming the literal used by the local variable table
            I think because it is marked as type "Asciz" rather than "class"?

            BEFORE
            $ javap -c -verbose Test
            Compiled from "Test.java"
            public class Test extends java.lang.Object
             SourceFile: "Test.java"
             minor version: 0
             major version: 49
             Constant pool:
            const #1 = Method #9.#20; // java/lang/Object."<init>":()V
            const #2 = class #21; // java/lang/StringBuilder
            const #3 = String #22; // hello
            const #4 = Method #2.#23; // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
            const #5 = Field #24.#25; // java/lang/System.out:Ljava/io/PrintStream;
            const #6 = Method #2.#26; // java/lang/StringBuilder.toString:()Ljava/lang/String;
            const #7 = Method #27.#28; // java/io/PrintStream.println:(Ljava/lang/String;)V
            const #8 = class #29; // Test
            const #9 = class #30; // java/lang/Object
            const #10 = Asciz <init>;
            const #11 = Asciz ()V;
            const #12 = Asciz Code;
            const #13 = Asciz LineNumberTable;
            const #14 = Asciz main;
            const #15 = Asciz ([Ljava/lang/String;)V;
            const #16 = Asciz Exceptions;
            const #17 = class #31; // java/lang/Exception
            const #18 = Asciz SourceFile;
            const #19 = Asciz Test.java;
            const #20 = NameAndType #10:#11;// "<init>":()V
            const #21 = Asciz java/lang/StringBuilder;
            const #22 = Asciz hello;
            const #23 = NameAndType #10:#32;// "<init>":(Ljava/lang/String;)V
            const #24 = class #33; // java/lang/System
            const #25 = NameAndType #34:#35;// out:Ljava/io/PrintStream;
            const #26 = NameAndType #36:#37;// toString:()Ljava/lang/String;
            const #27 = class #38; // java/io/PrintStream
            const #28 = NameAndType #39:#32;// println:(Ljava/lang/String;)V
            const #29 = Asciz Test;
            const #30 = Asciz java/lang/Object;
            const #31 = Asciz java/lang/Exception;
            const #32 = Asciz (Ljava/lang/String;)V;
            const #33 = Asciz java/lang/System;
            const #34 = Asciz out;
            const #35 = Asciz Ljava/io/PrintStream;;
            const #36 = Asciz toString;
            const #37 = Asciz ()Ljava/lang/String;;
            const #38 = Asciz java/io/PrintStream;
            const #39 = Asciz println;
            
            {
            public Test();
             Code:
             Stack=1, Locals=1, Args_size=1
             0: aload_0
             1: invokespecial #1; //Method java/lang/Object."<init>":()V
             4: return
             LineNumberTable:
             line 1: 0
            
            public static void main(java.lang.String[]) throws java.lang.Exception;
             Code:
             Stack=3, Locals=2, Args_size=1
             0: new #2; //class java/lang/StringBuilder
             3: dup
             4: ldc #3; //String hello
             6: invokespecial #4; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
             9: astore_1
             10: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
             13: aload_1
             14: invokevirtual #6; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
             17: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
             20: return
             LineNumberTable:
             line 5: 0
             line 6: 10
             line 7: 20
             Exceptions:
             throws java.lang.Exception
            }
            


            AFTER
            [ejort@warjort final-test]$ javap -c -verbose Test1
            Compiled from "Test.java"
            public class Test1 extends java.lang.Object
             SourceFile: "Test.java"
             minor version: 0
             major version: 49
             Constant pool:
            const #1 = Method #9.#20; // java/lang/Object."<init>":()V
            const #2 = class #41; // test/StringBuilder
            const #3 = String #22; // hello
            const #4 = Method #2.#23; // test/StringBuilder."<init>":(Ljava/lang/String;)V
            const #5 = Field #24.#25; // java/lang/System.out:Ljava/io/PrintStream;
            const #6 = Method #2.#26; // test/StringBuilder.toString:()Ljava/lang/String;
            const #7 = Method #27.#28; // java/io/PrintStream.println:(Ljava/lang/String;)V
            const #8 = class #40; // Test1
            const #9 = class #30; // java/lang/Object
            const #10 = Asciz <init>;
            const #11 = Asciz ()V;
            const #12 = Asciz Code;
            const #13 = Asciz LineNumberTable;
            const #14 = Asciz main;
            const #15 = Asciz ([Ljava/lang/String;)V;
            const #16 = Asciz Exceptions;
            const #17 = class #31; // java/lang/Exception
            const #18 = Asciz SourceFile;
            const #19 = Asciz Test.java;
            const #20 = NameAndType #10:#11;// "<init>":()V
            const #21 = Asciz java/lang/StringBuilder;
            const #22 = Asciz hello;
            const #23 = NameAndType #10:#32;// "<init>":(Ljava/lang/String;)V
            const #24 = class #33; // java/lang/System
            const #25 = NameAndType #34:#35;// out:Ljava/io/PrintStream;
            const #26 = NameAndType #36:#37;// toString:()Ljava/lang/String;
            const #27 = class #38; // java/io/PrintStream
            const #28 = NameAndType #39:#32;// println:(Ljava/lang/String;)V
            const #29 = Asciz Test;
            const #30 = Asciz java/lang/Object;
            const #31 = Asciz java/lang/Exception;
            const #32 = Asciz (Ljava/lang/String;)V;
            const #33 = Asciz java/lang/System;
            const #34 = Asciz out;
            const #35 = Asciz Ljava/io/PrintStream;;
            const #36 = Asciz toString;
            const #37 = Asciz ()Ljava/lang/String;;
            const #38 = Asciz java/io/PrintStream;
            const #39 = Asciz println;
            const #40 = Asciz Test1;
            const #41 = Asciz test/StringBuilder;
            
            {
            public Test1();
             Code:
             Stack=1, Locals=1, Args_size=1
             0: aload_0
             1: invokespecial #1; //Method java/lang/Object."<init>":()V
             4: return
             LineNumberTable:
             line 1: 0
            
            public static void main(java.lang.String[]) throws java.lang.Exception;
             Code:
             Stack=3, Locals=2, Args_size=1
             0: new #2; //class test/StringBuilder
             3: dup
             4: ldc #3; //String hello
             6: invokespecial #4; //Method test/StringBuilder."<init>":(Ljava/lang/String;)V
             9: astore_1
             10: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
             13: aload_1
             14: invokevirtual #6; //Method test/StringBuilder.toString:()Ljava/lang/String;
             17: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
             20: return
             LineNumberTable:
             line 5: 0
             line 6: 10
             line 7: 20
             Exceptions:
             throws java.lang.Exception
            }
            


            See the in both:
            const #21 = Asciz java/lang/StringBuilder;

            So this needs to be raised as a bug in javassist rather than jboss-retro

            • 3. Re: JBossRetro cannot replace final reference to StringBuild

               

              "adrian@jboss.org" wrote:

              Isn't renaming the literal used by the local variable table
              I think because it is marked as type "Asciz" rather than "class"?


              Actually the problem is that renameClass(s) is not processing the
              LocalVariableAttributes at all. The LocalVariableAttributes (which is where
              the local variable table lives) contains an index into the constant pool for the types.
              This needs to be updated to the new value.

              • 4. Re: JBossRetro cannot replace final reference to StringBuild
                • 5. Re: JBossRetro cannot replace final reference to StringBuild
                  chiba

                  Yes, renameClass() does not update local variable tables but this should not cause any problems at runtime because the JVM ignore local variable tables.

                  So I am wondering what is Keren's problem... Keren, could you tell us?

                  • 6. Re: JBossRetro cannot replace final reference to StringBuild
                    kfinkels

                    I have a class like that:

                    public class LabelTest {
                    public LabelTest(){
                    final JFrame frame = new JFrame();
                    frame.setSize(100, 100);
                    final StringBuilder builder = createStringBuilder();
                    JLabel label = new JLabel(builder.toString());

                    frame.add(label);
                    frame.setVisible(true);;
                    }

                    private StringBuilder createStringBuilder(){
                    return new StringBuilder("Hello");
                    }
                    }

                    the .class - after the manipulation of jbossretro is still holding the reference to StringBuilder in the Local variable table

                    • 7. Re: JBossRetro cannot replace final reference to StringBuild
                      chiba

                      Hi,

                      I understand the .class file after the manipulation has a wrong local variable
                      table but my question is why you think this is a prlbem.

                      Since the local variable table is not used *at all* at runtime, I guess you saw
                      another real problem and decompiled the .class file, and then you found the
                      local variable table contained a wrong entry. Another guess is you are using
                      another tool that refers to the local variable table.

                      In either case, I am happy to fix the bug(?) of Javassist but I would also like
                      to know what is your real problem.

                      Thank you!

                      • 8. Re: JBossRetro cannot replace final reference to StringBuild
                        kfinkels

                        I has a verify... error and I got an error of with arguments not compatible to the method.
                        I've looked in the code and it was somehow related to a final field.
                        I'm not sure that the problem I had was related to the fact that there is a reference in the local variable table to the StringBuilder or something else.
                        I just wonder if it could cause a problem...

                        • 9. Re: JBossRetro cannot replace final reference to StringBuild
                          chiba

                          Thank you, now I understand.

                          For further inspection, can you post all the bytecode?

                          public void actionPerformed(java.awt.event.ActionEvent e);
                          0 new org.jboss.lang.JBossStringBuilder [27]
                          3 dup
                          4 ldc <String "Hello"> [29]
                          .......
                          .......
                          (I want to know the rest)
                          


                          Also, I would also like to know the detailed message of the verification error.

                          Thank you for your cooperation!



                          • 10. Re: JBossRetro cannot replace final reference to StringBuild
                            kfinkels

                            sorry for the late response. I was not able to log into the forum for weeks.
                            here is the byte code for the following source code:

                            source code:

                            button.addActionListener(new ActionListener(){
                            public void actionPerformed(ActionEvent e) {
                            StringBuilder builder = new StringBuilder();
                            builder.append("Hello");
                            label = new JLabel(builder.toString());
                            frame.add(label);
                            }
                            });

                            bytecode after running with jboss-retro:

                            public void actionPerformed(java.awt.event.ActionEvent e);
                            0 new org.jboss.lang.[img]JBossStringBuilder [/img][23]
                            3 dup
                            4 invokespecial org.jboss.lang.JBossStringBuilder() [25]
                            7 astore_2 [builder]
                            8 aload_2 [builder]
                            9 ldc <String "Hello"> [26]
                            11 invokevirtual org.jboss.lang.JBossStringBuilder.append(java.lang.String) : org.jboss.lang.JBossStringBuilder [28]
                            14 pop
                            15 aload_0 [this]
                            16 getfield demoAOP.HelloMainFrame$1.this$0 : demoAOP.HelloMainFrame [12]
                            19 new javax.swing.JLabel [32]
                            22 dup
                            23 aload_2 [builder]
                            24 invokevirtual org.jboss.lang.JBossStringBuilder.toString() : java.lang.String [34]
                            27 invokespecial javax.swing.JLabel(java.lang.String) [38]
                            30 invokestatic demoAOP.HelloMainFrame.access$0(demoAOP.HelloMainFrame, javax.swing.JLabel) : void [41]
                            33 aload_0 [this]
                            34 getfield demoAOP.HelloMainFrame$1.this$0 : demoAOP.HelloMainFrame [12]
                            37 invokestatic demoAOP.HelloMainFrame.access$1(demoAOP.HelloMainFrame) : javax.swing.JFrame [47]
                            40 aload_0 [this]
                            41 getfield demoAOP.HelloMainFrame$1.this$0 : demoAOP.HelloMainFrame [12]
                            44 invokestatic demoAOP.HelloMainFrame.access$2(demoAOP.HelloMainFrame) : javax.swing.JLabel [51]
                            47 invokevirtual javax.swing.JFrame.add(java.awt.Component) : java.awt.Component [55]
                            50 pop
                            51 return
                            Line numbers:
                            [pc: 0, line: 31]
                            [pc: 8, line: 32]
                            [pc: 15, line: 33]
                            [pc: 33, line: 34]
                            [pc: 51, line: 35]
                            Local variable table:
                            [pc: 0, pc: 52] local: this index: 0 type: new demoAOP.HelloMainFrame(){}
                            [pc: 0, pc: 52] local: e index: 1 type: java.awt.event.ActionEvent
                            [pc: 8, pc: 52] local: builder index: 2 type: java.lang.StringBuilder

                            • 11. Re: JBossRetro cannot replace final reference to StringBuild
                              chiba

                              I fixed the bug. Now a local variable table will be correctly updated.