# Monday, 01 March 2004
« Object Model Mapping | Main | Object Model Mapping Part 3 »
Object Model Mapping Part 2

In yesterday's entry I didn't get to the stuff that kept me going in circles last week. I had decided on the mixed model a few months ago, but as usual the devil is in the details.

Initially I wanted to keep the map.xml format more or less the same and I think that put me on the wrong track. Let's start by taking a look at some of the current map.xml features. Within the <class> tag there are <constructor> and <method> tags. These can contain several different child tags:

  • Empty (i.e. no child tags)
    The method is identical to the corresponding method in the underlying type. Example: The constructor of java.lang.Object is identical to the constructor of System.Object, so the tag looks like this:
    <constructor sig="()V" modifiers="public" />
  • <redirect>
    The method is redirected to another method. This can be a static method in a helper class or a static or instance method in the underlying type. Example: java.lang.Object.notifyAll() is redirected to System.Threading.Monitor.PulseAll():
    <method name="notifyAll" sig="()V" modifiers="public final">
        <redirect class="System.Threading.Monitor, mscorlib" name="PulseAll" sig="(Ljava.lang.Object;)V" type="static" />
    </method>
  • <invokespecial>
    If the method is invoked using the invokespecial bytecode, this CIL sequence is emitted. Example: java.lang.Object.wait() is implemented by calling System.Threading.Monitor.Wait(), but Monitor.Wait returns a boolean that has to be discarded:
    <method name="wait" sig="()V" modifiers="public final">
        <invokespecial>
            <call type="System.Threading.Monitor, mscorlib" name="Wait" sig="(Ljava.lang.Object;)Z" />
            <pop />
        </invokespecial>
    </method>
  • <invokevirtual>
    Similar to <invokespecial>, but this defines the CIL that is emitted when the invokevirtual bytecode is used to call the method.
  • <override>
    Specifies that this method conceptually overrides the method named in the <override> tag. I say "conceptually" because in the equivalence model there is no real class. However, if a real subclass would override this method, it would actually be overriding the method named in the override tag. Example: java.lang.Object.hashCode() overrides System.Object.GetHashCode:
    <method name="hashCode" sig="()I" modifiers="public">
        <override name="GetHashCode" />
        <invokevirtual>
            <dup />
            <isinst type="System.String, mscorlib" />
            <brfalse name="skip" />
            <castclass type="System.String, mscorlib" />
            <call class="java.lang.StringHelper" name="hashCode" sig="(Ljava.lang.String;)I" />
            <br name="end" />
            <label name="skip" />
            <callvirt type="System.Object, mscorlib" name="GetHashCode" />
            <label name="end" />
        </invokevirtual>
    </method>
  • <newobj>
    Used in constructors to define the CIL that is emitted when a new instance is created. Example: java.lang.String has a default constructor, but System.String doesn't:
    <constructor sig="()V" modifiers="public">
        <newobj>
            <ldstr value="" />
            <call type="System.String, mscorlib" name="Copy" />
        </newobj>
    </constructor>

The thing to note is that some of the remapping implications are still handled manually in this scheme. For example, the <invokevirtual> of Object.hashCode has to check for string instances. This information can be derived from the remapping file and it shouldn't be necessary to do this explicitly.

I didn't really like the <invokespecial> and <invokevirtual> constructs and I explored the idea of only having a <body> tag that contains the method body. However, it soon became clear that that wouldn't be enough. For example, the implementation of java.lang.Object.clone needs to call the protected method System.Object.MemberwiseClone and this is only allowed in subclasses. So it wouldn't be possible to generate a verifiable helper method for that.

The obvious solution (in hindsight) came to me when I realised that there are actually two types of "subclasses" of java.lang.Object, the ones that really extend java.lang.Object (our generated .NET type) and the ones that don't (e.g. arrays, System.String and all other .NET types). I knew this before, of course, but I was trying to make the model too general. After this realisation, it became obvious that every method should have a <body> and an <alternateBody> (tentative names).

After I've modified the remapping engine to automatically handle all the overridden method implications, the <alternateBody> construct will not be needed for many methods. I think only for Object.clone and Object.finalize and both will be trivial. The <alternateBody> for clone will throw a CloneNotSupportedException (it could also check if the object implements ICloneable and if so, invoke that Clone method, but does this really help?) and the <alternateBody> for finalize will simply be empty, since there is no good reason to ever explicitly invoke the Finalize method of a .NET type.

As an aside, I'm also going to remove the <redirect> construct, because it doens't really add any value. It's just as easy to have a <body> with a <call>.

