please review fix for 4766230: invokevirtual override handling
Karen Kinnear
Karen.Kinnear at Sun.COM
Fri Mar 6 11:02:58 PST 2009
Folks,
I have a fix for 4766230: HotSpot vtable construction inconsistencies
cause core dump
webrev link:
http://cr.openjdk.java.net/~acorn/4766230/
Comments on the proposal as well as code reviews welcome.
thanks,
Karen
Virtual Method Table Access Checking Improvements
This bug, 4766230, highlights two problems with the current hotspot
virtual method table logic.
• Problem 1: in debug: there are cases that assert "index out of
bounds", and can crash in product mode.
• This is because in hotspot, the calculation for vtable size is
inconsistent with the calculation for adding vtable entries in the
hotspot implementation.
• Problem 2: the test case for 4766230 reports the wrong result,
"B.m", when it should report "C.m".
• The JVMS Clarifications and Addenda to the 2nd edition clarified
how this should be handled, by defining overriding behavior as
transitive.
Specification background
• Java Language Specification 3rd edition
http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html
• Java Virtual Machine Specification 2nd edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
• Java Virtual Machine Specification Clarifications and Addenda to
2nd edition: for JDK 1.5
http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html
• Why the Definition of Method Override was Revised
http://java.sun.com/docs/books/jls/method-override-rationale.html
Invokevirtual
Invokevirtual handling is based on the following specification extracts:
Invokevirtual, JVMS Clarifications and Addenda to 2nd edition:
Let C be the class of objectref. The actual method to be invoked is
selected by the following lookup procedure:
If C contains a declaration for an instance method M with the same
name and descriptor as the resolved method, and M overrides the
resolved method, then M is the method to be invoked, and the lookup
procedure terminates.
Otherwise, if C has a superclass, this same lookup procedure is
performed recursively using the direct superclass of C ; the method to
be invoked is the result of the recursive invocation of this lookup
procedure.
Otherwise, an AbstractMethodError is raised.
Inheritance rules: JLS 3rd edition, 8.4.8 Inheritance, Overriding, and
Hiding
A class C inherits from its direct superclass and direct
superinterfaces all non-private methods (whether abstract or not) of
the superclass and superinterfaces that are public, protected or
declared with default access in the same package as C and are neither
overridden ('8.4.8.1) nor hidden ('8.4.8.2) by a declaration in the
class.
JLS 3rd edition 8.4.8.1 Overriding (by Instance Methods)
An instance method m1 declared in a class C overrides another instance
method, m2, declared in class A iff all of the following are true:
1. C is a subclass of A.
2. The signature of m1 is a subsignature (§8.4.2) of the
signature of m2.
3. Either
o m2 is public, protected or declared with default access in
the same package as C, or
o m1 overrides a method m3, m3 distinct from m1, m3 distinct from m2,
such that m3 overrides m2.
Moreover, if m1 is not abstract, then m1 is said to implement any and
all declarations of abstract methods that it overrides.
Note that based on the JVMS 3rd edition transitive overriding rules,
we need to do an override check first for the direct superclass and if
the current class does not override the direct superclass, recursively
for the superclass' superclass.
Thanks to Alex Buckley, David Holmes and Vladimir V. Ivanov for help
understanding this.
See attached invokevirtual.classfile51.master.txt for expected behavior.
In all test cases: class B extends A, C extends B. compile-time target
class A, method m, runtime resolved target class C, method m. The
matrix defines behavior for a caller in class C, in same package as C,
in class B, in class A and in same package as class A. | implies
abstract in the results table.
Changes in invokevirtual behavior due to specification changes:
With classfile version 51, which is introduced in JDK7, the
invokevirtual behavior will be fixed to match the clarifications in
the JVMS which were made to handle the following test case:
Description of the original problem from: Why the Definition of Method
Override was Revised (see above):
package P1;
class A {
void m(){System.out.println("A.m");}
public void callM(){ m();}
}
public class B extends A {
protected void m() { System.out.println("B.m");}
}
package P2;
class C extends P1.B {
protected void m(){ System.out.println("C.m");}
public static void main(String[] args) {
C c = new C();
c.callM();
}
Given the original definition of override, we find that the method m()
declared in C overrides the method m() declared in B(), since it has
the same name and signature and is accessible from class C. However,
we a lso find that the method m() declared in C does not override the
method m() declared in A(), since it is private to package P1 and
therefore not accessible from class C.
In this case, invoking callM() on an instance of C causes a call of
m() where the target of the method invocation is an instance of C as
well. However, this call is in the class P1.A, and is attempting to
invoke a package private method of A. Since this method is not
overridden by C, the code will not print "C.m" as expected. Instead,
it prints "B.m".
This is quite unintuitive, and also incompatible with the behavior of
the classic VM.
We believe that this interpretation, while literally accurate, is
undesirable. It prevents a package private method from ever being
overridden outside its package, even if a subclass within the package
has increased the method's accessibility.
Therefore invokevirtual was modified in JVMS Clarifications and
Addenda to 2nd edition to:
Let C be the class of objectref. The actual method to be invoked is
selected by the following lookup procedure:
If C contains a declaration for an instance method M with the same
name and descriptor as the resolved method, and M overrides the
resolved method, then M is the method to be invoked, and the lookup
procedure terminates.
Otherwise, if C has a superclass, this same lookup procedure is
performed recursively using the direct superclass of C ; the method to
be invoked is the result of the recursive invocation of this lookup
procedure.
Otherwise, an AbstractMethodError is raised.
From the JVMS 2nd edition invokevirtual:
Let C be the class of objectref. The actual method to be invoked is
selected by the following lookup procedure:
If C contains a declaration for an instance method with the same
name and descriptor as the resolved method, and the resolved method is
accessible from C, then this is the method to be invoked, and the
lookup procedure terminates.
Otherwise, if C has a superclass, this same lookup procedure is
performed recursively using the direct superclass of C ; the method to
be invoked is the result of the recursive invocation of this lookup
procedure.
Otherwise, an AbstractMethodError is raised.
Note: the key difference here is the phrase: "the resolved method is
accessible from C" which in the Clarifications and Addenda has been
replaced with "M overrides the resolved method"
The old vs. new behavior is in the matrix in the attached
invokevirtual.changed.txt
Invokeinterface handling:
JVMS 3rd edition: invokeinterface:
Let C be the class of objectref. The actual method to be invoked is
selected by the following lookup procedure:
If C contains a declaration for an instance method with the same
name and descriptor as the resolved method, then this is the method to
be invoked, and the lookup procedure terminates.
Otherwise, if C has a superclass, this same lookup procedure is
performed recursively using the direct superclass of C; the method to
be invoked is the result of the recursive invocation of this lookup
procedure.
Otherwise, if the class of objectref does not implement the
resolved interface, invokeinterface throws an
IncompatibleClassChangeError.
Otherwise, if no method matching the resolved name and descriptor
is selected, invokeinterface throws an AbstractMethodError.
Otherwise, if the selected method is not public, invokeinterface
throws an IllegalAccessError.
InvokeInterface Master Table: see attachment invokeinterface.master.txt
A interface class, declares A.m,B compile-time resolved class, C
runtime resolved class
InvokeInterface will ALWAYS invoke C.m if C.m exists, regardless of
overriding or accessibility
InvokeInterface will invoke B.m if C.m does not exist, regardless of
overriding or accessibility
InvokeSpecial handling:
JVMS 3rd edition: invokespecial:
Invoke instance method; special handling for superclass, private, and
instance initialization method invocations
The named method is resolved (§5.4.3.3). Finally, if the resolved
method is protected (§4.7), and it is a member of a superclass of the
current class, and the method is not declared in the same run-time
package (§5.3) as the current class, then the class of objectref must
be either the current class or a subclass of the current class.
Next, the resolved method is selected for invocation unless all of
the following conditions are true:
The ACC_SUPER flag (see Table 4.1, "Class access and property
modifiers") is set for the current class.
The class of the resolved method is a superclass of the current
class.
The resolved method is not an instance initialization method
(§3.9).
If the above conditions are true, the actual method to be invoked
is selected by the following lookup procedure. Let C be the direct
superclass of the current class:
If C contains a declaration for an instance method with the same
name and descriptor as the resolved method, then this method will be
invoked. The lookup procedure terminates.
Otherwise, if C has a superclass, this same lookup procedure is
performed recursively using the direct superclass of C. The method to
be invoked is the result of the recursive invocation of this lookup
procedure.
Otherwise, an AbstractMethodError is raised.
During resolution of the symbolic reference to the method, any of the
exceptions pertaining to method resolution documented in Section
5.4.3.3 can be thrown.
Otherwise, if the resolved method is an instance initialization
method, and the class in which it is declared is not the class
symbolically referenced by the instruction, a NoSuchMethodError is
thrown.
Otherwise, if the resolved method is a class (static) method, the
invokespecial instruction throws an IncompatibleClassChangeError.
Otherwise, if no method matching the resolved name and descriptor
is selected, invokespecial throws an AbstractMethodError.
Otherwise, if the selected method is abstract, invokespecial throws
an AbstractMethodError.
Notes:
The difference between the invokespecial and the invokevirtual
instructions is that invokevirtual invokes a method based on the class
of the object. The invokespecial instruction is used to invoke
instance initialization methods (§3.9) as well as private methods and
methods of a superclass of the current class.
ACC_SUPER:
The setting of the ACC_SUPER flag indicates which of two alternative
semantics for its invokespecial instruction the Java virtual machine
is to express; the ACC_SUPER flag exists for backward compatibility
for code compiled by Sun's older compilers for the Java programming
language. All new implementations of the Java virtual machine should
implement the semantics for invokespecial documented in this
specification. All new compilers to the instruction set of the Java
virtual machine should set the ACC_SUPER flag. Sun's older compilers
generated ClassFile? flags with ACC_SUPER unset. Sun's older Java
virtual machine implementations ignore the flag if it is set.
ACC_SUPER 0x0020 Treat superclass methods specially when invoked by
the invokespecial instruction.
My Translation & implementation notes based on JVMS 3rd edition for
the example:
class hierarchy: a.C extends class a.B extends class A
method access: A.m PUB, a.B.m PROT, a.C.m PUB invoker A static
target: "B.m" dynamic target: "C.m"
5.4.3.3: The named method is resolved: compile-time resolves to B.m
If B.m is protected (4.7)
if the caller is in B
then runtime resolved class must be in B or C
if the caller is in C
then runtime resolved class must be in C
If
OR NOT: caller class A has ACC_SUPER set (Table 4.1) [FALSE]
OR NOT: resolved class B is a superclass of caller class A [FALSE}
OR NOT: B.m is NOT an instance initialization method (3.9)
then invoke B.m
6.6.2 B.m is protected, therefore throw IllegalAccessError? <-
The example case ends here
Else
If superclass of caller contains name/sig match, use it
Else, recursively through that superclass
If none found, throw AbstractMethodError
Note: there is NO mention of overriding or accessibility in
determining resolved method,
except for if the compile-time type is protected
====
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment.html
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: invokevirtual.classfile51.master.txt
Url: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment.txt
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment-0001.html
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: invokevirtual.changed.txt
Url: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment-0001.txt
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment-0002.html
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: invokeinterface.master.txt
Url: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment-0002.txt
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment-0003.html
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: invokespecial.master.txt
Url: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment-0003.txt
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20090306/ee4ca931/attachment-0004.html
More information about the hotspot-runtime-dev
mailing list