invokevirtual on package private override in different classloader

Karen Kinnear Karen.Kinnear at Sun.COM
Fri Apr 24 05:43:06 PDT 2009


Thanks David for catching that.

thanks,
Karen

On Apr 23, 2009, at 6:55 PM, David Holmes - Sun Microsystems wrote:

> Hi Karen,
>
> I was okay with the explanation until this point:
>
> > This discussion of access control omits a related restriction on the
> > target of a protected field access or method invocation (the target
> > must be of class D or a subtype of D). That requirement is checked  
> as
> > part of the verification process (§5.4.1); it is not part of link- 
> time
> > access control.
> > ==
> >
> > The point here is that the virtual machine in the case of  
> invokevirtual,
> > does an accessibility check for the compile-time resolved method,
> > Square.getColour() in your example, but does NOT perform an
> > accessibility check on the link-time resolved method, or
> > CustomSquare.getColour() in your example.
>
> I don't see the relevance of the quoted rule in this case, because  
> this is not about the target of protected field/method access.
>
> As far as I can see the reason there is no IllegalAccessError is  
> because there is no illegal access:
> - statically Printer's call to Square.getColor is fine as they are  
> in the same compile-time package
> - at run-time because CustomSquare.getColor does not override  
> Square.getColor (because they are in different run-time packages),  
> CustomSquare.getColor is not selected, but rather Square.getColor is
> - At runtime Printer and Square are in the same runtime package, so  
> Printer's invocation of Square.getColor is legal - even on a  
> CustomSquare instance
>
> In short there is no IllegalAccessError because  
> CustomSquare.getColor is not selected to be called.
>
> Cheers,
> David Holmes
>
> Karen Kinnear said the following on 04/24/09 07:35:
>> Jeff,
>> Thank you so much for making this easy for me to reproduce.
>> I think you have two questions here:
>> 1) why do you get "red" when you use a different class loader
>> for CustomSquare and
>> 2) why do you not get an IllegalAccessError when calling
>> package private CustomSquare.getColour() from a
>> different runtime package.
>> These are very important questions and use cases related to
>> the first caused significant confusion in the past which
>> actually led to a specification modification.
>> Let me see if I can explain, first why "red", not "blue",
>> and second why you don't get an IllegalAccessError.
>> First, I too found that Printer and Square are loaded by the
>> same class loader, and CustomSquare is loaded by a different class
>> loader. I ran a fastdebug vm with the flag: -XX: 
>> +PrintSystemDictionaryAtExit. This will print out on exit, the
>> internal vm cache of loaded classes including their class loaders.
>> To summarize/abbreviate the scenario:
>> class Main: // class loader 1
>>   Printer.print((Square)CustomSquare instance);
>> package 1, Printer.print // class loader 1
>>    System.out.println(square.getColour())
>> package 1, class Square // class loader 1
>>    public getColour() returns "blue"
>> package 1, class customSquare // class loader 2
>>    package private getColour() returns "red"
>> The first question is why does the call in Printer to  
>> Square.getColour()
>> print "red" when the class customSquare is loaded by
>> a different class loader, but print "blue" when loaded by the
>> same class loader.
>> Let's start with the call in Printer. This translates to
>> invokevirtual(...Square.getColour())
>> The Clarifications and Amendments to Java Virtual Machine  
>> Specification, 2nd edition definition of invokevirtual says:
>> http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html
>> ==
>> The lookup algorithm used by the invokevirtual  instruction (pages  
>> 291-292 of the JVMS, 2nd edition) should be revised as follows:
>>    Let C be the class of the target of the method invocation. 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.
>> The text highlighted in red is the only change. It replaces the text
>>    and the resolved method is accessible from C
>> in the current specification. See the revision of the definition of  
>> method override in the JLS 3rd Edition page for more details.
>> ==
>> // First we do the static compile-time resolution using the  
>> constant pool, which in this case refers to Square.getColour()
>> The named method is resolved (§5.4.3.3).
>> // Then we do the dynamic run-time resolution using CustomSquare as  
>> the // target of the method invocation
>>    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.
>> // The key question is: Does CustomSquare.getColour() override
>> // Square.getColour()?
>> // I'll get to the definitions of inherit and override in a second.
>> // Result is:
>> // If they use the same class loader: yes CustomSqure.getColour()  
>> overrides Square.getColour()
>> // If they use different class loaders: no CustomSquare.getColor()  
>> does NOT override Square.getColour()
>>    * 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.
>> // If CustomSquare.getColour() does NOT override Square.getColour(),
>> // then we use the method Square.getColour(), since Square is the
>> // direct superclass of CustomSquare.
>> Definitions of Inherit and Override:
>> ===
>> 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.
>> ===
>> So CustomSquare.getColour() does NOT override Square.getColour()  
>> because
>> it is NOT in the same runtime package as C.
>> ===========
>> ===========
>> Second question:
>> Why do you not get an IllegalAccessException when invoking package- 
>> private CustomSquare.getColour() from Printer.print() which
>> is in a different package?
>> We had the same question ourselves a couple of years ago and
>> researched this.
>> If you look closely at JVMS 2nd edition 5.4.4 Access Controls,
>> which goes into the details of accessibility checks for methods,
>> read the final sentences which say:
>> ==
>> This discussion of access control omits a related restriction on  
>> the target of a protected field access or method invocation (the  
>> target must be of class D or a subtype of D). That requirement is  
>> checked as part of the verification process (§5.4.1); it is not  
>> part of link-time access control.
>> ==
>> The point here is that the virtual machine in the case of  
>> invokevirtual,
>> does an accessibility check for the compile-time resolved method,
>> Square.getColour() in your example, but does NOT perform an
>> accessibility check on the link-time resolved method, or  
>> CustomSquare.getColour() in your example.
>> We proposed changing this behavior, and given that it has always been
>> this way, changing the specification and the multiple virtual
>> machines in the field would be highly likely to break multiple
>> shipping applications. So we decided it was not a good change
>> to make.
>> I hope this helps explain the current behavior, do let me
>> know if any of this needs further clarification or if I
>> misunderstood your original questions.
>> thanks,
>> Karen
>> p.s. you can get even more explanation of the inheritance,  
>> overriding, invokevirtual, invokespecial, etc. behavior, etc. in
>> the evaluation of the following bug:
>> http://monaco.sfbay.sun.com/detail.jsf?cr=4766230
>> p.p.s. The package private discussion is under
>> http://monaco.sfbay.sun.com/detail.jsf?cr=6810795
>> Jeffrey Sinclair wrote:
>>> Karen,
>>>
>>> Just to say that there is one slight bug in my reproduction  
>>> (although
>>> the result of 'Red' stays the same).
>>>
>>> My debug statements for which class loader the class was loaded  
>>> from is
>>> incorrect, they are done in the constructor when they should have  
>>> been
>>> done in a static block in the class.
>>>
>>> i.e.
>>> public class Square {
>>>  static {
>>>    System.out.println("Square loaded by classloader: "         +  
>>> Square.class.getClassLoader());
>>>  }
>>>
>>> This will give the output:
>>>
>>> Printer loaded by classloader: sun.misc.Launcher 
>>> $AppClassLoader at 553f5d07
>>> Square loaded by classloader: sun.misc.Launcher 
>>> $AppClassLoader at 553f5d07
>>> CustomSquare loaded by classloader: java.net.URLClassLoader at 3ae48e1b
>>> Red
>>>
>>> Which is still unexpected since I'm getting 'Red'.
>>>
>>> Regards,
>>>
>>> Jeff
>>>
>>> On Sat, 2009-04-18 at 17:32 +0100, Jeffrey Sinclair wrote:
>>>
>>>> Karen,
>>>>
>>>> Please find attached a zip file which packages the example up  
>>>> with a
>>>> build script that uses javac and a plain old run script.
>>>>
>>>> Simply run build.sh followed by run.sh (assumes that javac and  
>>>> java are
>>>> on the PATH).
>>>>
>>>> Regards,
>>>>
>>>> Jeff
>>>>
>>>> On Thu, 2009-04-16 at 17:42 -0400, Karen Kinnear wrote:
>>>>
>>>>> Jeff,
>>>>>
>>>>> Thank you for sending me this information. I have a theory, but I
>>>>> would be much more comfortable running the test first. I have   
>>>>> experimented
>>>>> with this, but I'd feel much more comfortable duplicating your  
>>>>> results.
>>>>>
>>>>> I do appreciate you offering to package this up using plain old  
>>>>> javac/  and
>>>>> a run script - I don't have Eclipse installed and much as I've  
>>>>> heard it
>>>>> is a great product, I don't have the cycles to do that in the  
>>>>> near  future.
>>>>>
>>>>> thanks so much,
>>>>> Karen
>>>>>
>>>>> On Apr 14, 2009, at 5:33 PM, Jeffrey Sinclair wrote:
>>>>>
>>>>>
>>>>>> Karen,
>>>>>>
>>>>>> Thanks for getting back to me.
>>>>>>
>>>>>> I was using 1.6.0_10 and have now tried 1.6.0_13 and get the same
>>>>>> result. Specifically I've tried the following versions:
>>>>>>
>>>>>> java version "1.6.0_10"
>>>>>> Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
>>>>>> Java HotSpot(TM) 64-Bit Server VM (build 11.0-b15, mixed mode)
>>>>>>
>>>>>> java version "1.6.0_13"
>>>>>> Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
>>>>>> Java HotSpot(TM) 64-Bit Server VM (build 11.3-b02, mixed mode)
>>>>>>
>>>>>> I've attached my source as Eclipse projects. The URL in the  
>>>>>> main  method
>>>>>> points to the class files generated by the project with the
>>>>>> CustomSquare. I've also added some debug info relating to which  
>>>>>> class
>>>>>> loader is being used to load the Square, Printer and Custom  
>>>>>> Square
>>>>>> respectively:
>>>>>>
>>>>>> Printer loaded by classloader: sun.misc.Launcher  
>>>>>> $AppClassLoader at 553f5d07
>>>>>> Square loaded by classloader: java.net.URLClassLoader at 3ae48e1b
>>>>>> CustomSquare loaded by classloader:  
>>>>>> java.net.URLClassLoader at 3ae48e1b
>>>>>> Red
>>>>>>
>>>>>> I still get Red. The key point is that the CustomSquare is not  
>>>>>> visible
>>>>>> at all to the Printer code. If I stick the CustomSquare in the  
>>>>>> same
>>>>>> project as Printer it will then be loaded by the AppClassLoader  
>>>>>> and I
>>>>>> get blue. However when it is in a completely separate project  
>>>>>> and  loaded
>>>>>> via the URLClassLoader (and Printer loaded via the  
>>>>>> AppClassLoader) I
>>>>>> always get Red. If I then change getColour() to be public in  
>>>>>> both  Square
>>>>>> and CustomSquare I get blue.
>>>>>>
>>>>>> This is completely baffling me. I may be doing something really  
>>>>>> silly
>>>>>> but I can't see it :)
>>>>>>
>>>>>> Let me know if you can't replicate the issue with the attached  
>>>>>> Eclipse
>>>>>> project and I'll try to package it up using plain old javac and  
>>>>>> a run
>>>>>> script.
>>>>>>
>>>>>> Even if you do get blue with the different ClassLoaders for  
>>>>>> Printer  and
>>>>>> CustomSquare, I would probably argue that this is incorrect but  
>>>>>> I have
>>>>>> to admit that this argument could be because I've misunderstood  
>>>>>> the
>>>>>> meaning of 'runtime package' in terms of package private  
>>>>>> accessibility
>>>>>> in the JVM spec.
>>>>>>
>>>>>> Jeff
>>>>>>
>>>>>> On Tue, 2009-04-14 at 15:30 -0400, Karen Kinnear wrote:
>>>>>>
>>>>>>> Jeff,
>>>>>>>
>>>>>>> Perhaps you can help me duplicate the problem.
>>>>>>>
>>>>>>> First - what does java -version say?
>>>>>>>
>>>>>>> I took the source code appended, and modified the URL setting,
>>>>>>> which I believe should just give me your second example, i.e.
>>>>>>> CustomSquare instantiated in a different ClassLoader than  
>>>>>>> Printer.
>>>>>>>
>>>>>>> When I run this with Sun's 1.6 or recent 1.7 I get "blue".
>>>>>>>
>>>>>>> What did we do differently?
>>>>>>>
>>>>>>> thanks,
>>>>>>> Karen
>>>>>>>
>>>>>>> Jeffrey Sinclair wrote:
>>>>>>>
>>>>>>>> hotspot-dev,
>>>>>>>>
>>>>>>>> I've been struggling to understand why HotSpot does not throw  
>>>>>>>> an
>>>>>>>> IllegalAccessError when a call is made to an override of a  
>>>>>>>> package
>>>>>>>> private method on an instance loaded by a different class  
>>>>>>>> loader.  I was
>>>>>>>> hoping someone could explain to me in technical detail where  
>>>>>>>> I'm  going
>>>>>>>> wrong because it appears to be a bug.
>>>>>>>>
>>>>>>>> (all source code is pasted at the end of the mail)
>>>>>>>>
>>>>>>>> I have a class called Square with a package private method  
>>>>>>>> named
>>>>>>>> getColour() which returns 'red'. I have a subclass of Square  
>>>>>>>> called
>>>>>>>> CustomSquare which overrides getColour() to return 'blue'. I  
>>>>>>>> have
>>>>>>>> another class called Printer which simply prints out  
>>>>>>>> getColour()  for the
>>>>>>>> Square passed to it. I then have two test cases:
>>>>>>>>
>>>>>>>> * Printer.print(Square) is called with a CustomSquare   
>>>>>>>> instantiated in
>>>>>>>> the same ClassLoader as Printer.
>>>>>>>> * Printer.print(Square) is called with a CustomSquare   
>>>>>>>> instantiated in
>>>>>>>> a different ClassLoader as Printer.
>>>>>>>>
>>>>>>>> What I find is that I get 'blue' in the first test (as  
>>>>>>>> expected) and
>>>>>>>> 'red' in the second test (not expected).
>>>>>>>>
>>>>>>>>
>>>>>>>>> From the Access Control constraints in the Linking section  
>>>>>>>>> of the  JVM
>>>>>>>>
>>>>>>>> specification (5.4.4), I as expecting an IllegalAccessError  
>>>>>>>> to be  thrown
>>>>>>>> because it states that a package private method is accessible  
>>>>>>>> to a  class
>>>>>>>> if it is declared by a class in the same runtime package.
>>>>>>>>
>>>>>>>> My understanding is that Printer is not in the same runtime   
>>>>>>>> package as
>>>>>>>> CustomSquare which explains why my override does not kick in,  
>>>>>>>> but it
>>>>>>>> does not explain why an IllegalAccessError is not thrown.
>>>>>>>>
>>>>>>>> I was wondering if someone could explain the behaviour to me.
>>>>>>>>
>>>>>>>> Regards,
>>>>>>>>
>>>>>>>> Jeff
>>>>>>>>
>>>>>>>> The source code:
>>>>>>>>
>>>>>>>> public class Main {
>>>>>>>> public static void main(String[] args) throws Exception {
>>>>>>>>  URL[] urls = new URL[]{new URL("path/to/CustomSquare")};
>>>>>>>>  URLClassLoader loader = new URLClassLoader(urls);     Class  
>>>>>>>> clazz =
>>>>>>>> loader.loadClass("uk.co.cooljeff.visibility.CustomSquare");
>>>>>>>>  Printer printer = new Printer();
>>>>>>>>  printer.print((Square)clazz.newInstance()); // 'red' gets  
>>>>>>>> printed
>>>>>>>> }   }
>>>>>>>>
>>>>>>>> package uk.co.cooljeff.visibility;
>>>>>>>>
>>>>>>>> public class Printer {
>>>>>>>> public void print(Square square) {
>>>>>>>>  System.out.println(square.getColour());
>>>>>>>> }
>>>>>>>> }
>>>>>>>>
>>>>>>>> package uk.co.cooljeff.visibility;
>>>>>>>>
>>>>>>>> public class CustomSquare extends Square {
>>>>>>>> public CustomSquare() {
>>>>>>>>  super(5);
>>>>>>>> }
>>>>>>>>
>>>>>>>> @Override
>>>>>>>> public String getColour() {
>>>>>>>>  return "blue";
>>>>>>>> }
>>>>>>>> }
>>>>>>>>
>>>>>>>> package uk.co.cooljeff.visibility;
>>>>>>>>
>>>>>>>> public class Square {
>>>>>>>> private float length;
>>>>>>>>
>>>>>>>> public Square(float length) {
>>>>>>>>  this.length = length;
>>>>>>>> }
>>>>>>>>
>>>>>>>> public float calculateArea() {
>>>>>>>>  return length * length;
>>>>>>>> }
>>>>>>>>
>>>>>>>> String getColour() {
>>>>>>>>  return "red";
>>>>>>>> }
>>>>>>>> }
>>>>>>>>
>>>>>>>
>>>>>> <DefaultVisibilityOverride.zip>
>>>>>
>>>




More information about the hotspot-dev mailing list