diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index b5cd4894cd5be4..3b82e34fce48fa 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -163,7 +163,6 @@ - @@ -172,6 +171,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs index b824378c665e6a..bac45a0c5484f4 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs @@ -646,7 +646,7 @@ protected override ParameterBuilder DefineParameterCore(int position, ParameterA throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_ParamSequence); attributes &= ~ParameterAttributes.ReservedMask; - return new ParameterBuilder(this, position, attributes, strParamName); + return new RuntimeParameterBuilder(this, position, attributes, strParamName); } protected override void SetImplementationFlagsCore(MethodImplAttributes attributes) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.cs similarity index 61% rename from src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs rename to src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.cs index 9700939f2ec373..71980345a00776 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.cs @@ -5,10 +5,10 @@ namespace System.Reflection.Emit { - public class ParameterBuilder + internal sealed class RuntimeParameterBuilder : ParameterBuilder { // Set the default value of the parameter - public virtual void SetConstant(object? defaultValue) + public override void SetConstant(object? defaultValue) { RuntimeTypeBuilder.SetConstantValue( _methodBuilder.GetModuleBuilder(), @@ -17,12 +17,8 @@ public virtual void SetConstant(object? defaultValue) defaultValue); } - // Use this function if client decides to form the custom attribute blob themselves - public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { - ArgumentNullException.ThrowIfNull(con); - ArgumentNullException.ThrowIfNull(binaryAttribute); - RuntimeTypeBuilder.DefineCustomAttribute( _methodBuilder.GetModuleBuilder(), _token, @@ -30,15 +26,7 @@ public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) binaryAttribute); } - // Use this function if client wishes to build CustomAttribute using CustomAttributeBuilder - public void SetCustomAttribute(CustomAttributeBuilder customBuilder) - { - ArgumentNullException.ThrowIfNull(customBuilder); - - customBuilder.CreateCustomAttribute((RuntimeModuleBuilder)(_methodBuilder.GetModule()), _token); - } - - internal ParameterBuilder( + internal RuntimeParameterBuilder( RuntimeMethodBuilder methodBuilder, int sequence, ParameterAttributes attributes, @@ -62,17 +50,11 @@ internal int GetToken() return _token; } - public virtual string? Name => _name; - - public virtual int Position => _position; - - public virtual int Attributes => (int)_attributes; - - public bool IsIn => (_attributes & ParameterAttributes.In) != 0; + public override string? Name => _name; - public bool IsOut => (_attributes & ParameterAttributes.Out) != 0; + public override int Position => _position; - public bool IsOptional => (_attributes & ParameterAttributes.Optional) != 0; + public override int Attributes => (int)_attributes; private readonly string? _name; private readonly int _position; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index dd0e469c284301..e400551f9a50ba 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -145,7 +145,6 @@ - diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs deleted file mode 100644 index 2c02ff1f1ea7b6..00000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Reflection.Emit -{ - public partial class ParameterBuilder - { - internal ParameterBuilder() - { - // Prevent generating a default constructor - } - - public virtual int Attributes - { - get - { - return default; - } - } - - public bool IsIn - { - get - { - return default; - } - } - - public bool IsOptional - { - get - { - return default; - } - } - - public bool IsOut - { - get - { - return default; - } - } - - public virtual string Name - { - get - { - return default; - } - } - - public virtual int Position - { - get - { - return default; - } - } - - public virtual void SetConstant(object defaultValue) - { - } - - public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) - { - } - - public void SetCustomAttribute(CustomAttributeBuilder customBuilder) - { - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 0759d0370079f0..7859d2b8ccff92 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -646,6 +646,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs new file mode 100644 index 00000000000000..c7c7e81e95ff97 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Reflection.Emit +{ + public abstract partial class ParameterBuilder + { + protected ParameterBuilder() { } + public virtual int Attributes => throw new NotImplementedException(); + public bool IsIn => ((ParameterAttributes)Attributes & ParameterAttributes.In) != 0; + public bool IsOptional => ((ParameterAttributes)Attributes & ParameterAttributes.Optional) != 0; + public bool IsOut => ((ParameterAttributes)Attributes & ParameterAttributes.Out) != 0; + public virtual string? Name => throw new NotImplementedException(); + public virtual int Position => throw new NotImplementedException(); + public virtual void SetConstant(object? defaultValue) => throw new NotImplementedException(); + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + ArgumentNullException.ThrowIfNull(con); + ArgumentNullException.ThrowIfNull(binaryAttribute); + + SetCustomAttributeCore(con, binaryAttribute); + } + protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute); + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + ArgumentNullException.ThrowIfNull(customBuilder); + + SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data); + } + } +} diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs index fbcc94202c48b8..1a200d8a56f71e 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs +++ b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs @@ -72,9 +72,9 @@ internal LocalBuilder() { } public override int LocalIndex { get { throw null; } } public override System.Type LocalType { get { throw null; } } } - public partial class ParameterBuilder + public abstract partial class ParameterBuilder { - internal ParameterBuilder() { } + protected ParameterBuilder() { } public virtual int Attributes { get { throw null; } } public bool IsIn { get { throw null; } } public bool IsOptional { get { throw null; } } @@ -84,6 +84,7 @@ internal ParameterBuilder() { } public virtual void SetConstant(object? defaultValue) { } public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { } public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); } public sealed partial class SignatureHelper { diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index dcbd221db545cb..02f0c2865c2e8f 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -126,9 +126,6 @@ Interface must be declared abstract. - - The signature {0} is not supported. - Assembly needs at least one module defined. @@ -141,11 +138,11 @@ The invoked member is not supported in a dynamic module. - + The type code may not be used as a type argument of a custom attribute . - - Custom attribute '{0}' doesn't contain a field named '{1}'. + + 'UnmanagedType.{0}' named parameter is only valid for fields. Custom attribute '{0}' data length is only '{1}'. @@ -162,4 +159,16 @@ DllName cannot be empty. + + The specified parameter index is not in range. + + + Invalid constructor argument {0} provided for MarshalAs atttribute. + + + Named parameter {0} is not valid for UnmanagedType.{1} type. + + + SizeConst parameter must be specified for UnmanagedType.ByValTStr type. + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj index a647e5707b5cea..2e7f66ddc2a37c 100644 --- a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj +++ b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj @@ -10,6 +10,8 @@ + + diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs index 2026b79c74e263..01813788bf7716 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; namespace System.Reflection.Emit { @@ -171,7 +173,7 @@ private static Type ElementTypeToType(PrimitiveSerializationTypeCode elementType PrimitiveSerializationTypeCode.Single => typeof(float), PrimitiveSerializationTypeCode.Double => typeof(double), PrimitiveSerializationTypeCode.String => typeof(string), - _ => throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"), + _ => throw new ArgumentException(SR.Argument_InvalidTypeCodeForTypeArgument, "binaryAttribute"), }; } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index 3025818ef1a001..0e4fe230fb8cd2 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -19,7 +19,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder private readonly Type _fieldType; private FieldAttributes _attributes; - internal MarshallingInfo? _marshallingInfo; + internal MarshallingData? _marshallingData; internal int _offset; internal List? _customAttributes; @@ -41,19 +41,19 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan case "System.Runtime.InteropServices.FieldOffsetAttribute": Debug.Assert(binaryAttribute.Length >= 6); _offset = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(2)); - return; + return; case "System.NonSerializedAttribute": #pragma warning disable SYSLIB0050 // 'FieldAttributes.NotSerialized' is obsolete: 'Formatter-based serialization is obsolete and should not be used'. _attributes |= FieldAttributes.NotSerialized; #pragma warning restore SYSLIB0050 - return; + return; case "System.Runtime.CompilerServices.SpecialNameAttribute": _attributes |= FieldAttributes.SpecialName; - return; + return; case "System.Runtime.InteropServices.MarshalAsAttribute": _attributes |= FieldAttributes.HasFieldMarshal; - _marshallingInfo = MarshallingInfo.ParseMarshallingInfo(con, binaryAttribute); - return; + _marshallingData = MarshallingData.CreateMarshallingData(con, binaryAttribute, isField : true); + return; } _customAttributes ??= new List(); @@ -101,386 +101,5 @@ public override void SetValue(object? obj, object? val, BindingFlags invokeAttr, public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_DynamicModule); #endregion - - internal sealed class MarshallingInfo - { - internal UnmanagedType _marshalType; - private int _marshalArrayElementType; // safe array: VarEnum; array: UnmanagedType - private int _marshalArrayElementCount; // number of elements in an array, length of a string, or Unspecified - private int _marshalParameterIndex; // index of parameter that specifies array size (short) or IID (int), or Unspecified - private object? _marshalTypeNameOrSymbol; // custom marshaller: string or Type; safe array: element type - private string? _marshalCookie; - - internal const int Invalid = -1; - private const UnmanagedType InvalidUnmanagedType = (UnmanagedType)Invalid; - private const VarEnum InvalidVariantType = (VarEnum)Invalid; - internal const int MaxMarshalInteger = 0x1fffffff; - - internal BlobHandle PopulateMarshallingBlob(MetadataBuilder builder) - { - var blobBuilder = new BlobBuilder(); - SerializeMarshallingDescriptor(blobBuilder); - return builder.GetOrAddBlob(blobBuilder); - - } - - // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs#L3543 - internal void SerializeMarshallingDescriptor(BlobBuilder writer) - { - writer.WriteCompressedInteger((int)_marshalType); - switch (_marshalType) - { - case UnmanagedType.ByValArray: // NATIVE_TYPE_FIXEDARRAY - Debug.Assert(_marshalArrayElementCount >= 0); - writer.WriteCompressedInteger(_marshalArrayElementCount); - if (_marshalArrayElementType >= 0) - { - writer.WriteCompressedInteger(_marshalArrayElementType); - } - break; - case UnmanagedType.CustomMarshaler: - writer.WriteUInt16(0); // padding - - switch (_marshalTypeNameOrSymbol) - { - case Type type: - writer.WriteSerializedString(type.FullName); // or AssemblyQualifiedName? - break; - case null: - writer.WriteByte(0); - break; - default: - writer.WriteSerializedString((string)_marshalTypeNameOrSymbol); - break; - } - - if (_marshalCookie != null) - { - writer.WriteSerializedString(_marshalCookie); - } - else - { - writer.WriteByte(0); - } - break; - case UnmanagedType.LPArray: // NATIVE_TYPE_ARRAY - Debug.Assert(_marshalArrayElementType >= 0); - writer.WriteCompressedInteger(_marshalArrayElementType); - if (_marshalParameterIndex >= 0) - { - writer.WriteCompressedInteger(_marshalParameterIndex); - if (_marshalArrayElementCount >= 0) - { - writer.WriteCompressedInteger(_marshalArrayElementCount); - writer.WriteByte(1); // The parameter number is valid - } - } - else if (_marshalArrayElementCount >= 0) - { - writer.WriteByte(0); // Dummy parameter value emitted so that NumberOfElements can be in a known position - writer.WriteCompressedInteger(_marshalArrayElementCount); - writer.WriteByte(0); // The parameter number is not valid - } - break; - case UnmanagedType.SafeArray: - VarEnum safeArrayElementSubtype = (VarEnum)_marshalArrayElementType; - if (safeArrayElementSubtype >= 0) - { - writer.WriteCompressedInteger((int)safeArrayElementSubtype); - - if (_marshalTypeNameOrSymbol is Type elementType) - { - writer.WriteSerializedString(elementType.FullName); - } - } - break; - case UnmanagedType.ByValTStr: // NATIVE_TYPE_FIXEDSYSSTRING - writer.WriteCompressedInteger(_marshalArrayElementCount); - break; - - case UnmanagedType.Interface: - case UnmanagedType.IDispatch: - case UnmanagedType.IUnknown: - if (_marshalParameterIndex >= 0) - { - writer.WriteCompressedInteger(_marshalParameterIndex); - } - break; - } - } - - internal void SetMarshalAsCustom(object typeSymbolOrName, string? cookie) - { - _marshalType = UnmanagedType.CustomMarshaler; - _marshalTypeNameOrSymbol = typeSymbolOrName; - _marshalCookie = cookie; - } - - internal void SetMarshalAsComInterface(UnmanagedType unmanagedType, int? parameterIndex) - { - Debug.Assert(parameterIndex == null || parameterIndex >= 0 && parameterIndex <= MaxMarshalInteger); - - _marshalType = unmanagedType; - _marshalParameterIndex = parameterIndex ?? Invalid; - } - - internal void SetMarshalAsArray(UnmanagedType? elementType, int? elementCount, short? parameterIndex) - { - Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger); - Debug.Assert(parameterIndex == null || parameterIndex >= 0); - - _marshalType = UnmanagedType.LPArray; - _marshalArrayElementType = (int)(elementType ?? (UnmanagedType)0x50); - _marshalArrayElementCount = elementCount ?? Invalid; - _marshalParameterIndex = parameterIndex ?? Invalid; - } - - internal void SetMarshalAsFixedArray(UnmanagedType? elementType, int? elementCount) - { - Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger); - Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger); - - _marshalType = UnmanagedType.ByValArray; - _marshalArrayElementType = (int)(elementType ?? InvalidUnmanagedType); - _marshalArrayElementCount = elementCount ?? Invalid; - } - - internal void SetMarshalAsSafeArray(VarEnum? elementType, Type? type) - { - Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger); - - _marshalType = UnmanagedType.SafeArray; - _marshalArrayElementType = (int)(elementType ?? InvalidVariantType); - _marshalTypeNameOrSymbol = type; - } - - internal void SetMarshalAsFixedString(int elementCount) - { - Debug.Assert(elementCount >= 0 && elementCount <= MaxMarshalInteger); - - _marshalType = UnmanagedType.ByValTStr; - _marshalArrayElementCount = elementCount; - } - - internal void SetMarshalAsSimpleType(UnmanagedType type) - { - Debug.Assert(type >= 0 && (int)type <= MaxMarshalInteger); - _marshalType = type; - } - - // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/Symbols/Attributes/MarshalAsAttributeDecoder.cs - internal static MarshallingInfo ParseMarshallingInfo(ConstructorInfo con, ReadOnlySpan binaryAttribute) - { - CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute); - MarshallingInfo info = new(); - UnmanagedType unmanagedType; - - if (attributeInfo._ctorArgs[0] is short shortValue) - { - unmanagedType = (UnmanagedType)shortValue; - } - else - { - unmanagedType = (UnmanagedType)attributeInfo._ctorArgs[0]!; - } - - switch (unmanagedType) - { - case UnmanagedType.CustomMarshaler: - DecodeMarshalAsCustom(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); - break; - case UnmanagedType.Interface: - case UnmanagedType.IDispatch: - case UnmanagedType.IUnknown: - DecodeMarshalAsComInterface(attributeInfo._namedParamNames, attributeInfo._namedParamValues, unmanagedType, info); - break; - case UnmanagedType.LPArray: - DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: false, info); - break; - case UnmanagedType.ByValArray: - DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: true, info); - break; - case UnmanagedType.SafeArray: - DecodeMarshalAsSafeArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); - break; - case UnmanagedType.ByValTStr: - DecodeMarshalAsFixedString(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); - break; -#pragma warning disable CS0618 // Type or member is obsolete - case UnmanagedType.VBByRefStr: -#pragma warning restore CS0618 - // named parameters ignored with no error - info.SetMarshalAsSimpleType(unmanagedType); - break; - default: - if ((int)unmanagedType < 0 || (int)unmanagedType > MaxMarshalInteger) - { - throw new ArgumentException(SR.Argument_InvalidTypeArgument, nameof(binaryAttribute)); - } - else - { - // named parameters ignored with no error - info.SetMarshalAsSimpleType(unmanagedType); - } - break; - } - - return info; - } - - private static void DecodeMarshalAsFixedString(string[] paramNames, object?[] values, MarshallingInfo info) - { - int elementCount = -1; - - for (int i = 0; i < paramNames.Length; i++) - { - switch (paramNames[i]) - { - case "SizeConst": - elementCount = (int)values[i]!; - break; - case "ArraySubType": - case "SizeParamIndex": - throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); - // other parameters ignored with no error - } - } - - if (elementCount < 0) - { - // SizeConst must be specified: - throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); - } - - info.SetMarshalAsFixedString(elementCount); - } - - private static void DecodeMarshalAsSafeArray(string[] paramNames, object?[] values, MarshallingInfo info) - { - VarEnum? elementTypeVariant = null; - Type? elementType = null; - int symbolIndex = -1; - - for (int i = 0; i < paramNames.Length; i++) - { - switch (paramNames[i]) - { - case "SafeArraySubType": - elementTypeVariant = (VarEnum)values[i]!; - break; - case "SafeArrayUserDefinedSubType": - elementType = (Type?)values[i]; - symbolIndex = i; - break; - case "ArraySubType": - case "SizeConst": - case "SizeParamIndex": - throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); - // other parameters ignored with no error - } - } - - switch (elementTypeVariant) - { - case VarEnum.VT_DISPATCH: - case VarEnum.VT_UNKNOWN: - case VarEnum.VT_RECORD: - // only these variants accept specification of user defined subtype - break; - - default: - if (elementTypeVariant != null && symbolIndex >= 0) - { - throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); - } - else - { - // type ignored: - elementType = null; - } - break; - } - - info.SetMarshalAsSafeArray(elementTypeVariant, elementType); - } - - private static void DecodeMarshalAsArray(string[] paramNames, object?[] values, bool isFixed, MarshallingInfo info) - { - UnmanagedType? elementType = null; - int? elementCount = isFixed ? 1 : null; - short? parameterIndex = null; - - for (int i = 0; i < paramNames.Length; i++) - { - switch (paramNames[i]) - { - case "ArraySubType": - elementType = (UnmanagedType)values[i]!; - break; - case "SizeConst": - elementCount = (int?)values[i]; - break; - case "SizeParamIndex": - if (isFixed) - { - goto case "SafeArraySubType"; - } - parameterIndex = (short?)values[i]; - break; - case "SafeArraySubType": - throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); - // other parameters ignored with no error - } - } - - if (isFixed) - { - info.SetMarshalAsFixedArray(elementType, elementCount); - } - else - { - info.SetMarshalAsArray(elementType, elementCount, parameterIndex); - } - } - - private static void DecodeMarshalAsComInterface(string[] paramNames, object?[] values, UnmanagedType unmanagedType, MarshallingInfo info) - { - int? parameterIndex = null; - for (int i = 0; i < paramNames.Length; i++) - { - if (paramNames[i] == "IidParameterIndex") - { - parameterIndex = (int?)values[i]; - break; - } - } - info.SetMarshalAsComInterface(unmanagedType, parameterIndex); - } - - private static void DecodeMarshalAsCustom(string[] paramNames, object?[] values, MarshallingInfo info) - { - string? cookie = null; - Type? type = null; - string? name = null; - for (int i = 0; i < paramNames.Length; i++) - { - switch (paramNames[i]) - { - case "MarshalType": - name = (string?)values[i]; - break; - case "MarshalTypeRef": - type = (Type?)values[i]; - break; - case "MarshalCookie": - cookie = (string?)values[i]; - break; - // other parameters ignored with no error - } - } - - info.SetMarshalAsCustom((object?)name ?? type!, cookie); - } - } } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 4f45898d62890d..99cdaceeb9f8cf 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -3,6 +3,7 @@ using System.Buffers.Binary; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection.Metadata; @@ -23,6 +24,7 @@ internal sealed class MethodBuilderImpl : MethodBuilder internal DllImportData? _dllImportData; internal List? _customAttributes; + internal ParameterBuilderImpl[]? _parameters; internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType, Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType) @@ -37,6 +39,7 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv if (parameterTypes != null) { _parameterTypes = new Type[parameterTypes.Length]; + _parameters = new ParameterBuilderImpl[parameterTypes.Length + 1]; // parameter 0 reserved for return type for (int i = 0; i < parameterTypes.Length; i++) { ArgumentNullException.ThrowIfNull(_parameterTypes[i] = parameterTypes[i], nameof(parameterTypes)); @@ -51,7 +54,18 @@ internal BlobBuilder GetMethodSignatureBlob() => protected override bool InitLocalsCore { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(params string[] names) => throw new NotImplementedException(); - protected override ParameterBuilder DefineParameterCore(int position, ParameterAttributes attributes, string? strParamName) => throw new NotImplementedException(); + protected override ParameterBuilder DefineParameterCore(int position, ParameterAttributes attributes, string? strParamName) + { + if (position > 0 && (_parameterTypes == null || position > _parameterTypes.Length)) + throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_ParamSequence); + + _parameters ??= new ParameterBuilderImpl[1]; + + attributes &= ~ParameterAttributes.ReservedMask; + ParameterBuilderImpl parameter = new ParameterBuilderImpl(this, position, attributes, strParamName); + _parameters[position] = parameter; + return parameter; + } protected override ILGenerator GetILGeneratorCore(int size) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { @@ -142,103 +156,4 @@ public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? bind public override MethodInfo MakeGenericMethod(params System.Type[] typeArguments) => throw new NotImplementedException(); } - - internal sealed class DllImportData - { - private readonly string _moduleName; - private readonly string? _entryPoint; - private readonly MethodImportAttributes _flags; - internal DllImportData(string moduleName, string? entryPoint, MethodImportAttributes flags) - { - _moduleName = moduleName; - _entryPoint = entryPoint; - _flags = flags; - } - - public string ModuleName => _moduleName; - - public string? EntryPoint => _entryPoint; - - public MethodImportAttributes Flags => _flags; - - internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out bool preserveSig) - { - string? moduleName = (string?)attr._ctorArgs[0]; - if (moduleName == null || moduleName.Length == 0) - { - throw new ArgumentException(SR.Argument_DllNameCannotBeEmpty); - } - - MethodImportAttributes importAttributes = MethodImportAttributes.None; - string? entryPoint = null; - preserveSig = true; - for (int i = 0; i < attr._namedParamNames.Length; ++i) - { - string name = attr._namedParamNames[i]; - object value = attr._namedParamValues[i]!; - switch (name) - { - case "PreserveSig": - preserveSig = (bool)value; - break; - case "CallingConvention": - importAttributes |= (CallingConvention)value switch - { - CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl, - CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall, - CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall, - CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall, - _=> MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this - }; - break; - case "CharSet": - importAttributes |= (CharSet)value switch - { - CharSet.Ansi => MethodImportAttributes.CharSetAnsi, - CharSet.Auto => MethodImportAttributes.CharSetAuto, - CharSet.Unicode => MethodImportAttributes.CharSetUnicode, - _ => MethodImportAttributes.CharSetAuto - }; - break; - case "EntryPoint": - entryPoint = (string?)value; - break; - case "ExactSpelling": - if ((bool)value) - { - importAttributes |= MethodImportAttributes.ExactSpelling; - } - break; - case "SetLastError": - if ((bool)value) - { - importAttributes |= MethodImportAttributes.SetLastError; - } - break; - case "BestFitMapping": - if ((bool)value) - { - importAttributes |= MethodImportAttributes.BestFitMappingEnable; - } - else - { - importAttributes |= MethodImportAttributes.BestFitMappingDisable; - } - break; - case "ThrowOnUnmappableChar": - if ((bool)value) - { - importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharEnable; - } - else - { - importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharDisable; - } - break; - } - } - - return new DllImportData(moduleName, entryPoint, importAttributes); - } - } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 775a5f647a5328..f18aace4f14afe 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -24,6 +24,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private int _nextTypeDefRowId = 1; private int _nextMethodDefRowId = 1; private int _nextFieldDefRowId = 1; + private int _nextParameterRowId = 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), @@ -133,10 +134,33 @@ internal void AppendMetadata() foreach (MethodBuilderImpl method in typeBuilder._methodDefinitions) { - MethodDefinitionHandle methodHandle = AddMethodDefinition(method, method.GetMethodSignatureBlob()); + MethodDefinitionHandle methodHandle = AddMethodDefinition(method, method.GetMethodSignatureBlob(), _nextParameterRowId); WriteCustomAttributes(method._customAttributes, methodHandle); _nextMethodDefRowId++; + if (method._parameters != null) + { + foreach (ParameterBuilderImpl parameter in method._parameters) + { + if (parameter != null) + { + ParameterHandle parameterHandle = AddParameter(parameter); + WriteCustomAttributes(parameter._customAttributes, parameterHandle); + _nextParameterRowId++; + + if (parameter._marshallingData != null) + { + AddMarshalling(parameterHandle, parameter._marshallingData.SerializeMarshallingData()); + } + + if (parameter._defaultValue != DBNull.Value) + { + AddDefaultValue(parameterHandle, parameter._defaultValue); + } + } + } + } + if (method._dllImportData != null) { AddMethodImport(methodHandle, method._dllImportData.EntryPoint ?? method.Name, @@ -155,9 +179,9 @@ internal void AppendMetadata() AddFieldLayout(fieldHandle, field._offset); } - if (field._marshallingInfo != null) + if (field._marshallingData != null) { - AddFieldMarshalling(fieldHandle, field._marshallingInfo.PopulateMarshallingBlob(_metadataBuilder)); + AddMarshalling(fieldHandle, field._marshallingData.SerializeMarshallingData()); } } } @@ -223,6 +247,9 @@ private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly) return handle; } + private void AddDefaultValue(ParameterHandle parameterHandle, object? defaultValue) => + _metadataBuilder.AddConstant(parameterHandle, defaultValue); + private FieldDefinitionHandle AddFieldDefinition(FieldBuilderImpl field, BlobBuilder fieldSignature) => _metadataBuilder.AddFieldDefinition( attributes: field.Attributes, @@ -238,14 +265,14 @@ private TypeDefinitionHandle AddTypeDefinition(TypeBuilderImpl type, EntityHandl fieldList: MetadataTokens.FieldDefinitionHandle(fieldToken), methodList: MetadataTokens.MethodDefinitionHandle(methodToken)); - private MethodDefinitionHandle AddMethodDefinition(MethodBuilderImpl method, BlobBuilder methodSignature) => + private MethodDefinitionHandle AddMethodDefinition(MethodBuilderImpl method, BlobBuilder methodSignature, int parameterToken) => _metadataBuilder.AddMethodDefinition( attributes: method.Attributes, implAttributes: method.GetMethodImplementationFlags(), name: _metadataBuilder.GetOrAddString(method.Name), signature: _metadataBuilder.GetOrAddBlob(methodSignature), bodyOffset: -1, // No body supported yet - parameterList: MetadataTokens.ParameterHandle(1)); + parameterList: MetadataTokens.ParameterHandle(parameterToken)); private TypeReferenceHandle AddTypeReference(Type type, AssemblyReferenceHandle parent) => _metadataBuilder.AddTypeReference( @@ -276,10 +303,14 @@ private ModuleReferenceHandle AddModuleReference(string moduleName) => private void AddFieldLayout(FieldDefinitionHandle fieldHandle, int offset) => _metadataBuilder.AddFieldLayout(field: fieldHandle, offset: offset); - private void AddFieldMarshalling(FieldDefinitionHandle fieldHandle, BlobHandle descriptor) - { - _metadataBuilder.AddMarshallingDescriptor(fieldHandle, descriptor); - } + private void AddMarshalling(EntityHandle fieldHandle, BlobBuilder builder) => + _metadataBuilder.AddMarshallingDescriptor(fieldHandle, _metadataBuilder.GetOrAddBlob(builder)); + + private ParameterHandle AddParameter(ParameterBuilderImpl parameter) => + _metadataBuilder.AddParameter( + attributes: (ParameterAttributes)parameter.Attributes, + name: parameter.Name != null ? _metadataBuilder.GetOrAddString(parameter.Name) : default, + sequenceNumber: parameter.Position); private AssemblyReferenceHandle AddAssemblyReference(string name, Version? version, string? culture, byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) => diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ParameterBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ParameterBuilderImpl.cs new file mode 100644 index 00000000000000..4d64a644ab47e9 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ParameterBuilderImpl.cs @@ -0,0 +1,63 @@ +// 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; + +namespace System.Reflection.Emit +{ + internal sealed class ParameterBuilderImpl : ParameterBuilder + { + private readonly string? _name; + private readonly int _position; + private readonly MethodBuilderImpl _methodBuilder; + private ParameterAttributes _attributes; + + internal List? _customAttributes; + internal MarshallingData? _marshallingData; + internal object? _defaultValue = DBNull.Value; + + public ParameterBuilderImpl(MethodBuilderImpl methodBuilder, int sequence, ParameterAttributes attributes, string? paramName) + { + _position = sequence; + _name = paramName; + _methodBuilder = methodBuilder; + _attributes = attributes; + } + + public override int Attributes => (int)_attributes; + + public override string? Name => _name; + + public override int Position => _position; + + public override void SetConstant(object? defaultValue) => _defaultValue = defaultValue; + + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + switch (con.ReflectedType!.FullName) + { + case "System.Runtime.InteropServices.InAttribute": + _attributes |= ParameterAttributes.In; + return; + case "System.Runtime.InteropServices.OutAttribute": + _attributes |= ParameterAttributes.Out; + return; + case "System.Runtime.InteropServices.OptionalAttribute": + _attributes |= ParameterAttributes.Optional; + return; + case "System.Runtime.InteropServices.MarshalAsAttribute": + _attributes |= ParameterAttributes.HasFieldMarshal; + _marshallingData = MarshallingData.CreateMarshallingData(con, binaryAttribute, isField: false); + return; + case "System.Runtime.InteropServices.DefaultParameterValueAttribute": + // MS.NET doesn't handle this attribute but we handle it for consistency TODO: not sure if we need to handle this + CustomAttributeInfo caInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute); + SetConstant(caInfo._ctorArgs[0]); + return; + } + + _customAttributes ??= new List(); + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } + } +} diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs new file mode 100644 index 00000000000000..93dd864ccedf10 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs @@ -0,0 +1,495 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Runtime.InteropServices; + +namespace System.Reflection.Emit +{ + internal sealed class DllImportData + { + private readonly string _moduleName; + private readonly string? _entryPoint; + private readonly MethodImportAttributes _flags; + + internal DllImportData(string moduleName, string? entryPoint, MethodImportAttributes flags) + { + _moduleName = moduleName; + _entryPoint = entryPoint; + _flags = flags; + } + + public string ModuleName => _moduleName; + + public string? EntryPoint => _entryPoint; + + public MethodImportAttributes Flags => _flags; + + internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out bool preserveSig) + { + string? moduleName = (string?)attr._ctorArgs[0]; + if (moduleName == null || moduleName.Length == 0) + { + throw new ArgumentException(SR.Argument_DllNameCannotBeEmpty); + } + + MethodImportAttributes importAttributes = MethodImportAttributes.None; + string? entryPoint = null; + preserveSig = true; + for (int i = 0; i < attr._namedParamNames.Length; ++i) + { + string name = attr._namedParamNames[i]; + object value = attr._namedParamValues[i]!; + switch (name) + { + case "PreserveSig": + preserveSig = (bool)value; + break; + case "CallingConvention": + importAttributes |= (CallingConvention)value switch + { + CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl, + CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall, + CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall, + CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall, + _ => MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this + }; + break; + case "CharSet": + importAttributes |= (CharSet)value switch + { + CharSet.Ansi => MethodImportAttributes.CharSetAnsi, + CharSet.Auto => MethodImportAttributes.CharSetAuto, + CharSet.Unicode => MethodImportAttributes.CharSetUnicode, + _ => MethodImportAttributes.CharSetAuto + }; + break; + case "EntryPoint": + entryPoint = (string?)value; + break; + case "ExactSpelling": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.ExactSpelling; + } + break; + case "SetLastError": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.SetLastError; + } + break; + case "BestFitMapping": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.BestFitMappingEnable; + } + else + { + importAttributes |= MethodImportAttributes.BestFitMappingDisable; + } + break; + case "ThrowOnUnmappableChar": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharEnable; + } + else + { + importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharDisable; + } + break; + } + } + + return new DllImportData(moduleName, entryPoint, importAttributes); + } + } + + internal sealed class MarshallingData + { + private UnmanagedType _marshalType; + private int _marshalArrayElementType; // safe array: VarEnum; array: UnmanagedType + private int _marshalArrayElementCount; // number of elements in an array, length of a string, or Unspecified + private int _marshalParameterIndex; // index of parameter that specifies array size (short) or IID (int), or Unspecified + private object? _marshalTypeNameOrSymbol; // custom marshaller: string or Type; safe array: element type + private string? _marshalCookie; + + internal const int Invalid = -1; + private const UnmanagedType InvalidUnmanagedType = (UnmanagedType)Invalid; + private const VarEnum InvalidVariantType = (VarEnum)Invalid; + private const int MaxMarshalInteger = 0x1fffffff; + + // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs#L3543 + internal BlobBuilder SerializeMarshallingData() + { + BlobBuilder writer = new BlobBuilder(); ; + writer.WriteCompressedInteger((int)_marshalType); + switch (_marshalType) + { + case UnmanagedType.ByValArray: // NATIVE_TYPE_FIXEDARRAY + Debug.Assert(_marshalArrayElementCount >= 0); + writer.WriteCompressedInteger(_marshalArrayElementCount); + if (_marshalArrayElementType >= 0) + { + writer.WriteCompressedInteger(_marshalArrayElementType); + } + break; + case UnmanagedType.CustomMarshaler: + writer.WriteUInt16(0); // padding + + switch (_marshalTypeNameOrSymbol) + { + case Type type: + writer.WriteSerializedString(type.FullName); // or AssemblyQualifiedName? + break; + case null: + writer.WriteByte(0); + break; + default: + writer.WriteSerializedString((string)_marshalTypeNameOrSymbol); + break; + } + + if (_marshalCookie != null) + { + writer.WriteSerializedString(_marshalCookie); + } + else + { + writer.WriteByte(0); + } + break; + case UnmanagedType.LPArray: // NATIVE_TYPE_ARRAY + Debug.Assert(_marshalArrayElementType >= 0); + writer.WriteCompressedInteger(_marshalArrayElementType); + if (_marshalParameterIndex >= 0) + { + writer.WriteCompressedInteger(_marshalParameterIndex); + if (_marshalArrayElementCount >= 0) + { + writer.WriteCompressedInteger(_marshalArrayElementCount); + writer.WriteByte(1); // The parameter number is valid + } + } + else if (_marshalArrayElementCount >= 0) + { + writer.WriteByte(0); // Dummy parameter value emitted so that NumberOfElements can be in a known position + writer.WriteCompressedInteger(_marshalArrayElementCount); + writer.WriteByte(0); // The parameter number is not valid + } + break; + case UnmanagedType.SafeArray: + VarEnum safeArrayElementSubtype = (VarEnum)_marshalArrayElementType; + if (safeArrayElementSubtype >= 0) + { + writer.WriteCompressedInteger((int)safeArrayElementSubtype); + + if (_marshalTypeNameOrSymbol is Type elementType) + { + writer.WriteSerializedString(elementType.FullName); + } + } + break; + case UnmanagedType.ByValTStr: // NATIVE_TYPE_FIXEDSYSSTRING + writer.WriteCompressedInteger(_marshalArrayElementCount); + break; + + case UnmanagedType.Interface: + case UnmanagedType.IDispatch: + case UnmanagedType.IUnknown: + if (_marshalParameterIndex >= 0) + { + writer.WriteCompressedInteger(_marshalParameterIndex); + } + break; + } + + return writer; + } + + internal void SetMarshalAsCustom(object typeSymbolOrName, string? cookie) + { + _marshalType = UnmanagedType.CustomMarshaler; + _marshalTypeNameOrSymbol = typeSymbolOrName; + _marshalCookie = cookie; + } + + internal void SetMarshalAsComInterface(UnmanagedType unmanagedType, int? parameterIndex) + { + Debug.Assert(parameterIndex == null || parameterIndex >= 0 && parameterIndex <= MaxMarshalInteger); + + _marshalType = unmanagedType; + _marshalParameterIndex = parameterIndex ?? Invalid; + } + + internal void SetMarshalAsArray(UnmanagedType? elementType, int? elementCount, short? parameterIndex) + { + Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger); + Debug.Assert(parameterIndex == null || parameterIndex >= 0); + + _marshalType = UnmanagedType.LPArray; + _marshalArrayElementType = (int)(elementType ?? (UnmanagedType)0x50); + _marshalArrayElementCount = elementCount ?? Invalid; + _marshalParameterIndex = parameterIndex ?? Invalid; + } + + internal void SetMarshalAsFixedArray(UnmanagedType? elementType, int? elementCount) + { + Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger); + Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger); + + _marshalType = UnmanagedType.ByValArray; + _marshalArrayElementType = (int)(elementType ?? InvalidUnmanagedType); + _marshalArrayElementCount = elementCount ?? Invalid; + } + + internal void SetMarshalAsSafeArray(VarEnum? elementType, Type? type) + { + Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger); + + _marshalType = UnmanagedType.SafeArray; + _marshalArrayElementType = (int)(elementType ?? InvalidVariantType); + _marshalTypeNameOrSymbol = type; + } + + internal void SetMarshalAsFixedString(int elementCount) + { + Debug.Assert(elementCount >= 0 && elementCount <= MaxMarshalInteger); + + _marshalType = UnmanagedType.ByValTStr; + _marshalArrayElementCount = elementCount; + } + + internal void SetMarshalAsSimpleType(UnmanagedType type) + { + Debug.Assert(type >= 0 && (int)type <= MaxMarshalInteger); + _marshalType = type; + } + + // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/Symbols/Attributes/MarshalAsAttributeDecoder.cs + internal static MarshallingData CreateMarshallingData(ConstructorInfo con, ReadOnlySpan binaryAttribute, bool isField) + { + CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute); + MarshallingData info = new(); + UnmanagedType unmanagedType; + + if (attributeInfo._ctorArgs[0] is short shortValue) + { + unmanagedType = (UnmanagedType)shortValue; + } + else + { + unmanagedType = (UnmanagedType)attributeInfo._ctorArgs[0]!; + } + + switch (unmanagedType) + { + case UnmanagedType.CustomMarshaler: + DecodeMarshalAsCustom(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); + break; + case UnmanagedType.Interface: + case UnmanagedType.IDispatch: + case UnmanagedType.IUnknown: + DecodeMarshalAsComInterface(attributeInfo._namedParamNames, attributeInfo._namedParamValues, unmanagedType, info); + break; + case UnmanagedType.LPArray: + DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: false, info); + break; + case UnmanagedType.ByValArray: + if (!isField) + { + throw new NotSupportedException(SR.Format(SR.NotSupported_UnmanagedTypeOnlyForFields, nameof(UnmanagedType.ByValArray))); + } + DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: true, info); + break; + case UnmanagedType.SafeArray: + DecodeMarshalAsSafeArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); + break; + case UnmanagedType.ByValTStr: + if (!isField) + { + throw new NotSupportedException(SR.Format(SR.NotSupported_UnmanagedTypeOnlyForFields, nameof(UnmanagedType.ByValArray))); + } + DecodeMarshalAsFixedString(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); + break; +#pragma warning disable CS0618 // Type or member is obsolete + case UnmanagedType.VBByRefStr: +#pragma warning restore CS0618 + // named parameters ignored with no error + info.SetMarshalAsSimpleType(unmanagedType); + break; + default: + if ((int)unmanagedType < 0 || (int)unmanagedType > MaxMarshalInteger) + { + throw new ArgumentException(SR.Argument_InvalidArgumentForAttribute, nameof(con)); + } + else + { + // named parameters ignored with no error + info.SetMarshalAsSimpleType(unmanagedType); + } + break; + } + + return info; + } + + private static void DecodeMarshalAsFixedString(string[] paramNames, object?[] values, MarshallingData info) + { + int elementCount = -1; + + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "SizeConst": + elementCount = (int)values[i]!; + break; + case "ArraySubType": + case "SizeParamIndex": + throw new ArgumentException(SR.Format(SR.Argument_InvalidParameterForUnmanagedType, paramNames[i], "ByValTStr"), "binaryAttribute"); + // other parameters ignored with no error + } + } + + if (elementCount < 0) + { + // SizeConst must be specified: + throw new ArgumentException(SR.Argument_SizeConstMustBeSpecified, "binaryAttribute"); + } + + info.SetMarshalAsFixedString(elementCount); + } + + private static void DecodeMarshalAsSafeArray(string[] paramNames, object?[] values, MarshallingData info) + { + VarEnum? elementTypeVariant = null; + Type? elementType = null; + int symbolIndex = -1; + + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "SafeArraySubType": + elementTypeVariant = (VarEnum)values[i]!; + break; + case "SafeArrayUserDefinedSubType": + elementType = (Type?)values[i]; + symbolIndex = i; + break; + case "ArraySubType": + case "SizeConst": + case "SizeParamIndex": + throw new ArgumentException(SR.Format(SR.Argument_InvalidParameterForUnmanagedType, paramNames[i], "SafeArray"), "binaryAttribute"); + // other parameters ignored with no error + } + } + + switch (elementTypeVariant) + { + case VarEnum.VT_DISPATCH: + case VarEnum.VT_UNKNOWN: + case VarEnum.VT_RECORD: + // only these variants accept specification of user defined subtype + break; + + default: + if (elementTypeVariant != null && symbolIndex >= 0) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidParameterForUnmanagedType, elementType, "SafeArray"), "binaryAttribute"); + } + else + { + // type ignored: + elementType = null; + } + break; + } + + info.SetMarshalAsSafeArray(elementTypeVariant, elementType); + } + + private static void DecodeMarshalAsArray(string[] paramNames, object?[] values, bool isFixed, MarshallingData info) + { + UnmanagedType? elementType = null; + int? elementCount = isFixed ? 1 : null; + short? parameterIndex = null; + + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "ArraySubType": + elementType = (UnmanagedType)values[i]!; + break; + case "SizeConst": + elementCount = (int?)values[i]; + break; + case "SizeParamIndex": + if (isFixed) + { + goto case "SafeArraySubType"; + } + parameterIndex = (short?)values[i]; + break; + case "SafeArraySubType": + throw new ArgumentException(SR.Format(SR.Argument_InvalidParameterForUnmanagedType, + paramNames[i], isFixed ? "ByValArray" : "LPArray"), "binaryAttribute"); + // other parameters ignored with no error + } + } + + if (isFixed) + { + info.SetMarshalAsFixedArray(elementType, elementCount); + } + else + { + info.SetMarshalAsArray(elementType, elementCount, parameterIndex); + } + } + + private static void DecodeMarshalAsComInterface(string[] paramNames, object?[] values, UnmanagedType unmanagedType, MarshallingData info) + { + int? parameterIndex = null; + for (int i = 0; i < paramNames.Length; i++) + { + if (paramNames[i] == "IidParameterIndex") + { + parameterIndex = (int?)values[i]; + break; + } + } + + info.SetMarshalAsComInterface(unmanagedType, parameterIndex); + } + + private static void DecodeMarshalAsCustom(string[] paramNames, object?[] values, MarshallingData info) + { + string? cookie = null; + Type? type = null; + string? name = null; + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "MarshalType": + name = (string?)values[i]; + break; + case "MarshalTypeRef": + type = (Type?)values[i]; + break; + case "MarshalCookie": + cookie = (string?)values[i]; + break; + // other parameters ignored with no error + } + } + + info.SetMarshalAsCustom((object?)name ?? type!, cookie); + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs index 0f1d3c929b3e36..79b9e77bfdf140 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -138,7 +138,7 @@ private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types, Lis TypeBuilder tb = mb.DefineType(type.FullName, type.Attributes, type.BaseType); typeAttributes.ForEach(tb.SetCustomAttribute); - DefineMethodsAndSetAttributes(methodAttributes, tb, type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly)); + DefineMethodsAndSetAttributes(methodAttributes, tb, type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly), methodAttributes); DefineFieldsAndSetAttributes(fieldAttributes, type.GetFields(), tb); } } @@ -152,12 +152,23 @@ private static void DefineFieldsAndSetAttributes(List? f } } - private static void DefineMethodsAndSetAttributes(List methodAttributes, TypeBuilder tb, MethodInfo[] methods) + private static void DefineMethodsAndSetAttributes(List methodAttributes, TypeBuilder tb, MethodInfo[] methods, List paramAttributes) { foreach (var method in methods) { - MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, null); + ParameterInfo[] parameters = method.GetParameters(); + MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray()); methodAttributes.ForEach(meb.SetCustomAttribute); + + foreach (ParameterInfo param in parameters) + { + ParameterBuilder pb = meb.DefineParameter(param.Position + 1, param.Attributes, param.Name); + paramAttributes.ForEach(pb.SetCustomAttribute); + if (param.ParameterType.Equals(typeof(string))) + { + pb.SetConstant("Hello"); + } + } } } @@ -247,26 +258,32 @@ public void InterfacesWithPseudoCustomAttributes() { Type dllType = typeof(DllImportAttribute); Type type = typeof(IMultipleMethod); - List typeAttributes = new() { new CustomAttributeBuilder(typeof(ComImportAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), - new CustomAttributeBuilder(typeof(SuppressUnmanagedCodeSecurityAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), - new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args) - }; - CustomAttributeBuilder[] methodAttributes = new[] { new CustomAttributeBuilder(typeof(PreserveSigAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), - new CustomAttributeBuilder(typeof(SuppressUnmanagedCodeSecurityAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), - new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), - new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args), - new CustomAttributeBuilder(typeof(MethodImplAttribute).GetConstructor(new Type[] { typeof(MethodImplOptions) }), - new object[] { MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization }), - new CustomAttributeBuilder(dllType.GetConstructor(new Type[] { typeof(string) }), new object[] { "test.dll" }, - new FieldInfo[] { dllType.GetField("CharSet"), dllType.GetField("SetLastError"), dllType.GetField("CallingConvention"), dllType.GetField("BestFitMapping"), - dllType.GetField("ThrowOnUnmappableChar") }, new object[]{ CharSet.Ansi, true, CallingConvention.FastCall, true, false }), - }; - - AssemblyBuilder ab = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( - PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); + List typeAttributes = new() { + new CustomAttributeBuilder(typeof(ComImportAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(typeof(SuppressUnmanagedCodeSecurityAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args)}; + List methodAttributes = new() { + new CustomAttributeBuilder(typeof(PreserveSigAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(typeof(SuppressUnmanagedCodeSecurityAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args), + new CustomAttributeBuilder(typeof(MethodImplAttribute).GetConstructor(new Type[] { typeof(MethodImplOptions) }), + new object[] { MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization }), + new CustomAttributeBuilder(dllType.GetConstructor(new Type[] { typeof(string) }), new object[] { "test.dll" }, new FieldInfo[] + { dllType.GetField("CharSet"), dllType.GetField("SetLastError"), dllType.GetField("CallingConvention"), dllType.GetField("BestFitMapping"), + dllType.GetField("ThrowOnUnmappableChar") }, new object[]{ CharSet.Ansi, true, CallingConvention.FastCall, true, false })}; + List parameterAttributes = new() { + new CustomAttributeBuilder(typeof(InAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(typeof(OutAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(typeof(OptionalAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args), + new CustomAttributeBuilder(marshalAsEnumCtor, new object[] { UnmanagedType.CustomMarshaler }, + new FieldInfo[] { typeof(MarshalAsAttribute).GetField("MarshalType")}, new object[] { typeof(EmptyTestClass).AssemblyQualifiedName })}; + + AssemblyBuilder ab = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod(PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes); typeAttributes.ForEach(tb.SetCustomAttribute); - DefineMethodsAndSetAttributes(methodAttributes.ToList(), tb, type.GetMethods()); + DefineMethodsAndSetAttributes(methodAttributes, tb, type.GetMethods(), parameterAttributes); saveMethod.Invoke(ab, new object[] { file.Path }); Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); @@ -302,7 +319,7 @@ public void InterfacesWithPseudoCustomAttributes() Assert.True((methodImpl & MethodImplAttributes.NoInlining) != 0); // MethodImplAttribute Assert.True((methodImpl & MethodImplAttributes.AggressiveOptimization) != 0); // MethodImplAttribute Assert.True((methodImpl & MethodImplAttributes.PreserveSig) != 0); // PreserveSigAttribute - Assert.Equal(methodAttributes.Length - 2, methodAttributesFromDisk.Count); + Assert.Equal(methodAttributes.Count - 2, methodAttributesFromDisk.Count); for (int i = 0; i < methodAttributesFromDisk.Count; i++) { @@ -347,6 +364,44 @@ public void InterfacesWithPseudoCustomAttributes() break; } } + + foreach(ParameterInfo param in method.GetParameters()) + { + IList paramAttributes = param.GetCustomAttributesData(); + + Assert.Equal(5, paramAttributes.Count); + Assert.True((param.Attributes & ParameterAttributes.In) != 0); // InAttribute + Assert.True((param.Attributes & ParameterAttributes.Out) != 0); // OutAttribute + Assert.True((param.Attributes & ParameterAttributes.Optional) != 0); // OptionalAttribute + Assert.True((param.Attributes & ParameterAttributes.HasFieldMarshal) != 0); // MarshalAsAttribute + + if (param.ParameterType.Equals(typeof(string))) + { + Assert.Equal("Hello", param.DefaultValue); + } + + for (int i = 0; i < paramAttributes.Count; i++) + { + switch (paramAttributes[i].AttributeType.Name) + { + case "InAttribute": + case "OutAttribute": + case "OptionalAttribute": + break; + case "MarshalAsAttribute": + Assert.Equal(UnmanagedType.CustomMarshaler, (UnmanagedType)paramAttributes[i].ConstructorArguments[0].Value); + Assert.Equal(typeof(EmptyTestClass).AssemblyQualifiedName, + paramAttributes[i].NamedArguments.First(na => na.MemberName == "MarshalType").TypedValue.Value); + break; + case "GuidAttribute": + Assert.Equal(s_guidPair.args[0], paramAttributes[i].ConstructorArguments[0].Value); + break; + default: + Assert.Fail($"Not expected attribute : {paramAttributes[i].AttributeType.Name}"); + break; + } + } + } } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs index bf256d909eb847..df89812f1c62db 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using Xunit; namespace System.Reflection.Emit.Tests @@ -129,21 +131,21 @@ public interface INoMethod public interface IMultipleMethod { - string Func(); + string Func(int a, string b); IOneMethod MoreFunc(); - StructWithFields DoIExist(); + StructWithFields DoIExist(int a, string b, bool c); void BuildAPerpetualMotionMachine(); } internal interface IAccess { - public Version BuildAI(); + public Version BuildAI(double field); public int DisableRogueAI(); } public interface IOneMethod { - object Func(); + object Func(string a, short b); } public struct EmptyStruct diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs index 0cb5fe311d37a2..4ec230e52bc8b1 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using Xunit; @@ -30,7 +31,12 @@ private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types) MethodInfo[] methods = type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly); foreach (var method in methods) { - MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, null); + ParameterInfo[] parameters = method.GetParameters(); + MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray()); + foreach(ParameterInfo param in parameters) + { + meb.DefineParameter(param.Position + 1, param.Attributes, param.Name); + } } foreach (FieldInfo field in type.GetFields()) @@ -122,6 +128,20 @@ internal static void AssertMethods(MethodInfo[] sourceMethods, MethodInfo[] meth Assert.Equal(sourceMethod.Name, methodFromDisk.Name); Assert.Equal(sourceMethod.Attributes, methodFromDisk.Attributes); Assert.Equal(sourceMethod.ReturnType.FullName, methodFromDisk.ReturnType.FullName); + AssertParameters(sourceMethod.GetParameters(), methodFromDisk.GetParameters()); + } + } + + private static void AssertParameters(ParameterInfo[] sourceParameters, ParameterInfo[] parametersLoaded) + { + Assert.Equal(sourceParameters.Length, parametersLoaded.Length); + + for (int i = 0; i < sourceParameters.Length; i++) + { + Assert.Equal(sourceParameters[i].Name, parametersLoaded[i].Name); + Assert.Equal(sourceParameters[i].ParameterType.FullName, parametersLoaded[i].ParameterType.FullName); + Assert.Equal(sourceParameters[i].Attributes, parametersLoaded[i].Attributes); + Assert.Equal(sourceParameters[i].Position, parametersLoaded[i].Position); } } } diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 1cc7d38b44fb1c..a76cd02986ccac 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -227,7 +227,6 @@ - @@ -236,6 +235,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs index 83c62a8e9817ac..ecfae6b04ab4bf 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs @@ -222,7 +222,7 @@ protected override ParameterBuilder DefineParameterCore(int iSequence, Parameter if (type.is_created) throw not_after_created(); - ParameterBuilder pb = new ParameterBuilder(this, iSequence, attributes, strParamName); + ParameterBuilder pb = new RuntimeParameterBuilder(this, iSequence, attributes, strParamName); pinfo ??= new ParameterBuilder[parameters!.Length + 1]; pinfo[iSequence] = pb; return pb; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs index cdae1832367f8c..88996e06647a30 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs @@ -335,7 +335,7 @@ protected override ParameterBuilder DefineParameterCore(int position, ParameterA if ((position < 0) || parameters == null || (position > parameters.Length)) throw new ArgumentOutOfRangeException(nameof(position)); - ParameterBuilder pb = new ParameterBuilder(this, position, attributes, strParamName); + ParameterBuilder pb = new RuntimeParameterBuilder(this, position, attributes, strParamName); pinfo ??= new ParameterBuilder[parameters.Length + 1]; pinfo[position] = pb; return pb; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.Mono.cs similarity index 82% rename from src/mono/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.Mono.cs rename to src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.Mono.cs index d634f330a21bb3..2f682a29d674aa 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.Mono.cs @@ -41,7 +41,7 @@ namespace System.Reflection.Emit { [StructLayout(LayoutKind.Sequential)] - public partial class ParameterBuilder + internal sealed class RuntimeParameterBuilder : ParameterBuilder { #region Sync with MonoReflectionParamBuilder in object-internals.h private MethodBase methodb; /* MethodBuilder, ConstructorBuilder or DynamicMethod */ @@ -55,7 +55,7 @@ public partial class ParameterBuilder #endregion [DynamicDependency(nameof(def_value))] // Automatically keeps all previous fields too due to StructLayout - internal ParameterBuilder(MethodBase mb, int pos, ParameterAttributes attributes, string? strParamName) + internal RuntimeParameterBuilder(MethodBase mb, int pos, ParameterAttributes attributes, string? strParamName) { name = strParamName; position = pos; @@ -67,32 +67,20 @@ internal ParameterBuilder(MethodBase mb, int pos, ParameterAttributes attributes table_idx = mb.get_next_table_index(0x08, 1); } - public virtual int Attributes + public override int Attributes { get { return (int)attrs; } } - public bool IsIn - { - get { return ((int)attrs & (int)ParameterAttributes.In) != 0; } - } - public bool IsOut - { - get { return ((int)attrs & (int)ParameterAttributes.Out) != 0; } - } - public bool IsOptional - { - get { return ((int)attrs & (int)ParameterAttributes.Optional) != 0; } - } - public virtual string? Name + public override string? Name { get { return name; } } - public virtual int Position + public override int Position { get { return position; } } - public virtual void SetConstant(object? defaultValue) + public override void SetConstant(object? defaultValue) { if (position > 0) { @@ -104,9 +92,10 @@ public virtual void SetConstant(object? defaultValue) attrs |= ParameterAttributes.HasDefault; } - public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { - string? attrname = customBuilder.Ctor.ReflectedType!.FullName; + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); + string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.InteropServices.InAttribute") { attrs |= ParameterAttributes.In; @@ -151,11 +140,6 @@ public void SetCustomAttribute(CustomAttributeBuilder customBuilder) cattrs[0] = customBuilder; } } - - public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) - { - SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute)); - } } }