-
-
Notifications
You must be signed in to change notification settings - Fork 94
Expand file tree
/
Copy pathIssue500_IndexOutOfRangeException_with_value_objects_implicit_conversions.cs
More file actions
124 lines (98 loc) · 4.51 KB
/
Issue500_IndexOutOfRangeException_with_value_objects_implicit_conversions.cs
File metadata and controls
124 lines (98 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using System;
using System.Reflection;
#if LIGHT_EXPRESSION
using static FastExpressionCompiler.LightExpression.Expression;
namespace FastExpressionCompiler.LightExpression.IssueTests;
#else
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
namespace FastExpressionCompiler.IssueTests;
#endif
public class Issue500_IndexOutOfRangeException_with_value_objects_implicit_conversions : ITest
{
public int Run()
{
Implicit_conv_op_via_abstract_base_class_param_in_closure();
Implicit_conv_op_via_non_abstract_base_class_param_in_closure();
Implicit_conv_op_via_abstract_base_class_param_directly();
return 3;
}
public abstract class PrimitiveValueObject<TInput, TOutput>
{
public TInput Value { get; protected set; } = default!;
public static implicit operator TInput(PrimitiveValueObject<TInput, TOutput>? primitiveValueObject) =>
primitiveValueObject == null ? default! : primitiveValueObject.Value;
}
public class MyPrimitive : PrimitiveValueObject<string, MyPrimitive>
{
public MyPrimitive(string val) => Value = val;
}
// Reproduces the original issue: implicit conversion operator on an abstract base class
// used within a closure-captured variable wrapped in Convert(..., typeof(object))
public void Implicit_conv_op_via_abstract_base_class_param_in_closure()
{
var captured = new MyPrimitive("Hello world");
// Expression with implicit conversion on closure-captured variable
System.Linq.Expressions.Expression<Func<string>> sysExpr = () => captured;
#if LIGHT_EXPRESSION
var innerExpr = sysExpr.FromSysExpression();
#else
var innerExpr = sysExpr;
#endif
// Wrap in Convert to object (common pattern in LINQ providers)
var toObject = Convert(innerExpr.Body, typeof(object));
var lambda = Lambda<Func<object>>(toObject);
lambda.PrintCSharp();
var fs = lambda.CompileSys();
Asserts.AreEqual("Hello world", (string)fs());
var ff = lambda.CompileFast(true);
Asserts.AreEqual("Hello world", (string)ff());
}
public class NonAbstractBase<TInput, TOutput>
{
public TInput Value { get; protected set; } = default!;
public static implicit operator TInput(NonAbstractBase<TInput, TOutput>? obj) =>
obj == null ? default! : obj.Value;
}
public class DerivedPrimitive : NonAbstractBase<string, DerivedPrimitive>
{
public DerivedPrimitive(string val) => Value = val;
}
// Same case but with a non-abstract base class (previously caused InvalidProgramException)
public void Implicit_conv_op_via_non_abstract_base_class_param_in_closure()
{
var captured = new DerivedPrimitive("Hello world");
System.Linq.Expressions.Expression<Func<string>> sysExpr = () => captured;
#if LIGHT_EXPRESSION
var innerExpr = sysExpr.FromSysExpression();
#else
var innerExpr = sysExpr;
#endif
var toObject = Convert(innerExpr.Body, typeof(object));
var lambda = Lambda<Func<object>>(toObject);
lambda.PrintCSharp();
var fs = lambda.CompileSys();
Asserts.AreEqual("Hello world", (string)fs());
var ff = lambda.CompileFast(true);
Asserts.AreEqual("Hello world", (string)ff());
}
// Same conversion but with a direct (non-closure) parameter; the method must be passed explicitly
// because the derived class does not directly declare the static op_Implicit (it is on the base class).
public void Implicit_conv_op_via_abstract_base_class_param_directly()
{
var param = Parameter(typeof(MyPrimitive), "p");
// Get the op_Implicit method declared on the abstract base class
var opImplicit = typeof(PrimitiveValueObject<string, MyPrimitive>)
.GetMethod("op_Implicit", BindingFlags.Public | BindingFlags.Static);
Asserts.IsNotNull(opImplicit);
// Convert MyPrimitive -> string via op_Implicit (which takes PrimitiveValueObject<string,MyPrimitive>)
// then Convert string -> object
var toObject = Convert(Convert(param, typeof(string), opImplicit), typeof(object));
var lambda = Lambda<Func<MyPrimitive, object>>(toObject, param);
lambda.PrintCSharp();
var fs = lambda.CompileSys();
Asserts.AreEqual("Hello world", (string)fs(new MyPrimitive("Hello world")));
var ff = lambda.CompileFast(true);
Asserts.AreEqual("Hello world", (string)ff(new MyPrimitive("Hello world")));
}
}