Bug related to StringBuilder.append method in IBM JDK for AIX

This article explores how a bug in a vendor-specific JDK might break the platform independence of your Java programs. To be more specific, it explores a bug due to which Java programs, compiled with a specific version of IBM’s JDK for AIX platform, fail on JVMs other than IBM J9 JVM. This bug is related to append method in java.lang.StringBuilder class.

 

Following are the details about the OSs, JDKs and JVMs around which this article revolves:

 

AIX platform

OS version: 5.3

OS architecture: ppc64

JDK: IBM 64-bit SDK for AIX, Java 2 Technology Edition, Version 5

JVM: IBM J9 VM (build 2.3, J2RE 1.5.0 IBM J9 2.3 AIX ppc64-64 j9vmap6423-20061003

 

HP-UX platform

OS version: B.11.23

OS architecture: PA_RISC2.0 architecture

JDK: HP-UX JDK for the Java(tm) Standard Edition Platform for HP Integrity and HP9000
PA-RISC systems

JVM: Java HotSpot(TM) Server VM (build 1.6.0.02 jinteg:09.26.08-16:50 PA2.0 (aCC_AP),
mixed mode)

 

What is the bug?

Consider the following Test class:

public class Test {

    public static void main(String[] args) {

        final StringBuilder sb2 = new StringBuilder(“Hello “);

        sb2.append(new StringBuilder(” World!”));

         System.out.println(sb2.toString());

    }

}

 This is a very simple and syntactically correct Java class, which is expected to print “Hello World!” string on the standard output.

It is also expected that if the byte-code of this class executes successfully on one JVM, it should execute successfully on other JVMs too. But surprisingly, if we compile the above source code on IBM JDK and execute the compiled code on HotSpot JVM, it throws the following error:

java.lang.IllegalAccessError: tried to access method
java.lang.StringBuilder.append(Ljava/lang/StringBuilder;)Ljava/lang/StringBuilder;

 But, if the same code is compiled on HP-UX JDK, it runs successfully on both HotSpot and IBM J9 JVMs.

 Why the IllegalAccessError?

Let us look at the error message carefully. It says there was an ‘access’ error while accessing StringBuilder.append(StringBuilder sb) method. We need to inspect the access qualifiers of this method. This information can be easily obtained by dis-assembling the StringBuilder class.

 

Dis-assembling the class file is not same as de-compiling it. De-compilation means converting binary class into Java language source-code. Dis-assembly means converting the binary class into human-readable byte-code format i.e. the output of dis-assembly is still the byte-code but in easily understandable text format.

 

 In HP-UX JDK, we find that the method StringBuilder.append(StringBuilder) is declared as private.

> javap -private java.lang.StringBuilder | grep append

privatejava.lang.StringBuilder append(java.lang.StringBuilder);

 Now we know the root cause for the IllegalAccessError; the byte-code of our class is trying to access a private method of a JDK class and the JVM detects this violation.

But, isn’t the compiler supposed to catch such errors during compilation? And why this error does not occur with the class compiled with HP-UX JDK? To answer these questions, we need to understand the difference between the byte-codes generated by the two different JDKs.

 What is the difference between byte-codes produced on different JDKs?

Let us first dis-assemble the byte-code produced in HP-UX JDK. Following is the disassembled code:

public static void main(java.lang.String[]);

Code:

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: aload_1

11: new #2; //class java/lang/StringBuilder

14: dup

15: ldc #5; //String World!

17: invokespecial #4; //Method java/lang/StringBuilder.”<init>”:(Ljava/lang/String;)V

20: invokevirtual #6; //Method
java/lang/StringBuilder.append:(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;

23: pop

24: getstatic #7; //Field
java/lang/System.out:Ljava/io/PrintStream;

27: aload_1

28: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

31: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

34: return

 The line which represents the call to the StringBuilder.append method is:

invokevirtual #6; //Method java/lang/StringBuilder.append: Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;

Now, let us dis-assemble the class file which was compiled with IBM JDK. The call to the StringBuilder.append method is represented by the following byte-code:

invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/StringBuilder;)Ljava/lang/StringBuilder;

It shows that the class compiled with IBM JDK tries to call that append method whose parameter type is java.lang.StringBuilder. But, the class compiled with HP-UX JDK tries to call that method whose parameter type is java.lang.CharSequence.

 Why different JDKs create different byte-code for same source-code?

The source code we are concentrating on is:

sb2.append(new StringBuilder(” World!”))

In HP-UX JDK, StringBuilder.append(StringBuilder) is a private method, which cannot be called by other classes. Therefore, the compiler selects the next closest matching method, which as per the rules of overloading, is StringBuilder.append(CharSequence).

In IBM JDK, StringBuilder.append(StringBuilder) is a public method. Hence, the compiler selects this method to be called.

So, there is nothing wrong with the compiler. The difference in byte-code is due to different access qualifiers for StringBuilder.append(StringBuilder) method in these two
different JDKs.

 Why the class compiled with HP-UX JDK runs successfully on both JVMs?

In the class compiled with HP-UX, the byte-code calls append(CharSequence) method. This method has public access in both JDKs and hence this class runs successfully on both HotSpot and J9 JVMs.

 Where is the bug?

See bug IY97884 where IBM accepts that the problem is because they add an extra public append(StringBuilder) method in StringBuilder class. As mentioned in the bug report, the problem summary is that “The J9 JCL inadvertently includes an extra append() method for the StringBuilder class.”

(I will be searching for what JCL stands for. This term seems to be relevant only for IBM JDK/JVM. )

 What is the solution?

Temporary solution

While passing an instance of StringBuilderto appendmethod, cast this instance to CharSequencetype. If this is done, the compiler creates the byte-code in which the call is made to StringBuilder.append(CharSequence) method, which is a public method.

For e.g., we need to change our test class as following:

public class Test {

    public static void main(String[] args) {

        final StringBuilder sb2 = new StringBuilder(“Hello “);

        sb2.append(((CharSequence) new StringBuilder(” World!”)));

         System.out.println(sb2.toString());

    }

}

 Permanent solution

As mentioned at IY97884, IBM has fixed this bug by removing the public append(StringBuilder) method. And it is supposed to be fixed in 5.0.0 SR6.

 Conclusion

On different JDKs or with different compilers, the same source code might produce different byte-code. In some cases, this difference in the byte-code might break the platform independence of your programs.

Advertisements
This entry was posted in Java and tagged , , , , , , , , , . Bookmark the permalink.

5 Responses to Bug related to StringBuilder.append method in IBM JDK for AIX

  1. Anonymous says:

    very informative!

  2. Vikas Nalwar says:

    IBM JVM releases are often buggy.

  3. BG says:

    JCL == Java Class Libraries.
    Informative article, btw [by the way :)]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s