Skip to content

Commit

Permalink
seize dep volnurability warning; improve the IL debug output for the …
Browse files Browse the repository at this point in the history
…type names; add test for #449
  • Loading branch information
dadhi committed Jan 28, 2025
1 parent 30322e5 commit 831f8ca
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 13 deletions.
9 changes: 8 additions & 1 deletion src/FastExpressionCompiler/FastExpressionCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9342,4 +9342,11 @@ internal enum DynamicallyAccessedMemberTypes
}
}
#endif
#nullable restore
#nullable restore

#if !NET5_0_OR_GREATER
namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit { }
}
#endif
171 changes: 165 additions & 6 deletions test/FastExpressionCompiler.ILDecoder/ILReaderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,19 @@ public static StringBuilder ToILString(this MethodInfo method, StringBuilder s =
var secondLine = false;
foreach (var il in ilReader)
{
try
try
{
if (secondLine)
if (secondLine)
s.AppendLine();
else
else
secondLine = true;
s.Append(il.Offset.ToString().PadRight(4, ' ')).Append(' ').Append(il.OpCode);
if (il is InlineFieldInstruction f)
s.Append(' ').Append(f.Field.DeclaringType.Name).Append('.').Append(f.Field.Name);
s.Append(' ').AppendTypeName(f.Field.DeclaringType).Append('.').Append(f.Field.Name);
else if (il is InlineMethodInstruction m)
s.Append(' ').Append(m.Method.DeclaringType.Name).Append('.').Append(m.Method.Name);
s.Append(' ').AppendTypeName(m.Method.DeclaringType).Append('.').Append(m.Method.Name);
else if (il is InlineTypeInstruction t)
s.Append(' ').Append(t.Type?.Name);
s.Append(' ').AppendTypeName(t.Type);
else if (il is InlineTokInstruction tok)
s.Append(' ').Append(tok.Member.Name);
else if (il is InlineBrTargetInstruction br)
Expand Down Expand Up @@ -112,4 +112,163 @@ public static StringBuilder ToILString(this MethodInfo method, StringBuilder s =
return s;
}

public static StringBuilder AppendTypeName(this StringBuilder sb, Type type, bool stripNamespace = false) =>
type == null ? sb : sb.Append(type.TypeToCode(stripNamespace));

public static string TypeToCode(this Type type,
bool stripNamespace = false, Func<Type, string, string> printType = null, bool printGenericTypeArgs = true)
{
if (type.IsGenericParameter)
return !printGenericTypeArgs ? string.Empty : (printType?.Invoke(type, type.Name) ?? type.Name);

if (Nullable.GetUnderlyingType(type) is Type nullableElementType && !type.IsGenericTypeDefinition)
{
var result = nullableElementType.TypeToCode(stripNamespace, printType, printGenericTypeArgs) + "?";
return printType?.Invoke(type, result) ?? result;
}

Type arrayType = null;
if (type.IsArray)
{
// store the original type for the later and process its element type further here
arrayType = type;
type = type.GetElementType();
}

// the default handling of the built-in types
string buildInTypeString = null;
if (type == typeof(void))
buildInTypeString = "void";
else if (type == typeof(object))
buildInTypeString = "object";
else if (type == typeof(bool))
buildInTypeString = "bool";
else if (type == typeof(int))
buildInTypeString = "int";
else if (type == typeof(short))
buildInTypeString = "short";
else if (type == typeof(byte))
buildInTypeString = "byte";
else if (type == typeof(double))
buildInTypeString = "double";
else if (type == typeof(float))
buildInTypeString = "float";
else if (type == typeof(char))
buildInTypeString = "char";
else if (type == typeof(string))
buildInTypeString = "string";

if (buildInTypeString != null)
{
if (arrayType != null)
buildInTypeString += "[]";
return printType?.Invoke(arrayType ?? type, buildInTypeString) ?? buildInTypeString;
}

var parentCount = 0;
for (var ti = type.GetTypeInfo(); ti.IsNested; ti = ti.DeclaringType.GetTypeInfo())
++parentCount;

Type[] parentTypes = null;
if (parentCount > 0)
{
parentTypes = new Type[parentCount];
var pt = type.DeclaringType;
for (var i = 0; i < parentTypes.Length; i++, pt = pt.DeclaringType)
parentTypes[i] = pt;
}

var typeInfo = type.GetTypeInfo();
Type[] typeArgs = null;
var isTypeClosedGeneric = false;
if (type.IsGenericType)
{
isTypeClosedGeneric = !typeInfo.IsGenericTypeDefinition;
typeArgs = isTypeClosedGeneric ? typeInfo.GenericTypeArguments : typeInfo.GenericTypeParameters;
}

var typeArgsConsumedByParentsCount = 0;
var s = new StringBuilder();
if (!stripNamespace && !string.IsNullOrEmpty(type.Namespace)) // for the auto-generated classes Namespace may be empty and in general it may be empty
s.Append(type.Namespace).Append('.');

if (parentTypes != null)
{
for (var p = parentTypes.Length - 1; p >= 0; --p)
{
var parentType = parentTypes[p];
if (!parentType.IsGenericType)
{
s.Append(parentType.Name).Append('.');
}
else
{
var parentTypeInfo = parentType.GetTypeInfo();
Type[] parentTypeArgs = null;
if (parentTypeInfo.IsGenericTypeDefinition)
{
parentTypeArgs = parentTypeInfo.GenericTypeParameters;

// replace the open parent args with the closed child args,
// and close the parent
if (isTypeClosedGeneric)
for (var t = 0; t < parentTypeArgs.Length; ++t)
parentTypeArgs[t] = typeArgs[t];

var parentTypeArgCount = parentTypeArgs.Length;
if (typeArgsConsumedByParentsCount > 0)
{
int ownArgCount = parentTypeArgCount - typeArgsConsumedByParentsCount;
if (ownArgCount == 0)
parentTypeArgs = null;
else
{
var ownArgs = new Type[ownArgCount];
for (var a = 0; a < ownArgs.Length; ++a)
ownArgs[a] = parentTypeArgs[a + typeArgsConsumedByParentsCount];
parentTypeArgs = ownArgs;
}
}
typeArgsConsumedByParentsCount = parentTypeArgCount;
}
else
{
parentTypeArgs = parentTypeInfo.GenericTypeArguments;
}

var parentTickIndex = parentType.Name.IndexOf('`');
s.Append(parentType.Name.Substring(0, parentTickIndex));

// The owned parentTypeArgs maybe empty because all args are defined in the parent's parents
if (parentTypeArgs?.Length > 0)
{
s.Append('<');
for (var t = 0; t < parentTypeArgs.Length; ++t)
(t == 0 ? s : s.Append(", ")).Append(parentTypeArgs[t].TypeToCode(stripNamespace, printType, printGenericTypeArgs));
s.Append('>');
}
s.Append('.');
}
}
}
var name = type.Name.TrimStart('<', '>').TrimEnd('&');

if (typeArgs != null && typeArgsConsumedByParentsCount < typeArgs.Length)
{
var tickIndex = name.IndexOf('`');
s.Append(name.Substring(0, tickIndex)).Append('<');
for (var i = 0; i < typeArgs.Length - typeArgsConsumedByParentsCount; ++i)
(i == 0 ? s : s.Append(", ")).Append(typeArgs[i + typeArgsConsumedByParentsCount].TypeToCode(stripNamespace, printType, printGenericTypeArgs));
s.Append('>');
}
else
{
s.Append(name);
}

if (arrayType != null)
s.Append("[]");

return printType?.Invoke(arrayType ?? type, s.ToString()) ?? s.ToString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<ItemGroup>
<PackageReference Include="System.Reflection.Emit" Version="4.7.0"/>
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.9"/>
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.1"/>
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using NUnit.Framework;

#if LIGHT_EXPRESSION
using static FastExpressionCompiler.LightExpression.Expression;
using FastExpressionCompiler.LightExpression;
namespace FastExpressionCompiler.LightExpression.IssueTests;
#else
using static System.Linq.Expressions.Expression;
using System.Linq.Expressions;
namespace FastExpressionCompiler.IssueTests;
#endif

[TestFixture]
public class Issue449_MemberInit_produces_InvalidProgram : ITest
{
public int Run()
{
// Original_case();
return 1;
}

public struct SampleType
{
public int? Value { get; set; }

public SampleType() { }
}

[Test]
public void Original_case()
{
var ctor = typeof(SampleType).GetConstructors()[0];
var valueProp = typeof(SampleType).GetProperty(nameof(SampleType.Value));

var expr = Lambda<Func<SampleType>>(
MemberInit(
New(ctor),
Bind(valueProp, Constant(666, typeof(int?)))));

expr.PrintCSharp();

var fs = expr.CompileSys();
fs.PrintIL();

var sr = fs();
Assert.AreEqual(666, sr.Value.Value);

var ff = expr.CompileFast(false);
ff.PrintIL();

var fr = ff();
Assert.AreEqual(666, sr.Value.Value);
}
}
7 changes: 2 additions & 5 deletions test/FastExpressionCompiler.TestsRunner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class Program
{
public static void Main()
{
new LightExpression.IssueTests.Issue449_MemberInit_produces_InvalidProgram().Run();

// new LightExpression.IssueTests.Issue437_Shared_variables_with_nested_lambdas_returning_incorrect_values().Run();
// new LightExpression.IssueTests.Issue353_NullReferenceException_when_calling_CompileFast_results().Run();
// new LightExpression.UnitTests.ArithmeticOperationsTests().Run();
Expand Down Expand Up @@ -362,8 +364,3 @@ void Run(Func<int> run, string name = null)
}
}
}

namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit { }
}

0 comments on commit 831f8ca

Please sign in to comment.