Add EnumBuilder implementation and other changes (#88503)
authorBuyaa Namnan <bunamnan@microsoft.com>
Thu, 13 Jul 2023 17:27:09 +0000 (10:27 -0700)
committerGitHub <noreply@github.com>
Thu, 13 Jul 2023 17:27:09 +0000 (10:27 -0700)
* Add EnumBuilder implementation and other changes

* Update src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* Apply feedback

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
16 files changed:
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs
src/libraries/System.Reflection.Emit/System.Reflection.Emit.sln
src/libraries/System.Reflection.Emit/src/Resources/Strings.resx
src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs [new file with mode: 0644]
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeNameBuilder.cs [new file with mode: 0644]
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs [new file with mode: 0644]
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs [moved from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs with 98% similarity]
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs [moved from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs with 91% similarity]
src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs

index 1d0703d..c54ded3 100644 (file)
@@ -292,35 +292,6 @@ namespace System.Reflection.Emit
             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",
index 8017aab..e013510 100644 (file)
@@ -39,5 +39,28 @@ namespace System.Reflection.Emit
 
         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)!;
+        }
     }
 }
index 7a5969e..b774027 100644 (file)
@@ -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
index 5ff83e0..54123a4 100644 (file)
   <data name="InvalidOperation_GenericParametersAlreadySet" xml:space="preserve">
     <value>The generic parameters are already defined on this MethodBuilder.</value>
   </data>
+  <data name="Argument_ShouldOnlySetVisibilityFlags" xml:space="preserve">
+    <value>Should only set visibility flags when creating EnumBuilder.</value>
+  </data>
+  <data name="Argument_ConstantDoesntMatch" xml:space="preserve">
+    <value>Constant does not match the defined type.</value>
+  </data>
+  <data name="Argument_ConstantNull" xml:space="preserve">
+    <value>Null is not a valid constant value for this type.</value>
+  </data>
+  <data name="InvalidOperation_NoUnderlyingTypeOnEnum" xml:space="preserve">
+    <value>Underlying type information on enumeration is not specified.</value>
+  </data>
 </root>
\ No newline at end of file
index 16e302c..dac4ca7 100644 (file)
@@ -7,14 +7,16 @@
   <ItemGroup>
     <Compile Include="System\Reflection\Emit\CustomAttributeWrapper.cs" />
     <Compile Include="System\Reflection\Emit\AssemblyBuilderImpl.cs" />
+    <Compile Include="System\Reflection\Emit\EnumBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\FieldBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\GenericTypeParameterBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\MethodBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\ModuleBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\ParameterBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\PseudoCustomAttributesData.cs" />
-    <Compile Include="System\Reflection\Emit\TypeBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\SignatureHelper.cs" />
+    <Compile Include="System\Reflection\Emit\TypeBuilderImpl.cs" />
+    <Compile Include="System\Reflection\Emit\TypeNameBuilder.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="$(CoreLibProject)" />
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 (file)
index 0000000..b11a3cb
--- /dev/null
@@ -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<byte> 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();
+    }
+}
index 0e4fe23..39e8881 100644 (file)
@@ -22,6 +22,7 @@ namespace System.Reflection.Emit
         internal MarshallingData? _marshallingData;
         internal int _offset;
         internal List<CustomAttributeWrapper>? _customAttributes;
+        internal object? _defaultValue = DBNull.Value;
 
         internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes)
         {
@@ -32,7 +33,61 @@ namespace System.Reflection.Emit
             _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<byte> binaryAttribute)
         {
             // Handle pseudo custom attributes
index a135ed2..c3ca601 100644 (file)
@@ -247,6 +247,11 @@ namespace System.Reflection.Emit
                 {
                     AddMarshalling(fieldHandle, field._marshallingData.SerializeMarshallingData());
                 }
+
+                if (field._defaultValue != DBNull.Value)
+                {
+                    AddDefaultValue(fieldHandle, field._defaultValue);
+                }
             }
         }
 
@@ -325,8 +330,8 @@ namespace System.Reflection.Emit
             }
         }
 
-        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 @@ namespace System.Reflection.Emit
                 return tb._handle;
             }
 
+            if (type is EnumBuilderImpl eb && Equals(eb.Module))
+            {
+                return eb._typeBuilder._handle;
+            }
+
             return GetTypeReference(type);
         }
 
