Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET: Accessing non-public members from a dynamic assembly

I'm working on a library that allows users to input arbitrary expressions. My library then compiles those expressions as part of a larger expression into a delegate. Now, for still unknown reasons compiling the expression with Compile sometimes/often results in code that is far slower than it would be if it weren't a compiled expression. I asked a question about this before and one workaround was to not use Compile, but CompileToMethod and create a static method on a new type in a new dynamic assembly. That works and the code is fast.

But users can input arbitrary expressions and it turns out that if the user calls a non-public function or accesses a non-public field in the expression, it throws a System.MethodAccessException (in the case of a non-public method) when the delegate is invoked.

What I could probably do here is create a new ExpressionVisitor that checks if the expression accesses anything non-public and use the slower Compile in those cases, but I'd rather have that the dynamic assembly somehow gets the rights to access the non-public members. Or find out if there's anything I can do about Compile being slower (sometimes).

The full code to reproduce this problem:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

namespace DynamicAssembly
{
  public class Program
  {
    private static int GetValue()
    {
      return 1;
    }

    public static int GetValuePublic()
    {
      return 1;
    }

    public static int Foo;

    static void Main(string[] args)
    {
      Expression<Func<int>> expression = () => 10 + GetValue();

      Foo = expression.Compile()();

      Console.WriteLine("This works, value: " + Foo);

      Expression<Func<int>> expressionPublic = () => 10 + GetValuePublic();

      var compiledDynamicAssemblyPublic = (Func<int>)CompileExpression(expressionPublic);

      Foo = compiledDynamicAssemblyPublic();

      Console.WriteLine("This works too, value: " + Foo);

      var compiledDynamicAssemblyNonPublic = (Func<int>)CompileExpression(expression);

      Console.WriteLine("This crashes");

      Foo = compiledDynamicAssemblyNonPublic();
    }

    static Delegate CompileExpression(LambdaExpression expression)
    {
      var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
        new AssemblyName("MyAssembly"+ Guid.NewGuid().ToString("N")), 
        AssemblyBuilderAccess.Run);

      var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");

      var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public);

      var methodBuilder = typeBuilder.DefineMethod("MyMethod", 
        MethodAttributes.Public | MethodAttributes.Static);

      expression.CompileToMethod(methodBuilder);

      var resultingType = typeBuilder.CreateType();

      var function = Delegate.CreateDelegate(expression.Type, 
        resultingType.GetMethod("MyMethod"));

      return function;
    }
  }
}
like image 304
JulianR Avatar asked Apr 22 '11 19:04

JulianR


People also ask

How to access non-public members of a class?

There is only one way to access non-public members: Reflection. The problem is that this method is much less supportable. For example, you can find a member by name, but if you mistype the string or name is changed later, compile-time check will not warn you — you won't find the member.

Why can't I access the internalclass class from another assembly?

Both the InternalClass class and its Add method are internal. Therefore they are not accessible from another assembly, so the following code cannot work: This can be fixed, however, with a single line of code, added to the assembly that is being tested (not the testing assembly). You can put it into the AssemblyInfo.cs file:

How do you test non-public types and members?

When faced with the need for testing non-public types and members you can use several approaches: change the accessibility to public; you can do that perhaps only for debug builds and keep the intended accessibility in release builds by using conditional compilation. use reflection.

Are there different levels of accessibility for non-public variables?

So in short, depending on the non-public variable, there are different accessibility levels on your members. No! There are 5 (five) different access specifiers (not keywords, but access specifiers). Who can name them all is the good boy. Read the C# standard. Besides, this does not answer OP question -- not at all.


Video Answer


3 Answers

The problem is not permissions because there is no permission that can allow you to access a non-public field or member of another class without reflection. This is analogous to the situation where you compiled two non-dynamic assemblies and one assembly calls a public method in the second assembly. Then if you change the method to private without recompiling the first assembly, the first assemblies call will now fail at runtime. In other words the expression in your dynamic assembly is being compiled into an ordinary method call which it doesn't have permission to call anymore than you do from another class even in the same assembly.

Since no permission can solve your problem, you might be able to transform non-public field and method references into subexpressions that use reflection.

Here is an example taken from your test case. This fails:

Expression<Func<int>> expression = () => 10 + GetValue();

but this will succeed:

Expression<Func<int>> expression = () => 10 + (int)typeof(Program).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);

Since this does not crash with an exception, you can see that your dynamic assembly does have reflection permission and it can access the private method, it just can't do it using an ordinary method call that CompileToMethod results in.

like image 62
Rick Sladkey Avatar answered Oct 17 '22 22:10

Rick Sladkey


I once had an issue accessing private elements of a class, from generated IL code using DynamicMethod.

It turned out that there was an overload of the constructor of the class DynamicMethod that receives the type of class into wich private access would be allowed:

http://msdn.microsoft.com/en-us/library/exczf7b9.aspx

This link contains samples of how to access private data... I know that this has nothing to do with expression trees, but it might give you some clues on how to do it.

May be there is some sort of similar thing when compiling expression trees... or that you can create that expression tree as a DynamicMethod.

like image 37
Miguel Angelo Avatar answered Oct 17 '22 23:10

Miguel Angelo


If the non-dynamic assembly is built by you, you can actually include a InternalsVisibleTo for the the dynamic assembly (even works with a strong name). That would allow using internal members, which may be enough in your case?

To get an idea, here's an example which shows hot to enable the dynamic assembly of Moq to use internal stuff from another assembly: http://blog.ashmind.com/2008/05/09/mocking-internal-interfaces-with-moq/

If this approach is not sufficient, I'd go with a combination of Rick's and Miguel's suggestions: create "proxy" DynamicMethods for each invocation to a non-public member and change the expression tree so that they are used instead of the original invocations.

like image 1
Lucero Avatar answered Oct 17 '22 22:10

Lucero