Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Type.GetMemberWithSameMetadataDefinitionAs #53704

Merged
merged 12 commits into from
Jun 15, 2021
144 changes: 144 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3068,6 +3068,150 @@ public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFla

return compressMembers;
}

public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member)
{
if (member is null) throw new ArgumentNullException(nameof(member));

MemberInfo? result = member.MemberType switch
{
MemberTypes.Method => GetMethodWithSameMetadataDefinitionAs(this, member),
MemberTypes.Constructor => GetConstructorWithSameMetadataDefinitionAs(this, member),
MemberTypes.Property => GetPropertyWithSameMetadataDefinitionAs(this, member),
MemberTypes.Field => GetFieldWithSameMetadataDefinitionAs(this, member),
MemberTypes.Event => GetEventWithSameMetadataDefinitionAs(this, member),
MemberTypes.NestedType => GetNestedTypeWithSameMetadataDefinitionAs(this, member),
_ => null
eerhardt marked this conversation as resolved.
Show resolved Hide resolved
};

return result ?? throw CreateGetMemberWithSameMetadataDefinitionAsNotFoundException(member);
}

private static MemberInfo? GetMethodWithSameMetadataDefinitionAs(RuntimeType? runtimeType, MemberInfo method)
{
while (runtimeType != null)
eerhardt marked this conversation as resolved.
Show resolved Hide resolved
{
RuntimeMethodInfo[] cache = runtimeType.Cache.GetMethodList(MemberListType.CaseSensitive, method.Name);

for (int i = 0; i < cache.Length; i++)
{
RuntimeMethodInfo candidate = cache[i];
if (candidate.HasSameMetadataDefinitionAs(method))
{
return candidate;
}
}

runtimeType = runtimeType.GetBaseType();
}

return null;
}

private static MemberInfo? GetConstructorWithSameMetadataDefinitionAs(RuntimeType? runtimeType, MemberInfo constructor)
{
while (runtimeType != null)
{
RuntimeConstructorInfo[] cache = runtimeType.Cache.GetConstructorList(MemberListType.CaseSensitive, constructor.Name);

for (int i = 0; i < cache.Length; i++)
{
RuntimeConstructorInfo candidate = cache[i];
if (candidate.HasSameMetadataDefinitionAs(constructor))
{
return candidate;
}
}

runtimeType = runtimeType.GetBaseType();
}

return null;
}

private static MemberInfo? GetPropertyWithSameMetadataDefinitionAs(RuntimeType? runtimeType, MemberInfo property)
{
while (runtimeType != null)
{
RuntimePropertyInfo[] cache = runtimeType.Cache.GetPropertyList(MemberListType.CaseSensitive, property.Name);

for (int i = 0; i < cache.Length; i++)
{
RuntimePropertyInfo candidate = cache[i];
if (candidate.HasSameMetadataDefinitionAs(property))
{
return candidate;
}
}

runtimeType = runtimeType.GetBaseType();
}

return null;
}

private static MemberInfo? GetFieldWithSameMetadataDefinitionAs(RuntimeType? runtimeType, MemberInfo field)
{
while (runtimeType != null)
{
RuntimeFieldInfo[] cache = runtimeType.Cache.GetFieldList(MemberListType.CaseSensitive, field.Name);

for (int i = 0; i < cache.Length; i++)
{
RuntimeFieldInfo candidate = cache[i];
if (candidate.HasSameMetadataDefinitionAs(field))
{
return candidate;
}
}

runtimeType = runtimeType.GetBaseType();
}

return null;
}

private static MemberInfo? GetEventWithSameMetadataDefinitionAs(RuntimeType? runtimeType, MemberInfo eventInfo)
{
while (runtimeType != null)
{
RuntimeEventInfo[] cache = runtimeType.Cache.GetEventList(MemberListType.CaseSensitive, eventInfo.Name);

for (int i = 0; i < cache.Length; i++)
{
RuntimeEventInfo candidate = cache[i];
if (candidate.HasSameMetadataDefinitionAs(eventInfo))
{
return candidate;
}
}

runtimeType = runtimeType.GetBaseType();
}

return null;
}

private static MemberInfo? GetNestedTypeWithSameMetadataDefinitionAs(RuntimeType? runtimeType, MemberInfo nestedType)
{
while (runtimeType != null)
{
RuntimeType[] cache = runtimeType.Cache.GetNestedTypeList(MemberListType.CaseSensitive, nestedType.Name);

for (int i = 0; i < cache.Length; i++)
{
RuntimeType candidate = cache[i];
if (candidate.HasSameMetadataDefinitionAs(nestedType))
{
return candidate;
}
}

runtimeType = runtimeType.GetBaseType();
}

return null;
}
#endregion

