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 PropertyBuilder implementation #95303

Merged
merged 2 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<Compile Include="System\Reflection\Emit\MethodBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\ModuleBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\ParameterBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\PropertyBuilderImpl.cs" />
<Compile Include="System\Reflection\Emit\PseudoCustomAttributesData.cs" />
<Compile Include="System\Reflection\Emit\SignatureHelper.cs" />
<Compile Include="System\Reflection\Emit\TypeBuilderImpl.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,57 +33,71 @@ internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type ty
}

protected override void SetConstantCore(object? defaultValue)
{
_typeBuilder.ThrowIfCreated();
ValidateDefaultValueType(defaultValue, _fieldType);

buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
_defaultValue = defaultValue;
}

internal static void ValidateDefaultValueType(object? defaultValue, Type destinationType)
{
if (defaultValue == null)
{
// nullable value types can hold null value.
if (_fieldType.IsValueType && !(_fieldType.IsGenericType && _fieldType.GetGenericTypeDefinition() == typeof(Nullable<>)))
if (destinationType.IsValueType && !(destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
throw new ArgumentException(SR.Argument_ConstantNull);
}
}
else
{
Type type = defaultValue.GetType();
Type destType = _fieldType;

Type sourceType = defaultValue.GetType();
// We should allow setting a constant value on a ByRef parameter
if (destType.IsByRef)
destType = destType.GetElementType()!;
if (destinationType.IsByRef)
{
destinationType = destinationType.GetElementType()!;
}

// Convert nullable types to their underlying type.
destType = Nullable.GetUnderlyingType(destType) ?? destType;
destinationType = Nullable.GetUnderlyingType(destinationType) ?? destinationType;

if (destType.IsEnum)
if (destinationType.IsEnum)
{
Type underlyingType;
if (destType is EnumBuilderImpl enumBldr)
if (destinationType is EnumBuilderImpl enumBldr)
{
underlyingType = enumBldr.GetEnumUnderlyingType();

if (type != enumBldr._typeBuilder.UnderlyingSystemType && type != underlyingType)
if (sourceType != enumBldr._typeBuilder.UnderlyingSystemType && sourceType != underlyingType)
throw new ArgumentException(SR.Argument_ConstantDoesntMatch);
}
else if (destType is TypeBuilderImpl typeBldr)
else if (destinationType is TypeBuilderImpl typeBldr)
{
underlyingType = typeBldr.UnderlyingSystemType;

if (underlyingType == null || (type != typeBldr.UnderlyingSystemType && type != underlyingType))
if (underlyingType == null || (sourceType != typeBldr.UnderlyingSystemType && sourceType != underlyingType))
{
throw new ArgumentException(SR.Argument_ConstantDoesntMatch);
}
}
else
{
underlyingType = Enum.GetUnderlyingType(destType);
underlyingType = Enum.GetUnderlyingType(destinationType);

if (type != destType && type != underlyingType)
if (sourceType != destinationType && sourceType != underlyingType)
{
throw new ArgumentException(SR.Argument_ConstantDoesntMatch);
}
}
}
else
{
if (!destType.IsAssignableFrom(type))
if (!destinationType.IsAssignableFrom(sourceType))
{
throw new ArgumentException(SR.Argument_ConstantDoesntMatch);
}
}

_defaultValue = defaultValue;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder
private int _nextMethodDefRowId = 1;
private int _nextFieldDefRowId = 1;
private int _nextParameterRowId = 1;
private int _nextPropertyRowId = 1;
private bool _coreTypesFullyPopulated;
private Type?[]? _coreTypes;
private static readonly Type[] s_coreTypes = { typeof(void), typeof(object), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int),
Expand Down Expand Up @@ -137,7 +138,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder)
parent = GetTypeHandle(typeBuilder.BaseType);
}

TypeDefinitionHandle typeHandle = AddTypeDefinition(typeBuilder, parent, typeBuilder._firsMethodToken, typeBuilder._firstFieldToken);
TypeDefinitionHandle typeHandle = AddTypeDefinition(typeBuilder, parent, typeBuilder._firstMethodToken, typeBuilder._firstFieldToken);
Debug.Assert(typeBuilder._handle.Equals(typeHandle));

if (typeBuilder.IsGenericType)
Expand Down Expand Up @@ -169,6 +170,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder)
}