@@ -429,11 +439,19 @@ namespace System.Reflection.Emit
         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 @@ namespace System.Reflection.Emit
             _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<byte> binaryAttribute)
index 828f3a0..ba77906 100644 (file)
@@ -15,6 +15,7 @@ namespace System.Reflection.Emit
         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 @@ namespace System.Reflection.Emit
         private TypeAttributes _attributes;
         private PackingSize _packingSize;
         private int _typeSize;
+        private Type? _enumUnderlyingType;
 
         internal readonly TypeDefinitionHandle _handle;
         internal readonly List<MethodBuilderImpl> _methodDefinitions = new();
@@ -73,14 +75,24 @@ namespace System.Reflection.Emit
             _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 @@ namespace System.Reflection.Emit
             _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
         }
 
+        internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            SetCustomAttributeCore(con, binaryAttribute);
+        }
+
         private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute);
@@ -249,11 +266,27 @@ namespace System.Reflection.Emit
         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 (file)
index 0000000..6297e14
--- /dev/null
@@ -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<int> _stack = new List<int>();
+        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<Type>();
+            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!);
+        }
+    }
+}
index 79b9e77..a43222a 100644 (file)
@@ -21,9 +21,9 @@ namespace System.Reflection.Emit.Tests
         };
 
         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 @@ namespace System.Reflection.Emit.Tests
             {
                 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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
                     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 @@ namespace System.Reflection.Emit.Tests
                 {
                     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 @@ namespace System.Reflection.Emit.Tests
             List<CustomAttributeBuilder>? moduleAttributes = null, List<CustomAttributeBuilder>? typeAttributes = null,
             List<CustomAttributeBuilder>? methodAttributes = null, List<CustomAttributeBuilder>? 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 @@ namespace System.Reflection.Emit.Tests
                                                               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<CustomAttributeData> attributesFromDisk = testType.GetCustomAttributesData();
@@ -280,13 +280,13 @@ namespace System.Reflection.Emit.Tests
                         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<CustomAttributeData> attributesFromDisk = testType.GetCustomAttributesData();
 
@@ -429,7 +429,7 @@ namespace System.Reflection.Emit.Tests
             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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
                 }
             }
         }
+
+        [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 (file)
index 0000000..0fddcb4
--- /dev/null
@@ -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<object[]> 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);
+        }
+    }
+}
@@ -9,7 +9,7 @@ using Xunit;
 
 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 @@ namespace System.Reflection.Emit.Tests
     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)
@@ -10,7 +10,7 @@ using Xunit;
 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 @@ namespace System.Reflection.Emit.Tests
                 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<object[]> VariousInterfacesStructsTestData()
@@ -73,9 +73,9 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
         {
             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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
 
         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 @@ namespace System.Reflection.Emit.Tests
         {
             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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
                 }
                 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 @@ namespace System.Reflection.Emit.Tests
                 }
                 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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
                 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 @@ namespace System.Reflection.Emit.Tests
         {
             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 @@ namespace System.Reflection.Emit.Tests
                 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();
index f672dc3..1395d38 100644 (file)
@@ -63,8 +63,9 @@
     <Compile Include="ModuleBuilder\ModuleBuilderSetCustomAttribute.cs" />
     <Compile Include="ParameterBuilder\ParameterBuilderSetConstant.cs" />
     <Compile Include="PersistableAssemblyBuilder\AssemblySaveCustomAttributeTests.cs" />
-    <Compile Include="PersistableAssemblyBuilder\AssemblySaveWithVariousMembersTests.cs" />
-    <Compile Include="PersistableAssemblyBuilder\AssemblyTools.cs" />
+    <Compile Include="PersistableAssemblyBuilder\AssemblySaveEnumBuilderTests.cs" />
+    <Compile Include="PersistableAssemblyBuilder\AssemblySaveTools.cs" />
+    <Compile Include="PersistableAssemblyBuilder\AssemblySaveTypeBuilderTests.cs" />
     <Compile Include="PropertyBuilder\PropertyBuilderAddOtherMethod.cs" />
     <Compile Include="PropertyBuilder\PropertyBuilderAttributes.cs" />
     <Compile Include="PropertyBuilder\PropertyBuilderCanRead.cs" />
index b417c32..ff8de04 100644 (file)
@@ -424,29 +424,6 @@ namespace System.Reflection.Emit
             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<byte> binaryAttribute)
         {
             _tb.SetCustomAttribute(con, binaryAttribute);