-
Notifications
You must be signed in to change notification settings - Fork 8
Expression Tree Issues
Referencing a type that is in the process of being generated is not possible in standard expression trees (System.Linq.Expression
). At generation time only a TypeBuilder
is available for those types. Unfortunately instances of TypeBuilder
, MethodBuilder
, etc… cannot be used for building expression trees, because the expression classes have argument checks which exercise members (e.g. MethodBuilder.GetParameters
) that are not implemented by the builder objects.
Instead of directly using TypeBuilder
, MethodBuilder
, etc… in expression trees custom reflection objects (MutableTypeInfo
, MutableMethodInfo
, etc…) are used. The custom reflection objects are derived from the abstract base classes (Type
, MethodInfo
) and implement the members exercised by the argument validation in the expression classes. They are also used to look up the real builder objects for code generation at a later time.
We discovered the following problems and workarounds (+WA+) for this approach in combination with the original System.Linq.Expression
and compiler classes (LambdaCompiler
).
-
LambdaExpression.CompileToMethod(MethodBuilder)
only works for static methods and thethis
reference cannot not be accessed directly.- +WA+: Compile to static helper method which explicitly takes the
this
reference as its first parameter and call it from the original method. Users would be supplied with an expression (e.g. from a context) that represents thethis
reference and can be used to create the expression tree. The This-Expression would then be switched out for an expression that represents the additionalthis
parameter.
- +WA+: Compile to static helper method which explicitly takes the
-
MethodCallExpression
does not support the concept of non-virtual calls to methods that are defined virtual. This is needed in an overriding method that wants to call the overridden (base) method.- +WA+: Manually generate a helper method in IL which does the non-virtual base call. Use this method instead of a direct base call. Users would be supplied with an appropriate expression.
- Referencing currently generated types is a problem in general. Tricking the expression classes by first supplying fake types, i.e.
MutableTypeInfo
(to pass argument validation) and then replacing the fake types with type builders via reflection just before code generation (LambdaExpression.CompileToMethod
) is not enough: For code generation some of the members which are not properly implemented byTypeBuilder
,MethodBuilder
, etc… are needed. In addition, the builder classes are sealed.- +WA+: none
The last issue implies that the standard code generation (
LambdaCompiler
) cannot be used as intended and has to be adapted for usage. Because theLambdaCompiler
references many internal members of theSystem.Linq.Expression
namespace, they cannot be used together with custom code generation.
- +WA+: none
The last issue implies that the standard code generation (
Custom (DLR) code generation and custom expressions (DLR) are needed. Either the user of the pipeline directly creates DLR expression tress or standard (System.Linq.Expression) expression trees are mapped onto DLR trees. The goal is to make as few changes to the original DLR classes as possible so that they can be upgraded to new versions with acceptable effort.
- Custom ILGenerator: The code generation was changed to use a custom ILGenerator (IEmitter). This emitter generates proper IL for the
MutableTypeInfo
,MutableMethodInfo
, etc… reflection objects by using the actual builder objects stored by them. - Extension expressions: Support for extension expressions (
exp.NodeType == ExpressionType.Extension
) was added. Such expressions implementIEmittableExpression
and have the chance to emit arbitrary IL code. This was needed for supporting a customThisExpression
(load first argument in instance method).