Monday, May 02, 2005

IKVM 0.14 rc1

GNU Classpath 0.15 was released yesterday, so based on that I'm releasing a new version of IKVM. Besides the many improvements to GNU Classpath, there have been a bunch of IKVM fixes and two major new features: support for defining .NET properties and for applying .NET attributes. Both these features are controlled via the xml remap file. Here's an example Java class with corresponding xml file:
public class IntList
{
  private int[] list;
  public IntList(int size)
  {
    list = new int[size];
  }
  public void setItem(int index, int value)
  {
    list[index] = value;
  }
  public int getItem(int index)
  {
    return list[index];
  }
}
<?xml version="1.0" encoding="utf-8" ?>
  <root>
    <assembly>
     <class name="IntList">
       <attribute type="System.Reflection.DefaultMemberAttribute, mscorlib" sig="(Ljava.lang.String;)V">
         <parameter>Item</parameter>
       </attribute>
       <property name="Item" sig="(I)I">
         <getter name="getItem" sig="(I)I" />
         <setter name="setItem" sig="(II)V" />
       </property>
     </class>
  </assembly>
</root>

When IntList.java is compiled to IntList.class and then ikvmc'ed using:

   ikvmc IntList.class -remap:IntList.xml

The resulting IntList.dll will be usable from C# like this:

   IntList l = new IntList(10);
   l[4] = 42;
   Console.WriteLine(l[4]);

Valdemar Mejstad created a tool to automatically generate the xml to define properties based on Java's java.beans.BeanInfo. The source is available here: MapFileGenerator.java.

Files:

ikvm-0.14-rc1.zip  (source + binaries)
ikvmbin-0.14-rc1.zip   (binaries)

Changes:

  • Integrated GNU Classpath 0.15 release
  • Dropped support for Mono 1.0.x, because Mono 1.1.x is now the recommended (by the Mono team) version to use.
  • Made some optimizations to System.arraycopy().
  • Removed the previously deprecated ikvm.lang.ByteArrayHack class.
  • Merged fixes to gnu.java.nio.channels.FileChannelImpl from Classpath.
  • Added CLR "bitness" (32 or 64) to ikvm -version output.
  • Fixed ByteCodeHelper.DynamicCast() and ByteCodeHelper.DynamicInstance() to handle null references properly (always succeed/fail the cast/instanceof, without trying to load the type).
  • Changed os.arch for "AMD64" to "amd64" to match JDK.
  • Added sun.arch.data.model system property.
  • Fixed VMThread.suspend()/resume() to not throw exceptions if invalid calls are done.
  • Fixed VMThread.stop() to better handle suspended threads.
  • Fixed a bug in invocation of JNI_OnLoad (if the method exited with a pending exception, the exception wasn't dispatched).
  • Added GC.KeepAlive() to FileChannelImpl.flush() to make sure the file channel isn't GCed (and thus closed) while the native method is running.
  • Fixed verifier to correctly identify stack overflows when double/long values are involved.
  • Fixed verifier to reject empty try blocks.
  • Fixed bug in VMTimeZone.inDaylightTime() [Fix by Alexander Zuev].
  • Fixed a whole bunch of bugs in java.lang.reflect.Field [Reported by Alexander Zuev].
  • Created two subclasses of CompiledTypeWrapper to handle the special cases for remapped and ghost type to make the normal types more efficient.
  • Added a micro optimization to FieldWrapper to avoid having to call FieldInfo.IsLiteral on each reflective field access, because FieldInfo.IsLiteral turns out to be quite expensive on the Microsoft CLR.
  • Removed -monoBugWorkaround switch from IKVM.GNU.Classpath.dll build script, since Mono 1.1.4 and later no longer require it.
  • Fixed bug in protected member access check (it was too strict, requiring the referenced class to be visible).
  • Improved error message when core library doesn't match runtime version.
  • Implemented memory mapped I/O.
  • Added support for defining properties through map.xml file.
  • Added check to ikvmc to prevent signing assemblies that reference unsigned assemblies.
  • Added support for adding custom attributes to assembly, classes, fields, methods and constructors in the map.xml file.
  • Removed -enabletls option from ikvmc (fields can now be marked as thread local by adding a custom attribute).
  • Added AssemblyCopyrightAttribute to IKVM.GNU.Classpath.dll.
  • Fixed a bug in .NET type reflection handling of explicit method implementations.
  • Fixed bug in JNI return value marshaling for methods with boolean return type.
  • Fixed NullReferenceException bug when compiling class with native finalize method.
  • Fixed JNI to continue to work during "finalization for exit".
  • Implemented System.runFinalizersOnExit(boolean). By default, Java finalizers no longer run at application exit.
5/2/2005 2:39:33 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]

