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