For all those spec believers out there: what would you think if I told you that all of the JVM implementations consistently violate the spec for at least one particular instance?
Our story begins on a cold and lonely evening when I discover a bug in JavaRebel in conjunction with Xerces XMLEntityManager. The problem was with the following method:
[java]
public String[] getRecognizedProperties() {
return (String[])(RPROPERTIES.clone());
}
[/java]
However when I looked at the class bytecode I saw a different picture:
[java]
{
GETSTATIC XMLEntityManager.RPROPERTIES : String[]
INVOKEVIRTUAL Object.clone() : Object[]
CHECKCAST String[]
ARETURN
}
[/java]
For those of you who do not spend hours staring at the Java bytecode, it does the following:
- Retrieve the
RPROPERTIESstatic field value (which is of typeString[]) and put it on the top of the stack. - Call
Object.clone()using the top of the stack as target object (in our case the array). The target is popped off the stack and the result is placed on top of the stack instead of it. - Cast the top of the stack to a
String[] - Return the top of the stack as method result.
Whooph. Now that is all fine except for one tiny thing. This is not legal. Object.clone() is a protected method and the JVM spec (see 2.7.4, INVOKEVIRTUAL) allows calls to protected method only if the following is true:
- The accessing class is a subclass of the target class or is the same class (check,
XMLEntityManageris a subclass ofObject, so’s everything else). - The class of the target object (the actual runtime class of the object, not the target class we use in the signature) is either a subclass of the accessing class or is the same class.
The second check may sound crazy, but it restricts the calls to protected methods only to the classes inheriting from the target class, as you would expect. It can also only be enforced during runtime and is the cause of the IllegalAccessError you may see once in a while.
And obviously in our case String[] that is the actual type of the target object is in no way a subclass of the XMLEntityManager. This call should have cause an error in the JVM, but for some inexplicable reason it doesn’t. Further tests show that this is only limited (AFAIK) to the Object.clone() call, which is public as far as JVM cares.
A little googling turned up two JVM bugs that were discussing the same issue (bug 1, bug 2). Turns out the Sun javac compiler has been emitting wrong code at least until 1.4.2 and the JVMs have accommodated accordingly right until today.
Does this have any practical importance? Not for most of you, but if you handle Java bytecode in any way be prepared to handle this exception as well. To me it proves that there is more to Java than just the spec.