WriteCustomAttributes(typeBuilder._customAttributes, typeHandle);
WriteProperties(typeBuilder);
WriteFields(typeBuilder);
WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder);
}
Expand All @@ -188,6 +190,42 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder)
}
}

private void WriteProperties(TypeBuilderImpl typeBuilder)
{
if (typeBuilder._propertyDefinitions.Count > 0)
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
{
AddPropertyMap(typeBuilder._handle, typeBuilder._firstPropertyToken);
foreach (PropertyBuilderImpl property in typeBuilder._propertyDefinitions)
{
PropertyDefinitionHandle propertyHandle = AddPropertyDefinition(property, MetadataSignatureHelper.PropertySignatureEncoder(property, this));
WriteCustomAttributes(property._customAttributes, propertyHandle);

if (property.GetMethod is MethodBuilderImpl gMb)
{
AddMethodSemantics(propertyHandle, MethodSemanticsAttributes.Getter, gMb._handle);
}

if (property.SetMethod is MethodBuilderImpl sMb)
{
AddMethodSemantics(propertyHandle, MethodSemanticsAttributes.Setter, sMb._handle);
}

if (property._otherMethods != null)
{
foreach (MethodBuilderImpl method in property._otherMethods)
Copy link
Member

Choose a reason for hiding this comment

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

Do we have tests for other property methods? IL-only tests?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have only this:

// Not sure how other methods should have loaded/tested
// 'propertyFromDisk.GetAccessors(true)' did not return other method
Assert.NotNull(typeFromDisk.GetMethod("OtherMethod", BindingFlags.NonPublic | BindingFlags.Instance));

In the generated assembly it was added just like a normal method, was not sure what else to verify

Copy link
Member

Choose a reason for hiding this comment

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

I assume this was for ".other" methods which are different than ".get" and ".set" (ECMA allows methods other than get\set)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it is the "other" method

{
AddMethodSemantics(propertyHandle, MethodSemanticsAttributes.Other, method._handle);
}
}

if (property._defaultValue != DBNull.Value)
{
AddDefaultValue(propertyHandle, property._defaultValue);
}
}
}
}

private void PopulateFieldDefinitionHandles(List<FieldBuilderImpl> fieldDefinitions)
{
foreach (FieldBuilderImpl field in fieldDefinitions)
Expand All @@ -204,13 +242,23 @@ private void PopulateMethodDefinitionHandles(List<MethodBuilderImpl> methods)
}
}

private void PopulatePropertyDefinitionHandles(List<PropertyBuilderImpl> properties)
{
foreach (PropertyBuilderImpl property in properties)
{
property._handle = MetadataTokens.PropertyDefinitionHandle(_nextPropertyRowId++);
}
}

internal void PopulateTypeAndItsMembersTokens(TypeBuilderImpl typeBuilder)
{
typeBuilder._handle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId);
typeBuilder._firsMethodToken = _nextMethodDefRowId;
typeBuilder._firstMethodToken = _nextMethodDefRowId;
typeBuilder._firstFieldToken = _nextFieldDefRowId;
typeBuilder._firstPropertyToken = _nextPropertyRowId;
PopulateMethodDefinitionHandles(typeBuilder._methodDefinitions);
PopulateFieldDefinitionHandles(typeBuilder._fieldDefinitions);
PopulatePropertyDefinitionHandles(typeBuilder._propertyDefinitions);
}

