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