# Wednesday, August 21, 2002
No Title

I changed java.lang.Class from being remapped to System.Type to its Java implementation.

Integrated some additional native code implementations from Stuart Ballard. Thanks Stuart!

Updated the binaries and source snaphots.

Wednesday, August 21, 2002 10:22:17 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [1]
# Monday, August 19, 2002
No Title
I changed the XML remapping language to support Object.hashCode() & Object.toString() twiddling (see this previous posting for more info). It is now possible to specify which CIL code sequence should be substituted for a call to a method. Here is how Object.hashCode() looks:

<method type="instance" name="hashCode" sig="()I" access="public">
<override name="GetHashCode" />
<call class="System.Object name="GetHashCode" />
<dup />
<isinst class="System.String" />
<brfalse name="skip" />
<call class="StringHelper" name="hashCode" />
<br name="end" />
<label name="skip" />
<callvirt class="System.Object name="GetHashCode" />
<label name="end" />

Every virtual call (the invokevirtual bytecode) is converted into a test to see if the call is being done on a string, if so it calls StringHelper.hashCode(), if not, it calls System.Object.GetHashCode(). This isn't yet how it should be, because when it is known at compile time that the reference that hashCode() is called on isn't a string, then this test shouldn't be emitted. I still have to figure out a way to support this optimization.

Updated the binaries and source snaphots.

Monday, August 19, 2002 2:53:35 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [1]
# Friday, August 16, 2002
No Title

I reimplemented java.io.FileDescriptor to directly use System.IO.Stream (compiling against the netexp generated mscorlib.jar). This approach seems workable. As an example of how this turns out, here is the FileDescriptor.sync() code:

 public synchronized void sync() throws SyncFailedException
  if(stream == null)
   throw new SyncFailedException("The handle is invalid");
   if(false) throw new system.io.IOException();
  catch(system.io.IOException x)
   throw new SyncFailedException(x.get_Message());

I decided (for the moment) to not have every .NET method throw Throwable, but used the if(false) throw new ... trick. Works quite well, and it doesn't generate any code. I wonder if it would be legal for the compiler to move the stream.Flush() out of the try block... Not that I think any compiler would do this.

Updated the binaries and source snapshots.

Friday, August 16, 2002 3:20:30 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
# Thursday, August 15, 2002
No Title

Nothing spectacular, many small changes (clean up, refactoring, bug fixes, enhancements).

Updated the binaries and source snapshots.

Thursday, August 15, 2002 6:34:28 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
# Monday, August 12, 2002
No Title

I started on the ahead-of-time-compiler again, with the goal of precompiling the classpath classes. I've created a zip containing all the binaries needed. Just unzip the file, make sure the directory is in the path and run a Java class:
ikvm <class>

It uses the CLASSPATH environment variable (instead of the earlier IKVM_CLASSPATH), and if that is not set, it assumes the current directory. Zips and jars are not yet supported.

The classpath.dll (from the binaries zip) can also be used from (for example) a C# program (don't forget to add a reference to classpath.dll & ik.vm.net.dll):

class JTest
public static void Main()
java.util.Hashtable h = new java.util.Hashtable();
h.put("foo", "bar");
h.put("fu", "baz");
Updated the source snapshot.
Monday, August 12, 2002 4:41:14 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
# Friday, August 9, 2002
No Title

I did some refactoring and various small changes. Field & method accessibilty is now enforced (although there still are some issues).

Updated the snapshot.

Friday, August 9, 2002 4:11:29 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
# Thursday, August 8, 2002
No Title

Stuart commented:

What do you do if you have a Java program that depends on String.hashCode() doing exactly what Java defines it to do (ie, produce consistent output with other VMs) rather than what .NET defines String.GetHashCode() to do? I encountered this in my own code so in theory I can implement (or steal from ClassPath) an implementation of String.hashCode() and use that, but the problem could come up in other scenarios. It's an interesting one, don't you think? :)

I haven't gotten around to implementing it, but the idea is that all virtual calls to Object.toString() are changed to:

  if(o instanceof String) return StringHelper.hashCode(o);
  else return Object.GetHashCode();

One reason I haven't done this yet, is because I think it is totally brain damaged that Sun actually specified the hashcode algorithm for String (and in JLS 1.0 they specified a broken algorithm, which JDK 1.0 didn't implement), another reason is that I haven't figured out how to build this generically (specified in the map.xml file, instead of hardcoded in the VM).

