invokevirtual on package private override in different classloader
Jeffrey Sinclair
jeff at cooljeff.co.uk
Sat Apr 18 10:07:52 PDT 2009
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