Value can not be null(parametr 'meth')
EgoPingvina opened this issue · 17 comments
This is a very strange error that is happening before my eyes for the second time (the first time I was unable to save any information).
Occurs on a method call:
internal static Func<TIn, TOut> AsFunc(Expression<Func<TIn, TOut>> expression)
=> Cache.GetOrAdd(expression, x => x.CompileFast());
where Cache
is private static readonly ConcurrentDictionary<Expression<Func<TIn, TOut>>, Func<TIn, TOut>>
.
Callstack from output:
System.ArgumentNullException: Value cannot be null. (Parameter 'meth')
at System.ArgumentNullException.Throw(String paramName)
at System.ArgumentNullException.ThrowIfNull(Object argument, String paramName)
at System.Reflection.Emit.DynamicILGenerator.Emit(OpCode opcode, MethodInfo meth)
at FastExpressionCompiler.ExpressionCompiler.EmittingVisitor.TryEmitConvert(UnaryExpression expr, IReadOnlyList`1 paramExprs, ILGenerator il, ClosureInfo& closure, CompilerFlags setup, ParentFlags parent) in /_/src/FastExpressionCompiler/FastExpressionCompiler.cs:line 2892
at FastExpressionCompiler.ExpressionCompiler.EmittingVisitor.TryEmit(Expression expr, IReadOnlyList`1 paramExprs, ILGenerator il, ClosureInfo& closure, CompilerFlags setup, ParentFlags parent, Int32 byRefIndex) in /_/src/FastExpressionCompiler/FastExpressionCompiler.cs:line 1847
at FastExpressionCompiler.ExpressionCompiler.EmittingVisitor.TryEmitComparison(Expression left, Expression right, ExpressionType nodeType, Type exprType, IReadOnlyList`1 paramExprs, ILGenerator il, ClosureInfo& closure, CompilerFlags setup, ParentFlags parent) in /_/src/FastExpressionCompiler/FastExpressionCompiler.cs:line 4784
at FastExpressionCompiler.ExpressionCompiler.EmittingVisitor.TryEmit(Expression expr, IReadOnlyList`1 paramExprs, ILGenerator il, ClosureInfo& closure, CompilerFlags setup, ParentFlags parent, Int32 byRefIndex) in /_/src/FastExpressionCompiler/FastExpressionCompiler.cs:line 1900
at FastExpressionCompiler.ExpressionCompiler.TryCompileBoundToFirstClosureParam(Type delegateType, Expression bodyExpr, IReadOnlyList`1 paramExprs, Type[] closurePlusParamTypes, Type returnType, CompilerFlags flags) in /_/src/FastExpressionCompiler/FastExpressionCompiler.cs:line 503
at FastExpressionCompiler.ExpressionCompiler.CompileFast[T1,R](Expression`1 lambdaExpr, Boolean ifFastFailedReturnNull, CompilerFlags flags) in /_/src/FastExpressionCompiler/FastExpressionCompiler.cs:line 203
at Test.Extensions.ExpressionEx.CompiledExpressions`2.<>c.<AsFunc>b__1_0(Expression`1 x) in C:\dev\Test\Extensions\ExpressionEx.cs:line 85
Moreover, whether last time or this time, if you call the same section of code again without restarting the solution, then everything works successfully.
Is this some kind of floating bug in the library? Or do you have any ideas about what mistake I am making that can lead to this behaviour?
Hi @EgoPingvina
- What version of FEC are you using; on what target?
- What are the example expressions where the exception occurs, e.g. you may use
expr.ToExpressionString()
to see the final expression.
For now, given that the ex happened in TryEmitConvert
with the meth
method being used there.
It is either the conversion method itself or the Nullable methods, like GetValueOrDefault
, HasValue
, get_Value
.
So I need to know more about kind of operands in the convert for your case.
Hi @EgoPingvina
- What version of FEC are you using; on what target?
- What are the example expressions where the exception occurs, e.g. you may use
expr.ToExpressionString()
to see the final expression.For now, given that the ex happened in
TryEmitConvert
with themeth
method being used there. It is either the conversion method itself or the Nullable methods, likeGetValueOrDefault
,HasValue
,get_Value
. So I need to know more about kind of operands in the convert for your case.
- FastExpressionCompiler version is 4.0.1
- the final expression from
expression.ToExpressionString()
:
var p = new ParameterExpression[1]; // the parameter expressions
var e = new Expression[6]; // the unique expressions
var expr = Lambda<System.Func<Test.Message, bool>>(
e[0]=MakeBinary(ExpressionType.NotEqual,
e[1]=Convert(
e[2]=Property(
p[0]=Parameter(typeof(Test.Message), "x"),
typeof(Test.Message).GetTypeInfo().GetDeclaredProperty("UserType")),
typeof(int?)),
e[3]=Convert(
e[4]=Field(
e[5]=Constant(default(Test.Logic.MessageSpec.c__DisplayClass0_0)/*Please provide the non-default value for the constant!*/),
typeof(Test.Logic.MessageSpec.c__DisplayClass0_0).GetTypeInfo().GetDeclaredField("type")),
typeof(int?))),
p[0 // (Test.Message x)
]);
property public UserType? UserType { get; set; }
contains the value of the enum of the same name
Ok, so if you convert to ToCSharpString()
it will be something like this?
(Test.Message x) => (int?)x.UserType != (int?)(UserType.FooBar)
First, not relevant to the issue - just a note to myself. But the constant probably should be used as literal constant and not put into a closure.
Second, it seems not related to the HasFlag
, but rather to the nullables in general... they are big PITA.
I will create the test case for that and see what I find.
yes, x => x.UserType != type
, where is UserType type
and Test.Message x
.
Like this:
(Test.Message x) => (int?)x.UserType != (int?)(FooBar.type)
Just wondering, why is there lowercase .type
?
Because of type
is the name of the method parameter. It's not a value of enum :) sorry for the confusion
Ehhh, I don't understand. It seems like a Field
to me:
e[4]=Field(
e[5]=Constant(default(Test.Logic.MessageSpec.c__DisplayClass0_0)/*Please provide the non-default value for the constant!*/),
typeof(Test.Logic.MessageSpec.c__DisplayClass0_0).GetTypeInfo().GetDeclaredField("type")),
typeof(int?))),
Could you actually run the ToCSharpString()
on this thing and paste the output?
(Func<Message, bool>)((Message x) =>
((int?)x.UserType) != ((int?)default(MessageSpec.c__DisplayClass0_0)/*Please provide the non-default value for the constant!*/.type));
@EgoPingvina Again about the type
. Is it really of the int?
type internally, or there are cases when it is not (just a plain int)?
It's always the UserType
enum member
@EgoPingvina
I have added the multiple tests (see the linked commits) with the multiple combination of nullable and not for UserType.
Everything is passing.
So maybe you will have an idea what is missing?
So far, the only thing I have discovered is a method for guaranteed reproduction: I need to restart the project and simultaneously call the method from two places. Only a clean run and multiple simultaneous invokes guarantee an exception throw. I haven't delved deep into your library's source code and I could be wrong, but perhaps non-thread-safe caches are being used somewhere or something?
@EgoPingvina Hey, yeah. This is very helpful!
There are some caches involved. Will check the problems there.
@dadhi Hello! Did you manage to fix this bug?
@EgoPingvina The v4.1.0 with the fix is out on NuGet
Thanks a lot for the notice!