views:

67

answers:

3

I'm writing a "weak event factory" - code which converts any Delegate into a new delegate with an identical signature, but with implementing a WeakReference on the target. I'm using MSIL to avoid calls to Delegate.CreateDelegate (which performance have shown to be slow).

The weak reference delegates work perfectly as long as the underlying method (the Method of the original delegate), was declared public. As soon as a private or anonymous method is used, the MSIL bombs at run time with a MethodAccessException.

Using compiled expression trees, I've been able to invoke private methods, so it must be possible to dynamically emit MSIL which invokes a private method. ...so what's wrong with the following?

        // var target = this.Target
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Callvirt, targetPropGetter);         
        il.Emit(OpCodes.Stloc, ilTarget);            

        // if(target != null)
        // {
        il.Emit(OpCodes.Ldloc, ilTarget);
        il.Emit(OpCodes.Brfalse_S, ilIsNullLabel);

        //      Method( @target, parm1, parm2 ...);
        il.Emit(OpCodes.Ldloc, ilTarget);                  // this = Target
        short argIndex = 1;
        foreach (var parm in delgParams)                   // push all other args
            il.Emit(OpCodes.Ldarg, argIndex++);

        il.Emit(OpCodes.Callvirt, delegat.Method);   // <-- Bombs if method is private
        il.Emit(OpCodes.Ret);

        // }
        il.MarkLabel(ilIsNullLabel);

So what's the secret to invoking a private member? Reflection can do it, expression trees can do it... why is the above code failing?


EDIT: Much thanks to all of you who provided answers here. It turns out that the only solution which consistently worked in my context was to use generic delegates (Action)... because Action originates from the mscorlib, the JIT seems perfectly happy to let it invoke a private method. try using your own delegate and the JIT pukes just the same as if you emit a Call or Callvirt directly to the target.

Anyone who's interested in seeing the working code can head over to codeplex - the answers given here helped in implementing the WeakDelegate capabilities.

A: 

Use Call, not Callvirt.

Callvirt is for calling virtual methods, where the destination address also depends upon the exact type of the instance. That doesn't work when you're using a weak reference.

The target of a private method, on the other hand, can be determined at compile-time. It is therefore appropriate to invoke it using Call.

Richard Flamsholt
1. Callvirt can be used on virtual or non-virtual instance methods. 2. The Call opcode cannot be used verifiably to call non-final virtual methods except in a very limited set of cases (mainly to allow `base` calls from a subclass's implementation of a method).
kvb
1. True, but irrelevant. Note the weak reference here: callvirt relies on knowing the instance type. 2. I think you're confusing something here: the issue is not about virtual methods, but quite the opposite. A private method is inherently a non-virtual method, and the Call opcode is typically emitted as the calling opcode for private members, at least in C#.
Richard Flamsholt
@Richard - when calling an instance method, `Callvirt` can *always* be used safely in place of `Call`, but the opposite is not true (because the method might be virtual). You are right that in this particular case, the OP's problem is unrelated to virtual-ness (because the method is evidently private and therefore non-virtual) - but replacing `Callvirt` with `Call` *will not* work, which is what I was trying to explain.
kvb
@kvb Ah, I was of course talking about this particular case, not generally suggesting that Call may substitute Callvirt. I should probably have been less terse in my answer, but I figured this was all understood since the OP's problem was only in dealing with private methods. A MethodAccessException could then possibly be caused by some (unknown to us) security-problem with the instance-type lookup that Callvirt requires and which Call doesn't. Hence my suggestion to try Call.
Richard Flamsholt
+1  A: 

Are you inserting your IL into a DynamicMethod or into a method within a dynamic assembly? As I understand it, there is no way to skip visibility checks from within a dynamic assembly, but you can skip them when using a DynamicMethod (see here).

kvb
Yeah - the IL is emitted ot a type, not a dynamic method.
Mark
A: 

The solution (to my particular problem), was to used delegates instead of direct method calls. You can comfortably construct an open delegate and pass it to the IL code, and then when the IL code invokes the delegate's Invoke method, the JIT accepts the pattern as legal and allows the invoke of the private methods.

Like I said, this is a solution (which happily allows runtime-generated code to call private methods), though it still doesn't explain how technolgies like Expression Trees and Reflection manage to call private methods.

Mark
Did you try using Call instead? Code generated by the Visual Studio C# compiler uses Call as opcode for private methods so it really is a perfectly fine way to call a private method. And yes, Callvirt can also be used to call non-virtual methods, but it may have bombed on you in this parcicular case because the CLR runtime can't determine the type of the object instance.
Richard Flamsholt
I believe that Expression Trees use `DynamicMethod` under the hood which allows them to skip visibility checks. Reflection is built into the runtime and is explicitly designed to allow private methods to be dynamically invoked, but that mechanism is not exposed to other code. If you emit code into a dynamic assembly, there is no way to evade the runtime's visibility checks.
kvb
@Richard - yes - I tried Call and Callvirt. It turns out that it's all to do with security.
Mark