A new snapshot with a few small fixes and bigger fix to support serialization of Throwable. Since java.lang.Throwable is a remapped type, it's fields don't really exist and therefor didn't get serialized (and I had totally failed to realise this). It turned out to be easy to add serialization support (compatible with Sun's serialization of Throwable) by using the (previously unknown to me) feature of serialization to manually do serialization in a way that is compatible with the default serialization implementation.

Changes:

Fixed ikvm.exe regression that caused it to fail to invoke main on package private classes. (Caused by reflection fix in the previous snapshot).

Added (small) hack to reflection invoke to support deserialization of java.lang.Throwable (and subclasses).

Fixed (de)serialization of java.lang.Throwable.

Fixed small bug in Throwable.initCause(). Previously, if an exception was constructed and null was passed as the cause, you could later on use initCause() to change the cause.

Added ldlen opcode to remapper.cs

Fixed String.copyValueOf(char[]) to throw NullPointerException if array is null.

Fixed String(char[]) constructor to throw NullPointerException if array is null.

Changed FileChannelImpl to call stream.WriteByte, which is now possible because of the new unsigned primitive support in ikvm.lang.CIL.

Rewrote a large part of reflective method invocation to fix all known issues. Reflection on remapped types now works as well as full support for ghost types (both in calling methods through ghost interfaces, as well as passing or returning ghost references).

