# Wednesday, March 10, 2004
To Invert Or Not To Invert

Stuart commented:
I'm not convinced that cli.System.Object should be visible to Java at all. AIUI, Java code will never see instances of cli.System.Object, because all such objects appear to inherit from java.lang.Object instead.

If cli.System.Object *is* visible to Java code, it introduces a paradox: java.lang.Object inherits from cli.System.Object (per the way it's actually implemented) but cli.System.Object should appear to inherit from java.lang.Object (per Java's rule that *everything* inherits from java.lang.Object). Now, it may be possible to create magic glue code that inverts the apparent inheritance relationship like that, but do you really want to go there? :)

The inversion is exactly what I was thinking about. Stuart's analysis above contains a crucial mistake, java.lang.Object does not inherit from cli.System.Object. However, it is virtually impossible not to get confused about this stuff, so let's try to make the discussion a little easier by defining a naming convention:

  • java.lang.Object
    This is the base class of all Java classes (as seen from the Java side of the world).
  • [classpath]java.lang.Object
    This is an implementation artifact of IKVM, it is a .NET type that is used as the base type for all non-remapped Java classes.
  • System.Object
    This is the base class of all .NET classes (as seen from the .NET side of the world).
  • cli.System.Object
    This is the IKVM manifestation of the System.Object type on the Java side of the world.

The paradox is that [classpath]java.lang.Object inherits from System.Object and cli.System.Object inherits from java.lang.Object, but hopefully it is now clear that this isn't a problem. (BTW, one of the definitions of a paradox is "A seemingly contradictory statement that may nonetheless be true").

