MethodAccessException & WinForms & Lambdas

Dec 30, 2008 at 10:51 PM
[I've not been able to build a succinct example that triggers this problem; please
pardon the verbosity.]

Problem: MethodAccessException is thrown trying to access something in
System.Windows.Forms and then only in certain circumstances.

Hosting:  A Console application running console input through DLR to compile
and execute.  The console loop does something like this:

         LambdaExpression ast = GenerateAst(consoleInput);
         ast = new GlobalLookupRewriter().RewriteLambda(ast);
         ScriptCode code = new ScriptCode(ast,sourceUnit);

From the console, I can create a Form+Panel with Paint callback on the Panel
running a windows message loop in its own thread.

From the console, I can do the equivalent of:

   print panel.Name

with no problem.

I have a mechanism for creating delegates that looks something like this:

   //delegate void MyDelType();  // for example
   Delegate d = Expression.Lambda(typeof(MyDelType),bodyExpr,parameters).Compile(true);

When bodyExpr is equivalent to (A):

      print panel.GetType().ToString()

When I do the equivalent of  d.Invoke(),  it works just fine.  

When bodyExpr is equivalent to  (B):
     print panel.Name.

I get a System.MethodAccessException.  (Stack trace at end.)

In other words, I can access Object methods of panel--GetType(),
ToString(), GetHashCode()-- from my delegate, but I can't access
other methods/properties.  

BTW, a delegate created using this mechanism is being used as the
paint callback on the panel.  However, it does not access anything
on the panel directly.  It can access the PaintEventArgs, grab the
graphics, play with a BufferedGraphics object and do drawing  --
all with no problem.


(1)  accessing panel.Name works fine when done from the console, i.e,
as the body of a DlrMainCallTarget generated from a ScriptSource

(2) accessing panel.Name throws a MethodAccessException when called from a
delegate generated by LambdaExpression.Compile.  Accessing panel.GetType()
does not.  I've not found any problem accessing objects of other types.


There's not a whole lot out there on MethodAccessException.  Two possible
causes are mentioned:
   (a) compiled and run against different versions of libraries
   (b) Security access violation.

Is there something in the guts of DLR that might cause (b)?  

Is there some fundamental difference in something executed in the
body of a DlrMainCallTarget generated from a SourceCode run versus
the same expression in the body of a delegate compiled from

Sample stack trace:

   Exception has been thrown by the target of an invocation.
   ---> System.MethodAccessException:
           SimpleREPL.Reflector.CallInstanceMethod(System.String, System.Object, System.Object[])
              at myLang.core$RefreshPanel$2083$2.myLang.core$RefreshPanel$2083(Closure )
   --- End of inner exception stack trace ---

   at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments,
        SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)

   at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments,
        Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)

   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr,
        Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)

   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr,
        Binder binder, Object[] parameters, CultureInfo culture)

   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder,
        Object target, Object[] providedArgs, ParameterModifier[] modifiers,
        CultureInfo culture, String[] namedParams)

   at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder,
        Object target, Object[] args)

   at SimpleREPL.Reflector.CallInstanceMethod(String methodName, Object target,Object[] args)
        in D:\work\\myLang\SimpleREPL\Reflector.cs:line 55

   at REPLCall$2102##2099(Closure , Scope , LanguageContext )

   at Microsoft.Scripting.ScriptCode.InvokeTarget(LambdaExpression code, Scope scope)
        in D:\work\DLR\Codeplex-DLR-0.9\Runtime\src\Microsoft.Scripting\Runtime\ScriptCode.cs:line 89

   at Microsoft.Scripting.ScriptCode.Run(Scope scope)
        in D:\work\DLR\Codeplex-DLR-0.9\Runtime\src\Microsoft.Scripting\Runtime\ScriptCode.cs:line 81

   at Microsoft.Scripting.Hosting.Shell.CommandLine.ExecuteCommand(SourceUnit source)
        in D:\work\DLR\Codeplex-DLR-0.9\Runtime\src\Microsoft.Scripting\Hosting\Shell\CommandLine.cs:line 257

   at Microsoft.Scripting.Hosting.Shell.CommandLine.RunOneInteraction()
        in D:\work\DLR\Codeplex-DLR-0.9\Runtime\src\Microsoft.Scripting\Hosting\Shell\CommandLine.cs:line 252

   at Microsoft.Scripting.Hosting.Shell.CommandLine.TryInteractiveAction()
        in D:\work\DLR\Codeplex-DLR-0.9\Runtime\src\Microsoft.Scripting\Hosting\Shell\CommandLine.cs:line 218

   at Microsoft.Scripting.Hosting.Shell.CommandLine.RunInteractiveLoop()
        in D:\work\DLR\Codeplex-DLR-0.9\Runtime\src\Microsoft.Scripting\Hosting\Shell\CommandLine.cs:line 183

Dec 31, 2008 at 4:25 PM
It's hard to be sure about what might be going wrong without seeing the source code that builds the expression.  But from your description of the problem, I'd say it's likely that the generated code has typed your variable to System.Object instead of System.Windows.Forms.Panel -- which is why you can call Object methods but not Panel methods.  You probably need to throw a Convert expression in there somewhere.