Fixed class file validation rules to ignored strictfp on abstract methods (according to the VM spec it isn't allowed for an abstract method to be strictfp, but javac is broken and sets the strictfp modifier for abstract method too and Sun's JVM obviously doesn't enforce this part of the spec).

Fixed JNI AllocObject to work correctly for java.lang.Object and java.lang.Throwable.

In the 2nd AppDomain (the first one that prints), the list of assemblies doesn't contain System.Data, even though it clearly is present in the AppDomain, after all, the new System.Data.RowNotInTableException() was just executed.

The problem only occurs when assemblies are shared across AppDomains (this is what the [LoaderOptimization(LoaderOptimization.MultiDomain)] attribute does). Comment out that line and the program works as expected.

I think that this effectively means that IKVM cannot be used inside of ASP.NET, which, I believe, always loads assemblies domain neutral.

In the May 2004 Community Technology Preview of Visual Studio 2005, the bug is fixed and the above program performs as expected.

I made a new snapshot with a couple of small fixes, but this is mainly to update the version of SharpZipLib to 0.6, because this is the version that the Mono Beta 2 ships with.

Here's what's new:

Verifier fix. Calling a method on an unloadable class could cause assertion to be fired.

Fixed ikvmc to tolerate multiple definitions of the same class (classes that have the same name as a class that was already processed are ignored now, with a warning instead of aborting compilation with a fatal error). I ran into a jar that contained multiple entries for the same class!

Compiled with 0.6 version of SharpZipLib to be compatible with the version shipped with Mono Beta 2.

Removed the IKVM runtime version number from the JavaModuleAttribute, because I realised the same information is available by using Assembly.GetReferencedAssemblies().

Fixed ikvmstub to print help message instead of crash, if invoked without argument.

Fixed the bytecode compiler to emit verifiable code when it is compiling invalid code (e.g. code that produces NoSuchFieldError or IllegalAccessError). This was the last know issue, all non-JNI using code generated by ikvmc should now be verifiable. If you encounter unverifiable code, please let me know.

On the ikvm-developers list, Kevin Gilpin suggested to create a list of Java applications and libraries that are compatible with IKVM. This is a great idea. Brian Sletten, who is working on the documentation, offered to collect the list.

So, if you have successfully (or unsuccessfully) run or used any Java application or library (open source or proprietary), please let us know, so it can be added to the list.

In addition to the name and version of the application or library, please supply the following information (not required, but it would be nice, if you have it available):

Version of IKVM you used.

Do you want your name and/or e-mail address in the list?

.NET runtime version used (e.g. MS .NET 1.1 or Mono 0.91).

Operating System version (e.g. Windows XP or Debian 3.0).

Whether you used the application/library in static (ikvmc.exe) or dynamic mode (ikvm.exe), or in a mixed scenario.

Any build issues you encountered.

Any other compatibility issues you encountered.

Please send your feedback to me or Brian, or leave a comment to this post.

Update: In the comments, Jamie Cansdale asks for unit test results for the application or library. That's a good point. If they are available please report the unit test results as well. The JUnit 3.8.1 command line test runner should work with IKVM (at least for the samples tests run successfully, apart from three failures that result from JUnit trying to use Runtime.exec() to start java.exe).

I renamed the assemblies and also took the opportunity to change the directory structure and Visual Studio project names accordingly. This is quite a painful process and also loses the cvs history for all the moved files (at least, I couldn't find any way of moving files in cvs, other than add/remove).

Hopefully this will be the only time I have to make these changes. Breaking all build scripts and what have you, isn't fun.

Note: To build this version, you require either Jikes 1.19 or 1.21.

Here's what's new:

Changed all assembly versions to 0.7.* (except for the IKVM.GNU.Classpath assembly, that now has version 0.9, to indicate the GNU Classpath version)

Fixed ikvmc regression that caused ArgumentException when compiling a bunch of classes with a wildcard expression.

Added dummy ftp protocol handler to work around Classpath bug (when it sees a file: url with a host, it treats it as an ftp url, but since the ftp protocol doesn't exist, the code gets stuck in an infinite loop).

In java.lang.Class. Why would you need a method to dynamically cast? If you understand how generics work under the covers, you know that T is actually java.lang.Object, so calling this cast method doesn't actually buy you anything (the compiler will insert a real cast after your call).

Before diving in, if your knowledge of how Java generics work under the covers is a bit rusty, I recommend reading the paper on GJ: Making the future safe for the past: Adding Genericity to the Java Programming Language. While I was re-reading it, a few things jumped out: "Adding generics to an existing language is almost routine.." In fact, it's so easy that today in 2004 we still don't have generics in Java, even though the design was already done in 1998. Another quote: "GJ comes with a cast-iron guarantee: no cast inserted by the compiler will ever fail. (Caveat: this guarantee is void if the compiler generates an 'unchecked' warning, which may occur if legacy and parametric code is mixed without benefit of retrofitting.)" Please note that the caveat applies when you use one of the principal features of GJ: "GJ contains a novel language feature, raw types, to capture the correspondence between generic and legacy types, and a retrofitting mechanism to allow generic types to be imposed on legacy code."

Note in particular the checkcast that I highlighted. This is inserted by the compiler and is key to how Java generics work. Presumably the Class.cast() method also checks that the passed in object is actually castable to the type, so effectively you get two casts for the price of one (or rather, you pay twice for the same cast).

Why would you want (or need) that? The answer became clear (?) to me when I was contrasting the Java generics with the .NET generics model.

Let's look at some more code (C# 2.0 this time):

T LameFactory<T>() where T : new()
{
return new T();
}

This method generically constructs instances. It's not relevant to my point, but it is interesting to note that the C# compiler uses reflection under the covers to implement this. Most C# generics constructs are actually supported by the CLR, but instantation isn't. The compiler generates something like this:

In essence, what you're doing here is the same as what the CLR is doing under the covers (passing an extra parameter with the type information, at least conceptually). Class.newInstance returns an appropriately typed reference, because Class is a generic type. Suppose it hadn't returned the appropriate type, but simply Object like in the good old days. You could have used Class.cast() to the the downcast instead! Admittedly this isn't the greatest example for explaining the existence of Class.cast(), but I do understand now that it provides real functionality that would have been impossible to (safely) get in any order way. Note that the obvious:

Isn't the right answer. When compiling this, the compiler rightfully warns: Note: cast.java uses unchecked or unsafe operations. The cast is unsafe, because it dissolves at compile time. Note that this doesn't break type safety (in the VM/security sense), because the caller of LameFactory will have inserted its own cast to T, but it does (allow you to) break compile time type safety. If you value compile time type safety, it's a good idea to stay away from code that generates this warning.

To clean up the assembly names, I propose to change to the following names for the next snapshot:

Current Name

New Name

awt.dll

IKVM.AWT.WinForms.dll

classpath.dll

IKVM.GNU.Classpath.dll

ik.vm.jni.dll

IKVM.JNI.CLR-Win32.dll

ik.vm.net.dll

IKVM.Runtime.dll

ikvm.exe

ikvm.exe

ikvmc.exe

ikvmc.exe

Mono.IKVM.JNI.dll

IKVM.JNI.Mono.dll

netexp.exe

ikvmstub.exe

OpenSystem.Java.dll

(will be removed)

I'm removing OpenSystem.Java.dll for the time being, because interop with dotGNU hasn't really happened so far and at this point it's just pre-mature design for reuse. Hopefully, in the future when they continue their work on Java support we can work together to make the two systems interoperable.

Any comments on the assembly names?

[Update: Stuart makes a good point in the comments. I've changed the suggested new name of netexp to ikvmstub.exe]

Fixed TypeWrapper.IsSamePackage to handle case where one type is array and other isn't.

Fixed bug introduced in last snapshot in bytecode compiler, that caused NullReferenceException if exception block starts on an unreachable instructions.

Added exception handling to VMRuntime.exec.

Added -Xwait option to ikvm, to keep the process hanging around (this is just a hack so I can look at the console window that contains the profiling statistics and trace messages after Eclipse has exited).