Monday, April 18, 2005

Whidbey Beta 2

I downloaded Whidbey Beta 2 from MSDN (subscription required) and installed it on my AMD64 machine (recently repaved with WinXP x64 RTM). Installation went very smooth and IKVM builds and runs its test suite (without requiring the workaround that the Februari CTP needed).

My Very Own Breaking Change

A long time ago I wrote about problems with the C# destructor and used System.WeakReference as example. Recently I discovered a related, but more serious problem with System.WeakReference and reported it to Microsoft.

Here is some evil code:

using System;
class Class1 : WeakReference
{
    Class1(object obj)
      : base(obj)
    {
    }
    
    static void Main(string[] args)
    {
        Class1 r = new Class1("foo");
        Console.WriteLine(r.Target);
        Class1 clone = (Class1)r.MemberwiseClone();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        new Class1("bar");
        Console.WriteLine(clone.Target);
    }
}

The last statement prints out bar. Notice that we aren't supposed to have a reference to bar. This is a variation of a handle recycle attack.

In Beta 2 this problem was "fixed" by adding an unmanaged code inheritance demand to System.WeakReference, so untrusted code will not be able to subclass WeakReference (which is needed to get access to MemberwiseClone). This is a (minor) breaking change, but obviously the right thing to do.

4/18/2005 9:04:12 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [1]

Monday, March 21, 2005

Saxon.NET RC1

The Saxon.NET developers have announced RC1 of Saxon.NET. I like this project, because it is a good example of the kind of project I envisioned when I started IKVM.NET. They take an existing Java library and convert it to a .NET assembly using ikvmc and they add important value by packaging and testing the converted assembly and by providing examples and utilities.

64-bit

In unrelated news, I've been able to successfully run my internal test suite on x64 (An AMD64 system running Windows XP Professional x64 Edition RC1 running the Whidbey February CTP). I had to work around one Whidbey February CTP specific bug and had to make a change to VMThread.stop() relating to a Whidbey change in Thread.Abort(). It's too bad there isn't an x64 Windows version of Eclipse yet, because that would make a good real world test.

3/21/2005 4:47:35 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [2]

Wednesday, March 02, 2005

How Not To Fix Bugs

At the moment ikvm/runtime/JniInterface.cs contains the following workaround:

#if __MonoCS__ 
  // MONOBUG mcs requires this bogus fixed construct (and Microsoft doesn't allow it)
  fixed(void** p = &pJavaVM->firstVtableEntry) { pJavaVM->vtable = p; }
#else
  pJavaVM->vtable = &pJavaVM->firstVtableEntry;
#endif

Up until Mono 1.1.4 this workaround is required, but current Mono svn has a fixed mcs that doesn't require (nor allow) the workaround.

This means that current IKVM cvs isn't compilable with current Mono svn and that's dumb. The proper way to fix mcs would have been to temporarily (at least until after the next Mono release) allow the incorrect usage of fixed and issue a warning that such usage is illegal and will stop working in a future release.

3/2/2005 11:05:32 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

IKVM 0.12 Released

I put the 0.12 release up on SourceForge. Basically the same as yesterday's snapshot, only now the version is 0.12.0.0 and the binaries are signed.

