# Thursday, 06 January 2005

Stuart Ballard posted instructions on his blog on how to build ecj (the Eclipse Compiler for Java) and compile it into an executable with ikvmc.

This is really cool, because ecj is an open source pure-Java compiler that supports all of the Java 5.0 features.

Thursday, 06 January 2005 21:24:58 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
New Snapshot

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).


  • Sync'ed with GNU Classpath cvs.
  • Added empty NetToolkit.createRobot method (to make it compile with recent Classpath change).
  • Only do the MethodBuilder private field scrubbing hack if we're on .NET 1.1
  • Added -opt:fields ikvmc option to remove unused private static fields.
  • 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.

New snapshots: just the binaries and source plus binaries.

Thursday, 06 January 2005 11:46:32 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Wednesday, 05 January 2005
Mono Problems

Just a quick entry to point out problems with the most recent development snapshot on Mono. Mono gives a bunch of assertion failures:

** (ikvm.exe): CRITICAL **: file metadata.c: line 804 (mono_metadata_string_heap): assertion `index < meta->heap_strings.size' failed

** (ikvm.exe): CRITICAL **: file metadata.c: line 832 (mono_metadata_blob_heap): assertion `index < meta->heap_blob.size' failed

Not sure what's going on yet, but in the mean time, if you want to run on Mono, you can use the previous snapshot: binaries and source + binaries.

[Update: As usual, Zoltan Varga was on the case and quickly fixed the problem in the Mono 1.0 and 1.1 source trees. He also told me how to workaround it, so the new snapshot has a workaround.]

Wednesday, 05 January 2005 17:37:48 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, 04 January 2005
Using IKVM as a JRE with Eclipse

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

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

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.
Tuesday, 04 January 2005 09:09:48 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Monday, 03 January 2005
Design Flaws

Happy New Year!

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).


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.


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.


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#.


  • 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.
  • Totally rewrote Thread.interrupt() handling and detached Java thread interruption from .NET thread interruption.
  • Removed the MethodDescriptor class.
  • Removed ByteCodeHelper.monitorenter().
  • Changed class file parser to intern all class, field, method names and signatures.
  • Added check for class names that are too long for .NET to handle.
  • Changed class loading locking so that no locks (outside of the lock on the ClassLoader) are held when Java code is called, to prevent potential deadlocks.
  • Changed SimpleCallMethodWrapper and SmartCallMethodWrapper not to have two OpCode fields but use smaller enum type instead.
  • Changed field resolving/linking to use the same strategy as methods.
  • Unified NonPrimitiveValueTypeFieldWrapper, GhostFieldWrapper and SimpleFieldWrapper.
  • 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.

New snapshots: just the binaries and source plus binaries.

Monday, 03 January 2005 18:11:32 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Wednesday, 22 December 2004
New Snapshot

More fixes and stabilization work. More to come...


  • Merged with GNU Classpath cvs.
  • Fixed a relatively obscure bytecode compiler bug (that caused a "critical failure" because of an undefined label).
  • Fixed error handling for sockets to deal with closed sockets.
  • Implemented support for parsing relative ikvmres URLs and improved error handling.
  • Fixed ikvmres protocol handler to set port to -1 instead of 0 to enable better URL/URI roundtripping.
  • Fixed defineClass to throw NoClassDefFoundError instead of ClassNotFoundException (in some cases).
  • Fixed reflection to use a classes real accessibility instead of the access flags from the inner classes attribute.
  • Fixed ikvmc to always return errorcode when compilation fails.
  • Improved exception printing in ikvmstub.
  • Many fixes to class file format checking.
  • Fixed dynamic bytecode helper methods to throw NoClassDefFoundError instead of ClassNotFoundException.
  • Improved detection of class circularity.
  • Added workaround for .NET bug for classes that end with a period.
  • Implemented support for enums that use unsigned integral types as underlying type.
  • Fixed several constant field related bugs.
  • Fixed several .NET literal field related bugs.
  • Added workaround for .NET 1.1 verifier bug that made accessing the Value field of an enum unverifiable (for integral types smaller than Int32).
  • Fixed obscure bytecode compiler bug dealing with assigning uninitialized object references to local variables.
  • Implemented verifier checks to enforce calling the base class constructor before returning from a constructor.
  • Fixed obscure verifier bug that incorrectly disallowed having the unitialized this in a local or on the stack when doing a backward branch.
  • Fixed several verifier bugs when dealing with incorrect bytecode (would previously throw an IndexOutOfBoundsException instead of a VerifyError).
  • Added validation of the exception handling tables to the verifier and class file parser.
  • Implemented invokeinterface verification for bogus bytes following the instruction.
  • Moved java.lang.Class object <-> TypeWrapper mapping from hashtable to field in TypeWrapper.
  • Fixed class loading deadlocking bug.
  • Fixed JNI methods to handle RetargetedJavaExceptions.
  • Fixed Class.getProtectionDomain() for array types (not in Classpath cvs yet).
  • Fixed Class.getModifiers() for inner class array types.

