diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs index 1d0703dc51c02e..c54ded37c10e95 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs @@ -292,35 +292,6 @@ public override bool IsDefined(Type attributeType, bool inherit) return m_typeBuilder.IsDefined(attributeType, inherit); } - /***************************************************** - * - * private/protected functions - * - */ - - public override Type MakePointerType() - { - return SymbolType.FormCompoundType("*", this, 0)!; - } - - public override Type MakeByRefType() - { - return SymbolType.FormCompoundType("&", this, 0)!; - } - - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] - public override Type MakeArrayType() - { - return SymbolType.FormCompoundType("[]", this, 0)!; - } - - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] - public override Type MakeArrayType(int rank) - { - string s = GetRankString(rank); - return SymbolType.FormCompoundType(s, this, 0)!; - } - // Constructs a EnumBuilder. // EnumBuilder can only be a top-level (not nested) enum type. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2064:UnrecognizedReflectionPattern", diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs index 8017aabdcdb52c..e013510453c281 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs @@ -39,5 +39,28 @@ public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) public void SetCustomAttribute(CustomAttributeBuilder customBuilder) => SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data); + + public override Type MakePointerType() + { + return SymbolType.FormCompoundType("*", this, 0)!; + } + + public override Type MakeByRefType() + { + return SymbolType.FormCompoundType("&", this, 0)!; + } + + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] + public override Type MakeArrayType() + { + return SymbolType.FormCompoundType("[]", this, 0)!; + } + + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] + public override Type MakeArrayType(int rank) + { + string s = GetRankString(rank); + return SymbolType.FormCompoundType(s, this, 0)!; + } } } diff --git a/src/libraries/System.Reflection.Emit/System.Reflection.Emit.sln b/src/libraries/System.Reflection.Emit/System.Reflection.Emit.sln index 7a5969e89e2c85..b774027405c401 100644 --- a/src/libraries/System.Reflection.Emit/System.Reflection.Emit.sln +++ b/src/libraries/System.Reflection.Emit/System.Reflection.Emit.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{772C93D4-FC45-46AA-B09F-26F01B672EDC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{E5543842-139D-43BD-B604-E65EBB91649E}" @@ -596,4 +596,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {739AA767-154B-4C69-8C9B-C3D332833D92} EndGlobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 5ff83e0f7b86d4..54123a4d079f2f 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -171,4 +171,16 @@ The generic parameters are already defined on this MethodBuilder. + + Should only set visibility flags when creating EnumBuilder. + + + Constant does not match the defined type. + + + Null is not a valid constant value for this type. + + + Underlying type information on enumeration is not specified. + \ 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 16e302c4efce12..dac4ca741abbc0 100644 --- a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj +++ b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj @@ -7,14 +7,16 @@ + - + + diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs new file mode 100644 index 00000000000000..b11a3cb2989d18 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs @@ -0,0 +1,144 @@ +// 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.CodeAnalysis; +using System.Reflection.Metadata; + +namespace System.Reflection.Emit +{ + internal sealed class EnumBuilderImpl : EnumBuilder + { + private readonly FieldBuilder _underlyingField; + internal readonly TypeBuilderImpl _typeBuilder; + + internal EnumBuilderImpl(string name, Type underlyingType, TypeAttributes visibility, ModuleBuilderImpl module, TypeDefinitionHandle typeHandle) + { + if ((visibility & ~TypeAttributes.VisibilityMask) != 0) + throw new ArgumentException(SR.Argument_ShouldOnlySetVisibilityFlags, nameof(name)); + + _typeBuilder = new TypeBuilderImpl(name, visibility | TypeAttributes.Sealed, typeof(Enum), module, typeHandle, null, PackingSize.Unspecified, TypeBuilder.UnspecifiedTypeSize, null); + + // Define the underlying field for the enum. It will be a non-static, private field with special name bit set. + _underlyingField = _typeBuilder.DefineField("value__", underlyingType, FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName); + } + + protected override FieldBuilder UnderlyingFieldCore => _underlyingField; + + [return: DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] + protected override TypeInfo CreateTypeInfoCore() => _typeBuilder.CreateTypeInfo(); + + protected override FieldBuilder DefineLiteralCore(string literalName, object? literalValue) + { + FieldBuilder fieldBuilder = _typeBuilder.DefineField( + literalName, + _typeBuilder, + FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal); + fieldBuilder.SetConstant(literalValue); + return fieldBuilder; + } + + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) => + _typeBuilder.SetCustomAttribute(con, binaryAttribute); + + public override Guid GUID => _typeBuilder.GUID; + + public override string Name => _typeBuilder.Name; + + public override Module Module => _typeBuilder.Module; + + public override Assembly Assembly => _typeBuilder.Assembly; + + public override RuntimeTypeHandle TypeHandle => _typeBuilder.TypeHandle; + + public override string? FullName => _typeBuilder.FullName; + + public override string? AssemblyQualifiedName => _typeBuilder.AssemblyQualifiedName; + + public override string? Namespace => _typeBuilder.Namespace; + + public override Type? BaseType => _typeBuilder.BaseType; + + public override bool IsByRefLike => false; + + public override bool IsTypeDefinition => true; + + public override bool IsSZArray => false; + + public override bool IsConstructedGenericType => false; + + public override Type? DeclaringType => _typeBuilder.DeclaringType; + + public override Type? ReflectedType => _typeBuilder.ReflectedType; + + public override Type UnderlyingSystemType => GetEnumUnderlyingType(); + + public override Type GetEnumUnderlyingType() => _underlyingField.FieldType; + + protected override bool IsArrayImpl() => false; + + protected override bool IsPrimitiveImpl() => false; + + protected override bool IsValueTypeImpl() => true; + + protected override bool IsByRefImpl() => false; + + protected override bool IsPointerImpl() => false; + + protected override bool IsCOMObjectImpl() => false; + + public override Type? GetElementType() => _typeBuilder.GetElementType(); + + protected override bool HasElementTypeImpl() => _typeBuilder.HasElementType; + + protected override TypeAttributes GetAttributeFlagsImpl() => _typeBuilder.Attributes; + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces() => EmptyTypes; + + public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); + public override object[] GetCustomAttributes(bool inherit) => throw new NotImplementedException(); + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotImplementedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, + object?[]? args, ParameterModifier[]? modifiers, Globalization.CultureInfo? culture, string[]? namedParameters) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, + CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] + public override EventInfo[] GetEvents() => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, + CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type? GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, + Type? returnType, Type[]? types, ParameterModifier[]? modifiers) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type? GetNestedType(string name, BindingFlags bindingAttr) => throw new NotSupportedException(); + [DynamicallyAccessedMembers(TypeBuilderImpl.GetAllMembers)] + public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => throw new NotSupportedException(); + public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) + => throw new NotSupportedException(); + [DynamicallyAccessedMembers(TypeBuilderImpl.GetAllMembers)] + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(); + public override bool IsAssignableFrom([NotNullWhen(true)] Type? c) => throw new NotSupportedException(); + } +} 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 0e4fe230fb8cd2..39e8881c2ab8c5 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 @@ -22,6 +22,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder internal MarshallingData? _marshallingData; internal int _offset; internal List? _customAttributes; + internal object? _defaultValue = DBNull.Value; internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes) { @@ -32,7 +33,61 @@ internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type ty _offset = -1; } - protected override void SetConstantCore(object? defaultValue) => throw new NotImplementedException(); + protected override void SetConstantCore(object? defaultValue) + { + if (defaultValue == null) + { + // nullable value types can hold null value. + if (_fieldType.IsValueType && !(_fieldType.IsGenericType && _fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))) + throw new ArgumentException(SR.Argument_ConstantNull); + } + else + { + Type type = defaultValue.GetType(); + Type destType = _fieldType; + + // We should allow setting a constant value on a ByRef parameter + if (destType.IsByRef) + destType = destType.GetElementType()!; + + // Convert nullable types to their underlying type. + destType = Nullable.GetUnderlyingType(destType) ?? destType; + + if (destType.IsEnum) + { + Type underlyingType; + if (destType is EnumBuilderImpl enumBldr) + { + underlyingType = enumBldr.GetEnumUnderlyingType(); + + if (type != enumBldr._typeBuilder.UnderlyingSystemType && type != underlyingType) + throw new ArgumentException(SR.Argument_ConstantDoesntMatch); + } + else if (destType is TypeBuilderImpl typeBldr) + { + underlyingType = typeBldr.UnderlyingSystemType; + + if (underlyingType == null || (type != typeBldr.UnderlyingSystemType && type != underlyingType)) + throw new ArgumentException(SR.Argument_ConstantDoesntMatch); + } + else + { + underlyingType = Enum.GetUnderlyingType(destType); + + if (type != destType && type != underlyingType) + throw new ArgumentException(SR.Argument_ConstantDoesntMatch); + } + } + else + { + if (!destType.IsAssignableFrom(type)) + throw new ArgumentException(SR.Argument_ConstantDoesntMatch); + } + + _defaultValue = defaultValue; + } + } + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { // Handle pseudo custom attributes 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 a135ed225785e8..c3ca6012544f34 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 @@ -247,6 +247,11 @@ private void WriteFields(TypeBuilderImpl typeBuilder) { AddMarshalling(fieldHandle, field._marshallingData.SerializeMarshallingData()); } + + if (field._defaultValue != DBNull.Value) + { + AddDefaultValue(fieldHandle, field._defaultValue); + } } } @@ -325,8 +330,8 @@ private void AddGenericTypeParametersAndConstraintsCustomAttributes(EntityHandle } } - private void AddDefaultValue(ParameterHandle parameterHandle, object? defaultValue) => - _metadataBuilder.AddConstant(parent: parameterHandle, value: defaultValue); + private void AddDefaultValue(EntityHandle parentHandle, object? defaultValue) => + _metadataBuilder.AddConstant(parent: parentHandle, value: defaultValue); private FieldDefinitionHandle AddFieldDefinition(FieldBuilderImpl field, BlobBuilder fieldSignature) => _metadataBuilder.AddFieldDefinition( @@ -407,6 +412,11 @@ internal EntityHandle GetTypeHandle(Type type) return tb._handle; } + if (type is EnumBuilderImpl eb && Equals(eb.Module)) + { + return eb._typeBuilder._handle; + } + return GetTypeReference(type); } @@ -429,11 +439,19 @@ internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [Dynamic public override int GetStringMetadataToken(string stringConstant) => throw new NotImplementedException(); public override int GetTypeMetadataToken(Type type) => throw new NotImplementedException(); protected override void CreateGlobalFunctionsCore() => throw new NotImplementedException(); - protected override EnumBuilder DefineEnumCore(string name, TypeAttributes visibility, Type underlyingType) => throw new NotImplementedException(); + + protected override EnumBuilder DefineEnumCore(string name, TypeAttributes visibility, Type underlyingType) + { + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId); + EnumBuilderImpl enumBuilder = new EnumBuilderImpl(name, underlyingType, visibility, this, typeHandle); + _typeDefinitions.Add(enumBuilder._typeBuilder); + return enumBuilder; + } protected override MethodBuilder DefineGlobalMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers, Type[]? parameterTypes, Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers) => throw new NotImplementedException(); protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) => throw new NotImplementedException(); [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); + protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize) { @@ -442,6 +460,7 @@ protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, _typeDefinitions.Add(_type); return _type; } + protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException(); protected override MethodInfo GetArrayMethodCore(Type arrayClass, string methodName, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 828f3a0820fb09..ba77906508ebbd 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -15,6 +15,7 @@ internal sealed class TypeBuilderImpl : TypeBuilder private readonly ModuleBuilderImpl _module; private readonly string _name; private readonly string? _namespace; + private string? _strFullName; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private Type? _typeParent; private readonly TypeBuilderImpl? _declaringType; @@ -22,6 +23,7 @@ internal sealed class TypeBuilderImpl : TypeBuilder private TypeAttributes _attributes; private PackingSize _packingSize; private int _typeSize; + private Type? _enumUnderlyingType; internal readonly TypeDefinitionHandle _handle; internal readonly List _methodDefinitions = new(); @@ -73,14 +75,24 @@ protected override void AddInterfaceImplementationCore([DynamicallyAccessedMembe _interfaces.Add(interfaceType); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2083:DynamicallyAccessedMembers", Justification = "Not sure how to handle")] [return: DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] - protected override TypeInfo CreateTypeInfoCore() => throw new NotImplementedException(); + protected override TypeInfo CreateTypeInfoCore() => this; protected override ConstructorBuilder DefineConstructorCore(MethodAttributes attributes, CallingConventions callingConvention, Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers) => throw new NotImplementedException(); protected override ConstructorBuilder DefineDefaultConstructorCore(MethodAttributes attributes) => throw new NotImplementedException(); protected override EventBuilder DefineEventCore(string name, EventAttributes attributes, Type eventtype) => throw new NotImplementedException(); protected override FieldBuilder DefineFieldCore(string fieldName, Type type, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers, FieldAttributes attributes) { + if (_enumUnderlyingType == null && IsEnum) + { + if ((attributes & FieldAttributes.Static) == 0) + { + // remember the underlying type for enum type + _enumUnderlyingType = type; + } + } + var field = new FieldBuilderImpl(this, fieldName, type, attributes); _fieldDefinitions.Add(field); return field; @@ -156,6 +168,11 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } + internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + SetCustomAttributeCore(con, binaryAttribute); + } + private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan binaryAttribute) { CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute); @@ -249,11 +266,27 @@ protected override void SetParentCore([DynamicallyAccessedMembers(DynamicallyAcc public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotImplementedException(); public override Type GetElementType() => throw new NotSupportedException(); public override string? AssemblyQualifiedName => throw new NotSupportedException(); - public override string? FullName => throw new NotSupportedException(); + public override string? FullName => _strFullName ??= TypeNameBuilder.ToString(this, TypeNameBuilder.Format.FullName); public override string? Namespace => _namespace; public override Assembly Assembly => _module.Assembly; public override Module Module => _module; - public override Type UnderlyingSystemType => this; + public override Type UnderlyingSystemType + { + get + { + if (IsEnum) + { + if (_enumUnderlyingType == null) + throw new InvalidOperationException(SR.InvalidOperation_NoUnderlyingTypeOnEnum); + + return _enumUnderlyingType; + } + else + { + return this; + } + } + } public override Guid GUID => throw new NotSupportedException(); public override Type? BaseType => _typeParent; public override int MetadataToken => MetadataTokens.GetToken(_handle); diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeNameBuilder.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeNameBuilder.cs new file mode 100644 index 00000000000000..6297e14ca22ae0 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeNameBuilder.cs @@ -0,0 +1,325 @@ +// 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.Diagnostics; +using System.Text; + +namespace System.Reflection.Emit +{ + internal sealed class TypeNameBuilder + { + private readonly StringBuilder _str = new StringBuilder(); + private int _instNesting; + private bool _firstInstArg; + private bool _nestedName; + private bool _hasAssemblySpec; + private readonly List _stack = new List(); + private int _stackIdx; + + private TypeNameBuilder() + { + } + + private void OpenGenericArguments() + { + _instNesting++; + _firstInstArg = true; + + Append('['); + } + + private void CloseGenericArguments() + { + Debug.Assert(_instNesting != 0); + + _instNesting--; + + if (_firstInstArg) + { + _str.Remove(_str.Length - 1, 1); + } + else + { + Append(']'); + } + } + + private void OpenGenericArgument() + { + Debug.Assert(_instNesting != 0); + + _nestedName = false; + + if (!_firstInstArg) + Append(','); + + _firstInstArg = false; + + Append('['); + + PushOpenGenericArgument(); + } + + private void CloseGenericArgument() + { + Debug.Assert(_instNesting != 0); + + if (_hasAssemblySpec) + { + Append(']'); + } + + PopOpenGenericArgument(); + } + + private void AddName(string name) + { + Debug.Assert(name != null); + + if (_nestedName) + Append('+'); + + _nestedName = true; + + EscapeName(name); + } + + private void AddArray(int rank) + { + Debug.Assert(rank > 0); + + if (rank == 1) + { + Append("[*]"); + } + else if (rank > 64) + { + // Only taken in an error path, runtime will not load arrays of more than 32 dimensions + _str.Append('[').Append(rank).Append(']'); + } + else + { + Append('['); + for (int i = 1; i < rank; i++) + Append(','); + Append(']'); + } + } + + private void AddAssemblySpec(string assemblySpec) + { + if (assemblySpec != null && !assemblySpec.Equals("")) + { + Append(", "); + + if (_instNesting > 0) + { + EscapeEmbeddedAssemblyName(assemblySpec); + } + else + { + EscapeAssemblyName(assemblySpec); + } + + _hasAssemblySpec = true; + } + } + + public override string ToString() + { + Debug.Assert(_instNesting == 0); + + return _str.ToString(); + } + + private static bool ContainsReservedChar(string name) + { + foreach (char c in name) + { + if (c == '\0') + break; + if (IsTypeNameReservedChar(c)) + return true; + } + return false; + } + + private static bool IsTypeNameReservedChar(char ch) + { + switch (ch) + { + case ',': + case '[': + case ']': + case '&': + case '*': + case '+': + case '\\': + return true; + + default: + return false; + } + } + + private void EscapeName(string name) + { + if (ContainsReservedChar(name)) + { + foreach (char c in name) + { + if (c == '\0') + break; + if (IsTypeNameReservedChar(c)) + _str.Append('\\'); + _str.Append(c); + } + } + else + Append(name); + } + + private void EscapeAssemblyName(string name) + { + Append(name); + } + + private void EscapeEmbeddedAssemblyName(string name) + { + if (name.Contains(']')) + { + foreach (char c in name) + { + if (c == ']') + Append('\\'); + + Append(c); + } + } + else + { + Append(name); + } + } + + private void PushOpenGenericArgument() + { + _stack.Add(_str.Length); + _stackIdx++; + } + + private void PopOpenGenericArgument() + { + int index = _stack[--_stackIdx]; + _stack.RemoveAt(_stackIdx); + + if (!_hasAssemblySpec) + _str.Remove(index - 1, 1); + + _hasAssemblySpec = false; + } + + private void Append(string pStr) + { + int i = pStr.IndexOf('\0'); + if (i < 0) + { + _str.Append(pStr); + } + else if (i > 0) + { + _str.Append(pStr.AsSpan(0, i)); + } + } + + private void Append(char c) + { + _str.Append(c); + } + + internal enum Format + { + ToString, + FullName, + AssemblyQualifiedName, + } + + internal static string? ToString(Type type, Format format) + { + if (format == Format.FullName || format == Format.AssemblyQualifiedName) + { + if (!type.IsGenericTypeDefinition && type.ContainsGenericParameters) + return null; + } + + var tnb = new TypeNameBuilder(); + tnb.AddAssemblyQualifiedName(type, format); + return tnb.ToString(); + } + + private void AddElementType(Type type) + { + if (!type.HasElementType) + return; + + AddElementType(type.GetElementType()!); + + if (type.IsPointer) + Append('*'); + else if (type.IsByRef) + Append('&'); + else if (type.IsSZArray) + Append("[]"); + else if (type.IsArray) + AddArray(type.GetArrayRank()); + } + + private void AddAssemblyQualifiedName(Type type, Format format) + { + Type rootType = type; + + while (rootType.HasElementType) + rootType = rootType.GetElementType()!; + + // Append namespace + nesting + name + var nestings = new List(); + for (Type? t = rootType; t != null; t = t.IsGenericParameter ? null : t.DeclaringType) + nestings.Add(t); + + for (int i = nestings.Count - 1; i >= 0; i--) + { + Type enclosingType = nestings[i]; + string name = enclosingType.Name; + + if (i == nestings.Count - 1 && !string.IsNullOrEmpty(enclosingType.Namespace)) + name = enclosingType.Namespace + "." + name; + + AddName(name); + } + + // Append generic arguments + if (rootType.IsGenericType && (!rootType.IsGenericTypeDefinition || format == Format.ToString)) + { + Type[] genericArguments = rootType.GetGenericArguments(); + + OpenGenericArguments(); + for (int i = 0; i < genericArguments.Length; i++) + { + Format genericArgumentsFormat = format == Format.FullName ? Format.AssemblyQualifiedName : format; + + OpenGenericArgument(); + AddAssemblyQualifiedName(genericArguments[i], genericArgumentsFormat); + CloseGenericArgument(); + } + CloseGenericArguments(); + } + + // Append pointer, byRef and array qualifiers + AddElementType(type); + + if (format == Format.AssemblyQualifiedName) + AddAssemblySpec(type.Module.Assembly.FullName!); + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs index 79b9e77bfdf140..a43222afe34f05 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -21,9 +21,9 @@ public class AssemblySaveCustomAttributeTests }; private static readonly Type s_comVisibleType = typeof(ComVisibleAttribute); - private static readonly Type s_guideType = typeof(GuidAttribute); + private static readonly Type s_guidType = typeof(GuidAttribute); private static readonly (ConstructorInfo con, object[] args) s_comVisiblePair = (s_comVisibleType.GetConstructor(new Type[] { typeof(bool) }), new object[] { true }); - private static readonly (ConstructorInfo con, object[] args) s_guidPair = (s_guideType.GetConstructor(new Type[] { typeof(string) }), new object[] { "9ED54F84-A89D-4fcd-A854-44251E925F09" }); + private static readonly (ConstructorInfo con, object[] args) s_guidPair = (s_guidType.GetConstructor(new Type[] { typeof(string) }), new object[] { "9ED54F84-A89D-4fcd-A854-44251E925F09" }); private static AssemblyName PopulateAssemblyName() { @@ -42,10 +42,10 @@ public void AssemblyModuleWithCustomAttributes() { WriteAssemblyToDisk(assemblyName, Type.EmptyTypes, file.Path, _attributes, _attributes); - Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); Module moduleFromDisk = assemblyFromDisk.Modules.First(); - AssemblyTools.AssertAssemblyNameAndModule(assemblyName, assemblyFromDisk.GetName(), moduleFromDisk); + AssemblySaveTools.AssertAssemblyNameAndModule(assemblyName, assemblyFromDisk.GetName(), moduleFromDisk); ValidateAttributes(assemblyFromDisk.GetCustomAttributesData()); ValidateAttributes(moduleFromDisk.GetCustomAttributesData()); } @@ -61,7 +61,7 @@ public void MethodFieldWithCustomAttributes() WriteAssemblyToDisk(PopulateAssemblyName(), types, file.Path, typeAttributes: _attributes, methodAttributes: _attributes, fieldAttributes: _attributes); - Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); Module moduleFromDisk = assemblyFromDisk.Modules.First(); Type[] typesFromDisk = moduleFromDisk.GetTypes(); @@ -75,9 +75,9 @@ public void MethodFieldWithCustomAttributes() MethodInfo[] methodsFromDisk = typeFromDisk.IsValueType ? typeFromDisk.GetMethods(BindingFlags.DeclaredOnly) : typeFromDisk.GetMethods(); FieldInfo[] fieldsFromDisk = typeFromDisk.GetFields(); - AssemblyTools.AssertTypeProperties(sourceType, typeFromDisk); - AssemblyTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); - AssemblyTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); + AssemblySaveTools.AssertTypeProperties(sourceType, typeFromDisk); + AssemblySaveTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); + AssemblySaveTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); ValidateAttributes(typeFromDisk.GetCustomAttributesData()); for (int j = 0; j < methodsFromDisk.Length; j++) @@ -109,7 +109,7 @@ private void ValidateAttributes(IList attributesFromDisk) { Assert.Equal(s_guidPair.con.MetadataToken, attribute.Constructor.MetadataToken); Assert.Equal(s_guidPair.args[0].GetType().FullName, attribute.ConstructorArguments[0].ArgumentType.FullName); - Assert.Equal(attribute.AttributeType.Name, s_guideType.Name); + Assert.Equal(attribute.AttributeType.Name, s_guidType.Name); Assert.Equal(s_guidPair.args[0], attribute.ConstructorArguments[0].Value); } } @@ -119,7 +119,7 @@ private static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, List? moduleAttributes = null, List? typeAttributes = null, List? methodAttributes = null, List? fieldAttributes = null) { - AssemblyBuilder assemblyBuilder = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod(assemblyName, assemblyAttributes, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(assemblyName, assemblyAttributes, typeof(string), out MethodInfo saveMethod); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name); PopulateMembersForModule(mb, types, moduleAttributes, typeAttributes, methodAttributes, fieldAttributes); saveMethod.Invoke(assemblyBuilder, new object[] { fileLocation }); @@ -191,14 +191,14 @@ public void CreateStructWithPseudoCustomAttributesTest() new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { }) }; - AssemblyBuilder ab = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes, type.BaseType); DefineFieldsAndSetAttributes(fieldAttributes.ToList(), type.GetFields(), tb); typeAttributes.ForEach(tb.SetCustomAttribute); saveMethod.Invoke(ab, new object[] { file.Path }); - Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); Module moduleFromDisk = assemblyFromDisk.Modules.First(); Type testType = moduleFromDisk.GetTypes()[0]; IList attributesFromDisk = testType.GetCustomAttributesData(); @@ -280,13 +280,13 @@ public void InterfacesWithPseudoCustomAttributes() 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); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes); typeAttributes.ForEach(tb.SetCustomAttribute); DefineMethodsAndSetAttributes(methodAttributes, tb, type.GetMethods(), parameterAttributes); saveMethod.Invoke(ab, new object[] { file.Path }); - Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; IList attributesFromDisk = testType.GetCustomAttributesData(); @@ -429,7 +429,7 @@ public void MarshalAsPseudoCustomAttributesTest(CustomAttributeBuilder attribute using (TempFile file = TempFile.Create()) { Type type = typeof(StructWithFields); - AssemblyBuilder ab = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes, type.BaseType); FieldInfo stringField = type.GetFields()[1]; @@ -437,7 +437,7 @@ public void MarshalAsPseudoCustomAttributesTest(CustomAttributeBuilder attribute fb.SetCustomAttribute(attribute); saveMethod.Invoke(ab, new object[] { file.Path }); - Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); FieldInfo field = assemblyFromDisk.Modules.First().GetTypes()[0].GetFields()[0]; CustomAttributeData attributeFromDisk = field.GetCustomAttributesData()[0]; @@ -458,5 +458,51 @@ public void MarshalAsPseudoCustomAttributesTest(CustomAttributeBuilder attribute } } } + + [Fact] + public void EnumBuilderSetCustomAttributesTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( + PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); + EnumBuilder enumBuilder = ab.DefineDynamicModule("Module").DefineEnum("TestEnum", TypeAttributes.Public, typeof(int)); + + ConstructorInfo attributeConstructor = typeof(BoolAttribute).GetConstructor(new Type[] { typeof(bool) }); + CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeConstructor, new object[] { true }); + enumBuilder.SetCustomAttribute(attributeBuilder); + enumBuilder.SetCustomAttribute(new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args)); + saveMethod.Invoke(ab, new object[] { file.Path }); + + Type testEnum = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestEnum"); + + Assert.True(testEnum.IsEnum); + AssemblySaveTools.AssertTypeProperties(enumBuilder, testEnum); + + CustomAttributeData[] attributes = testEnum.GetCustomAttributesData().ToArray(); + if (attributes[0].AttributeType.Name == s_guidType.Name) + { + AssertEnumAttributes(s_guidType.FullName, "9ED54F84-A89D-4fcd-A854-44251E925F09", attributes[0]); + AssertEnumAttributes(typeof(BoolAttribute).FullName, true, attributes[1]); + } + else + { + AssertEnumAttributes(s_guidType.FullName, "9ED54F84-A89D-4fcd-A854-44251E925F09", attributes[1]); + AssertEnumAttributes(typeof(BoolAttribute).FullName, true, attributes[0]); + } + + } + } + private void AssertEnumAttributes(string fullName, object value, CustomAttributeData testAttrbiute) + { + Assert.Equal(fullName, testAttrbiute.AttributeType.FullName); + Assert.Equal(value, testAttrbiute.ConstructorArguments[0].Value); + } + } + + public class BoolAttribute : Attribute + { + private bool _b; + public BoolAttribute(bool myBool) { _b = myBool; } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs new file mode 100644 index 00000000000000..0fddcb4f54b98c --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs @@ -0,0 +1,182 @@ +// 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.IO; +using System.Linq; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public class AssemblySaveEnumBuilderTests + { + private static AssemblyName PopulateAssemblyName() + { + AssemblyName assemblyName = new AssemblyName("MyDynamicAssembly"); + assemblyName.Version = new Version("7.0.0.0"); + assemblyName.CultureInfo = Globalization.CultureInfo.InvariantCulture; + return assemblyName; + } + + public static IEnumerable DefineLiteral_TestData() + { + yield return new object[] { typeof(byte), (byte)0 }; + yield return new object[] { typeof(byte), (byte)1 }; + + yield return new object[] { typeof(sbyte), (sbyte)0 }; + yield return new object[] { typeof(sbyte), (sbyte)1 }; + + yield return new object[] { typeof(ushort), (ushort)0 }; + yield return new object[] { typeof(ushort), (ushort)1 }; + + yield return new object[] { typeof(short), (short)0 }; + yield return new object[] { typeof(short), (short)1 }; + + yield return new object[] { typeof(uint), (uint)0 }; + yield return new object[] { typeof(uint), (uint)1 }; + + yield return new object[] { typeof(int), 0 }; + yield return new object[] { typeof(int), 1 }; + + yield return new object[] { typeof(ulong), (ulong)0 }; + yield return new object[] { typeof(ulong), (ulong)1 }; + + yield return new object[] { typeof(long), (long)0 }; + yield return new object[] { typeof(long), (long)1 }; + + yield return new object[] { typeof(char), (char)0 }; + yield return new object[] { typeof(char), (char)1 }; + + yield return new object[] { typeof(bool), true }; + yield return new object[] { typeof(bool), false }; + + yield return new object[] { typeof(float), 0f }; + yield return new object[] { typeof(float), 1.1f }; + + yield return new object[] { typeof(double), 0d }; + yield return new object[] { typeof(double), 1.1d }; + } + + [Theory] + [MemberData(nameof(DefineLiteral_TestData))] + public void DefineLiteral(Type underlyingType, object literalValue) + { + using (TempFile file = TempFile.Create()) + { + EnumBuilder enumBuilder = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod, out TypeBuilder _, underlyingType); + FieldBuilder literal = enumBuilder.DefineLiteral("FieldOne", literalValue); + saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); + Module moduleFromDisk = assemblyFromDisk.Modules.First(); + Type testEnum = moduleFromDisk.GetType("TestEnum"); + + Assert.True(testEnum.IsEnum); + AssemblySaveTools.AssertTypeProperties(enumBuilder, testEnum); + Assert.Equal(underlyingType.FullName, testEnum.GetEnumUnderlyingType().FullName); + + FieldInfo testField = testEnum.GetField("FieldOne"); + Assert.Equal(enumBuilder.Name, testField.DeclaringType.Name); + Assert.Equal(FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal, literal.Attributes); + Assert.Equal(enumBuilder.AsType().FullName, testField.FieldType.FullName); + } + } + + [Theory] + [InlineData(0, "TestEnum[]")] + [InlineData(1, "TestEnum[]")] + [InlineData(2, "TestEnum[,]")] + [InlineData(3, "TestEnum[,,]")] + public void SaveArrayTypeSignature(int rank, string name) + { + using (TempFile file = TempFile.Create()) + { + EnumBuilder enumBuilder = CreateAssemblyAndDefineEnum(out AssemblyBuilder ab, out MethodInfo saveMethod, out TypeBuilder tb); + Type arrayType = rank == 0 ? enumBuilder.MakeArrayType() : enumBuilder.MakeArrayType(rank); + MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); + mb.SetReturnType(arrayType); + mb.SetParameters(new Type[] { typeof(INoMethod), arrayType }); + saveMethod.Invoke(ab, new object[] { file.Path }); + + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestInterface"); + MethodInfo testMethod = testType.GetMethod("TestMethod"); + + AssertArrayTypeSignature(rank, name, testMethod.ReturnType); + AssertArrayTypeSignature(rank, name, testMethod.GetParameters()[1].ParameterType); + } + } + + private EnumBuilder CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, + out MethodInfo saveMethod, out TypeBuilder type, Type? underlyingType = null) + { + assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( + PopulateAssemblyName(), null, typeof(string), out saveMethod); + ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); + type = mb.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract); + return mb.DefineEnum("TestEnum", TypeAttributes.Public, underlyingType == null ? typeof(int) : underlyingType); + } + + private static void AssertArrayTypeSignature(int rank, string name, Type arrayType) + { + Assert.True(rank < 2 ? arrayType.IsSZArray : arrayType.IsArray); + rank = rank == 0 ? rank + 1 : rank; + Assert.Equal(rank, arrayType.GetArrayRank()); + Assert.Equal(name, arrayType.Name); + } + + [Fact] + public void SaveByRefTypeSignature() + { + using (TempFile file = TempFile.Create()) + { + EnumBuilder eb = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod, out TypeBuilder tb); + Type byrefType = eb.MakeByRefType(); + MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); + mb.SetReturnType(byrefType); + mb.SetParameters(new Type[] { typeof(INoMethod), byrefType }); + saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestInterface"); + MethodInfo testMethod = testType.GetMethod("TestMethod"); + + Assert.False(testMethod.GetParameters()[0].ParameterType.IsByRef); + AssertByRefType(testMethod.GetParameters()[1].ParameterType); + AssertByRefType(testMethod.ReturnType); + } + } + + private static void AssertByRefType(Type byrefParam) + { + Assert.True(byrefParam.IsByRef); + Assert.Equal("TestEnum&", byrefParam.Name); + } + + [Fact] + public void SavePointerTypeSignature() + { + using (TempFile file = TempFile.Create()) + { + EnumBuilder eb = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod, out TypeBuilder tb); + Type pointerType = eb.MakePointerType(); + MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); + mb.SetReturnType(pointerType); + mb.SetParameters(new Type[] { typeof(INoMethod), pointerType }); + saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestInterface"); + MethodInfo testMethod = testType.GetMethod("TestMethod"); + + Assert.False(testMethod.GetParameters()[0].ParameterType.IsPointer); + AssertPointerType(testMethod.GetParameters()[1].ParameterType); + AssertPointerType(testMethod.ReturnType); + } + } + + private void AssertPointerType(Type testType) + { + Assert.True(testType.IsPointer); + Assert.Equal("TestEnum*", testType.Name); + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs similarity index 98% rename from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs rename to src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs index 4ec230e52bc8b1..088e33235e3974 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs @@ -9,7 +9,7 @@ namespace System.Reflection.Emit.Tests { - internal static class AssemblyTools + internal static class AssemblySaveTools { internal static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation) { @@ -150,7 +150,7 @@ private static void AssertParameters(ParameterInfo[] sourceParameters, Parameter internal sealed class CoreMetadataAssemblyResolver : MetadataAssemblyResolver { public static Assembly s_coreAssembly = typeof(object).Assembly; - public static Assembly s_emitAssembly = typeof(AssemblyTools).Assembly; + public static Assembly s_emitAssembly = typeof(AssemblySaveTools).Assembly; public CoreMetadataAssemblyResolver() { } public override Assembly Resolve(MetadataLoadContext context, AssemblyName assemblyName) diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs similarity index 91% rename from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs rename to src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index ec9fa93dc13425..7a76202fc8a8d8 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -10,7 +10,7 @@ namespace System.Reflection.Emit.Tests { [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] - public class AssemblySaveWithVariousMembersTests + public class AssemblySaveTypeBuilderTests { private static readonly AssemblyName s_assemblyName = new AssemblyName("MyDynamicAssembly") { @@ -28,15 +28,15 @@ public void EmptyAssemblyAndModuleTest() Assembly assemblyFromDisk = WriteAndLoadAssembly(Type.EmptyTypes, file.Path); Assert.Empty(assemblyFromDisk.GetTypes()); - AssemblyTools.AssertAssemblyNameAndModule(s_assemblyName, assemblyFromDisk.GetName(), assemblyFromDisk.Modules.FirstOrDefault()); + AssemblySaveTools.AssertAssemblyNameAndModule(s_assemblyName, assemblyFromDisk.GetName(), assemblyFromDisk.Modules.FirstOrDefault()); } } private static Assembly WriteAndLoadAssembly(Type[] types, string filePath) { - AssemblyTools.WriteAssemblyToDisk(s_assemblyName, types, filePath); + AssemblySaveTools.WriteAssemblyToDisk(s_assemblyName, types, filePath); - return AssemblyTools.LoadAssemblyFromPath(filePath); + return AssemblySaveTools.LoadAssemblyFromPath(filePath); } public static IEnumerable VariousInterfacesStructsTestData() @@ -73,9 +73,9 @@ private static void AssertTypesAndTypeMembers(Type[] types, Type[] typesFromDisk Type sourceType = types[i]; Type typeFromDisk = typesFromDisk[i]; - AssemblyTools.AssertTypeProperties(sourceType, typeFromDisk); - AssemblyTools.AssertMethods(sourceType.GetMethods(), typeFromDisk.GetMethods()); - AssemblyTools.AssertFields(sourceType.GetFields(), typeFromDisk.GetFields()); + AssemblySaveTools.AssertTypeProperties(sourceType, typeFromDisk); + AssemblySaveTools.AssertMethods(sourceType.GetMethods(), typeFromDisk.GetMethods()); + AssemblySaveTools.AssertFields(sourceType.GetFields(), typeFromDisk.GetFields()); } } @@ -85,8 +85,8 @@ public void WriteAssemblyWithVariousTypesToStreamAndReadBackTest(Type[] types) { using (var stream = new MemoryStream()) { - AssemblyTools.WriteAssemblyToStream(s_assemblyName, types, stream); - Assembly assemblyFromStream = AssemblyTools.LoadAssemblyFromStream(stream); + AssemblySaveTools.WriteAssemblyToStream(s_assemblyName, types, stream); + Assembly assemblyFromStream = AssemblySaveTools.LoadAssemblyFromStream(stream); AssertTypesAndTypeMembers(types, assemblyFromStream.Modules.First().GetTypes()); } @@ -101,7 +101,7 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest() tb.DefineMethod("TestMethod", MethodAttributes.Public); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); Module moduleFromDisk = assemblyFromDisk.Modules.First(); Assert.Equal("MyModule", moduleFromDisk.ScopeName); @@ -119,7 +119,7 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest() private static TypeBuilder CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod) { - assemblyBuilder = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod(s_assemblyName, null, typeof(string), out saveMethod); + assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(s_assemblyName, null, typeof(string), out saveMethod); return assemblyBuilder.DefineDynamicModule("MyModule") .DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract); } @@ -129,7 +129,7 @@ public void AddInterfaceImplementationTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder assemblyBuilder = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( s_assemblyName, null, typeof(string), out MethodInfo saveMethod); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); TypeBuilder tb = mb.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract, null, new Type[] { typeof(IOneMethod)}); @@ -137,7 +137,7 @@ public void AddInterfaceImplementationTest() tb.DefineNestedType("NestedType", TypeAttributes.Interface | TypeAttributes.Abstract); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; Type[] interfaces = testType.GetInterfaces(); @@ -174,7 +174,7 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames) } saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Type testType = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; MethodInfo testMethod = testType.GetMethod("TestMethod"); Type[] genericTypeParams = testType.GetGenericArguments(); @@ -237,7 +237,7 @@ public void SaveGenericTypeParametersForAMethod(string[] typeParamNames) } saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Type testType = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; MethodInfo testMethod = testType.GetMethod("TestMethod"); Type[] genericTypeParams = testMethod.GetGenericArguments(); @@ -267,7 +267,7 @@ public void SaveArrayTypeSignature(int rank, string name) mb.SetParameters(new Type[] { typeof(INoMethod), arrayType, typeof(int[,,,]) }); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Type testType = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; MethodInfo testMethod = testType.GetMethod("TestMethod"); Type intArray = testMethod.GetParameters()[2].ParameterType; @@ -300,7 +300,7 @@ public void SaveByRefTypeSignature() mb.SetParameters(new Type[] { typeof(INoMethod), byrefType }); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Type testType = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; MethodInfo testMethod = testType.GetMethod("TestMethod"); Assert.False(testMethod.GetParameters()[0].ParameterType.IsByRef); @@ -327,7 +327,7 @@ public void SavePointerTypeSignature() mb.SetParameters(new Type[] { typeof(INoMethod), pointerType }); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Type testType = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; MethodInfo testMethod = testType.GetMethod("TestMethod"); Assert.False(testMethod.GetParameters()[0].ParameterType.IsPointer); @@ -364,7 +364,7 @@ public void SaveGenericTypeSignature(string[] genericParams, Type[] typeArgument mb.SetParameters(new Type[] { typeof(INoMethod), genericType }); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Type testType = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; MethodInfo testMethod = testType.GetMethod("TestMethod"); Type paramType = testMethod.GetParameters()[1].ParameterType; @@ -398,7 +398,7 @@ public void SaveGenericTypeSignatureWithGenericParameter() mb.SetParameters(new Type[] { typeof(INoMethod), genericType, typeParams[1] }); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Type testType = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; + Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; MethodInfo testMethod = testType.GetMethod("TestMethod"); Type paramType = testMethod.GetParameters()[1].ParameterType; Type genericParameter = testMethod.GetParameters()[2].ParameterType; @@ -425,7 +425,7 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder assemblyBuilder = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( s_assemblyName, null, typeof(string), out MethodInfo saveMethod); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); TypeBuilder tb = mb.DefineType("TestInterface1", TypeAttributes.Interface | TypeAttributes.Abstract); @@ -447,7 +447,7 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() typePar[0].SetBaseTypeConstraint(typeof(EmptyTestClass)); saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - Module m = AssemblyTools.LoadAssemblyFromPath(file.Path).Modules.First(); + Module m = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First(); Type[] type1Params = m.GetTypes()[0].GetGenericArguments(); Type[] type2Params = m.GetTypes()[1].GetGenericArguments(); Type[] type3Params = m.GetTypes()[2].GetGenericArguments(); diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index f672dc37d9e828..1395d38e10de55 100644 --- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -63,8 +63,9 @@ - - + + + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs index b417c32f06abae..ff8de04d7b791b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs @@ -424,29 +424,6 @@ public override bool IsDefined(Type attributeType, bool inherit) return _tb.IsDefined(attributeType, inherit); } - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] - public override Type MakeArrayType() - { - return SymbolType.FormCompoundType("[]", this, 0)!; - } - - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] - public override Type MakeArrayType(int rank) - { - string s = GetRankString(rank); - return SymbolType.FormCompoundType(s, this, 0)!; - } - - public override Type MakeByRefType() - { - return SymbolType.FormCompoundType("&", this, 0)!; - } - - public override Type MakePointerType() - { - return SymbolType.FormCompoundType("*", this, 0)!; - } - protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { _tb.SetCustomAttribute(con, binaryAttribute);