3/2/2005 10:51:21 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]

Tuesday, March 01, 2005

New Snapshot

I made a new snapshot that will become the 0.12 release if no showstoppers are found. Based on the Eclipse inlining mess I decided to measure how much of a performance hit it is to disable inlining for all non-leaf methods and I found it to be very small for the benchmarks I tried. This combined with the fact that inlining would need to be disabled (for many methods) in the future to support the Java security model, I decided to go ahead and do that already. This is the first IKVM version that actually supports running Eclipse 3.0 and using it to develop Java code (even if it is horribly slow to get warmed up).

I've also disabled dynamic binding in ikvmc compilation, because it doesn't really work. The problem is that package access doesn't work across assemblies, so any package accessible types/members in statically compiled code cannot be accessed by dynamically loaded classes. I may re-enable this functionality at some point in the future, by combining it with an option to make all package accessible types/members public.

For dynamically compiled code, dynamic binding continues to work (to support scenarios were the more eager loading of classes by IKVM would otherwise cause problems).

Changes:

  • Integrated GNU Classpath 0.14 release.
  • Added ToString to ClassLoaderWrapper, to enable efficient tracing.
  • Added support for HideFromJava attribute to map.xml parser.
  • Added JNI trace switch.
  • Added extensive tracing to JNI code.
  • Added JVM.EnableReflectionOnMethodsWithUnloadableTypeParameters HACK to allow ikvm.exe to do reflection on main class that has unloadable types in method parameters.
  • Added JVM.DisableDynamicBinding to disable dynamic binding when compiling with ikvmc.
  • Cleaned up ikvmc warning/error messages.
  • Added TypeWrapper.EnsureLoadable() to consistently handle unloadable types.
  • Added generation of warning messages (in ikvmc scenario) to UnloadableTypeWrapper.
  • Changed JNI method ptr field name to get more consistent JNI trace messages.
  • Fixed JNI bug that caused JNI_GetCreatedJavaVMs not to work if the JVM wasn't started with JNI_CreateJavaVM.
  • Added exception mapping methods __<map>() to java.lang.Throwable (not visible from Java and most other CLR languages, but used internally by IKVM).
  • java.lang.ExceptionHelper is no longer public.
  • Disabled method inlining for non-leaf methods.
3/1/2005 10:09:25 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

Tuesday, February 22, 2005

Stack Traces

Corey comments on the previous entry:

While I agree that the Eclipse code is performing some nasty checks there I don't think I agree with the implication that the stacktrace created by the VM should change based on whether the method was inlined or not. I'm not sure what is mandated by the JVM spec but the stacktrace isn't going to be very useful for a developer debugging his program if the stacktrace changes depending on whether something got inlined or optimized away by the VM...

Here's what the Javadocs for Throwable.getStackTrace() say:

Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this throwable is permitted to return a zero-length array from this method.

On JDK 1.1 it was quite common to miss stack frames in a stack trace, but HotSpot is actually very good at creating a full stack trace even when it inlines methods. The CLR could certainly do better in this regard.

Bottom line is that the Eclipse code is very JVM specific and as far as I can tell it doesn't even serve any useful purpose.

2/22/2005 9:20:05 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [2]

Eclipse is Evil

While it's been possible to start up Eclipse with IKVM for a long time, editing a Java source didn't really work. Today I decided to look into the problem. It turned out to be caused by an IllegalStateException thrown from the following method in MarkerAnnotationPreferences.java:

/**
 * Checks correct access.
 * 
 * @throws IllegalStateException if not called by {@link EditorsUI}
 * @since 3.0
 */
private static void checkAccess() throws IllegalStateException {
  StackTraceElement[] elements=  new Throwable().getStackTrace();
  if (!(elements[2].getClassName().equals(EditorsUI.class.getName())
      || elements[3].getClassName().equals(EditorsUI.class.getName())))
      throw new IllegalStateException();
}

