Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ public void DefineMethodOverride(System.Reflection.MethodInfo methodInfoBody, Sy
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicProperties | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)]
public override System.Reflection.PropertyInfo[] GetProperties(System.Reflection.BindingFlags bindingAttr) { throw null; }
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicProperties | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)]
protected override System.Reflection.PropertyInfo GetPropertyImpl(string name, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, System.Type? returnType, System.Type[]? types, System.Reflection.ParameterModifier[]? modifiers) { throw null; }
protected override System.Reflection.PropertyInfo? GetPropertyImpl(string name, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, System.Type? returnType, System.Type[]? types, System.Reflection.ParameterModifier[]? modifiers) { throw null; }
Comment thread
steveharter marked this conversation as resolved.
protected override bool HasElementTypeImpl() { throw null; }
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)]
public override object? InvokeMember(string name, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder? binder, object? target, object?[]? args, System.Reflection.ParameterModifier[]? modifiers, System.Globalization.CultureInfo? culture, string[]? namedParameters) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -891,18 +891,130 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr)
return candidates.ToArray();
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063:UnrecognizedReflectionPattern")]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type? GetInterface(string name, bool ignoreCase) => throw new NotSupportedException();
public override Type? GetInterface(string name, bool ignoreCase)
{
ArgumentNullException.ThrowIfNull(name);
ThrowIfNotCreated();

Type[] interfaces = GetInterfaces();

Type? match = null;
StringComparison compare = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
for (int i = 0; i < interfaces.Length; ++i)
{
Type interfaceType = interfaces[i];

if (name.Equals(interfaceType.Name, compare))
{
if (match != null)
{
// TypeBuilder doesn't validate for duplicates when fields are defined, throw if duplicates found.
throw new AmbiguousMatchException(SR.Format(SR.AmbiguousMatch_MemberInfo, interfaceType.DeclaringType, interfaceType.Name));
}

match = interfaceType;
}
}

return match;
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type[] GetInterfaces() => _interfaces == null ? EmptyTypes : _interfaces.ToArray();

internal static BindingFlags GetBindingFlags(PropertyBuilderImpl property)
{
MethodInfo? getMethod = property.GetMethod;
MethodInfo? setMethod = property.SetMethod;

BindingFlags bindingFlags = BindingFlags.Default;

if (getMethod != null)
{
bindingFlags = GetBindingFlags(getMethod);
}
else if (setMethod != null)
{
bindingFlags = GetBindingFlags(setMethod);
}

return bindingFlags;
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
ThrowIfNotCreated();

List<PropertyBuilderImpl> candidates = new List<PropertyBuilderImpl>(_propertyDefinitions.Count);
foreach (PropertyBuilderImpl property in _propertyDefinitions)
{
BindingFlags fieldFlags = GetBindingFlags(property);
if ((bindingAttr & fieldFlags) == fieldFlags)
{
candidates.Add(property);
}
}

return candidates.ToArray();
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException();
protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder,
Type? returnType, Type[]? types, ParameterModifier[]? modifiers)
{
ArgumentNullException.ThrowIfNull(name);

List<PropertyInfo> candidates = GetPropertyCandidates(name, bindingAttr, types);

if (candidates.Count == 0)
return null;

if (types == null || types.Length == 0)
{
// no arguments
PropertyInfo firstCandidate = candidates[0];

if (candidates.Count == 1)
{
if (returnType is not null && !returnType.IsEquivalentTo(firstCandidate.PropertyType))
return null;

return firstCandidate;
}
else
{
if (returnType is null)
// if we are here we have no args or property type to select over and we have more than one property with that name
throw new AmbiguousMatchException(SR.Format(SR.AmbiguousMatch_MemberInfo, firstCandidate.DeclaringType, firstCandidate.Name));
}
}

binder ??= DefaultBinder;
return binder.SelectProperty(bindingAttr, candidates.ToArray(), returnType, types, modifiers);
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder,
Type? returnType, Type[]? types, ParameterModifier[]? modifiers) => throw new NotSupportedException();
private List<PropertyInfo> GetPropertyCandidates(string name, BindingFlags bindingAttr, Type[]? types)
{
PropertyInfo[] properties = GetProperties(bindingAttr);

List<PropertyInfo> candidates = new List<PropertyInfo>(properties.Length);
for (int i = 0; i < properties.Length; i++)
{
PropertyInfo propertyInfo = properties[i];
if (propertyInfo.Name == name &&
(types == null || (propertyInfo.GetIndexParameters().Length == types.Length)))
{
candidates.Add(propertyInfo);
}
}

return candidates;
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
public override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is nested type data available somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like it is available by going through the module. Should be easy. Will try to add that one too tomorrow. I'm not sure of all the different options of mapping between BindingFlags and the attribute enum used to create types so I will create a base version and we can augment it after

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, should be good now if you want to have a look

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,94 @@ namespace System.Reflection.Emit.Tests
[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public class AssemblySavePropertyBuilderTests
{
[Fact]
public void GetPropertiesAndGetProperty()
{
PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type);
FieldBuilder field = type.DefineField("TestField", typeof(int), FieldAttributes.Private | FieldAttributes.Static);
CreateProperty("PublicGetSet", isPublic: true, isStatic: false, hasSet: true);
CreateProperty("PublicGet", isPublic: true, isStatic: false, hasSet: false);

CreateProperty("PrivateGetSet", isPublic: false, isStatic: false, hasSet: false);
CreateProperty("PrivateGet", isPublic: false, isStatic: false, hasSet: false);

CreateProperty("StaticPublicGetSet", isPublic: true, isStatic: true, hasSet: true);
CreateProperty("StaticPublicGet", isPublic: true, isStatic: true, hasSet: true);

CreateProperty("StaticPrivateGetSet", isPublic: false, isStatic: true, hasSet: true);
CreateProperty("StaticPrivateGet", isPublic: false, isStatic: true, hasSet: true);

// Try get before creation
Assert.Throws<NotSupportedException>(() => type.GetProperties());
Assert.Throws<NotSupportedException>(() => type.GetProperty("PublicGetSet"));

// Create the type
type.CreateType();

// By default GetProperties doesn't return private properties
Assert.Equal(4, type.GetProperties().Length);
Assert.True(type.GetProperties().All(x => x.Name.Contains("Public")));

// get all properties
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
Assert.Equal(8, properties.Length);

// get only private properties
properties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
Assert.Equal(4, properties.Length);
Assert.True(properties.All(x => x.Name.Contains("Private")));

// get only static properties
properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
Assert.Equal(4, properties.Length);
Assert.True(properties.All(x => x.Name.Contains("Static")));

// get public property by name only
PropertyInfo? property = type.GetProperty("PublicGetSet");
Assert.NotNull(property);
Assert.Equal("PublicGetSet", property.Name);

// get public property by name and binding flags
property = type.GetProperty("PublicGetSet", BindingFlags.Public | BindingFlags.Instance);
Assert.NotNull(property);
Assert.Equal("PublicGetSet", property.Name);

// get public property by name and binding flags when there is not "set" method
property = type.GetProperty("StaticPublicGet", BindingFlags.Public | BindingFlags.Static);
Assert.NotNull(property);
Assert.Equal("StaticPublicGet", property.Name);

// Returns null when not found
Assert.Null(type.GetProperty("NotFound"));
Assert.Null(type.GetProperty("PublicGetSet", BindingFlags.NonPublic | BindingFlags.Instance));
Assert.Null(type.GetProperty("PublicGetSet", BindingFlags.Public | BindingFlags.Static));


void CreateProperty(string name, bool isPublic, bool isStatic, bool hasSet)
{
MethodAttributes methodAttributes = (isPublic ? MethodAttributes.Public : MethodAttributes.Private) | (isStatic ? MethodAttributes.Static : default) | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
PropertyBuilder property = type.DefineProperty(name, PropertyAttributes.SpecialName | PropertyAttributes.HasDefault, typeof(int), null);

MethodBuilder getMethod = type.DefineMethod($"{name}_GetMethod", methodAttributes, typeof(int), null);
ILGenerator getterLGenerator = getMethod.GetILGenerator();
getterLGenerator.Emit(OpCodes.Ldarg_0);
getterLGenerator.Emit(OpCodes.Ldfld, field);
getterLGenerator.Emit(OpCodes.Ret);
property.SetGetMethod(getMethod);

if (hasSet)
{
MethodBuilder setMethod = type.DefineMethod($"{name}_SetMethod", methodAttributes, typeof(void), [typeof(int)]);
ILGenerator setterILGenerator = setMethod.GetILGenerator();
setterILGenerator.Emit(OpCodes.Ldarg_0);
setterILGenerator.Emit(OpCodes.Ldarg_1);
setterILGenerator.Emit(OpCodes.Stfld, field);
setterILGenerator.Emit(OpCodes.Ret);
property.SetSetMethod(setMethod);
}
}
}

[Fact]
public void SetPropertyAccessorsAndOtherValues()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,41 @@ public void GetInterfaceMap_Validations()
Assert.Throws<ArgumentException>(() => type.GetInterfaceMap(typeof(InterfaceWithMethod))); // not implemented
}

[Fact]
public void GetInterface_Validations()
{
PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type);
ModuleBuilder module = ab.GetDynamicModule("MyModule");

TypeBuilder interfaceType = module.DefineType("InterfaceType", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract, parent: null);
MethodBuilder svmInterface = interfaceType.DefineMethod("InterfaceMethod1", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract, CallingConventions.Standard, typeof(int), Type.EmptyTypes);
MethodBuilder mInterface = interfaceType.DefineMethod("InterfaceMethod2", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract, typeof(string), Array.Empty<Type>());
MethodBuilder vmInterface = interfaceType.DefineMethod("InterfaceMethod3", MethodAttributes.Assembly | MethodAttributes.Virtual | MethodAttributes.Abstract, CallingConventions.HasThis, typeof(void), [typeof(bool)]);
Type interfaceTypeActual = interfaceType.CreateType();

// Implicit implementations (same name, signatures)
TypeBuilder implType = module.DefineType("ImplType", TypeAttributes.Public, parent: typeof(Impl), new Type[] { interfaceTypeActual });
MethodBuilder mImpl = implType.DefineMethod("InterfaceMethod2", MethodAttributes.Public | MethodAttributes.Virtual, typeof(string), Array.Empty<Type>());
ILGenerator ilGenerator = mImpl.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldstr, "Hello");
ilGenerator.Emit(OpCodes.Ret);
MethodBuilder m2Impl = implType.DefineMethod("InterfaceMethod3", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), [typeof(bool)]);
ilGenerator = m2Impl.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldc_I4_1);
ilGenerator.Emit(OpCodes.Ret);

Type implTypeActual = implType.CreateType();

Type? interfaceReceived = implTypeActual.GetInterface("InterfaceType");
Assert.Equal(interfaceTypeActual, interfaceReceived);

interfaceReceived = implTypeActual.GetInterface("interfacetype", false);
Assert.Null(interfaceReceived);

interfaceReceived = implTypeActual.GetInterface("interfacetype", true);
Assert.NotNull(interfaceReceived);
}

public interface InterfaceDerivedFromOtherInterface : DefineMethodOverrideInterface
{
public string M2(int a);
Expand Down