New snapshots: just the binaries and source plus binaries.

Wednesday, 22 December 2004 09:40:49 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Monday, 13 December 2004
Calling Conventions & New Snapshot

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.

Other changes:

  • Merged with GNU Classpath cvs.
  • Fixed Class.forName() bugs when dealing with invalid array class names.
  • Fixed Array.newInstance() when used on an unfinished class.
  • Changed os.name and os.version system properties to be more compatible with Sun (on Windows).
  • Changed ikvmc to allow building empty assemblies (mainly useful for creating executable that start a main method in an already compiled assembly).
  • Fixed FileChannelImpl to use a smaller buffer (FileStream forces the use of a buffer, but we don't want any buffering at all).
  • Fixed FileChannelImpl.write(int) to flush and handle exceptions.
  • Fixed FileChannelImpl to handle (asynchronously) closed streams correctly.
  • Fixed some weak reference bugs.
  • Fixed ikvmc to use correct resource names on Mono (leading slash wasn't chopped off).
  • Fixed JNI method lookup to support unmangled names.
  • Cleaned up handling of VerifyError and log the error in the right place.
  • Changed compilation "exception" trace messages to Error category (from Warning).
  • Added compiler support for special casing VMSystem.arraycopy (which is now called directly by Classpath's StringBuffer implementation).

New snapshots: just the binaries and source plus binaries.

Monday, 13 December 2004 08:56:12 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Thursday, 02 December 2004
New Runtime -> Library Interface & New Snapshot

The major new feature in this version is that I introduced a new interface (ikvm.internal.LibraryVMInterface) to replace all late-bound library operations in the runtime. In some cases this is a performance optimization, but I mainly did it to get better compile time checking.

In addition to that a number of small steps on the long road to 1.0:

  • Merged with GNU Classpath cvs.
  • Made the Field/Method/Constructor constructors package private. These objects are constructed from java.lang.VMClass but they live in java.lang.reflect, so they needed to be public, but I realised that since package private turns into assembly accessibility, I could use some map.xml glue to make factory methods in java.lang.VMClass that access the package private constructors in the java.lang.reflect package.
  • Fixed a whole bunch of DatagramSocket bugs.
  • Increased my Mauve test set enormously. I now run almost all Mauve tests. Current results: 238 of 3600422 tests failed.
  • Fixed FileChannelImpl.truncate to only truncate (not grow) the file.
  • Fixed several small java.io.File compatibility problems.
  • Fixed exception cleaning code to not skip package private methods.
  • Fixed a regression in ikvmc that caused it not to emit the names of method parameters.
  • Fixed regression that caused an exception when processing a constant boolean field.
  • Introduced ikvm.internal.LibraryVMInterface to replace all late binding from the runtime to the class library.
  • Added public API IKVM.Runtime.Util.MapException() to map exceptions from non-Java code.
  • Added ldobj instruction to remapper.cs.
  • Added a tools sub-project (currently only contains one simple app to generate an assembly reference for ilasm).
  • Implemented java.nio.channels.Channels.new[In|Out]putStream native methods.
  • Added stack trace cleaning to remove java.lang.LibraryVMInterfaceImpl methods.
  • Fixed some uncommon cross language Finalize method issues (e.g. when a Java class inherits from a C# class that inherits from Java class).
  • Optimized empty finalize methods. They are no longer registered as real Finalize methods.
  • Fixed reflection on .NET types to properly handle Java ghost interfaces.
  • Added a jvm sub-project to build JVM.DLL (Windows only).
  • Removed stub ikvm/classpath/gnu/java/net/protocol/ftp/Handler.java now that GNU Classpath has an ftp implementation.

New snapshots: just the binaries and source plus binaries.

Thursday, 02 December 2004 10:09:11 (W. Europe Standard Time, UTC+01:00)  #    Comments [2]
# Wednesday, 24 November 2004
Direct ByteBuffer, Inlining & New Snapshot

Michael Koch finished Classpath's implementation of DirectByteBufferImpl. Direct ByteBuffers allow Java code to access unmanaged memory. You can allocate them from Java using ByteBuffer.allocateDirect or from JNI using NewDirectByteBuffer. The default Classpath implementation uses native methods to implement accessing the buffer, but for IKVM I really didn't want to do it that way, because I like to use managed code whenever possible (for both maintainability and portability) and also because native method invocation is fairly slow. I therefore decided to reimplement gnu.classpath.RawData (the Classpath abstract native memory pointer) and java.nio.VMDirectByteBuffer.

At first RawData was a value type containing a single IntPtr field in an attempt to avoid an indirection when loading the pointer. This required that I replaced the implementation of DirectByteBufferImpl.get/put with some handwritten CIL instructions, because the compiled Java code resulted in unnecessarily boxing the RawData struct and that obviously killed performance. Since I was replacing the get and put methods anyway, I also inlined the index check and position update. After some experimentation I discovered that making RawData a value type didn't really help performance (the .NET JIT isn't very good at taking advantage of value type optimization opportunities) so I changed it into a reference type, this removed the need for special casing get and put, but when I removed the handcoded CIL performance went down significantly (approx. 50% slower). For some reason the JIT wasn't inlining the index check method, so I left the handcoded CIL in map.xml.

Here's a random microbenchmark:

import java.nio.*;
class DirectByteBuffer {
  public static void main(String[] args) {
    final int SIZE = 10 * 1024 * 1024;
    for(int j = 0; j < 2; j++) {
      ByteBuffer b = ByteBuffer.allocateDirect(SIZE);
      long start = System.currentTimeMillis();
      for(int i = 0; i < SIZE; i++)
        b.put(i, (byte)i);
      long sum = 0;
      for(int i = 0; i < SIZE; i++)
        sum += b.get(i) & 0xFF;
      long end = System.currentTimeMillis();
      System.out.println(end - start);
      System.out.println("sum = " + sum);


JRE 1.4.2 HotSpot Client VM Average
671 530 541 521 461 545
450 461 450 460 460 456
JRE 1.5 HotSpot Client VM  
401 461 411 481 480 447
410 360 441 410 411 406
JRE 1.5 HotSpot Server VM  
220 120 110 120 120 138
100 110 110 160 100 116
IKVM on .NET 1.1  
201 231 231 240 251 231
210 210 210 221 220 214
IKVM on Mono 1.0.2 (--optimize=all)  
430 391 400 401 390 402
401 390 401 390 401 397

Pretty good results. Most other operations perform similarly, except for compact, copy a large buffer takes about twice as long as on the JRE. The .NET class library really should have a memcpy or memmove method that is properly optimized. Note that there is CIL instruction cpblk to copy memory and I tried that as well, it is about 30% faster than my straightforward C# loop, but it doesn't handle overlapping regions and I didn't want to use an unverfiable instruction anyway (as that would cause IKVM.GNU.Classpath to fail verification and I use verification as simple smoke test during build to catch the most obvious regressions in the compiler).

I've also updated the Japi status results: IKVM vs JDK and JDK vs IKVM.

Other changes:

  • Resync'ed with GNU Classpath cvs.
  • Removed java/net/SocketInputStream.java and java/net/SocketOutputStream.java.
  • Moved contents of java/net/PlainSocketImpl.java to gnu/java/net/PlainSocketImpl.java.
  • Moved contents of java/net/PlainDatagramSocketImpl.java to gnu/java/net/PlainDatagramSocketImpl.java.
  • Added deprecated attribute to the three deprecated String methods.
  • Added deprecated attribute support to map.xml parser.
  • Added deprecated attribute support to ClassFileWriter.
  • Added deprecated attribute support to ikvmstub.
  • Added public API to query deprecatedness of fields, methods, constructors and classes.
  • Changed ikvmstub to emit serialVersionUID fields even if it is private, to make Japicompat status more accurate.
  • Added public API to query the constant value of a field.
  • Added ConstantValueAttribute to persist the constant value of constant instance fields (which are treated the same as static constant fields, by javac).
  • Added support to ikvmstub for constant instance fields.
  • Changed japi build file to generate both forward and backward compatibility reports.
  • Fixed runtime to support loading a pre-compiled Java class by referencing an ikvmstub generated stub class.
  • Fixed module names when generating -Xsave files.
  • Moved JNI method pointer field to proxy class when generating -Xsave files.
  • Replaced gnu.classpath.RawData with an IKVM specific version.
  • Added optimized special case implementation of several java.nio.DirectByteBufferImpl methods to map.xml.
  • Removed CharConversionException handlers from StringHelper (I fixed Classpath's character/string encoders/decoders to no longer throw exceptions).
  • ClassLoaderWrapper has a new method PublishLibraryImplementationHelperType to publish types that live in IKVM.Runtime.dll (this is used for the new gnu.classpath.RawData that lives in IKVM.Runtime.dll because it contains unverifiable code).
  • Improved line number table encoding (more space efficient and simpler code).
  • Implemented JNI methods NewDirectByteBuffer, GetDirectBufferAddress and GetDirectBufferCapacity.
  • Added new method AttributeHelper.SetEditorBrowsableNever to decorate methods with the EditorBrowsableAttribute (to prevent them from showing up in Intellisense).
  • Implemented support to override method implementations in map.xml (not implemented for constructors yet).
  • Renamed all of the IKVM introduced fields and methods to __<xxx>.
  • Added support for several new CIL instructions to remapper.cs.
  • Shadow types (e.g. the .NET java.lang.Object type) now hide all inherited methods and decorate them with EditorBrowsableAttribute(Never) so that they no longer show up in Intellisense.
  • I broke JNI.RegisterNatives in this snapshot (but it's fixed in cvs).

New snapshots: just the binaries and source plus binaries.

Wednesday, 24 November 2004 13:56:56 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, 09 November 2004
New Snapshot

A new snapshot. Nothing new, except that FileDescriptor.sync now actually works on Linux.

Thanks to lupus for reporting that it didn't work before.

New snapshots: just the binaries and source plus binaries.

Tuesday, 09 November 2004 17:37:29 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]