I'm trying to create a type with Reflection.Emit with a method named EvaluateOnCondition. I generate the method's body with Linq Expressions, and I want to inject the IL of the expression into EvaluateOnCondition with the CompileToMethod method of LambdaExpression, but when I execute the CompileToMethod method I'm getting the following error:
CompileToMethod cannot compile constant 'some value' because it is a non-trivial value, such as a live object. Instead, create an expression tree that can construct this value.
This is what I'm trying to do:
MethodBuilder evaluateOnCondition = tb.DefineMethod("EvaluateOnCondition", MethodAttributes.Public | MethodAttributes.Static, typeof(bool), new[] { typeof(object), typeof(object) });
onCondition.CompileToMethod(evaluateOnCondition); // throw the error
onCondition variable is a LambdaExpression and is a comparative condition created with Linq Expressions representing the following pseudo-code: obj1.prop1 == obj2.prop1
This is my StackTrace:
at System.Linq.Expressions.Compiler.BoundConstants.EmitCacheConstants(LambdaCompiler lc)
at System.Linq.Expressions.Compiler.LambdaCompiler..ctor(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method)
at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda, MethodBuilder method, DebugInfoGenerator debugInfoGenerator)
at System.Linq.Expressions.LambdaExpression.CompileToMethodInternal(MethodBuilder method, DebugInfoGenerator debugInfoGenerator)
at System.Linq.Expressions.LambdaExpression.CompileToMethod(MethodBuilder method)
at Integra.Space.Language.Runtime.LanguageTypeBuilder.CreateEqualsMethod(TypeBuilder tb, Type parentType, Type typeOtherSource, Boolean isSecondSource, LambdaExpression onCondition) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\TypeBuilders\LanguageTypeBuilder.cs:line 343
at Integra.Space.Language.Runtime.LanguageTypeBuilder.CreateTypeBuilder(List`1 listOfFields, Type parentType, Boolean overrideGetHashCodeMethod, Boolean overrideEquals, Type typeOtherSource, Boolean isSecondSource, LambdaExpression onCondition) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\TypeBuilders\LanguageTypeBuilder.cs:line 165
at Integra.Space.Language.Runtime.LanguageTypeBuilder.CompileExtractedEventDataComparerTypeForJoin(Type parentType, Type typeOfTheOtherSource, Boolean isSecondSource, LambdaExpression onCondition) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\TypeBuilders\LanguageTypeBuilder.cs:line 92
at Integra.Space.Language.Runtime.ObservableConstructor.CreateProjectionExpression(PlanNode plans) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 3033
at Integra.Space.Language.Runtime.ObservableConstructor.CreateExpressionNode(PlanNode actualNode, Expression leftNode, Expression rightNode) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 358
at Integra.Space.Language.Runtime.ObservableConstructor.GenerateExpressionTree(PlanNode plan) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 304
at Integra.Space.Language.Runtime.ObservableConstructor.GenerateExpressionTree(PlanNode plan) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 313
at Integra.Space.Language.Runtime.ObservableConstructor.GenerateExpressionTree(PlanNode plan) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 299
at Integra.Space.Language.Runtime.ObservableConstructor.GenerateExpressionTree(PlanNode plan) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 299
at Integra.Space.Language.Runtime.ObservableConstructor.GenerateExpressionTree(PlanNode plan) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 313
at Integra.Space.Language.Runtime.ObservableConstructor.GenerateExpressionTree(PlanNode plan) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 313
at Integra.Space.Language.Runtime.ObservableConstructor.CreateObservableJoin(PlanNode actualNode) in C:\Users\Oscar\Documents\GitHubVisualStudio\Integra.Space.Language\Integra.Space.Language\Runtime\ObservableConstructor.cs:line 936
Debug view of the onCondition variable
.Block(System.Boolean $variable) {
.Try {
.Block() {
.If (
True
) {
.Call System.Diagnostics.Debug.WriteLine("Start of the equal operation '==': ")
} .Else {
.Default(System.Void)
};
$variable = (System.Object).Block(System.Object $variable) {
.If (.Constant<System.Reflection.RuntimePropertyInfo>(System.Object _adapter_Name) == null) { // 12 line
$variable = .Default(System.Object)
} .Else {
.Try {
.Block() {
.If (
True
) {
.Call System.Diagnostics.Debug.WriteLine("Start of the get property operation: _adapter_Name")
} .Else {
.Default(System.Void)
};
$variable = $NewScopeParameter_0._adapter_Name;
.If (
True
) {
.Call System.Diagnostics.Debug.WriteLine("End of the get property operation: _adapter_Name")
} .Else {
.Default(System.Void)
};
.Default(System.Void)
}
} .Catch (System.Exception $var1) {
.Block() {
.Call System.Diagnostics.Debug.WriteLine("No fue posible obtener la propiedad _adapter_Name, error en la linea: 0 columna: 162 con [email protected]")
;
.Throw .New Integra.Space.Language.Exceptions.RuntimeException(
"RuntimeException: Line: 0, Column: 162, Instruction: [email protected], Error: RE5: Error with the get property operation",
$var1)
}
}
};
$variable
} == (System.Object).Block(System.Object $variable) {
.If (.Constant<System.Reflection.RuntimePropertyInfo>(System.Object _adapter_Name) == null) {
$variable = .Default(System.Object)
} .Else {
.Try {
.Block() {
.If (
True
) {
.Call System.Diagnostics.Debug.WriteLine("Start of the get property operation: _adapter_Name")
} .Else {
.Default(System.Void)
};
$variable = $NewScopeParameter_1._adapter_Name;
.If (
True
) {
.Call System.Diagnostics.Debug.WriteLine("End of the get property operation: _adapter_Name")
} .Else {
.Default(System.Void)
};
.Default(System.Void)
}
} .Catch (System.Exception $var2) {
.Block() {
.Call System.Diagnostics.Debug.WriteLine("No fue posible obtener la propiedad _adapter_Name, error en la linea: 0 columna: 188 con [email protected]")
;
.Throw .New Integra.Space.Language.Exceptions.RuntimeException(
"RuntimeException: Line: 0, Column: 188, Instruction: [email protected], Error: RE5: Error with the get property operation",
$var2)
}
}
};
$variable
};
.If (
True
) {
.Call System.Diagnostics.Debug.WriteLine("End of the equal operation")
} .Else {
.Default(System.Void)
};
.Default(System.Void)
}
} .Catch (System.Exception $var3) {
.Block() {
.Call System.Diagnostics.Debug.WriteLine("Error con la expresion de igualdad en la linea: 0 columna: 167 con [email protected] == [email protected]")
;
.Throw .New Integra.Space.Language.Exceptions.RuntimeException(
"RuntimeException: Line: 0, Column: 167, Instruction: [email protected] == [email protected], Error: RE43: Error with the equal operation '=='",
$var3)
}
};
$variable
}
I suspect the error is thrown in this line of the core.
BTW: What is non-trivial value in C#? How could I fix this?
Tell me if you need more information
What is non-trivial value in C#? How could I fix this?*
In general null
, bool
, char
, sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, float
, double
, decimal
, string
, Type
, MethodBase
are ok. Everything else isn't.
You can see it by starting from VariableBinder.VisitConstant
:
if (ILGen.CanEmitConstant(node.Value, node.Type))
{
return node;
}
this._constants.Peek().AddReference(node.Value, node.Type);
Going to the AddReference
is bad, and will cause in the long run the exception you saw.
Then you can look at ILGen.CanEmitConstant
and see the tests that are done.
Why this happens? Because you are trying to compile inside a method an object. While this is normally possible if you use the LambdaExpression.Compile()
, because the object isn't really saved inside the method but is simply a reference to the object that is living in memory, if you use CompileToMethod()
the .NET must be able to save a copy of the object inside the method, because CompileToMethod() (and MethodBuilder
/TypeBuilder
/AssemblyBuilder
) are able to generate new dlls. These dlls can be shared with other machines/loaded days later (they are "normal" dlls), so they aren't guaranteed to be run only during the lifetime of the current program. To do this the .NET would need to serialize the object and then deserialize it when the method is called. But this isn't supported. Clearly this doesn't happen with some primitive types (and I'll note that not all the .NET primitive types are supported. Missing are IntPtr
and UIntPtr
) and with some special types like Type
(that have a special handling inside the .NET).