# Monday, 17 November 2003
C# Destructor Considered Harmful

After last week's Java finalization bashing, it turns out that C# is even more broken.

A C# Destructor Cannot Be Sealed

This is really bad! If you recall my example of a proper class that uses finalization correctly, you might remember that the class was final. I still  highly recommend this, but in some scenarios it might be preferable to allow others to extend your class. In such cases it is highly recommended that you make your finalize method final. Otherwise the subclasser might override finalize and forget to call your finalize method.

If you wrap an unmanaged resource and your class is non-final and it is exposed to untrusted code, you must make your finalize method final.

If you don't, the untrusted code can (intentionally or not) create a subclass of your class, override finalize (not call super.finalize()) and start leaking unmanaged resources that will never be cleaned up as long as the JVM is running.

Back To C#

To see why the C# design is a problem, we only need to look at System.WeakReference. It is a public non-sealed class that wraps an unmanaged resource (a GCHandle), the destructor is obviously not sealed and it does not require any privileges to use, this equals a recipe for disaster. Untrusted code can leak GCHandles that will never be reclaimed as long as the CLR is running [1]. Not even when the AppDomain is unloaded!

IMO, the destructor syntax should be deprecated and Finalize should be treated like any other method. This current design is hardly a pit of success.

[1] While the C# destructor is nice enough to always call the base class Finalize method, other languages (e.g. ILASM or VB.NET) don't require this.

Monday, 17 November 2003 14:19:49 (W. Europe Standard Time, UTC+01:00)  #    Comments [10]
New Snapshot

As usual it took a mighty long time, but I finally managed to check in my changes and create a new snapshot.