#region Identity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace System.ComponentModel
/// </summary>
public class NullableConverter : TypeConverter
{
private static readonly ConstructorInfo s_nullableConstructor = typeof(Nullable<>).GetConstructor(typeof(Nullable<>).GetGenericArguments())!;

/// <summary>
/// Nullable converter is initialized with the underlying simple type.
/// </summary>
Expand Down Expand Up @@ -108,7 +110,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul
}
else if (destinationType == typeof(InstanceDescriptor))
{
ConstructorInfo ci = GetNullableConstructor();
ConstructorInfo ci = (ConstructorInfo)NullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor);
Debug.Assert(ci != null, "Couldn't find constructor");
return new InstanceDescriptor(ci, new object[] { value }, true);
}
Expand All @@ -128,14 +130,6 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul
return base.ConvertTo(context, culture, value, destinationType);
}

[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(Nullable<>))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
Justification = "The Nullable<T> ctor will be preserved by the DynamicDependency.")]
private ConstructorInfo GetNullableConstructor()
{
return NullableType.GetConstructor(new Type[] { UnderlyingType })!;
}

/// <summary>
/// </summary>
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal static class TypeUtils
.Select(i => i.GetGenericTypeDefinition())
.ToArray();

private static readonly ConstructorInfo s_nullableConstructor = typeof(Nullable<>).GetConstructor(typeof(Nullable<>).GetGenericArguments())!;

public static Type GetNonNullableType(this Type type) => IsNullableType(type) ? type.GetGenericArguments()[0] : type;

public static Type GetNullableType(this Type type)
Expand All @@ -29,15 +31,11 @@ public static Type GetNullableType(this Type type)
return type;
}

[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(Nullable<>))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The Nullable<T> ctor will be preserved by the DynamicDependency.")]
public static ConstructorInfo GetNullableConstructor(Type nullableType, Type nonNullableType)
public static ConstructorInfo GetNullableConstructor(Type nullableType)
{
Debug.Assert(nullableType.IsNullableType());
Debug.Assert(!nonNullableType.IsNullableType() && nonNullableType.IsValueType);

return nullableType.GetConstructor(new Type[] { nonNullableType })!;
return (ConstructorInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor);
}

public static bool IsNullableType(this Type type) => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ namespace System.Linq.Expressions.Compiler
{
internal static class ILGen
{
private static readonly MethodInfo s_nullableHasValueGetter = typeof(Nullable<>).GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public)!;
private static readonly MethodInfo s_nullableValueGetter = typeof(Nullable<>).GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public)!;
private static readonly MethodInfo s_nullableGetValueOrDefault = typeof(Nullable<>).GetMethod("GetValueOrDefault", Type.EmptyTypes)!;

internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase)
{
Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo);
Expand Down Expand Up @@ -485,7 +489,7 @@ private static bool TryEmitILConstant(this ILGenerator il, object value, Type ty

if (TryEmitILConstant(il, value, nonNullType))
{
il.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type, nonNullType));
il.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type));
return true;
}

Expand Down Expand Up @@ -826,7 +830,7 @@ private static void EmitNullableToNullableConversion(this ILGenerator il, Type t
Type nnTypeTo = typeTo.GetNonNullableType();
il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked, locals);
// construct result type
ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo, nnTypeTo);
ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo);
il.Emit(OpCodes.Newobj, ci);
labEnd = il.DefineLabel();
il.Emit(OpCodes.Br_S, labEnd);
Expand All @@ -846,7 +850,7 @@ private static void EmitNonNullableToNullableConversion(this ILGenerator il, Typ
Debug.Assert(typeTo.IsNullableType());
Type nnTypeTo = typeTo.GetNonNullableType();
il.EmitConvertToType(typeFrom, nnTypeTo, isChecked, locals);
ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo, nnTypeTo);
ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo);
il.Emit(OpCodes.Newobj, ci);
}

Expand Down Expand Up @@ -898,38 +902,29 @@ private static void EmitNullableConversion(this ILGenerator il, Type typeFrom, T
il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked, locals);
}

[DynamicDependency("get_HasValue", typeof(Nullable<>))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The Nullable<T> method will be preserved by the DynamicDependency.")]
internal static void EmitHasValue(this ILGenerator il, Type nullableType)
{
Debug.Assert(nullableType.IsNullableType());

MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public)!;
MethodInfo mi = (MethodInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableHasValueGetter);
Debug.Assert(nullableType.IsValueType);
il.Emit(OpCodes.Call, mi);
}

[DynamicDependency("get_Value", typeof(Nullable<>))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The Nullable<T> method will be preserved by the DynamicDependency.")]
internal static void EmitGetValue(this ILGenerator il, Type nullableType)
{
Debug.Assert(nullableType.IsNullableType());

MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public)!;
MethodInfo mi = (MethodInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableValueGetter);
Debug.Assert(nullableType.IsValueType);
il.Emit(OpCodes.Call, mi);
}

