[Prev][Next][Index][Thread]

RE: Type soundness issues in Java




> I thought about this a little more, and while I think the principles I
> described are more or less right, I'm not sure about the details.  For
> example, verification should have loaded I.class to verify the field access
> "i.c".  Similarly, it looks like it's the loading of class I that causes the
> final failure, but you would have thought the binary compatibility checks
> would have failed when class A was loaded in line 1 of main().  If not, it
> may be that the assumption tracking in the loader/verifier a messy business
> indeed, which may leave plenty of room for implementation bugs...  
> 
> So perhaps the story could be cleaner, and a formalization or model
> implementation might help with this!

I looked at the bytecode produced by javac on the Main class. That 
explained a lot to me and reminded me of something I had forgotten. 
The thing I could not figure out was how the expression i.c was producing 
a meaningful value. 

        public static void main(String args[]) {
                A a = new A();
                I i;
                System.out.println("I've been started");
                if(a instanceof I)
                        System.out.println("a is instance of I!");
                else
                        System.out.println("a Is NOT instance of I");
                i=a;
                System.out.println("Cast succeeded!");
                System.out.println(java.lang.Integer.toString(i.c,10));
                i.m();
        }

Well, it turns out the expression is "optimized" by javac.

        public static void main(String args[]) {
                A a = new A();
                I i;
                System.out.println("I've been started");
                if(a instanceof I)
                        System.out.println("a is instance of I!");
                else
                        System.out.println("a Is NOT instance of I");
                i=a;
                System.out.println("Cast succeeded!");
                System.out.println(java.lang.Integer.toString(i.c,10));
                i.m();
        }

Method void main(java.lang.String[])
   0 new #5 <Class A>
   3 dup
   4 invokespecial #12 <Method A()>
   7 astore_1
   8 getstatic #15 <Field java.io.PrintStream out>
  11 ldc #2 <String "I've been started">
  13 invokevirtual #16 <Method void println(java.lang.String)>
  16 aload_1
  17 instanceof #6 <Class I>
  20 ifeq 34
  23 getstatic #15 <Field java.io.PrintStream out>
  26 ldc #4 <String "a is instance of I!">
  28 invokevirtual #16 <Method void println(java.lang.String)>
  31 goto 42
  34 getstatic #15 <Field java.io.PrintStream out>
  37 ldc #3 <String "a Is NOT instance of I">
  39 invokevirtual #16 <Method void println(java.lang.String)>
  42 aload_1
  43 astore_2
  44 getstatic #15 <Field java.io.PrintStream out>
  47 ldc #1 <String "Cast succeeded!">
  49 invokevirtual #16 <Method void println(java.lang.String)>
  52 getstatic #15 <Field java.io.PrintStream out>
  55 iconst_4               <-------------------------------- i.c
  56 bipush 10
  58 invokestatic #17 <Method java.lang.String toString(int, int)>
  61 invokevirtual #16 <Method void println(java.lang.String)>
  64 aload_2
  65 invokeinterface (args 1) #14 <InterfaceMethod void m()>
  70 return

I write "optimized" above because fields in interfaces actually have
the meaning of constants. 

The above bytecode also shows where the first "active" use of I is:
at the invokeinterface instruction at PC 65. So, indeed, the principle
doing things as lazily as possible explains this case.

Sorry if this was obvious to everyone all the time. I thought it was
worth spelling out. 

Ole