It looks at the stack trace that the Throwable produces to ascertain if it was called by the correct method. This is obvious not very robust (as is born out by the fact that it checks two different slots in the array).

The calling method in EditorsUI.java is:

public static void useAnnotationsPreferencePage(IPreferenceStore store) {
  MarkerAnnotationPreferences.useAnnotationsPreferencePage(store);
}

A perfect candidate for inlining! Which the CLR does, of course. So the EditorsUI class didn't appear in the stack trace at all, causing checkAccess() to fail.

For the time being I added a workaround to IKVM to disable inlining for all methods in classes named org.eclipse.ui.editors.text.EditorsUI, but that's obviously a disgusting hack.

2/22/2005 1:49:56 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [3]

Friday, February 18, 2005

Snapshot

Mostly bug fixing and a couple of work arounds for bugs in the CLR and Mono.

Changes:

  • Sync'ed with GNU Classpath cvs.
  • Fixed bug in ClassLoaderWrapper that caused exception when defining the ModuleBuilder if the first class loader to define a class had a toString() representation that contained characters that are illegal in an assembly name.
  • Changed System.identityHashCode implementation to use non-virtual call to Object.GetHashCode instead of RuntimeHelpers.GetHashCode, to work around .NET runtime bug.
  • Fixed reflection to support modifying final instance fields (as per JSR-133).
  • Added optional verbose cast failure messages (set the IKVM_VERBOSE_CAST environment variable to enable this).
  • Fixed unloadable type support in native method signatures.
  • Changed handling of delegate types that have unsupported types in method signature (inner interface is no longer generated). This fixes bug 1119575.
  • Added -enabletls option to ikvmc to automagically add ThreadStaticAttribute to fields starting with __tls_
  • Changed IKVM specific Classpath methods to use __tls_ fields instead of LocalDataStoreSlot. This improves performance, but also works around a bug in the CLR relating to AppDomain shutdown and a GC bug in Mono on Windows.
  • Added workaround for Mono bug in GCHandle.IsAllocated.
  • Fixed implicit interface downcasting (which is sometimes required due to a quirk in the JVM spec) to throw IncompatibleClassChange instead of ClassCastException.
  • Fixed bug in argument globbing code that caused command line of statically converted executables to start with the name of the executable (in args[0]) on unix.
  • Fixed bug that caused Thread.join() on main thread to hang.
  • Fixed JNI name mangling to support encoding Unicode and special characters correctly.
  • Fixed JavaException to not call String.Format on non-format strings.
  • Optimized VMThread.jniDetach to not do anything if a Java thread object was never created.

New snapshots: just the binaries and source plus binaries.

2/18/2005 1:18:32 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]

Tuesday, February 08, 2005

Weird Bug

Jamie Cansdale reported a strange problem on the mailing list. His code ran successfully when run for the first time, but subsequent runs in the same process, but a different AppDomain failed with a ClassCastException.

He was kind enough to work with me to try and isolate the problem. So several hours and various private IKVM builds later, I was able to reproduce the root problem:

using System;
using System.Runtime.CompilerServices;
class Class1
{
  static void Main(string[] args)
  {
    RuntimeHelpers.GetHashCode(new Class1());
    if(AppDomain.CurrentDomain.FriendlyName != "2nd")
    {
      AppDomain dom = AppDomain.CreateDomain("2nd");
      dom.ExecuteAssembly(typeof(Class1).Assembly.Location);
    }
  }
}

This code dies with a System.MissingMethodException! Yet another weird multi-AppDomain bug.

Fortunately, the work around is easy. Instead of using RuntimeHelpers.GetHashCode() to implement System.identityHashCode(), I've gone back to using some handcoded IL in map.xml to call System.Object.GetHashCode() non-virtually. This was also what I did on v1.0 of the CLR (where RuntimeHelpers.GetHashCode() didn't yet exist).

2/8/2005 2:52:47 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]