Skip to content

Commit 831f8ca

Browse files
committed
seize dep volnurability warning; improve the IL debug output for the type names; add test for #449
1 parent 30322e5 commit 831f8ca

File tree

5 files changed

+231
-13
lines changed

5 files changed

+231
-13
lines changed

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9342,4 +9342,11 @@ internal enum DynamicallyAccessedMemberTypes
93429342
}
93439343
}
93449344
#endif
9345-
#nullable restore
9345+
#nullable restore
9346+
9347+
#if !NET5_0_OR_GREATER
9348+
namespace System.Runtime.CompilerServices
9349+
{
9350+
internal static class IsExternalInit { }
9351+
}
9352+
#endif

test/FastExpressionCompiler.ILDecoder/ILReaderFactory.cs

Lines changed: 165 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,19 @@ public static StringBuilder ToILString(this MethodInfo method, StringBuilder s =
6262
var secondLine = false;
6363
foreach (var il in ilReader)
6464
{
65-
try
65+
try
6666
{
67-
if (secondLine)
67+
if (secondLine)
6868
s.AppendLine();
69-
else
69+
else
7070
secondLine = true;
7171
s.Append(il.Offset.ToString().PadRight(4, ' ')).Append(' ').Append(il.OpCode);
7272
if (il is InlineFieldInstruction f)
73-
s.Append(' ').Append(f.Field.DeclaringType.Name).Append('.').Append(f.Field.Name);
73+
s.Append(' ').AppendTypeName(f.Field.DeclaringType).Append('.').Append(f.Field.Name);
7474
else if (il is InlineMethodInstruction m)
75-
s.Append(' ').Append(m.Method.DeclaringType.Name).Append('.').Append(m.Method.Name);
75+
s.Append(' ').AppendTypeName(m.Method.DeclaringType).Append('.').Append(m.Method.Name);
7676
else if (il is InlineTypeInstruction t)
77-
s.Append(' ').Append(t.Type?.Name);
77+
s.Append(' ').AppendTypeName(t.Type);
7878
else if (il is InlineTokInstruction tok)
7979
s.Append(' ').Append(tok.Member.Name);
8080
else if (il is InlineBrTargetInstruction br)
@@ -112,4 +112,163 @@ public static StringBuilder ToILString(this MethodInfo method, StringBuilder s =
112112
return s;
113113
}
114114

115+
public static StringBuilder AppendTypeName(this StringBuilder sb, Type type, bool stripNamespace = false) =>
116+
type == null ? sb : sb.Append(type.TypeToCode(stripNamespace));
117+
118+
public static string TypeToCode(this Type type,
119+
bool stripNamespace = false, Func<Type, string, string> printType = null, bool printGenericTypeArgs = true)
120+
{
121+
if (type.IsGenericParameter)
122+
return !printGenericTypeArgs ? string.Empty : (printType?.Invoke(type, type.Name) ?? type.Name);
123+
124+
if (Nullable.GetUnderlyingType(type) is Type nullableElementType && !type.IsGenericTypeDefinition)
125+
{
126+
var result = nullableElementType.TypeToCode(stripNamespace, printType, printGenericTypeArgs) + "?";
127+
return printType?.Invoke(type, result) ?? result;
128+
}
129+
130+
Type arrayType = null;
131+
if (type.IsArray)
132+
{
133+
// store the original type for the later and process its element type further here
134+
arrayType = type;
135+
type = type.GetElementType();
136+
}
137+
138+
// the default handling of the built-in types
139+
string buildInTypeString = null;
140+
if (type == typeof(void))
141+
buildInTypeString = "void";
142+
else if (type == typeof(object))
143+
buildInTypeString = "object";
144+
else if (type == typeof(bool))
145+
buildInTypeString = "bool";
146+
else if (type == typeof(int))
147+
buildInTypeString = "int";
148+
else if (type == typeof(short))
149+
buildInTypeString = "short";
150+
else if (type == typeof(byte))
151+
buildInTypeString = "byte";
152+
else if (type == typeof(double))
153+
buildInTypeString = "double";
154+
else if (type == typeof(float))
155+
buildInTypeString = "float";
156+
else if (type == typeof(char))
157+
buildInTypeString = "char";
158+
else if (type == typeof(string))
159+
buildInTypeString = "string";
160+
161+
if (buildInTypeString != null)
162+
{
163+
if (arrayType != null)
164+
buildInTypeString += "[]";
165+
return printType?.Invoke(arrayType ?? type, buildInTypeString) ?? buildInTypeString;
166+
}
167+
168+
var parentCount = 0;
169+
for (var ti = type.GetTypeInfo(); ti.IsNested; ti = ti.DeclaringType.GetTypeInfo())
170+
++parentCount;
171+
172+
Type[] parentTypes = null;
173+
if (parentCount > 0)
174+
{
175+
parentTypes = new Type[parentCount];
176+
var pt = type.DeclaringType;
177+
for (var i = 0; i < parentTypes.Length; i++, pt = pt.DeclaringType)
178+
parentTypes[i] = pt;
179+
}
180+
181+
var typeInfo = type.GetTypeInfo();
182+
Type[] typeArgs = null;
183+
var isTypeClosedGeneric = false;
184+
if (type.IsGenericType)
185+
{
186+
isTypeClosedGeneric = !typeInfo.IsGenericTypeDefinition;
187+
typeArgs = isTypeClosedGeneric ? typeInfo.GenericTypeArguments : typeInfo.GenericTypeParameters;
188+
}
189+
190+
var typeArgsConsumedByParentsCount = 0;
191+
var s = new StringBuilder();
192+
if (!stripNamespace && !string.IsNullOrEmpty(type.Namespace)) // for the auto-generated classes Namespace may be empty and in general it may be empty
193+
s.Append(type.Namespace).Append('.');
194+
195+
if (parentTypes != null)
196+
{
197+
for (var p = parentTypes.Length - 1; p >= 0; --p)
198+
{
199+
var parentType = parentTypes[p];
200+
if (!parentType.IsGenericType)
201+
{
202+
s.Append(parentType.Name).Append('.');
203+
}
204+
else
205+
{
206+
var parentTypeInfo = parentType.GetTypeInfo();
207+
Type[] parentTypeArgs = null;
208+
if (parentTypeInfo.IsGenericTypeDefinition)
209+
{
210+
parentTypeArgs = parentTypeInfo.GenericTypeParameters;
211+
212+
// replace the open parent args with the closed child args,
213+
// and close the parent
214+
if (isTypeClosedGeneric)
215+
for (var t = 0; t < parentTypeArgs.Length; ++t)
216+
parentTypeArgs[t] = typeArgs[t];
217+
218+
var parentTypeArgCount = parentTypeArgs.Length;
219+
if (typeArgsConsumedByParentsCount > 0)
220+
{
221+
int ownArgCount = parentTypeArgCount - typeArgsConsumedByParentsCount;
222+
if (ownArgCount == 0)
223+
parentTypeArgs = null;
224+
else
225+
{
226+
var ownArgs = new Type[ownArgCount];
227+
for (var a = 0; a < ownArgs.Length; ++a)
228+
ownArgs[a] = parentTypeArgs[a + typeArgsConsumedByParentsCount];
229+
parentTypeArgs = ownArgs;
230+
}
231+
}
232+
typeArgsConsumedByParentsCount = parentTypeArgCount;
233+
}
234+
else
235+
{
236+
parentTypeArgs = parentTypeInfo.GenericTypeArguments;
237+
}
238+
239+
var parentTickIndex = parentType.Name.IndexOf('`');
240+
s.Append(parentType.Name.Substring(0, parentTickIndex));
241+
242+
// The owned parentTypeArgs maybe empty because all args are defined in the parent's parents
243+
if (parentTypeArgs?.Length > 0)
244+
{
245+
s.Append('<');
246+
for (var t = 0; t < parentTypeArgs.Length; ++t)
247+
(t == 0 ? s : s.Append(", ")).Append(parentTypeArgs[t].TypeToCode(stripNamespace, printType, printGenericTypeArgs));
248+
s.Append('>');
249+
}
250+
s.Append('.');
251+
}
252+
}
253+
}
254+
var name = type.Name.TrimStart('<', '>').TrimEnd('&');
255+
256+
if (typeArgs != null && typeArgsConsumedByParentsCount < typeArgs.Length)
257+
{
258+
var tickIndex = name.IndexOf('`');
259+
s.Append(name.Substring(0, tickIndex)).Append('<');
260+
for (var i = 0; i < typeArgs.Length - typeArgsConsumedByParentsCount; ++i)
261+
(i == 0 ? s : s.Append(", ")).Append(typeArgs[i + typeArgsConsumedByParentsCount].TypeToCode(stripNamespace, printType, printGenericTypeArgs));
262+
s.Append('>');
263+
}
264+
else
265+
{
266+
s.Append(name);
267+
}
268+
269+
if (arrayType != null)
270+
s.Append("[]");
271+
272+
return printType?.Invoke(arrayType ?? type, s.ToString()) ?? s.ToString();
273+
}
115274
}

test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<ItemGroup>
1313
<PackageReference Include="System.Reflection.Emit" Version="4.7.0"/>
14-
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.9"/>
14+
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.1"/>
1515
</ItemGroup>
1616

1717
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using NUnit.Framework;
3+
4+
#if LIGHT_EXPRESSION
5+
using static FastExpressionCompiler.LightExpression.Expression;
6+
using FastExpressionCompiler.LightExpression;
7+
namespace FastExpressionCompiler.LightExpression.IssueTests;
8+
#else
9+
using static System.Linq.Expressions.Expression;
10+
using System.Linq.Expressions;
11+
namespace FastExpressionCompiler.IssueTests;
12+
#endif
13+
14+
[TestFixture]
15+
public class Issue449_MemberInit_produces_InvalidProgram : ITest
16+
{
17+
public int Run()
18+
{
19+
// Original_case();
20+
return 1;
21+
}
22+
23+
public struct SampleType
24+
{
25+
public int? Value { get; set; }
26+
27+
public SampleType() { }
28+
}
29+
30+
[Test]
31+
public void Original_case()
32+
{
33+
var ctor = typeof(SampleType).GetConstructors()[0];
34+
var valueProp = typeof(SampleType).GetProperty(nameof(SampleType.Value));
35+
36+
var expr = Lambda<Func<SampleType>>(
37+
MemberInit(
38+
New(ctor),
39+
Bind(valueProp, Constant(666, typeof(int?)))));
40+
41+
expr.PrintCSharp();
42+
43+
var fs = expr.CompileSys();
44+
fs.PrintIL();
45+
46+
var sr = fs();
47+
Assert.AreEqual(666, sr.Value.Value);
48+
49+
var ff = expr.CompileFast(false);
50+
ff.PrintIL();
51+
52+
var fr = ff();
53+
Assert.AreEqual(666, sr.Value.Value);
54+
}
55+
}

test/FastExpressionCompiler.TestsRunner/Program.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public class Program
99
{
1010
public static void Main()
1111
{
12+
new LightExpression.IssueTests.Issue449_MemberInit_produces_InvalidProgram().Run();
13+
1214
// new LightExpression.IssueTests.Issue437_Shared_variables_with_nested_lambdas_returning_incorrect_values().Run();
1315
// new LightExpression.IssueTests.Issue353_NullReferenceException_when_calling_CompileFast_results().Run();
1416
// new LightExpression.UnitTests.ArithmeticOperationsTests().Run();
@@ -362,8 +364,3 @@ void Run(Func<int> run, string name = null)
362364
}
363365
}
364366
}
365-
366-
namespace System.Runtime.CompilerServices
367-
{
368-
internal static class IsExternalInit { }
369-
}

0 commit comments

Comments
 (0)