private void WriteMethods(List<MethodBuilderImpl> methods, List<GenericTypeParameterBuilderImpl> genericParams, MethodBodyStreamEncoder methodBodyEncoder)
Expand Down Expand Up @@ -466,6 +514,23 @@ private void AddGenericTypeParametersAndConstraintsCustomAttributes(EntityHandle
private void AddDefaultValue(EntityHandle parentHandle, object? defaultValue) =>
_metadataBuilder.AddConstant(parent: parentHandle, value: defaultValue);

private void AddMethodSemantics(EntityHandle parentHandle, MethodSemanticsAttributes attribute, MethodDefinitionHandle methodHandle) =>
_metadataBuilder.AddMethodSemantics(
association: parentHandle,
semantics: attribute,
methodDefinition: methodHandle);

private PropertyDefinitionHandle AddPropertyDefinition(PropertyBuilderImpl property, BlobBuilder signature) =>
_metadataBuilder.AddProperty(
attributes: property.Attributes,
name: _metadataBuilder.GetOrAddString(property.Name),
signature: _metadataBuilder.GetOrAddBlob(signature));

private void AddPropertyMap(TypeDefinitionHandle typeHandle, int firstPropertyToken) =>
_metadataBuilder.AddPropertyMap(
declaringType: typeHandle,
propertyList: MetadataTokens.PropertyDefinitionHandle(firstPropertyToken));

private FieldDefinitionHandle AddFieldDefinition(FieldBuilderImpl field, BlobBuilder fieldSignature) =>
_metadataBuilder.AddFieldDefinition(
attributes: field.Attributes,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Globalization;
using System.Reflection.Metadata;

namespace System.Reflection.Emit
{
internal sealed class PropertyBuilderImpl : PropertyBuilder
{
private readonly string _name;
private readonly CallingConventions _callingConvention;
private readonly Type _propertyType;
private readonly Type[]? _parameterTypes;
private readonly TypeBuilderImpl _containingType;
private PropertyAttributes _attributes;
private MethodInfo? _getMethod;
private MethodInfo? _setMethod;
internal List<MethodInfo>? _otherMethods;

internal PropertyDefinitionHandle _handle;
internal List<CustomAttributeWrapper>? _customAttributes;
internal object? _defaultValue = DBNull.Value;

internal PropertyBuilderImpl(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? parameterTypes, TypeBuilderImpl containingType)
{
ArgumentException.ThrowIfNullOrEmpty(name);

_name = name;
_attributes = attributes;
_callingConvention = callingConvention;
_propertyType = returnType;
_parameterTypes = parameterTypes;
_containingType = containingType;
}

internal Type[]? ParameterTypes => _parameterTypes;
internal CallingConventions CallingConventions => _callingConvention;

protected override void AddOtherMethodCore(MethodBuilder mdBuilder)
{
ArgumentNullException.ThrowIfNull(mdBuilder);
_containingType.ThrowIfCreated();

_otherMethods ??= new List<MethodInfo>();
_otherMethods.Add(mdBuilder);
}

protected override void SetConstantCore(object? defaultValue)
{
_containingType.ThrowIfCreated();
FieldBuilderImpl.ValidateDefaultValueType(defaultValue, _propertyType);

_defaultValue = defaultValue;
}

protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
{
_containingType.ThrowIfCreated();

if (con.ReflectedType!.FullName == "System.Runtime.CompilerServices.SpecialNameAttribute")
{
_attributes |= PropertyAttributes.SpecialName;
return;
}

_customAttributes ??= new List<CustomAttributeWrapper>();
_customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
}

protected override void SetGetMethodCore(MethodBuilder mdBuilder)
{
ArgumentNullException.ThrowIfNull(mdBuilder);
_containingType.ThrowIfCreated();

_getMethod = mdBuilder;
}

protected override void SetSetMethodCore(MethodBuilder mdBuilder)
{
ArgumentNullException.ThrowIfNull(mdBuilder);
_containingType.ThrowIfCreated();

_setMethod = mdBuilder;
}

public override Module Module => _containingType.Module;

public override Type PropertyType => _propertyType;

public override PropertyAttributes Attributes => _attributes;

public override bool CanRead => _getMethod != null ? true : false;

public override bool CanWrite => _setMethod != null ? true : false;

public override string Name => _name;

public override Type? DeclaringType => _containingType;

public override Type? ReflectedType => _containingType;

public override MethodInfo? GetGetMethod(bool nonPublic)
{
if (nonPublic || _getMethod == null)
{
return _getMethod;
}

if ((_getMethod.Attributes & MethodAttributes.Public) == MethodAttributes.Public)
{
return _getMethod;
}

return null;
}

public override MethodInfo? GetSetMethod(bool nonPublic)
{
if (nonPublic || _setMethod == null)
{
return _setMethod;
}

if ((_setMethod.Attributes & MethodAttributes.Public) == MethodAttributes.Public)
{
return _setMethod;
}

return null;
}

public override object? GetConstantValue() => _defaultValue == DBNull.Value ? null : _defaultValue;

public override object GetValue(object? obj, object?[]? index) => throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override object GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) =>
throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override void SetValue(object? obj, object? value, object?[]? index) => throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) =>
throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override MethodInfo[] GetAccessors(bool nonPublic) => throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override ParameterInfo[] GetIndexParameters() => throw new NotSupportedException(SR.NotSupported_DynamicModule);
public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_DynamicModule);
}
}