# Friday, December 2, 2011
« New Development Snapshot | Main | Function Pointer Types »
Custom Modifiers

As I wrote earlier today, the theme of today's snapshot is custom modifiers, but I decided to expand a bit on that, since they are a relatively unknown feature of .NET.

In the early days of IKVM.NET I ran into the problem that some Java types must be erased in .NET signatures. An example are arrays of what I call ghost types. Fortunately these are not very common, but here's an example method:

void foo(CharSequence[] array) { }

When this is compiled with ikvmc, you end up with something like this:

[NameSig("foo", "([Ljava.lang.CharSequence;)V")]
void foo(object[] array) { }

If the class happened to already have a foo method with that signature, the method name would be mangled with a numerical suffix (e.g. foo_0).

Now, instead of adding a custom attribute and using name mangling, .NET has a much better solution for this. Custom modifiers, but unfortunately you couldn't use them via .NET's System.Reflection.Emit API back in the .NET 1.x days.

When .NET 2.0 came around they added (some) support for custom modifiers, but it both had some issues and by then I already had a working solution, so there was not much need for changing the implementation. The one exception was in constructor signatures, since there you cannot mangle the method name, you have to rely on the signature types to make the signature unique, so there I did add custom modifiers when necessary to make sure the signatures are unique.

In today's snapshot I've changed fields and constructors to always use custom modifiers (when necessary) and removed the usage of the NameSigAttribute. Methods still need to be done.

Here's an example with two constructors that end up with the same signatures:

public class Test {
  public Test(Object obj) { }
  public Test(cli.System.Object obj) { }
}

When you compile this with ikvmc, you get something like this:

public class Test {
  public Test(object obj) { }
  public Test(object modopt(object) obj) { }
}

The modopt(object) construct here stands for an optional custom modifier of type object. When this class is encountered by IKVM (reflection or ikvmc) it can recover the original Java types based on the signature and custom modifier. When you use this class from C#, the C# compiler will prefer the constructor without the custom modifier, so you can still use the class. The cli.System.Object overload is not useable from C#, but when there is no signature conflict, C# will also allow you to use the version with the custom modifier. So it allows for nice cross language interop (especially given how unlikely all these scenarios are).

As I said, I haven't done methods yet, but I did experiment a little to see if C# interop would work. I was especially concerned about overriding/implementing methods with a custom modifier in the signature, but the .NET C# compiler supports this pretty well. The Mono C# compiler does not yet have support for this. Only when you have two or more abstract methods with the same signature, you can run into a problem. For base classes with "duplicate" abstract methods, the C# compiler will not allow you to subclass that class, but you can implement interfaces with "duplicate" methods and in that case the C# compiler will emit bridge methods for all signatures that call into your (necessarily) single implemenation method.

Crazy Stuff

With the new IKVM.Reflection custom modifier support, you can now generate signatures that cannot be generated with System.Reflection.Emit. Here's an example of interleaved required and optional custom modifiers:

var u = new Universe();
var ab = u.DefineDynamicAssembly(new AssemblyName("Foo"), AssemblyBuilderAccess.Save);
var modb = ab.DefineDynamicModule("Foo", "Foo.dll");
var tb = modb.DefineType("FooType", TypeAttributes.Public | TypeAttributes.Abstract);
tb.DefineDefaultConstructor(MethodAttributes.Public);
CustomModifiersBuilder mods = new CustomModifiersBuilder();
mods.AddOptional(u.Import(typeof(object)));
mods.AddRequired(u.Import(typeof(object)));
mods.AddOptional(u.Import(typeof(object)));
var mb = tb.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.NewSlot | MethodAttributes.Virtual);
mb.__SetSignature(null, new CustomModifiers(), new Type[] { u.Import(typeof(int)) }, new CustomModifiers[] { mods.Create() });
tb.CreateType();
ab.Save("Foo.dll");

The resulting FooType in Foo.dll contains a method M that cannot be called (or overridden) by System.Reflection.Emit generated code. It also can't be used from C#,  but that is by design, the C# compiler will not allow you to use any members that have required custom modifiers that it doesn't understand (I think that the only required custom modifier that it understands is IsVolatile on fields).

Of course, IKVM itself is not very good about dealing with custom modifiers yet. It only understands its own custom modifiers, but can get confused when accessing other .NET code with custom modifiers.

Friday, December 2, 2011 8:57:09 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
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