[DynamicDependency("GetValueOrDefault()", typeof(Nullable<>))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The Nullable<T> method will be preserved by the DynamicDependency.")]
internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType)
{
Debug.Assert(nullableType.IsNullableType());

MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", Type.EmptyTypes)!;
MethodInfo mi = (MethodInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableGetValueOrDefault);
Debug.Assert(nullableType.IsValueType);
il.Emit(OpCodes.Call, mi);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ private void EmitLiftedBinaryArithmetic(ExpressionType op, Type leftType, Type r
EmitBinaryOperator(op, leftType.GetNonNullableType(), rightType.GetNonNullableType(), resultNonNullableType, liftedToNull: false);

// construct result type
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, resultNonNullableType);
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType);
_ilg.Emit(OpCodes.Newobj, ci);
_ilg.Emit(OpCodes.Stloc, locResult);
_ilg.Emit(OpCodes.Br_S, labEnd);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic.Utils;
using System.Reflection;
using System.Reflection.Emit;
Expand All @@ -14,6 +13,8 @@ namespace System.Linq.Expressions.Compiler
{
internal sealed partial class LambdaCompiler
{
private static readonly FieldInfo s_callSiteTargetField = typeof(CallSite<>).GetField("Target")!;

[Flags]
internal enum CompilationFlags
{
Expand Down Expand Up @@ -613,13 +614,10 @@ private void EmitDynamicExpression(Expression expr)
EmitWriteBack(wb);
}

[DynamicDependency("Target", typeof(CallSite<>))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The 'Target' field will be preserved by the DynamicDependency.")]
private static FieldInfo GetCallSiteTargetField(Type siteType)
{
Debug.Assert(siteType.IsGenericType && siteType.GetGenericTypeDefinition() == typeof(CallSite<>));
return siteType.GetField("Target")!;
return (FieldInfo)siteType.GetMemberWithSameMetadataDefinitionAs(s_callSiteTargetField);
}

private void EmitNewExpression(Expression expr)
Expand Down Expand Up @@ -1171,7 +1169,7 @@ private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpres
EmitMethodCallExpression(mc);
if (resultType.IsNullableType() && !TypeUtils.AreEquivalent(resultType, mc.Type))
{
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, mc.Type);
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType);
_ilg.Emit(OpCodes.Newobj, ci);
}
_ilg.Emit(OpCodes.Br_S, exit);
Expand Down Expand Up @@ -1275,7 +1273,7 @@ private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpres
EmitMethodCallExpression(mc);
if (resultType.IsNullableType() && !TypeUtils.AreEquivalent(resultType, mc.Type))
{
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, mc.Type);
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType);
_ilg.Emit(OpCodes.Newobj, ci);
}
_ilg.Emit(OpCodes.Br_S, exit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private void EmitUnary(UnaryExpression node, CompilationFlags flags)
EmitBinaryOperator(ExpressionType.SubtractChecked, nnType, nnType, nnType, liftedToNull: false);

// construct result
_ilg.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type, nnType));
_ilg.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type));
_ilg.Emit(OpCodes.Br_S, end);

// if null then push back on stack
Expand Down Expand Up @@ -164,7 +164,7 @@ private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultT
EmitUnaryOperator(op, nnOperandType, nnOperandType);

// construct result
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, nnOperandType);
ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType);
_ilg.Emit(OpCodes.Newobj, ci);
_ilg.Emit(OpCodes.Br_S, labEnd);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace System.Linq.Expressions
[DebuggerTypeProxy(typeof(LambdaExpressionProxy))]
public abstract class LambdaExpression : Expression, IParameterProvider
{
private static readonly MethodInfo s_expressionCompileMethodInfo = typeof(Expression<>).GetMethod("Compile", Type.EmptyTypes)!;

private readonly Expression _body;

internal LambdaExpression(Expression body)
Expand Down Expand Up @@ -119,20 +121,7 @@ internal static MethodInfo GetCompileMethod(Type lambdaExpressionType)
return typeof(LambdaExpression).GetMethod("Compile", Type.EmptyTypes)!;
}

return GetDerivedCompileMethod(lambdaExpressionType);
}

[DynamicDependency("Compile()", typeof(Expression<>))]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The 'Compile' method will be preserved by the DynamicDependency.")]
private static MethodInfo GetDerivedCompileMethod(Type lambdaExpressionType)
{
Debug.Assert(lambdaExpressionType.IsAssignableTo(typeof(LambdaExpression)) && lambdaExpressionType != typeof(LambdaExpression));

MethodInfo result = lambdaExpressionType.GetMethod("Compile", Type.EmptyTypes)!;
Debug.Assert(result.DeclaringType!.IsGenericType && result.DeclaringType.GetGenericTypeDefinition() == typeof(Expression<>));

return result;
return (MethodInfo)lambdaExpressionType.GetMemberWithSameMetadataDefinitionAs(s_expressionCompileMethodInfo);
}

/// <summary>
Expand Down
Loading