There are actually two reasons why I would want to do this:

  1. If a Java class extends a .NET type (that was exported using netexp) you see both the virtual methods in java.lang.Object as well as the ones in System.Object that the class in question happens to override (a fairly arbitrary set). By introducing cli.System.Object as the penultimate base class for all .NET types, this can be made much more consistent. cli.System.Object would have final implementations for all the virtual methods in java.lang.Object (to make sure that the essentially non-existing methods don't get overridden) and it would introduce the real virtual methods of System.Object.

  2. If you want to define your own "first class" .NET exception class in Java, you need to extend cli.System.Exception. In other words, it makes for a more powerful programming model to expose the remapped types in this way.

Wednesday, March 10, 2004 10:15:16 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [6]
# Tuesday, March 9, 2004
Object Model Mapping Part 4

Yesterday I checked in a major change set that implements the new object model mapping infrastructure. Today I put the new snapshots online as well. The new implementation is about a thousand lines less code than the previous.

What's new

  • Many code changes to implement the new model.
  • When compiling classpath.dll, ikvmc now requires the -remap:map.xml option. This is the only time the mapping information is read from the XML. When code actually runs, or when other classes are compiled, the remapping information is read from custom attributes in classpath.dll.
  • Tracing infrastructure. Interesting points in the runtime now contain trace calls that can be enabled with a command line switch (or app.config setting). In addition, when Java code is compiled it can optionally be instrumented so that each method called writes its name and signature to the tracer. This has a big performance impact (it will be optimized a little bit in the future, but don't expect too much), so it is not enabled for classpath.dll, by default.
  • classpath.dll now contains the remapped types (java.lang.Object, java.lang.Throwable, java.lang.String and java.lang.Comparable). This means that if you want to create a Java like class in C# you can now extend java.lang.Object. Note however that you should never define your references as java.lang.Object, use System.Object instead. If you want to call a java.lang.Object instance method on a System.Object reference, use the corresponding static instancehelper method on java.lang.Object.


From the Java side of the fence, finalization continues to work as it always has, but when C# code is subclassing Java code, you should use the C# destructor if you need finalization. If you override the finalize method, you run the risk that it isn't called (it only gets called if one of your Java base classes actually overrides it). The C# destructor does the right thing. If you use another .NET language, you have to override Finalize and make sure that you call the base class Finalize. More complicated mixed scenarios (e.g. Java code subclassing C# code that subclasses Java code) are not supported at the moment (wrt finalization, other aspects should work fine).

What's next?

It's not quite done yet, but I'll be going through a stabilization phase before making any more changes. I have some ideas for changes to the way the remapped .NET types appear on the Java side (e.g. should it be possible to extend cli.System.Object in Java?). There are also some optimizations that can be done and there still remains some restructuring to be done.


I've tested this snapshot pretty well, but considering the scale of the changes, I expect some regressions. Bug reports are appreciated (as always).

New snapshots: just the binaries and source plus binaries.


Next month I'm speaking again at the rOOts conference in Bergen, Norway, where I had a very good time last year. Come and say hi if you're there. Also, I'm happy to be speaking again at the excellent (and fun) Colorado Software Summit in Keystone, Colorado in October.

Tuesday, March 9, 2004 10:42:12 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]
# Wednesday, March 3, 2004
Object Model Mapping Part 3

A brief update on the progress and some random thoughts on remapping. Today I got classpath.dll to build (and verify) for the first time using the new remapping infrastructure. Finally some progress. However, there is still some more work to do before it runs again.

Part of the new model is that there are now two different type wrappers (the internal class that contains the meta data of an IKVM type) for the remapped types. There is one type wrapper that is used during static compilation (RemapperTypeWrapper) and another one that is used during at runtime (CompiledTypeWrapper, also used for normal (i.e. non-remapped) statically compiled classes). The advantage of this is that there is less overhead at runtime and the code is also a bit less complex. This is also the final step in an invisible process that has been going on for a long time. It is now no longer possible to run IKVM without a statically compiled classpath.dll (it hasn't been possible for a while, but theoretically it could have been made to work). When I just got started, there was no static compiler yet and the only way for it to work was to load all classes dynamically, after the static compiler started to work, support for dynamically loading the core classes began to degenerate. That degeneration is now final and there is no way back ;-)

What's next?

More metadata needs to be emitted on the remapped types and CompiledTypeWrapper needs to be changed to understand it. The code needs to be cleaned up. I'm not sure yet, but I think a lot of complexity can be removed now. Virtual method invocation needs to be optimized. At the moment all virtual method call to remapped types go through the helper methods, but this is only needed is the reference type is not know to be a Java subclass. For example, calling java.lang.Object.toString() on a java.lang.Object reference requires the call to go through the helper method, but calling java.lang.Object.toString() on java.lang.Math (this is just a random example) doesn't need to go through the helper method.

Of course, there are also various loose ends that need to be tied up, but I think I'm on track to have a working version sometime next week.


Patrik posted an overview of the FOSDEM Java talks. It also includes links to the slides for some of the talks.

Wednesday, March 3, 2004 8:59:25 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Monday, March 1, 2004
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" />
  • <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">
            <call type="System.Threading.Monitor, mscorlib" name="Wait" sig="(Ljava.lang.Object;)Z" />
            <pop />
  • <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" />
            <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" />
  • <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">
            <ldstr value="" />
            <call type="System.String, mscorlib" name="Copy" />

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]);
    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, March 1, 2004 10:59:33 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [4]
# Sunday, February 29, 2004
Object Model Mapping

After suffering from coder's block (if that's the programmer's equivalent of writer's block) for weeks, I finally got started the past week on the new object model remapping infrastructure. I spent most of the week going in circles. Writing code, deleting it, writing more code, deleting it again. However, I think I figured it out now. To test that theory I decided to write a blog entry about it. Explaining something is usually a good way to find holes in your own understanding.

I'm going to start out by describing the problem. Then I'll look at the existing solution and it's limitations. Finally I'll explain the new approach I came up with. If everything goes well, by end the of the entry you'll be wondering "why did it take him so long to come up with this, it's obvious!". That means I came up with the right solution and I explained it well.

Note that I'm ignoring ghost interfaces in this entry. The current model will stay the same. For details, see the previous entries on ghost interfaces.

What are the goals?

  • We need a way to have the Java object model fit into the .NET model
  • We would like to enable full interoperability between the two models
  • Performance should be acceptable
  • Implementation shouldn't be overly complex

The .NET model (relevant types only):

For comparison, here is the Java model that we want to map to the .NET model:

