This shows that the compiler passes an extra parameter to the Class.forName() method that carries the identity of the caller's class.

The source for ikvm.internal.CallerID is available in cvs. The class is public and has a protected constructor but has no public methods (they are all internal to the IKVM.OpenJDK.ClassLibrary assembly). The intended usage is shown above, you should extend it with a private class that is nested inside the class whose identity you want to pass to a CallerID requiring API.

The example also demonstrates how you can call these methods from C# if you want to get the best performance, however I recommend using the standard versions of the APIs unless the performance difference is critical to your application.

Above we looked at the caller, now let's look at the callee. Here's the Java source for the Class.forName() method:

I added the HasCallerID annotation to signal to ikvmc that it should add the implicit CallerID parameter and ClassLoader.getCallerClassLoader() has been turned into an intrinsic that calls getCallerClassLoader() on the passed in CallerID object instead. Here's the C# equivalent that ikvmc produces for the Class.forName() method:

In the Java API there are a number of APIs that rely on being able to walk up the call stack to determine certain properties of the immediate caller of the API. The most common scenario is determining the caller's class or class loader. This information is used to implement security checks or to use the right class loader.

Up until now IKVM used the System.Diagnostics.StackFrame .NET API to walk the stack. There are however two major issues with this API. The first issue is that it is unreliable and IKVM jumps through various hoops to make sure that the CLR doesn't inline methods it shouldn't inline or use tail call optimizations it shouldn't use. The second issue is that it is relatively slow.

I finally got around to implementing an alternative scheme that eliminates the usage of System.Diagnostics.StackFrame in most cases. In part 2 I'll describe the implementation, but for now let's just look at some performance numbers.

I've cooked up two microbenchmarks that clearly demonstrate the difference between the current approach and the new approach.

Class.forName()

In this microbenchmark we look at the Class.forName() API. It walks the call stack to determine the class loader of the immediate caller, because that is the class loader that it needs to use to load the class.

More .NET 2.0 work and bug fixing, but the biggest change is that class library initialization has been refactored (building on the property support added in the previous snaphot which allows java.lang.System to lazily initialize the I/O streams). Instead of it being an all or nothing approach (i.e. initialize all core classes at once, like in Java) the initialization dependencies have been mostly removed and each part can now be initialized on demand. This makes the code easier to follow, startup more efficient and removed the need for some ugly hacks to make initialization work correctly.

One thing isn't yet working, if you specify a Java security manager in your app.config it isn't guaranteed that the security manager will be installed immediately (currently it's only installed the first time ClassLoader.getSystemClassLoader() is called.) I'm not sure if it is worth fixing this, because using the Java security manager in this way doesn't seem to make much sense (i.e. if you are running your application as a .NET application, why would you use the Java security manager to restrict what it can do, it won't be able do a good job anyway, since it doesn't know about your non-Java .NET code that directly calls .NET Framework APIs.)

Changes since previous development snapshot:

Call suppressFillInStackTrace before instantiating a remapped exception in the remap implementation method.

The conversion isn't actually inlined, but instead a local variable of
value type IKVM.Runtime.DoubleConverter is added to the method
and a static method on that type that takes the value to be converted and a
reference to the local variable is called. Here's the code for IKVM.Runtime.DoubleConverter:

It actually unrolled the loop 16 times (which appears not be helping in the
case), but otherwise the code generated is pretty similar to what we saw on
the CLR. Of course, in HotSpot Double.doubleToRawIntBits() is also an
intrinsic because in Java the only alternative would be to write it in
native code and the JNI transition would add significant overhead in this
case.

Note that the assembly qualified type names are displayed, as I believe this feature is particularly useful when trying to debug issues that arise from having loaded multiple assemblies that contain the "same" types.

While writing this I discovered that both JDK 1.6 and .NET 2.0 always generate descriptive exception messages for invalid casts:

C:\j>\jdk1.6\bin\java testException in thread "main" java.lang.ClassCastException: [Ljava.lang.String; can not be cast to java.lang.String at test.main(test.java:5)

This last result is my local ikvm development version running on .NET 2.0 with a patch to enable taking the exception message from the .NET InvalidCastException, which I didn't previously do because on .NET 1.1 this message didn't contain any useful information.

It's been quite a while since the last development snapshot. I'm still working on integrating .NET 2.0 features, but I'm also doing random fixes/improvements here and there as I come across them. The biggest visible change in this snapshot is the support for defining .NET properties as Java fields. The main motivation was that I wanted to handle the System.in/out/err fields more cleanly. They are now implemented like this:

@ikvm.lang.Property(get="get_in")public final staticInputStream in;

static { in = null; }

private staticInputStream get_in(){ return StdIO.in;}

This defines a .NET property called "in" and associates the getter method of the property with the get_in() method. Note that we're only specifying the getter here in the Property annotation because the field is final, but you can also specify a setter method. The static initializer that initializes the property to null is necessary to keep javac happy, but it doesn't actually do anything. The ikvm bytecode compiler will ignore any assignments to read-only properties. Another thing to note is that the get_in() will automatically be made public (because the field is public), but from Java it will still appear private.

Changes since previous development snapshot:

Fixed regression in return value annotation value.

Forked Class, Constructor and Field.

Made class annotation handling lazy and bypass encode/decode.

Fixed ReflectionOnly referenced assembly loading order (ikvmstub).

Initialize class library in JVM_CreateJavaVM.

Reintroduced guard against recursive FinishCore invocations.

Implemented support for annotations on .NET fields/methods/parameters.

Hide ikvmc generated GetEnumerator() method from Java.

Simplified annotation handling.

Added support to Class.forName() for assembly qualified Java type names.

Replaced notion of DynamicOnly types with Fake types. Fake types are implemented as generic type instances and can have DynamicOnly methods.

Changed System.nanoTime() implementation to use Stopwatch.GetTimestamp().

Ripped out annotation/constant pool support that is no longer needed.

Added support for defining unloadable (i.e. missing) types to use as custom modifiers in signatures.

Use custom modifiers to make sure constructor signature is unique (if necessary).

Restructured code to remove compiler warnings.

Updated FlushFileBuffers p/invoke to use SafeFileHandle.

Forked OpenJDK sources that are going to be modified to refactor the library initialization.

Made __Fields nested class abstract (it was already sealed) and removed the constructor.

Restored the special case for interface .cctor methods to fix bug #1930303

When I released IKVM 0.36 I said I intended to support 0.36 for a longer period since it is the last version that runs on .NET 1.1. Today I've released the first update release of 0.36 to SourceForge. For those who have been tracking the release candidates, this release is identical to release candidate 5.