In Tuesday's snapshot I finally implemented the
CallNonvirtualMethod JNI routines. I've never seen any JNI code that actually uses them, but I obviously needed to implement them at some point.
For those of you unfamiliar with these routines, let me explain them briefly. Normally when you call a virtual method, the runtime type of the instance the method is called on determines the actual method you end up at (so called virtual method dispatch), but these routines allow you to call a base class method as well. So, if you'd use
java.lang.Object.toString() on a
java.lang.String instance (let's say “foo“), the result you'd get would be
java.lang.String@17e809 instead of the string “foo“.
The CLR Reflection API doesn't have the ability to invoke a virtual method non-virtually. Unlike Java however, the CLR does allow the CIL
call instruction to be used to call arbitrary methods non-virtually (the Java invokespecial method can only be used to call methods in the same class or in a base class). So the obvious way to implement these routines, would seem to be to dynamically emit some code that calls the requested method non-virtually, but there is problem with this approach, the JNI routines don't enforce accessibility (they allow you to call non-public methods) and the CLR enforces accessibility. I got around this by using
RuntimeMethodHandle.GetFunctionPointer() incombination with the CIL
calli instruction. This also has the advantage that the dynamically emitted stubs can be reused for methods with the same signature, because the target address isn't baked into the method, but passed in.
While I don't have a very compelling use case for non-virtual method invocation through reflection, based on API orthogonality and completeness a case could be made for it, but more importantly, I think that ECMA/Microsoft should consider changing the CLI spec to allow non-verifiable code to bypass the accessibility checks. Note that the new
DynamicMethod in Whidbey already allows accessibility checks to be bypassed (and note also that whoever wrote the doc for
DynamicMethod doesn't understand the difference between visibility and accessibility).
The trick of using
calli to bypass the accessibility checks doesn't work for virtual method invocation, because there is no way to dynamically and efficiently get the function pointer of a method by applying the virtual method dispatch rules (i.e. there is no dynamic equivalent of the CIL
To summarize, here is what I think should be changed:
- CLR shouldn't bother enforcing accessibility in non-verifiable code.
- MethodInfo should get a new InvokeNonvirtual method.
- MethodInfo should get a GetFunctionPointer(object) method that returns the method pointer that results from virtual dispatch.