A similar issue also occurs with Object.toString(). Nonvirtual calls to Object.toString() should be redirected to a helper function that returns:

  getClass().getName() + "@" + Integer.toHexString(hashCode())

This isn't really a problem (if you accept the fact that non-Java classes return something different for toString(), which I do), but for arrays it does pose a problem. For arrays to return the proper string when toString() is called on them (virtually through an object reference), all virtual calls to Object.toString() need to be treated like hashCode() above.

btw, how come your example isn't "import System.Reflection"? Does netexp auto-lowercase all namespace names?

At the moment it does. Not because this is the Java convention, but because Java cannot really deal with a namespace System (because of the class java.lang.System).

That seems risky if (as I believe, but don't know for sure) it's possible to have two distinct .NET namespaces with names differing only by case...

This is true, but in practice it seems unlikely you'll ever encounter a problem with this, but the case conversion will definitely be optional in the future.

Thursday, August 8, 2002 10:13:57 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
# Wednesday, August 7, 2002
No Title

I started work netexp. This is the tool that generates Java stubs for all public types in a .NET assembly, so that Java code can be compiled against .NET code (using any standard Java compiler). The VM understands the stubs and replaces any references to them with the actual .NET types.

It's still in its very early stages, but here is an example that already runs:

import system.*;
import system.reflection.*;
class test
  public static void main(String[] args)
    Type type = Type.GetType("System.Object");
    ConstructorInfo ci = type.GetConstructor(
BindingFlags.Public | BindingFlags.Instance,
       null, Type.EmptyTypes, null);

I updated the snapshot.

Wednesday, August 7, 2002 4:11:58 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [2]
# Tuesday, August 6, 2002
No Title

I wrote a jasmin class that "uses" all bytecode instructions, to check if I had implemented all of them. It turns out three were missing:

  • jsr_w  -- jump to subroutine using a 32 bit offset (methods have a maximum size of 64KB, so why we need this, I don't know)
  • goto_w -- jump to instruction using a 32 bit offset (see jsr_w)
  • dup2_x2 -- scramble the contents of the stack ;-)

I implemented all three, although I very much doubt that they'll ever be used.

In the process of doing this, I found and fixed a few bugs in the verifier and compiler. I also changed the compiler to generate a "throw VerifyError("...")" when compiling a method that doesn't verify (instead of throwing the exception at compile time). This matches the JVM behaviour better.

Updated snapshot is here.

Tuesday, August 6, 2002 2:09:00 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [0]
# Friday, August 2, 2002
No Title

Stuart commented:

Btw, I'm just trying out your latest snapshot. I'll add further comments as I get further, but so far I had to make a couple of changes to let me build:

VS.NET treated "if (false)" on line 1732 of vm.cs as creating unreachable code. I changed it to "if (false.Equals(true))" so that it didn't treat it as a compiletime constant.

I'm used to using the if(false) construct, because that's the way to not get the compiler to complain in Java. Anyway, I just commented it out now, it's only there for debugging. So it will go away in the near future, I hope.

And the default compilation options disallowed unsafe code, so I had to change that in order for BigEndianBinaryReader.cs to compile.

Aren't you using my VS.NET project? That should include the proper switches for it to build, at least it builds on my machine ;-)

Okay, I've got it to the point of giving me an intelligent error at runtime, at least. Issues so far:

It doesn't (seem to) honor CLASSPATH, at least when run from a command prompt.

That is correct, for the time being it uses the IKVM_CLASSPATH environment variable. BTW, zips and jars are not supported (and won't be for a while, because I want to write the Zip/Jar class loader in Java. That isn't going to happen until after I've got the ahead-of-time compiler working).

Now the showstopper that I haven't been able to figure out how to get past. As far as I can see the download doesn't include *any* of the GNU Classpath libraries, which means that (as far as I can see) it can't run anything at all. I'm guessing that this is just a simple omission from the zip - is that right?

I don't include the classpath stuff to keep the zip small. I did earlier post a link to the compiled classpath classes that I use. I've created a new snapshot (many small changes) and also posted a zip containing the classpath classes plus a few of my own modifications (the source for those is in the classes directory in ikvm.zip).

Friday, August 2, 2002 10:12:28 AM (W. Europe Daylight Time, UTC+02:00)  #    Comments [2]