There are several possible ways to go (I made up some random names):

  • Equivalence
    This is the current model. The Java classes are mapped to the equivalent .NET types. This works because System.Object has mostly the same virtual methods as java.lang.Object. For the java.lang.Throwable to System.Exception, a little more work is needed and that is where the java.lang.Throwable$VirtualMethods interface comes in. When a virtual method on Throwable is called through a System.Exception reference, the compiler calls a static helper method in Throwable$VirtualMethodsHelper that checks if the passed object implements the Throwable$VirtualMethods interface and if so, it calls the interface method, if not, it calls the Throwable implementation of the method (i.e. it considers the method not overridden by a subclass). A downside of using an interface for this is that all interface methods must be public, at the moment this isn't a problem because all virtual methods (except for clone and finalize derived from Object) in Throwable are public, but it could become a problem later on.
  • Extension
    This is a fairly straightforward approach where java.lang.Object extends System.Object and the Java array, String and Throwable classes are simply subclasses of java.lang.Object. It is easy to implement. The obvious downsides are that arrays will be slow (extra indirection), Strings need to be wrapped/unwrapped when doing interop with .NET code and Throwable is not a subclass of System.Exception (the CLI supports this, but once again not a good idea for interop).
  • Wrapping
    I apologise in advance, because I probably can't explain this one very well (because it doesn't make any sense to me). Many people have actually suggested this model. In the model java.lang.Object extends System.Object, but arrays, String and Throwable do not extend java.lang.Object, instead whenever an instance of those types is assigned to a java.lang.Object reference, it is wrapped in an instance of a special java.lang.Object wrapper class. The downside of this model is that wrapping and unwrapping is expensive and (and this is why I don't like this approach at all) that the expense is paid in ways that are very unexpected to the Java programmer (who expects simple assignment to be expensive?).
  • Mixed
    This is the new model. Explanation follows below.

What's wrong with equivalance?

Both J# and the current version of IKVM use equivalence (although many of the details differ and J# doesn't consider Throwable and System.Exception to be equivalent) and it works well. So why change it? There are four advantages to the mixed model:

  • Interop works better
    In the current model, if you subclass a Java class from C# and you want to override Object.equals, depending on whether any of the base classes overrides Object.equals you need tot override either Equals or equals. If you want to call Throwable.getStackTrace() from C# on a reference of type System.Exception there is no obvious way to do that.
  • More efficient representation of remapping model
    Currently every subclass of java.lang.Object overrides toString() to call ObjectHelper.toStringSpecial, this is needless code bloat. More importantly, before any classes can be resolved the map.xml remapping information needs to be parsed and interpreted (to know about java.lang.Object, java.lang.String and java.lang.Throwable). In the new model, java.lang.Object, java.lang.String and java.lang.Throwable will be in the classpath.dll, so they can be resolved on demand. The classes will be decorated with custom attributes to explain to the runtime that they are special, but no other metadata will need to be parsed or interpreted.
  • Cleaner model
    java.lang.ObjectHelper and java.lang.StringHelper no longer need to be public and the various $VirtualMethod helper types aren't needed anymore.
  • Easier to get right
    There are a few subtle bugs in the current implementation. Try the following for example:
    class ThrowableToString
      public static void main(String[] args) throws Exception
        String s = "cli.System.NotImplementedException";
        Object o = Class.forName(s).newInstance();
        Throwable t = (Throwable)o;
    It prints out:
    System.NotImplementedException: The method or operation is not implemented.
    cli.System.NotImplementedException: The method or operation is not implemented.

    Obvously, both lines should be the same. Another (at the moment theoretical) problem is that it is legal for code in the java.lang package to call Object.clone or Object.finalize (both methods are protected, but in Java, protected also implies package access), currently that wouldn't work.

Here is the mixed model I ended up with:

I called it mixed because it combines some features of equivalence and extension. For example, references of type java.lang.Object are still compiled as System.Object (like in the equivalence model), but the non-remapped Java classes extend java.lang.Object (like in the extension model).

java.lang.Object will contain all methods of the real java.lang.Object and in addition to those also a bunch of static helper methods that allow you to call java.lang.Object instance methods on System.Object references. The helper methods will test if the passed object is either a java.lang.Object or a java.lang.Throwable (for virtual methods) and if so, it will downcast and call the appropriate method on those classes, if not, it will perform an alternative action (that was specified in map.xml when this classpath.dll was compiled).

Object.finalize requires some special treatment since we don't want java.lang.Object.finalize to override System.Object.Finalize because that would cause all Java objects to end up on the finalizer queue and that's very inefficient. So the compiler will contain a rule to override System.Object.Finalize when a Java class overrides java.lang.Object.finalize.

I glossed over a lot of details, but those will have to wait for next time.


Finally a short note on FOSDEM (Free and Open Source Software Developer's Meeting). Last weekend I visisted FOSDEM in Brussels. I enjoyed seeing Dalibor, Chris, Mark, Sascha and Patrik again and I also enjoyed meeting gjc hackers Tom Tromey and Andrew Haley for the first time. Mark wrote up a nice report about it. If you haven't read it yet, go read it now. All in all a very good and productive get-together.

Sunday, February 29, 2004 3:43:01 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]
# Wednesday, February 18, 2004

Stuart pointed out the F.A.Q. was out of date, so I updated it a little bit. He also asked:

Speaking of which, I noticed while perusing the FAQ that the JIT compiler is included in IK.VM.NET.dll which means it's required for running even statically-compiled code. For apps that don't use clever classloading tricks, the JIT isn't needed at all when everything's been statically compiled. Would it be possible to separate the JIT out into a different DLL to reduce the necessary dependencies for a statically-compiled Java app?

Sure, the 275K of IK.VM.NET.dll is miniscule compared to the 3Mb of classpath.dll, but it's the principle of the thing ;)

This is definitely something I want to do. In fact, I would also like to have the option to only include parts of the Classpath code when statically compiling a library. So instead of having a dependency on classpath.dll, you'd suck in only the required classes.

Wednesday, February 18, 2004 9:36:11 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [4]
# Monday, February 16, 2004
Jikes 1.19, Bytecode Bug, Serialization and a New Snapshot


I upgraded to Jikes 1.19 that was released recently. It didn't like the netexp generated stub jars (which is good, because it turns out they were invalid), so I fixed netexp to be better behaved in what it emits. Jikes didn't like the fact that the inner interfaces that I created had the ACC_STATIC modifier set at the class level, rightly so, but the error message it came up with was very confusing. Along the way I also discovered that it is illegal for constructors to be marked native (kind of odd, I don't really see why you couldn't have a native constructor). So I made them non-native and have a simple method body that only contains a return. That isn't correct either (from the verifier's point of view) and I guess I should change it to throw an UnsatifiedLinkError. That would also be more clear in case anyone tries to run the stubs on a real JVM.