Here is what's new:

  • Support for @deprecated attribute in ikvmc. Compiled as System.ObsoleteAttribute (IsError = false).
  • Support for declared exceptions. Reflection (Method.getExceptionTypes) will now return all declared exceptions that the method throws. C# (or VB.NET or whatever) code can use the OpenSystem.Java.ThrowsAttribute attribute to declare exceptions.
  • Changed value type handling to treat null reference as default instance when unboxing (instead of throwing an unexpected NullPointerException). Added (experimental) support for customized boxing in map.xml.
  • Remapped types can now have constant (non-blank static final) fields.
  • java.lang.String and java.lang.Throwable now have the correct serialVersionUID.
  • Fixed a bug (introduced with the ghost interfaces change) that caused virtual call to Object.clone() to fail with a CloneNotSupportedException.
  • Changed static initializer support to use a special field (__<clinit>) in the base class to trigger the base class static initializer to run (instead of using RuntimeHelpers.RunClassConstructor). This should make static initialization a little faster.
  • Changed "managed JNI" method lookup to support overloading.
  • Made all introduced methods in ghost wrappers HideFromReflection.
  • Re-enabled the warning for missing native methods on static compilation.
  • Fixed reflection on .NET types to hide static methods on interfaces (Java doesn't allow them).
  • Added dummy "native" methods for unused methods in java.lang.reflect.Proxy (to avoid warnings from ikvmc when compiling classpath.dll).
  • Fixed InetAddress.getLocalHostname() method name typo.
  • Added skeleton implementation of "native" methods for java.nio.channels.FileChannelImpl.
  • Changed DupHelper in compiler to support "unitialized this" references.
  • Changed compiler to emit ldc_i4_0 / conv_i8 instead of ldc_8 0 and various similar (small) optimizations.
  • Added support for declaring exceptions on methods of remapped types and added appropriate declaration to map.xml.
  • Added experimental remapping of gnu.classpath.RawData to System.IntPtr and custom boxing operator for gnu.classpath.RawData to box IntPtr.Zero to a null reference.
  • Added support for ldnull and box opcodes to remapping instruction set.
  • Added optional constant attribute to field element of remapping xml.
  • Added ikvm\classpath\java\lang\ref\Reference.java (copied from GNU Classpath and modified to implement (most of) the required functionality).
  • Added exclusion list to compilation of classpath.dll to remove unused classes (VMObject, VMString and VMThrowable).
  • Fixed netexp to create classes for non-public base classes.
  • Fixed netexp to export the interfaces a class implements.
  • Fixed netexp to export declared exceptions.
  • Some refactoring and a few small other changes.

I used Stuart's excellent japitools to test the remapping, reflection and round-tripping and found a lot of bugs this way. I ran japize on a  classpath.jar generated by netexp from classpath.dll. This way, the whole tool chain is tested (ikvmc -> reflection -> netexp).

Note that @deprecated doesn't round-trip, because Java reflection doesn't expose the fact that something is deprecated.

The full patch is here (excluding the new files, Reference.java and exclude.lst).

Updated binary and source snapshots are available.

Monday, 17 November 2003 13:04:39 (W. Europe Standard Time, UTC+01:00)  #    Comments [2]
# Saturday, 15 November 2003
Weak References

I finally implemented (partial) support for weak references. Java has three types of weak references:

Only WeakReference is implemented 100% correctly (barring any bugs), but I'm pretty sure that the difference between the IKVM and JVM implementations of PhantomReference is undetectable. SoftReference is currently implemented as a WeakReference, this means that they will be cleared too soon and this could adversly affect performance of caches that depend on SoftReference.

ReferenceQueue

ReferenceQueue is fully supported, but it isn't very efficient. Every Reference instance that is associated with a queue has a corresponding object that watches for GC activity (by having a finalize method and being unreachable), each time the finalizer runs it checks the associated Reference to see if it has been cleared, if it has the Reference is inserted into the ReferenceQueue, if it hasn't, a new watcher object instance is created.

PhantomReference

The difference between the IKVM and JVM PhantomReference is that the IKVM implementation doesn't actually prevent the object from being collected, but since you cannot possibly get a reference to a phantom reachable object (apart from using non-portable reflection hacks), I don't understand the point of the specified behavior for PhantomReference.

Source

Source is not yet in CVS. I made many other changes as well and I'll try to check them in and create a new snapshot tomorrow.

Saturday, 15 November 2003 22:03:33 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Friday, 14 November 2003
Generic Algorithms

Anders Hejlsberg came up with a clever trick to implement generic algorithms with the current C# generics implementation.

According to the C# Version 2.0 Language Specification, interface method invocations on value types (uses a generics parameters) will not cause the value to be boxed. This gave me the idea to use a value type for the calculator to avoid the virtual function dispatch.

using System;
using System.Collections.Generic;

interface ICalculator<T>
{
  T Add(T t1, T t2);
}

struct IntCalculator : ICalculator<int>
{
 
public int Add(int t1, int t2)
  {
   
return t1 + t2;
  }
}

class AlgorithmLibrary<C, T>
where C : ICalculator<T>
{
 
static C calculator = C.default;

  public static T Sum(List<T> items)
  {
    T sum = T.
default;
   
for(int i = 0; i < items.Count; i++)
   
{
      sum = calculator.Add(sum, items[i]);
    }
   
return sum;
  }
}

public class Class4
{
 
static void Main()
  {
   
List<int> foo = new List <int>();
    foo.Add(1);
    foo.Add(2);
    foo.Add(3);

    int sum = AlgorithmLibrary<IntCalculator
, int>.Sum(foo);
    
Console.WriteLine(sum);
  }
}

Depending on how the JIT decides to compile this, this could be very efficient (the JIT could decide to generate specialized x86 code for every type that Sum() is used on).

Friday, 14 November 2003 11:53:43 (W. Europe Standard Time, UTC+01:00)  #    Comments [4]
# Sunday, 09 November 2003
Finalize Considered Harmful

Last week at the PDC I talked to Anders Hejlsberg (btw, he's a really nice person). I asked him about having a way to prevent boxing and he told me they had already considered it and found too many problems with it (reflection, generics, more on that in another post). I also told him that I felt that C# had only one design flaw: The destructor syntax. I was reminded of this when I was working on some java.nio classes yesterday.

The Java Community Doesn't Understand Finalization

How's that for a section title? It's obviously a generalization, but I don't think it is too far from the truth. To prepare for writing this entry I looked at finalization in three books:

All three are excellent books. Highly recommended.

Addison-Wesley put online the relevant section of Peter's book here. First, let me say that Peter is a very smart guy (I know him from the Colorado Software Summit) and that this doesn't reflect on the quality of the rest of the book, but I'm picking on him because part of this particular "praxis" demonstrates the, IMO, typical misunderstanding of finalization in the Java community (and because his book is one of the few Java books I own).

So, what is wrong with his code? He uses the finalize method to cleanup managed objects that already have their own finalizer!
This practice is widely used in the Java libraries as well and it is a really bad idea. At best it doesn't help (the ServerSocket and FileInputStream will have already been finalized (see JLS 12.6.2 Finalizer Invocations are Not Ordered), or will be finalized soon anyway) and at worst you create APIs that are very difficult to use (or code) because multiple objects "own" the same resource.

The JLS doesn't say anything useful about finalization, but does mention that you should always call super.finalize() in your finalizer (like Peter does in his code).

THIS IS USELESS!

Why? All finalizable classes should extend java.lang.Object and be final, because there is no reason for them not to be. Here is what a finalizable class should look like.

final class FileHandle {
  private int nativeHandle;

  FileHandle(String filename) {
    nativeHandle = nativeOpen(filename);
  }
  private static int nativeOpen(String filename);

  synchronized void close() {
    if(nativeHandle != 0) {
      nativeClose(nativeHandle);
      nativeHandle = 0;
    }
  }
  private static native void nativeClose(int handle);
  
  protected void finalize() {
    close();
  }

  // example operation, the others are omitted
  synchronized int read(...) {
    return nativeRead(nativeHandle, ...);
  }
  private static native int nativeRead(int handle, ...);
} 

Other than exposing the primitive operations that can be performed on the unmanaged resource (thru the handle), the class should have no functionality. The class is package private and used only by the public classes that actually implement the exposed API. The class should never have any references to other objects.

This pattern of factoring out the finalizable resource into a separate class is discussed in the Jones/Lins book. It seems that not enough people read it.

The above discussion is specific to Java and .NET finalization, there are other implementations of finalization that do finalize in order. In such an implementation it can actually be useful to use references to other objects in your finalize method, because you know that the object you're using hasn't already been finalized.

Back To C#

So what's wrong with the C# destructor syntax? I think it encourages the mistake of thinking of Finalize as a destructor (and thus using it to cleanup other managed objects). Also, a "feature" of the C# destructor is that it always call the base class Finalize method. I hope I've shown that this isn't very useful.

Anders agreed with me. "In retrospect we probably shouldn't have done that." (paraphrased from memory).

Sunday, 09 November 2003 15:53:01 (W. Europe Standard Time, UTC+01:00)  #    Comments [1]
# Wednesday, 29 October 2003
More Name Dropping
I finally met Miguel de Icaza at the Unofficial Mono BoF and he gave me a cool Mono T-shirt (Thanks Miguel!). I talked about IKVM with Chris Brumme and discussed reflection with Peter Drayton and Dario Russi. I asked Eric Gunnerson about a custom attribute to prevent boxing, but from his response I take it that it is unlikely that'll ever happen. I met Chris Hollander at the "alternate programming languages BoF" last night (at 11pm, ugh). He told me he got a lot of referrers from my site. Since his blog is named "Objective", this makes me wonder if people are looking for information on the objectives of IKVM. I really should write some more documentation ;-)
Wednesday, 29 October 2003 20:59:44 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, 28 October 2003
Whidbey

Whidbey was unveiled this morning. It has lots of nice new features, but I'm particularly interested in some of the improvements to reflection.

  • Finally, there is support for custom modifiers in Reflection.Emit
  • DynamicMethod allows methods to be generated that "implement" a delegate and the code gets garbage collected along with the delegate when it is no longer reachable.
  • There is (some) support for doing more of Reflection.Emit based on tokens (a token is an Int32 that refers to metadata in a module), instead of the more heavy weight MethodInfo/FieldInfo/etc. objects.
  • Reflection objects (e.g. MethodInfo) are now kept in a weak reference cache, so that they can be garbage collected if they aren't needed anymore.

I need to do some rewriting to take advantage of the last two, but once I do, the memory usage of IKVM should (hopefully) go down significantly. I'm not entirely sure that the changes they made are powerful enough, but it is an important step in the right direction.

I know I promised I wasn't going to move to Whidbey anytime soon, but I am going to introduce some conditional compilation for Whidbey specific features, so that I can play around with them. Mainstream development will continue to be on 1.1

Tuesday, 28 October 2003 21:23:21 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Monday, 27 October 2003
Eclipse Reception

Last night I visisted the Eclipse reception at OOPSLA 2003. Chris Laffra from IBM Canada had prepared a poster on Alternative Eclipse Execution Models and he included IKVM on it. He also invited me to the Eclipse Reception. There was a fair amount of interest in IKVM and I talked to a bunch of people, including Erich Gamma and Doug Lea. That was cool.

This morning I atttended the PDC keynote by Bill Gates and Jim Allchin. See the other PDC blogs for details ;-)

If you're at the PDC, I'm wearing a red/white/blue (vertical bars, as in the Dutch flag) polo shirt with a small Java logo in the front. If you see me, say hi.

Monday, 27 October 2003 21:15:03 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Sunday, 26 October 2003
PDC

I arrived in LA last Friday. No internet access in the hotel room, but fortunately the conference center has excellent connectivity.

I'm not going to be PDC blogging, but expect more non-technical entries this week than usual.

Tonight I'm going to the Eclipse reception at OOPSLA 2003. It'll be fun to chat with some of the Eclipse developers. May be I can get them to fix their class loading ;-)

Sunday, 26 October 2003 18:07:35 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Wednesday, 22 October 2003
Private Super Classes

While trying to get Stuart Ballard's Japitools to work on a netexp generated export of IKVM's classpath.dll, I encountered java.awt.BufferCapabilities.FlipContents. This public (inner) class has non-public base class (java.awt.AttributeValue). What kind of a bizarre design is that?

I had never realised that the Java language allowed this. Fortunately, the C# designers didn't make the same mistake.

Note that a similar issue exists for fields (and method arguments). In Java it is legal to have a public field of a non-public type, or a public method that takes an argument of a non-public type. C# prohibits both of these.

I fixed netexp to export non-public base classes (and I should do the same for interfaces, fields and method arguments/return type).

Wednesday, 22 October 2003 18:36:30 (W. Europe Daylight Time, UTC+02:00)  #    Comments [4]