Skip to content

Commit

Permalink
Add Type.GetMemberWithSameMetadataDefinitionAs (#53704)
Browse files Browse the repository at this point in the history
* Add API to find MethodInfo on instantiated generic type from generic type definition

Fix #45771

* Rename to GetMemberWithSameMetadataDefinitionAs
* Fix a bug for NestedType
* Use new method libraries that were working around not having it

* Implement GetMemberWithSameMetadataDefinitionAs in mono

* Revert JavaScript Runtime changes.

* Support inheritance in GetMemberWithSameMetadataDefinitionAs.
  • Loading branch information
eerhardt committed Jun 15, 2021
1 parent 71e13c2 commit cf7e7a4
Show file tree
Hide file tree
Showing 19 changed files with 436 additions and 108 deletions.
125 changes: 125 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,131 @@ 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));

RuntimeType? runtimeType = this;
while (runtimeType != null)
{
MemberInfo? result = member.MemberType switch
{
MemberTypes.Method => GetMethodWithSameMetadataDefinitionAs(runtimeType, member),
MemberTypes.Constructor => GetConstructorWithSameMetadataDefinitionAs(runtimeType, member),
MemberTypes.Property => GetPropertyWithSameMetadataDefinitionAs(runtimeType, member),
MemberTypes.Field => GetFieldWithSameMetadataDefinitionAs(runtimeType, member),
MemberTypes.Event => GetEventWithSameMetadataDefinitionAs(runtimeType, member),
MemberTypes.NestedType => GetNestedTypeWithSameMetadataDefinitionAs(runtimeType, member),
_ => null
};

if (result != null)
{
return result;
}

runtimeType = runtimeType.GetBaseType();
}

throw CreateGetMemberWithSameMetadataDefinitionAsNotFoundException(member);
}

private static MemberInfo? GetMethodWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo method)
{
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;
}
}

return null;
}

private static MemberInfo? GetConstructorWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo constructor)
{
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;
}
}

return null;
}

private static MemberInfo? GetPropertyWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo property)
{
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;
}
}

return null;
}

private static MemberInfo? GetFieldWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo field)
{
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;
}
}

return null;
}

private static MemberInfo? GetEventWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo eventInfo)
{
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;
}
}

return null;
}

private static MemberInfo? GetNestedTypeWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo nestedType)
{
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;
}
}

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
Original file line number Diff line number Diff line change
Expand Up @@ -3781,4 +3781,7 @@
<data name="PlatformNotSupported_FileEncryption" xml:space="preserve">
<value>File encryption is not supported on this platform.</value>
</data>
<data name="Arg_MemberInfoNotFound" xml:space="preserve">
<value>A MemberInfo that matches '{0}' could not be found.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ public TypeDelegator([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.
[DynamicallyAccessedMembers(GetAllMembers)]
public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => typeImpl.GetMembers(bindingAttr);

public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) => typeImpl.GetMemberWithSameMetadataDefinitionAs(member);

protected override TypeAttributes GetAttributeFlagsImpl() => typeImpl.Attributes;

public override bool IsTypeDefinition => typeImpl.IsTypeDefinition;
Expand Down
Loading

0 comments on commit cf7e7a4

Please sign in to comment.