# Sunday, 21 November 2010
« New Development Snapshot | Main | New Development Snapshot »
How to Detect if a Method is Overridden

Suppose you want to know if (the class of) a particular object overrides a virtual method. For an example of this see OpenJDK's Thread.isCCLOverriden() (line 1573).

In Java the obvious way to do this would be to use reflection. On the CLR there is another way that is both more accurate1 and more efficient.

Here's the MSIL method from IKVM's java.lang.Thread.isCCLOverridden() implementation:

.method private hidebysig static bool isCCLOverridden(class java.lang.Thread A_0) cil managed
{
  ldftn      instance class java.lang.ClassLoader java.lang.Thread::getContextClassLoader()
  ldarg.0
  ldvirtftn  instance class java.lang.ClassLoader java.lang.Thread::getContextClassLoader()
  ceq
  ldftn      instance void java.lang.Thread::setContextClassLoader(class java.lang.ClassLoader)
  ldarg.0
  ldvirtftn  instance void java.lang.Thread::setContextClassLoader(class java.lang.ClassLoader)
  ceq
  and
  ldc.i4.0
  ceq
  ret
}

Instead of running a zillion instructions and accessing a lot of cold data for reflection, this simply leverages the information the JIT already has about virtual methods.

Here's the x86 code this turns into:

  push        ebp 
  mov         ebp,esp 
  push        esi 
  push        ebx 
  mov         esi,ecx 
  push        258F58h 
  mov         ecx,esi 
  mov         edx,259240h 
  call        JIT_VirtualFunctionPointer
  mov         edx,25DCA0h 
  cmp         eax,edx 
  sete        bl 
  movzx       ebx,bl 
  push        2591A0h 
  mov         ecx,esi 
  mov         edx,259240h 
  call        JIT_VirtualFunctionPointer
  mov         edx,25DCB0h 
  cmp         eax,edx 
  sete        al 
  movzx       eax,al 
  and         ebx,eax 
  sete        al 
  movzx       eax,al 
  pop         ebx 
  pop         esi 
  pop         ebp 
  ret

To get an idea what JIT_VirtualFunctionPointer does, take a look at the Shared Source CLI.

On the CLR, in the common case it only executes about 40 instructions.

The downside to this method is that it only works if you have an object instance. Although you could use FormatterServices.GetUninitializedObject() to create an instance.

Why Optimize This?

In the OpenJDK code, isCCLOverridden() is only called if a SecurityManager is installed, but I wanted to use it always to avoid calling getContextClassLoader() during thread construction, because that would trigger the system class loader to be constructed and I my long term goal for IKVM is to make initialization more lazy to reduce the huge startup overhead.


1This method is more accurate (on the CLR) because you don't need to worry about non-virtual methods or virtual methods that are new (and hence don't override the base class virtual method) or explicit overrides that override a method but have a different name.

Update: See this article for a caveat.

Sunday, 21 November 2010 11:36:21 (W. Europe Standard Time, UTC+01:00)  #    Comments [2]
Tuesday, 23 November 2010 23:30:18 (W. Europe Standard Time, UTC+01:00)
I'm not an expert at .NET, but if you use FormatterServices.GetUninitializedObject(), won't the object's Finalize() method be invoked once it gets GCed?
Artemus Harper
Wednesday, 24 November 2010 05:58:38 (W. Europe Standard Time, UTC+01:00)
Yes, but you can prevent that by calling GC.SuppressFinalize() on the object.
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