Note that IKVM still has a bug that causes a particular code construct that ecj generate to be compiled into unverifiable (but working) code. The code construct is using a class literal in the invocation of a base class constructor, ecj compiles this with inline code (javac and jikes put the generated code in a new method) and code has an exception handler around the Class.forName(), but the CLI doesn't allow exception handlers before the base class constructor is called.

Changes:

Added ikvm/lang/ByteArrayHack.java, ../../classpath-0.13/vm/reference/java/lang/VMCompiler.java and ../../classpath-0.13/gnu/java/awt/peer/GLightweightPeer.java to classpath/allsources.lst. Jikes automatically added these files, but when compiling with ecj the list needs to be complete (which is actually much nicer, since it doesn't require the classpath to be set in addition to the list of source files).

Added support for IKVM_EXPERIMENTAL_JDK_5_0 environment variable to enable Java 5 class file format support (note that since none of the JDK 5.0 APIs are available this isn't very useful, except for experimentation).

I finally decided to make a long overdue new release (releases are what you download from the IKVM SourceForge project download and are better tested and include signed binaries). When I release a new development snapshot, I usually test it by running Mauve and my ikvm specific tests and by starting up Eclipse. I build my code on Windows using the Microsoft .NET Framework 1.1 and I don't generally test them on Mono on Linux. Releases are tested on Mono (although fairly superficially, since many of my tests don't run on Mono yet) and more importantly, for a release I also make sure that you can build IKVM on Mono on Linux. In a stroke of irony, while I was testing this release candidate, I discovered that my workaround for the Mono metadata bug that I introduced yesterday actually causes Mono to crash when building IKVM.GNU.Classpath.dll (the workaround itself works, you can run a Windows built IKVM.GNU.Classpath.dll that includes the workaround on Mono), so I added a workaround for that as well.

Basic info:

This releases is based on the GNU Classpath 0.13 release. Get it here.

It was tested on .NET Framework 1.1 on Windows, Mono 1.0.5 on Debian 3.0 and Mono 1.1.2 on Debian 3.0(I couldn't get Mono 1.1.3 to build, so I only tested with 1.1.2)

I added a workaround for the Mono metadata bug and made a couple of other changes, most notably the -opt:fields ikvmc option to remove unused private static fields (except for serialVersionUID), this makes the generated IKVM.GNU.Classpath.dll about 1MB smaller, because of the large number of string constant fields in the locale table classes. I haven't investigated this fully, but it appears that string literals in the code are in a different heap than the string field literals and thus they end up in the file twice. If this is indeed true than it is a rather unfortunate design flaw in the CLI metadata spec (especially since you're not allowed to use the CIL ldsfld instruction on a literal field).

Added -monoBugWorkaround option to ikvmc (and added it to the IKVM.GNU.Classpath.dll build file) to workaround Mono 1.0.5 and 1.1.3 metadata bug. Only use this option if your compiled exe/dll fails to load on Mono (this happens if it has more than 32768 methods), it causes the generated file to grow by about 300KB.

Added workaround for NullReferenceException in Thread.SetData() on Mono.

A while ago Howard Gilbert wrote instructions on how to use IKVM as the target runtime with Eclipse (i.e. not running Eclipse in IKVM, but running the code you develop with Eclipse in IKVM).

My apologies to him for not posting this earlier (and for not adding it to www.ikvm.net, where it really belongs).

I could not find instructions in the documentation about using vanilla
Eclipse (under Java) to develop code that you then run (in Eclipse) under
IKVM. The trick is to define IKVM to Eclipse as an additional Java Runtime
Environment. It seems that this might be useful to post somewhere:
[This uses Windows and an installed Microsoft .NET Framework, but it can
probably be adapted.]
Eclipse runs under the default version of Java installed on your machine
(type "java -version" to find out). However, it can be configured with the
directories of other Java Runtime Environments (JRE's). In the Run . panel,
you can select a particular JRE in which to run each application.
Eclipse believes that a directory is a JRE if it has two things.
1. It must have a {jre}/bin/java.exe file.
2. It must have a {jre}/lib/rt.jar file.
Start with a binary distribution of IKVM in c:\ikvm. Copy
c:\ikvm\bin\ikvm.exe c:\ikvm\bin\java.exe. Create a c:\ikvm\lib and copy
into it the rt.jar from your favorite real Java runtime. Don't worry
because, of course, it will not really be used.
[Jeroen: You can also run ikvmstub on IKVM.GNU.Classpath.dll and rename the
resulting IKVM.GNU.Classpath.jar to rt.jar]
In Eclipse, pull down the Window menu option and select Preferences - Java -
Installed JRE's. Click the Add... button. Leave the type as "Standard VM". A
suggested "JRE name" is "IKVM". Point the "JRE home directory" to c:\ikvm.
The JRE system libraries box fills up with the libraries found in the
{jre}/lib directory. For the moment, the only such library is rt.jar.
Now build a simple Java program. When you want to run it, select Run - Run
... In the JRE tab, click the "Alternate JRE" option and select IKVM from
the pulldown. If you want to be clear, in the "Java executable" select
Alternate and type in "ikvm" (that is the ikvm.exe program).
Now run the program. It ran under IKVM, but it is indistinguishable from
Java.
Now run ikvmstub on your favorite version of mscorlib.dll and put the
resulting jar file in c:\ikvm\lib\mscorlib.jar. After that, you should be
able to ikvmc your favorite JAR files, then put the jar file into /lib and
the generated dll into /bin. This is just a suggested use of the library
structure.
Sanity check: The Eclipse Package Explorer displays the JAR files as Eclipse
sees them during compilation. Even if you intend to run the program under
IKVM, Eclipse is going to compile them with your default real Java and its
libraries. So when you add JAR files to c:\ikvm\lib this doesn't put them in
the compiler path. You have to manually add the extra JAR files to your
project, and then the compiler knows to use them as the source is compiled.
At runtime the DLLs will either be automatically located because they are in
the same directory with ikvm.exe.
So use Project - Properties - Java Build Path - Libraries - Add External
Jars to add mscorlib.jar to your project, and write a small Java program
using some CLI classes. Note that all the usual Eclipse IDE features work.
Type "cli.System.Console." and pause. Eclipse likes (default behavior of a
configurable feature) to convert explicit class names to imports, so it will
add a
import cli.System.Console
to the import group, replace what you have just typed with "Console." and
then display a popup with the list of methods of the System.Console class to
complete the expression.
When it doubt, in the Explorer panel on the left expand any of the jar files
(such as mscorlib.jar) to see the actual fully qualified names of the
classes generated by ikvmc.
Save the file and it gets compiled.
Run it (assuming again you set Run ... to use the IKVM "JRE") and you get
the expected output. Now there is no question that you are running IKVM
because you are calling CLI libraries.

The common theme in today's snapshot is clearly design flaws. I have two of my own to discuss and two others. Note that this reflects only an small portion of all design flaws in my (and other) code, but these are the ones that are at least mildly interesting to read about (I hope).

Thread.interrupt()

When I originally designed the Thread.interrupt() support, it seemed like an obvious thing to map this onto the .NET support for interrupting threads. In retrospect I can see that this was a mistake (when something seems obvious it's tempting not to think about it anymore), but at the time it appeared to work fairly well. Only after a while I discovered that the monitorenter bytecode instruction (implemented by calling System.Threading.Monitor.Enter()) wasn't supposed to be interruptable in Java. I "solved" this by redirecting monitorenter to ByteCodeHelper.monitorenter() that repeatedly calls Monitor.Enter() (and catches any ThreadInterruptedExceptions) until it is successful. When I fixed this, I should have noticed a more fundamental problem with the Thread.interrupt() mapping approach, but I only realised the problem when I was working on the class loading deadlock bug a couple of weeks ago. Since java.lang.InterruptedException is a checked exception and can only occur in a couple of well defined places in the Java library, it simply wasn't a good fit to map it onto System.Threading.ThreadInterruptedException, which can occur any time a synchronized code block is entered (and this basically includes any .NET method).

Once I realised the problem, the fix was fairly easy. Java threads now maintain their own interrupt pending flag and whenever a Java thread enters one of the interruptable waits (Object.wait() or Thread.join()) it checks the flag and marks the thread as inside an interruptable wait. When another thread interrupts the waiting thread, it uses the .NET Thread.Interrupt() to interrupt the wait and the interrupted wait notices that the interrupt pending flag is set and catches the .NET System.Threading.ThreadInterruptedException and throws a java.lang.InterruptedException.

System.Reflection.Emit.MethodBuilder

I did some heap analysis on a started Eclipse process and discovered that a ridiculous amount of memory was being held by the various MethodBuilder instances that are kept alive by the IKVM type information data structures. This wasn't a matter of a couple of wasted bytes, but somewhere around 20MB of useless objects that were still being referenced. I attempted several different workarounds, but the simplest and safest one turned out to be using reflection to clear all of the unused private fields in MethodBuilder after a dynamic type is baked. This is obviously a nasty hack, but the resulting memory reduction is so significant that I felt it was worth it. I filed a bug with Microsoft, so hopefully it'll get fixed for Whidbey. Another reflection design flaw is that System.Reflection.Emit.OpCode is a value type that contains a whole bunch of instance fields, instead of a lightweight enum. This means that whenever you use an OpCode (which is often when you're generating code with ILGenerator) you are carrying around about 40 bytes, instead of an int sized value.

Hashtables

After getting rid of (most of) the MethodBuilder overhead, I turned my attention to my own code and noticed that a lot of memory was being used up by the hashtables that the TypeWrapper instances used for the fields and methods. Using hashtables for this turned out to be a design flaw, because most classes don't have that many types or fields that the memory overhead of a hashtable of worthwhile (I'm doing a simple linear search through an array now, and it doesn't affect performance at all). Besides this change I also made a whole bunch of other changes to reduce memory consumption, the result is that starting up Eclipse now takes less than half the heap space than it did in the previous snapshot.

C# and the CLS

The C# team uses the CLS as a (lame) excuse not to have to support accessing static fields in interfaces, so you cannot access Java interface fields in C#. To work around this, I've added code to create an inner class named __Fields to every interface that has static fields and that inner class contains duplicates of the interface fields, so that they can be accessed from C#.

Changes:

Sync'ed with GNU Classpath cvs (IKVM.GNU.Classpath now contains GNU JAXP XML processing library). You can now run Eclipse without having to add the xml stuff to the bootclasspath: eclipse -vm c:\ikvm\bin\ikvm.exe

Moved ObjectHelper.wait() to VMThread.objectWait().

Moved ObjectHelper.toStringSpecial() to map.xml.

Removed java/lang/ObjectHelper.java

Removed remapping of System.Threading.ThreadInterruptedException to java.lang.InterruptedException.

Added code to set "user.language" and "user.region" system properties based on .NET culture.

Fixed two shutdown hooks bugs that could cause the shutdown hooks not to run or run very slowly.

Fixed Process.waitFor() to be interruptable.

Fixed bug in VMThread.join() that could cause it not to return when a native thread detaches the thread.

Changed compilation of synchronized instance methods to use MethodImplAttribute(MethodImplOptions.Synchronized).

Changed TypeWrapper to use simple arrays to store fields and methods, instead of hashtables.

Integrated lazy member publishing into TypeWrapper and removed LazyTypeWrapper.

Added BakedTypeCleanupHack to workaround bug/design flaw in Reflection.Emit that causes too much memory to be retained (the workaround is to use reflection to clear some private fields of MethodBuilder, ConstructorBuilder and FieldBuilder).

Added workaround to make interface fields usable from C# (by duplicating them in an inner class named __Fields in the interface).

Changed bytecode compiler to use CIL switch opcode for tableswitch bytecode (this is a bit more space efficient).

Added conv_i1, conv_i2, conv_i4 and conv_i8 instruction support to remapper.

Puneet Nayyar had some problems with JNI and was kind enough to send me that DLL that didn't work with IKVM. It turned out that the DLL exported a bunch of methods without the name decorations that __stdcall (the JNICALL calling convention on Windows) requires. Originally I thought that the DLL used the __cdecl calling convention (which uses undecorated names, at least on the Visual Studio .NET 2003 C++ compiler, despite what Raymond Chen says), but an examination of the code showed that it did in fact pop the arguments of the stack. So the fix was trivial, just look up JNI methods in their undecorated form as well.

It worries me a little that this allows __cdecl methods to be found too (which is what you get when you forget the JNICALL modifier), because they would (most likely) result in a crash when called. The Sun JDK seems to (accidentally?) support the __cdecl calling convention, maybe they restore the stack pointer from EBP in their thunk.