# Monday, 01 September 2003
« More discussion on netexp and Class.forN... | Main | Vacation »
Constructing Inner Classes

Alan Macek pointed out an interesting bug in the verifier.

First, some background. Let's look at this code fragment:

class Test {
    Test() {
      new Runnable() {
          public void run() {}
      };
    }
    public static void main(String[] args) {
      new Test();
    }
}

Inner classes aren't supported by the VM, it's a compiler fiction, so when you compile the above code you get the equivalent of the following:

class Test {
    Test() {
      new Test$1(this);
    }
    public static void main(String[] args) {
      new Test();
    }
}
 
class Test$1 implements Runnable {
    private Test outer;
    Test$1(Test outer) {
      this.outer = outer;
    }
    public void run() {}
}

The interesting bit here is that the outer field is initialized after the base class constructor runs. In this case the base class is Object so this isn't significant, but when you have a base class that calls virtual methods it is:

abstract class Base {
    Base() {
      run();
    }
    public abstract void run();
}
 
class Test {
    Test() {
      new Base() {
          public void run() {
            System.out.println(Test.this);
          }
      };   
    }
    public static void main(String[] args) {
      new Test();
    }
}

When you compile this and run it, it prints out: null. The outer this reference is not yet initialized when the Base constructor runs.

However, when you compile it with the -target 1.4 option, it prints out: Test@17182c1 (or something similar). So clearly an interesting change was made to the compiler.

Let's take a look at the bytecode of the constructor of the original code fragment:

<init>(LTest;)V
0  aload_0
1  invokespecial java/lang/Object/<init>()V
4  aload_0
5  aload_1
6  putfield Test$1/this$0 Test
9  return

First, the base class constructor is called and then the this$0 field is initialized.

Now take a look at the same method, but now as compiled with the -target 1.4 option:

<init>(LTest;)V
0  aload_0
1  aload_1
2  putfield Test$1/this$0 Test
5  aload_0
6  invokespecial java/lang/Object/<init>()V
9  return

The order of the two steps is reversed. Now it first initializes the this$0 field and then calls the base class constructor.

I had missed the fact that it is legal to initialize fields in an unitialized object. So when the latter code was run, the verifier compiled that an uninitialized object reference was used.

The big question is why the compiler only does this when the -target 1.4 option is specified. This rule of allowing instance fields of unitialized object to be written has always been in the JVM specification, as far as I can tell.

New Snapshots

Friday, I put up new snapshots. Source and binaries and binaries only.

Changes since the last (on the blog announced) snapshot:

  • New version of netexp based on Java reflection (all .NET to Java mapping is now in the IKVM reflection runtime).
  • To support netexp, the .NET type reflection is now much better. This includes support for delegates and byref arguments. Instead of lower casing the .NET namespaces, all .NET types are now prefixed with the "cli." package name. Remapped this are also available and appear as final classes without constructors, but with static methods for all instance methods (taking the remapped type as the first argument). Constructors are available as static __new methods.
  • Bootstrap class loader is now a flat type space. All loaded assemblies are searched for bootstrap types when Class.forName is used.
  • Enums are now treated as other value types (i.e. boxed). This allows better accesses to overloaded .NET methods.
  • Lots of clean up and refactoring to support the new reflection capabilities.
  • Various bug fixes.
  • Updated to work with latest GNU Classpath CVS.
Monday, 01 September 2003 16:32:56 (W. Europe Daylight Time, UTC+02:00)  #    Comments [9]