From a87e2fa6b5d0df27236bd1c4d5aaebd2ba4e99f3 Mon Sep 17 00:00:00 2001 From: ABykiev Date: Tue, 29 Apr 2025 21:54:20 +0300 Subject: [PATCH 1/4] Remove GetTypeInfo calls This PR removes GetTypeInfo calls, which improves performance --- .../Expression.cs | 8 +-- .../FastExpressionCompiler.cs | 68 +++++++++---------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index 6fffc900..90525233 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -2912,23 +2912,23 @@ public static bool IsImplicitlyConvertibleTo(this Type source, Type target) => [RequiresUnreferencedCode(Trimming.Message)] internal static PropertyInfo FindProperty(this Type type, string propertyName) { - var properties = type.GetTypeInfo().DeclaredProperties.AsArray(); + var properties = type.GetProperties(BindingFlags.DeclaredOnly); for (var i = 0; i < properties.Length; i++) if (properties[i].Name == propertyName) return properties[i]; - return type.GetTypeInfo().BaseType?.FindProperty(propertyName); + return type.BaseType?.FindProperty(propertyName); } [RequiresUnreferencedCode(Trimming.Message)] internal static FieldInfo FindField(this Type type, string fieldName) { - var fields = type.GetTypeInfo().DeclaredFields.AsArray(); + var fields = type.GetFields(BindingFlags.DeclaredOnly); for (var i = 0; i < fields.Length; i++) if (fields[i].Name == fieldName) return fields[i]; - return type.GetTypeInfo().BaseType?.FindField(fieldName); + return type.BaseType?.FindField(fieldName); } internal const BindingFlags InstanceMethods = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 70f7f94a..649d1d08 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -2630,7 +2630,7 @@ private static bool TryEmitCoalesceOperator(BinaryExpression expr, IReadOnlyList private static void EmitDefault(Type type, ILGenerator il) { - if (!type.GetTypeInfo().IsValueType) + if (!type.IsValueType) { il.Demit(OpCodes.Ldnull); } @@ -3761,7 +3761,7 @@ private static void EmitDecimalConstant(decimal value, ILGenerator il) private static readonly Lazy _decimalCtor = new Lazy(() => { - foreach (var ctor in typeof(decimal).GetTypeInfo().DeclaredConstructors) + foreach (var ctor in typeof(decimal).GetConstructors(BindingFlags.DeclaredOnly)) if (ctor.GetParameters().Length == 5) return ctor; return null; @@ -3807,7 +3807,7 @@ private static bool EmitNewArrayBounds(NewArrayExpression expr, IReadOnlyList i.DeclareLocal(t).LocalIndex; // now let's try to acquire the more efficient less allocating method - var ilGenTypeInfo = typeof(ILGenerator).GetTypeInfo(); - var localSignatureField = ilGenTypeInfo.GetDeclaredField("m_localSignature"); + var ilGenType = typeof(ILGenerator); + var localSignatureField = ilGenType.GetField("m_localSignature", BindingFlags.DeclaredOnly); if (localSignatureField == null) return; - var localCountField = ilGenTypeInfo.GetDeclaredField("m_localCount"); + var localCountField = ilGenType.GetField("m_localCount", BindingFlags.DeclaredOnly); if (localCountField == null) return; // looking for the `SignatureHelper.AddArgument(Type argument, bool pinned)` MethodInfo addArgumentMethod = null; - foreach (var m in typeof(SignatureHelper).GetTypeInfo().GetDeclaredMethods("AddArgument")) + foreach (var m in typeof(SignatureHelper).GetMethods(BindingFlags.DeclaredOnly) + .Where(_ => _.Name.Equals("AddArgument", StringComparison.OrdinalIgnoreCase))) { var ps = m.GetParameters(); if (ps.Length == 2 && ps[0].ParameterType == typeof(Type) && ps[1].ParameterType == typeof(bool)) @@ -7035,7 +7036,7 @@ static ILGeneratorHacks() return; // our own helper - always available - var postIncMethod = typeof(ILGeneratorHacks).GetTypeInfo().GetDeclaredMethod(nameof(PostInc)); + var postIncMethod = typeof(ILGeneratorHacks).GetMethod(nameof(PostInc), BindingFlags.DeclaredOnly); var efficientMethod = new DynamicMethod(string.Empty, typeof(int), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(ILGenerator), typeof(Type) }, @@ -7421,9 +7422,9 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu return sb.Append("New(").AppendTypeOf(e.Type, stripNamespace, printType).Append(')'); sb.Append("New( // ").Append(args.Count).Append(" args"); - var ctorIndex = x.Constructor.DeclaringType.GetTypeInfo().DeclaredConstructors.AsArray().GetFirstIndex(x.Constructor, default(RefEq)); + var ctorIndex = x.Constructor.DeclaringType.GetConstructors(BindingFlags.DeclaredOnly).GetFirstIndex(x.Constructor, default(RefEq)); sb.NewLineIndent(lineIndent).AppendTypeOf(x.Type, stripNamespace, printType) - .Append(".GetTypeInfo().DeclaredConstructors.AsArray()[").Append(ctorIndex).Append("],"); + .Append(".GetConstructors(BindingFlags.DeclaredOnly)[").Append(ctorIndex).Append("],"); sb.NewLineIndentArgumentExprs(args, paramsExprs, uniqueExprs, lts, lineIndent, stripNamespace, printType, indentSpaces, notRecognizedToCode); return sb.Append(')'); } @@ -8977,12 +8978,12 @@ member is FieldInfo f internal static StringBuilder AppendField(this StringBuilder sb, FieldInfo field, bool stripNamespace = false, Func printType = null) => sb.AppendTypeOf(field.DeclaringType, stripNamespace, printType) - .Append(".GetTypeInfo().GetDeclaredField(\"").Append(field.Name).Append("\")"); + .Append(".GetField(\"").Append(field.Name).Append("\", BindingFlags.DeclaredOnly)"); internal static StringBuilder AppendProperty(this StringBuilder sb, PropertyInfo property, bool stripNamespace = false, Func printType = null) => sb.AppendTypeOf(property.DeclaringType, stripNamespace, printType) - .Append(".GetTypeInfo().GetDeclaredProperty(\"").Append(property.Name).Append("\")"); + .Append(".GetField(\"").Append(property.Name).Append("\", BindingFlags.DeclaredOnly)"); internal static StringBuilder AppendEnum(this StringBuilder sb, TEnum value, bool stripNamespace = false, Func printType = null) => @@ -9139,7 +9140,7 @@ public static string ToCode(this Type type, } var parentCount = 0; - for (var ti = type.GetTypeInfo(); ti.IsNested; ti = ti.DeclaringType.GetTypeInfo()) + for (var ti = type; ti.IsNested; ti = ti.DeclaringType) ++parentCount; Type[] parentTypes = null; @@ -9151,13 +9152,13 @@ public static string ToCode(this Type type, 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; + isTypeClosedGeneric = !type.IsGenericTypeDefinition; + typeArgs = isTypeClosedGeneric ? type.GenericTypeArguments : type.GetGenericArguments(); } var typeArgsConsumedByParentsCount = 0; @@ -9176,11 +9177,10 @@ public static string ToCode(this Type type, } else { - var parentTypeInfo = parentType.GetTypeInfo(); Type[] parentTypeArgs = null; - if (parentTypeInfo.IsGenericTypeDefinition) + if (parentType.IsGenericTypeDefinition) { - parentTypeArgs = parentTypeInfo.GenericTypeParameters; + parentTypeArgs = parentType.GetGenericArguments(); // replace the open parent args with the closed child args, // and close the parent @@ -9206,7 +9206,7 @@ public static string ToCode(this Type type, } else { - parentTypeArgs = parentTypeInfo.GenericTypeArguments; + parentTypeArgs = parentType.GenericTypeArguments; } var parentTickIndex = parentType.Name.IndexOf('`'); @@ -9272,8 +9272,8 @@ public static string ToEnumValueCode(this Type enumType, object x, return typeStr + "." + string.Join(orTypeDot, flags); } - private static Type[] GetGenericTypeParametersOrArguments(this TypeInfo typeInfo) => - typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters : typeInfo.GenericTypeArguments; + private static Type[] GetGenericTypeParametersOrArguments(this Type type) => + type.IsGenericTypeDefinition ? type.GetGenericArguments() : type.GenericTypeArguments; /// Custom handler for output the object in valid C#. /// Note, the `printGenericTypeArgs` is excluded because it cannot be a open-generic object. @@ -9319,7 +9319,7 @@ public static string ToArrayInitializerCode(this IEnumerable items, Type itemTyp } private static readonly Type[] TypesImplementedByArray = - typeof(object[]).GetInterfaces().Where(t => t.GetTypeInfo().IsGenericType).Select(t => t.GetGenericTypeDefinition()).ToArray(); + typeof(object[]).GetInterfaces().Where(t => t.IsGenericType).Select(t => t.GetGenericTypeDefinition()).ToArray(); // todo: @simplify convert to using StringBuilder and simplify usage call-sites, or ADD the method // todo: @simplify add `addTypeof = false` @@ -9365,31 +9365,27 @@ public static string ToCode(this object x, return "TimeSpan.Parse(" + time.ToString().ToCode() + ")"; var xType = x.GetType(); - var xTypeInfo = xType.GetTypeInfo(); // check if item is implemented by array and then use the array initializer only for these types, // otherwise we may produce the array initializer but it will be incompatible with e.g. `List` - if (xTypeInfo.IsArray || - xTypeInfo.IsGenericType && TypesImplementedByArray.Contains(xType.GetGenericTypeDefinition())) + if (xType.IsArray || + xType.IsGenericType && TypesImplementedByArray.Contains(xType.GetGenericTypeDefinition())) { - var elemType = xTypeInfo.IsArray - ? xTypeInfo.GetElementType() - : xTypeInfo.GetGenericTypeParametersOrArguments().GetFirst(); + var elemType = xType.IsArray + ? xType.GetElementType() + : xType.GetGenericTypeParametersOrArguments().GetFirst(); if (elemType != null && elemType != xType) // avoid self recurring types e.g. `class A : IEnumerable` return ((IEnumerable)x).ToArrayInitializerCode(elemType, notRecognizedToCode, stripNamespace, printType); } // unwrap the Nullable struct - if (xTypeInfo.IsGenericType && xTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - xType = xTypeInfo.GetElementType(); - xTypeInfo = xType.GetTypeInfo(); - } + if (xType.IsGenericType && xType.GetGenericTypeDefinition() == typeof(Nullable<>)) + xType = xType.GetElementType(); - if (xTypeInfo.IsEnum) + if (xType.IsEnum) return x.GetType().ToEnumValueCode(x, stripNamespace, printType); - if (xTypeInfo.IsPrimitive) // output the primitive casted to the type + if (xType.IsPrimitive) // output the primitive casted to the type return "(" + x.GetType().ToCode(true, null) + ")" + x.ToString(); return notRecognizedToCode?.Invoke(x, stripNamespace, printType) ?? x.ToString(); From 192b9bee8aa1b4dbc83c5ee458ee8481c5df5a01 Mon Sep 17 00:00:00 2001 From: ABykiev Date: Tue, 29 Apr 2025 22:09:13 +0300 Subject: [PATCH 2/4] fix --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 649d1d08..df2f87be 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -898,7 +898,7 @@ private static Label GetOrDefineLabel(ref this LabelInfo label, ILGenerator il) public static readonly ArrayClosure EmptyArrayClosure = new ArrayClosure(null); public static FieldInfo ArrayClosureArrayField = - typeof(ArrayClosure).GetField(nameof(ArrayClosure.ConstantsAndNestedLambdas)); + typeof(ArrayClosure).GetField((nameof(ArrayClosure.ConstantsAndNestedLambdas)); public static FieldInfo ArrayClosureWithNonPassedParamsField = typeof(ArrayClosureWithNonPassedParams).GetField(nameof(ArrayClosureWithNonPassedParams.NonPassedParams)); @@ -8983,7 +8983,7 @@ internal static StringBuilder AppendField(this StringBuilder sb, FieldInfo field internal static StringBuilder AppendProperty(this StringBuilder sb, PropertyInfo property, bool stripNamespace = false, Func printType = null) => sb.AppendTypeOf(property.DeclaringType, stripNamespace, printType) - .Append(".GetField(\"").Append(property.Name).Append("\", BindingFlags.DeclaredOnly)"); + .Append(".GetProperty(\"").Append(property.Name).Append("\", BindingFlags.DeclaredOnly)"); internal static StringBuilder AppendEnum(this StringBuilder sb, TEnum value, bool stripNamespace = false, Func printType = null) => From 230fbf20880c906291aebe7a9c47af0bbcc58f97 Mon Sep 17 00:00:00 2001 From: ABykiev Date: Tue, 29 Apr 2025 22:10:12 +0300 Subject: [PATCH 3/4] fix2 --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index df2f87be..daedbe92 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -898,7 +898,7 @@ private static Label GetOrDefineLabel(ref this LabelInfo label, ILGenerator il) public static readonly ArrayClosure EmptyArrayClosure = new ArrayClosure(null); public static FieldInfo ArrayClosureArrayField = - typeof(ArrayClosure).GetField((nameof(ArrayClosure.ConstantsAndNestedLambdas)); + typeof(ArrayClosure).GetField(nameof(ArrayClosure.ConstantsAndNestedLambdas)); public static FieldInfo ArrayClosureWithNonPassedParamsField = typeof(ArrayClosureWithNonPassedParams).GetField(nameof(ArrayClosureWithNonPassedParams.NonPassedParams)); From 8df6b1b36aaad3b59d4c9449f360a6bcef7fbf65 Mon Sep 17 00:00:00 2001 From: ABykiev Date: Wed, 30 Apr 2025 20:48:15 +0300 Subject: [PATCH 4/4] Fix BindingFlags --- .../Expression.cs | 4 +- .../FastExpressionCompiler.cs | 40 +++++++++++++------ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index 90525233..b8dae035 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -2912,7 +2912,7 @@ public static bool IsImplicitlyConvertibleTo(this Type source, Type target) => [RequiresUnreferencedCode(Trimming.Message)] internal static PropertyInfo FindProperty(this Type type, string propertyName) { - var properties = type.GetProperties(BindingFlags.DeclaredOnly); + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); for (var i = 0; i < properties.Length; i++) if (properties[i].Name == propertyName) return properties[i]; @@ -2923,7 +2923,7 @@ internal static PropertyInfo FindProperty(this Type type, string propertyName) [RequiresUnreferencedCode(Trimming.Message)] internal static FieldInfo FindField(this Type type, string fieldName) { - var fields = type.GetFields(BindingFlags.DeclaredOnly); + var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); for (var i = 0; i < fields.Length; i++) if (fields[i].Name == fieldName) return fields[i]; diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index daedbe92..1af8054c 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -3761,7 +3761,7 @@ private static void EmitDecimalConstant(decimal value, ILGenerator il) private static readonly Lazy _decimalCtor = new Lazy(() => { - foreach (var ctor in typeof(decimal).GetConstructors(BindingFlags.DeclaredOnly)) + foreach (var ctor in typeof(decimal).GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) if (ctor.GetParameters().Length == 5) return ctor; return null; @@ -3807,7 +3807,7 @@ private static bool EmitNewArrayBounds(NewArrayExpression expr, IReadOnlyList _.Name.Equals("AddArgument", StringComparison.OrdinalIgnoreCase))) + foreach (var m in typeof(SignatureHelper).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) + .Where(_ => _.Name == "AddArgument")) { var ps = m.GetParameters(); if (ps.Length == 2 && ps[0].ParameterType == typeof(Type) && ps[1].ParameterType == typeof(bool)) @@ -7036,7 +7036,7 @@ static ILGeneratorHacks() return; // our own helper - always available - var postIncMethod = typeof(ILGeneratorHacks).GetMethod(nameof(PostInc), BindingFlags.DeclaredOnly); + var postIncMethod = typeof(ILGeneratorHacks).GetMethod(nameof(PostInc), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); var efficientMethod = new DynamicMethod(string.Empty, typeof(int), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(ILGenerator), typeof(Type) }, @@ -7422,9 +7422,9 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu return sb.Append("New(").AppendTypeOf(e.Type, stripNamespace, printType).Append(')'); sb.Append("New( // ").Append(args.Count).Append(" args"); - var ctorIndex = x.Constructor.DeclaringType.GetConstructors(BindingFlags.DeclaredOnly).GetFirstIndex(x.Constructor, default(RefEq)); + var ctorIndex = x.Constructor.DeclaringType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).GetFirstIndex(x.Constructor, default(RefEq)); sb.NewLineIndent(lineIndent).AppendTypeOf(x.Type, stripNamespace, printType) - .Append(".GetConstructors(BindingFlags.DeclaredOnly)[").Append(ctorIndex).Append("],"); + .Append(".GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)[").Append(ctorIndex).Append("],"); sb.NewLineIndentArgumentExprs(args, paramsExprs, uniqueExprs, lts, lineIndent, stripNamespace, printType, indentSpaces, notRecognizedToCode); return sb.Append(')'); } @@ -8976,14 +8976,28 @@ member is FieldInfo f : sb.AppendProperty((PropertyInfo)member, stripNamespace, printType); internal static StringBuilder AppendField(this StringBuilder sb, FieldInfo field, - bool stripNamespace = false, Func printType = null) => + bool stripNamespace = false, Func printType = null) + { sb.AppendTypeOf(field.DeclaringType, stripNamespace, printType) - .Append(".GetField(\"").Append(field.Name).Append("\", BindingFlags.DeclaredOnly)"); + .Append(".GetField(\"").Append(field.Name); + + if (field.IsPublic) + return sb.Append("\", BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)"); + + return sb.Append("\", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)"); + } internal static StringBuilder AppendProperty(this StringBuilder sb, PropertyInfo property, - bool stripNamespace = false, Func printType = null) => + bool stripNamespace = false, Func printType = null) + { sb.AppendTypeOf(property.DeclaringType, stripNamespace, printType) - .Append(".GetProperty(\"").Append(property.Name).Append("\", BindingFlags.DeclaredOnly)"); + .Append(".GetProperty(\"").Append(property.Name); + + if (property.PropertyType.IsPublic) + return sb.Append("\", BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)"); + + return sb.Append("\", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)"); + } internal static StringBuilder AppendEnum(this StringBuilder sb, TEnum value, bool stripNamespace = false, Func printType = null) =>