Jikes 1.19 has a bunch of new pedantic warnings (some enabled by default). I don't think this is a helpful feature at  the moment. Warnings are only useful if you can make sure you don't get any (by keeping your code clean), but when you already have an existing codebase, this is very hard and in the case of Classpath, where you have to implement a spec, you often don't have the option to do the right thing. So I would like to have to option to have lint like comment switches to disable specific warnings in a specific part of the code.

Bytecode Bug

I also did some work to reduce the number of failing Mauve testcases on IKVM and that caused me to discover that the bit shifting instructions were broken (oops!). On the JVM the shift count is always masked by the number of bits (-1) in the integral type you're shifting. So for example:

int i = 3;
System.out.println(i << 33);

This prints out 6 ( 3 << (33 & 31)). On the CLI, if the shift count is greater than the number of bits in the integral type, the result is undefined. I had to fix the bytecode compiler to explicitly do the mask operation.


Brian J. Sletten reported on the mailing list that deserialization was extremely slow. That was caused by the fact that reflection didn't cache the member information for statically compiled Java classes or .NET types. I fixed that and after that I also made some improvements to GNU Classpath's ObjectInputStream to speed it up even more. It's still marginally slower than the Sun JRE, but the difference shouldn't cause any problems.


I made a new snapshot. Here's what's new:

  • Changed classpath.build to disable jikes warnings (I know it's lame, but I grew tired of the useless warnings). I also added the -noTypeInitWarning option to ikvmc, to get rid of all the warning about running type initializers.
  • Implemented accessibility checks for Java reflection.
  • Cleaned up socket code and implemented all of the socket options (well, except for IP_MULTICAST_IF2).
  • Implemented Get###ArrayRegion/Set###ArrayRegion and GetPrimitiveArrayCritical/SetPrimitiveArray JNI functions.
  • Added all the 1.4 functions to the JNIEnv vtable.
  • Implemented support for field name overloading (a single class can have several different fields with the same name, if the types are different).
  • Changed the class format errors thrown by ClassFile.cs to .NET exception, instead of Java exception, to have better error handling in ikvmc.
  • Changed VMClass.getWrapperFromClass to use a delegate instead of reflection, to speed up reflection.
  • Fixed the compiler to mask shift counts for ishl, lshl, iushr, lushr, ishr, lshr bytecodes.
  • Fixed a bug in ghost handling (the bug could cause a "System.NotSupportedException: The invoked member is not supported in a dynamic module." exception).
  • Added EmitCheckcast and EmitInstanceOf virtual functions to TypeWrapper.
  • Added a LazyTypeWrapper base class for DotNetTypeWrapper and CompiledTypeWrapper, to cache the member information to speed up reflection.
  • Improved error handling in ikvmc.
  • Fixed netexp to generate valid (or less invalid) classes.
  • Regenerated (and checked in) mscorlib.jar, System.jar and System.Xml.jar with the new (fixed) version of netexp.

I didn't get around yet to removing the "virtual helpers" and introducing base classes for non-final remapped types (java.lang.Object and java.lang.Throwable).

New snapshots: just the binaries and source plus binaries.

Monday, February 16, 2004 4:33:14 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [1]
# Sunday, January 11, 2004
New Snapshot
I made a new snapshot. Here's what's new:
  • Renamed Type property of TypeWrapper to TypeAsTBD. This is to make clear that eventually all references to TypeAsTBD should be removed (a more specific TypeAsXXX should be used).
  • Changed several InvalidOperationException tests to assertions.
  • Changed JavaAssemblyAttribute to JavaModuleAttribute. In the future this attribute will go away and be replaced by an attribute that is attached to each type. This is to support linking Java and non-Java types into the same (single file) assembly with the Whidbey linker.
  • Removed getTypeFromWrapper, getWrapperFromType, getType and getName from VMClass and added getWrapperFromClass.
  • Added TypeWrapper.RunClassInit.
  • Fixed serialization and reflection to be able to call constructor on already allocated objects.
  • Added class accessibility checks in various places.
  • Added EmitCheckCast and EmitInstanceOf to TypeWrapper.
  • Changed all .NET type names in map.xml to partially qualified names.
  • Added -target:module support to ikvmc.
  • Added assembly signing support to ikvmc.
  • Added option to set assembly version to ikvmc.
  • Fixed bug in ikvmc that caused crash if jar contained a manifest.
  • Inner class support for native methods that are mapped to NativeCode.* classes.
  • Miscellaneous clean up and restructuring.

Most of this is clean up and restructuring to facilitate the next major change, removing the "virtual helpers" and introducing base classes for non-final remapped types (java.lang.Object and java.lang.Throwable).

New snapshots: just the binaries and source plus binaries.

Sunday, January 11, 2004 2:23:27 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Saturday, December 27, 2003

I switched from blogX to dasBlog. Mainly because Chris is no longer maintaining blogX, but also because I wanted some new functionality.

Hopefully the transition will be smooth, but if something is not working please let me know.

Saturday, December 27, 2003 7:24:44 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [2]
C# Shortcoming?
Tomas Restrepo posted some weird C# code on his weblog. It inspired me to test a few more corner cases:

namespace System {
   class Object { }
class ValueType { }

struct Int32 { }
class String { }
class A : string { public static void Main() { string s = new A(); } } }

If you compile this and then run peverify on the resulting executable, it will complain about every single type. Here is what's going on:

  • Object doesn't have a base class
  • ValueType extends our Object not the one in mscorlib
  • Int32 extends our ValueType
  • String extends our Object
  • A extends our String, but the local variable s is of type [mscorlib]System.String, so the assignment is not legal.

An obvious explanation for this (broken) behavior of the C# compiler is that Microsoft use the C# compiler to build mscorlib.dll. When compiling mscorlib.dll, the above behavior makes perfect sense. Of course, when you're not compiling mscorlib.dll, it doesn't make much sense and, in fact, it violates the language specification. For example, section 4.2.3 says:

       The keyword string is simply an alias for the predefined class System.String.

"predefined" being the important word here.

I guess it would be relatively easy to fix the compiler, but I think that there is actually an underlying problem: The C# language doesn't have support for assembly identities. Besides the above corner cases, this also causes problems in more real world situations.

Saturday, December 27, 2003 1:06:10 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0]