I'm not clear on the performance implications of these changes. In the existing model, many of the remapping constructs are inlined, but in the new model they won't be, invokespecial will end up calling the real method in the new classes and invokevirtual will call the static helper method. This will probably be slightly slower, but I think the other advantages easily outweigh this.

Another advantage of this scheme that I didn't yet mention is that reflection on remapped methods is now trivial. Currently, the following program doesn't work on IKVM, in the new model the call would simply end up at the static helper method for clone:

class ReflectClone
{
  public static void main(String[] args) throws Exception
  {
    java.lang.reflect.Method m;
    m = Object.class.getDeclaredMethod("clone", new Class[0]);
    m.setAccessible(true);
    System.out.println(m.invoke(args, new Object[0]));
  }
}

BTW, I originally tried this by getting the public clone method on the array class, but oddly enough on the Sun JVM array types don't appear to have a public clone method (even though you can call it just fine!).

Monday, 01 March 2004 10:59:33 (W. Europe Standard Time, UTC+01:00)  #    Comments [4]
Monday, 01 March 2004 20:04:09 (W. Europe Standard Time, UTC+01:00)
There's some malformed HTML at the top of this entry (after "different child tags:")

If you were hoping for more constructive feedback... sorry ;) These last two entries had a lot of stuff to digest, and I wasn't aware of the problems with the existing model until I read them. I may have some later once the concepts have stewed in my head for a while, for whatever my comments are worth.

My first gut reaction was that the old model was conceptually simpler, and therefore should be favored unless there was no way to fix the problems with it. It sounds like that might be the case, though - or fixing the problems would make it so much more complex that it would no longer be conceptually simpler. So I'm reserving comment until I've better comprehended the nature of the problems and of the solution you offer.
Stuart
Tuesday, 02 March 2004 10:34:28 (W. Europe Standard Time, UTC+01:00)
>>There's some malformed HTML at the top of this entry (after "different child tags:") <<

Thanks. That explains the text fragment that was suddenly missing (when I was writing the entry in Frontpage 2003).

>>My first gut reaction was that the old model was conceptually simpler<<

It's hard to say, but I think the new model will definitely be simpler from an implementation point-of-view.

I don't have any doubts that this is the right way to go, but feedback is always welcome of course (it'll just be extremely difficult to change my mind ;-)).
Tuesday, 02 March 2004 15:18:52 (W. Europe Standard Time, UTC+01:00)
Heh, that's okay, I relish a challenge ;)

As I said, I haven't yet decided either way on which model I prefer; I don't understand the new model or the problems with the old well enough to know whether I think my gut reaction is sound or not.

Currently I think I can sum up the essence of the old model in a single sentence: "java.lang.Object *is* System.Object, likewise for String and Throwable/Exception; everything else is just the fiddly details needed to make that actually work". Obviously that's not a terribly elegant or well-expressed sentence and glosses over a heck of a lot, including some significant problems, but conceptually, that sentence is easy enough to grasp.

So far, I don't understand the new model well enough to come up with a similarly concise sentence distilling its essence down to the bare minimum. Would this sentence be along the right lines?:

"References of type java.lang.Object are translated to references of System.Object, but *instances* of java objects really do inherit from an actual java.lang.Object class"?

If that sentence is true, what will Java types find if they try to do reflection to find their superclass chain - java.lang.Object followed by System.Object, or just java.lang.Object? What about if they try to do reflection on a .NET object, will they still find java.lang.Object at the top of the superclass chain? Will they find System.Object at all? And if all the answers are "java.lang.Object only", how does the magic work that makes that happen?

(Note: I'm still not trying to change your mind, just to understand the new model well enough to know whether I *want* to change your mind or not ;) )
Stuart
Tuesday, 02 March 2004 15:38:27 (W. Europe Standard Time, UTC+01:00)
>>"References of type java.lang.Object are translated to references of System.Object, but *instances* of java objects really do inherit from an actual java.lang.Object class"? <<

Right.

>>If that sentence is true, what will Java types find if they try to do reflection to find their superclass chain - java.lang.Object followed by System.Object, or just java.lang.Object? What about if they try to do reflection on a .NET object, will they still find java.lang.Object at the top of the superclass chain? Will they find System.Object at all? And if all the answers are "java.lang.Object only", how does the magic work that makes that happen? <<

Right again, java.lang.Object only. There really isn't much magic required. The Java reflection is based on the internal model of IKVM and it knows that java.lang.Object is special.
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview