AssemblyBuilder.Save add custom attributes handling. (#84580)
authorBuyaa Namnan <bunamnan@microsoft.com>
Wed, 26 Apr 2023 16:28:25 +0000 (09:28 -0700)
committerGitHub <noreply@github.com>
Wed, 26 Apr 2023 16:28:25 +0000 (09:28 -0700)
* Initial custom attributes handling

* Pseudo custom attributes handling and tests

* Use ReadOnlySpan for CustomAttribute binaryData

* Update some pseudo attributes handling and apply feedback

* Add one more pseudo attribute and a few small changes

* Move pseudo attributes handling

* Remove type loading logic from CustomAttributes parsing, use constants for some values

* Add MarshalAsAttribute handling and apply other feedbacks

---------

Co-authored-by: Aaron Robinson <arobins@microsoft.com>
48 files changed:
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs
src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs
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/AssemblyBuilderImpl.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.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/MethodBuilderImpl.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs
src/libraries/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderSetCustomAttribute.cs
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs [new file with mode: 0644]
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs [moved from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs with 62% similarity]
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs
src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs
src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs

index b740d46..842ebaf 100644 (file)
@@ -21,10 +21,14 @@ namespace System.Reflection.Emit
 {
     public class CustomAttributeBuilder
     {
-        internal readonly ConstructorInfo m_con;
+        private readonly ConstructorInfo m_con;
         private readonly object?[] m_constructorArgs;
         private readonly byte[] m_blob;
 
+        internal ConstructorInfo Ctor => m_con;
+
+        internal byte[] Data => m_blob;
+
         // public constructor to form the custom attribute with constructor and constructor
         // parameters.
         public CustomAttributeBuilder(ConstructorInfo con, object?[] constructorArgs) :
index b2d8248..dd6c841 100644 (file)
@@ -288,7 +288,7 @@ namespace System.Reflection.Emit
         /// <summary>
         /// Use this function if client decides to form the custom attribute blob themselves.
         /// </summary>
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             lock (SyncRoot)
             {
@@ -299,16 +299,5 @@ namespace System.Reflection.Emit
                     binaryAttribute);
             }
         }
-
-        /// <summary>
-        /// Use this function if client wishes to build CustomAttribute using CustomAttributeBuilder.
-        /// </summary>
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            lock (SyncRoot)
-            {
-                customBuilder.CreateCustomAttribute(_manifestModuleBuilder, AssemblyDefToken);
-            }
-        }
     }
 }
index 86b20be..bb1c16b 100644 (file)
@@ -157,16 +157,11 @@ namespace System.Reflection.Emit
             return m_methodBuilder.ReturnType;
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             m_methodBuilder.SetCustomAttribute(con, binaryAttribute);
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            m_methodBuilder.SetCustomAttribute(customBuilder);
-        }
-
         protected override void SetImplementationFlagsCore(MethodImplAttributes attributes)
         {
             m_methodBuilder.SetImplementationFlags(attributes);
index 3e81b8e..2e14270 100644 (file)
@@ -274,18 +274,11 @@ namespace System.Reflection.Emit
         }
 
         // Use this function if client decides to form the custom attribute blob themselves
-
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             m_typeBuilder.SetCustomAttribute(con, binaryAttribute);
         }
 
-        // Use this function if client wishes to build CustomAttribute using CustomAttributeBuilder
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            m_typeBuilder.SetCustomAttribute(customBuilder);
-        }
-
         // Return the class that declared this Field.
         public override Type? DeclaringType => m_typeBuilder.DeclaringType;
 
@@ -293,7 +286,6 @@ namespace System.Reflection.Emit
 
         public override Type? ReflectedType => m_typeBuilder.ReflectedType;
 
-
         // Returns true if one or more instance of attributeType is defined on this member.
         public override bool IsDefined(Type attributeType, bool inherit)
         {
@@ -329,7 +321,6 @@ namespace System.Reflection.Emit
             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 41befaa..cf3da5e 100644 (file)
@@ -80,7 +80,7 @@ namespace System.Reflection.Emit
 
         // Use this function if client decides to form the custom attribute blob themselves
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             m_type.ThrowIfCreated();
 
@@ -91,13 +91,6 @@ namespace System.Reflection.Emit
                 binaryAttribute);
         }
 
-        // Use this function if client wishes to build CustomAttribute using CustomAttributeBuilder
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            m_type.ThrowIfCreated();
-            customBuilder.CreateCustomAttribute(m_module, m_evToken);
-        }
-
         private readonly string m_name;         // The name of the event
         private readonly int m_evToken;      // The token of this event
         private readonly RuntimeModuleBuilder m_module;
index da3341e..e607b15 100644 (file)
@@ -150,7 +150,7 @@ namespace System.Reflection.Emit
             RuntimeTypeBuilder.SetConstantValue(m_typeBuilder.GetModuleBuilder(), m_fieldTok, m_fieldType, defaultValue);
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             RuntimeModuleBuilder moduleBuilder = (RuntimeModuleBuilder)m_typeBuilder.Module;
 
@@ -160,13 +160,6 @@ namespace System.Reflection.Emit
                 m_fieldTok, moduleBuilder.GetMethodMetadataToken(con), binaryAttribute);
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            m_typeBuilder.ThrowIfCreated();
-
-            customBuilder.CreateCustomAttribute((RuntimeModuleBuilder)m_typeBuilder.Module, m_fieldTok);
-        }
-
         #endregion
     }
 }
index 6301e2a..4245e40 100644 (file)
@@ -215,16 +215,11 @@ namespace System.Reflection.Emit
         #endregion
 
         #region Protected Members Overrides
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             m_type.SetGenParamCustomAttribute(con, binaryAttribute);
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            m_type.SetGenParamCustomAttribute(customBuilder);
-        }
-
         protected override void SetBaseTypeConstraintCore([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? baseTypeConstraint)
         {
             m_type.SetParent(baseTypeConstraint);
index 6b2cdad..b824378 100644 (file)
@@ -695,7 +695,7 @@ namespace System.Reflection.Emit
             return GetModuleBuilder();
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             ThrowIfGeneric();
             RuntimeTypeBuilder.DefineCustomAttribute(m_module, MetadataToken,
@@ -706,15 +706,6 @@ namespace System.Reflection.Emit
                 ParseCA(con);
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            ThrowIfGeneric();
-            customBuilder.CreateCustomAttribute(m_module, MetadataToken);
-
-            if (IsKnownCA(customBuilder.m_con))
-                ParseCA(customBuilder.m_con);
-        }
-
         // this method should return true for any and every ca that requires more work
         // than just setting the ca
         private static bool IsKnownCA(ConstructorInfo con)
index 637f7cd..496c24b 100644 (file)
@@ -1289,7 +1289,7 @@ namespace System.Reflection.Emit
 
         #region Other
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             RuntimeTypeBuilder.DefineCustomAttribute(
                 this,
@@ -1298,11 +1298,6 @@ namespace System.Reflection.Emit
                 binaryAttribute);
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            customBuilder.CreateCustomAttribute(this, 1);   // This is hard coding the module token to 1
-        }
-
         #endregion
 
         #endregion
index ee71d32..eccafa7 100644 (file)
@@ -97,8 +97,7 @@ namespace System.Reflection.Emit
         }
 
         // Use this function if client decides to form the custom attribute blob themselves
-
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             m_containingType.ThrowIfCreated();
             RuntimeTypeBuilder.DefineCustomAttribute(
@@ -108,13 +107,6 @@ namespace System.Reflection.Emit
                 binaryAttribute);
         }
 
-        // Use this function if client wishes to build CustomAttribute using CustomAttributeBuilder
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
-        {
-            m_containingType.ThrowIfCreated();
-            customBuilder.CreateCustomAttribute(m_moduleBuilder, m_tkProperty);
-        }
-
         // Not supported functions in dynamic module.
         public override object GetValue(object? obj, object?[]? index)
         {
index 02108de..d550f65 100644 (file)
@@ -111,13 +111,12 @@ namespace System.Reflection.Emit
             private readonly byte[]? m_binaryAttribute;
             private readonly CustomAttributeBuilder? m_customBuilder;
 
-            public CustAttr(ConstructorInfo con, byte[] binaryAttribute)
+            public CustAttr(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
             {
                 ArgumentNullException.ThrowIfNull(con);
-                ArgumentNullException.ThrowIfNull(binaryAttribute);
 
                 m_con = con;
-                m_binaryAttribute = binaryAttribute;
+                m_binaryAttribute = binaryAttribute.ToArray();
             }
 
             public CustAttr(CustomAttributeBuilder customBuilder)
@@ -173,21 +172,13 @@ namespace System.Reflection.Emit
 
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeBuilder_DefineCustomAttribute")]
         private static partial void DefineCustomAttribute(QCallModule module, int tkAssociate, int tkConstructor,
-            byte[]? attr, int attrLength);
+            ReadOnlySpan<byte> attr, int attrLength);
 
         internal static void DefineCustomAttribute(RuntimeModuleBuilder module, int tkAssociate, int tkConstructor,
-            byte[]? attr)
+            ReadOnlySpan<byte> attr)
         {
-            byte[]? localAttr = null;
-
-            if (attr != null)
-            {
-                localAttr = new byte[attr.Length];
-                Buffer.BlockCopy(attr, 0, localAttr, 0, attr.Length);
-            }
-
             DefineCustomAttribute(new QCallModule(ref module), tkAssociate, tkConstructor,
-                localAttr, (localAttr != null) ? localAttr.Length : 0);
+                attr, attr.Length);
         }
 
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeBuilder_DefineProperty", StringMarshalling = StringMarshalling.Utf16)]
@@ -670,7 +661,7 @@ namespace System.Reflection.Emit
             m_genParamAttributes = genericParameterAttributes;
         }
 
-        internal void SetGenParamCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
+        internal void SetGenParamCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             CustAttr ca = new CustAttr(con, binaryAttribute);
 
@@ -1858,14 +1849,14 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
+        internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
-            DefineCustomAttribute(m_module, m_tdType, m_module.GetMethodMetadataToken(con), binaryAttribute);
+            SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
-            customBuilder.CreateCustomAttribute(m_module, m_tdType);
+            DefineCustomAttribute(m_module, m_tdType, m_module.GetMethodMetadataToken(con), binaryAttribute);
         }
 
         #endregion
index 5a80b43..818dccc 100644 (file)
@@ -24,5 +24,10 @@ namespace System.Reflection.Emit
         {
             ReflectionEmitThrower.ThrowPlatformNotSupportedException();
         }
+
+#pragma warning disable CA1822 // Member 'Ctor' does not access instance data and can be marked as static
+        internal ConstructorInfo Ctor => default;
+        internal byte[] Data => default;
+#pragma warning restore CA1822
     }
 }
index 3abc692..2ce19ba 100644 (file)
@@ -39,17 +39,15 @@ namespace System.Reflection.Emit
             SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
 
-            SetCustomAttributeCore(customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
         }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
-
         [System.ObsoleteAttribute("Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead.", DiagnosticId = "SYSLIB0012", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
         [RequiresAssemblyFiles(ThrowingMessageInRAF)]
         public override string? CodeBase => throw new NotSupportedException(SR.NotSupported_DynamicAssembly);
index 3daac6f..82d95cb 100644 (file)
@@ -31,14 +31,21 @@ namespace System.Reflection.Emit
         protected abstract ILGenerator GetILGeneratorCore(int streamSize);
 
         public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
-            => SetCustomAttributeCore(con, binaryAttribute);
+        {
+            ArgumentNullException.ThrowIfNull(con);
+            ArgumentNullException.ThrowIfNull(binaryAttribute);
+
+            SetCustomAttributeCore(con, binaryAttribute);
+        }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
-            => SetCustomAttributeCore(customBuilder);
+        {
+            ArgumentNullException.ThrowIfNull(customBuilder);
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
+        }
 
         public void SetImplementationFlags(MethodImplAttributes attributes)
             => SetImplementationFlagsCore(attributes);
index f3cdb7f..8017aab 100644 (file)
@@ -35,11 +35,9 @@ namespace System.Reflection.Emit
         public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
             => SetCustomAttributeCore(con, binaryAttribute);
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
-            => SetCustomAttributeCore(customBuilder);
-
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
+            => SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
     }
 }
index 4c4a8f0..75b1a9e 100644 (file)
@@ -27,17 +27,15 @@ namespace System.Reflection.Emit
             SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
 
-            SetCustomAttributeCore(customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
         }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
-
         public void SetRaiseMethod(MethodBuilder mdBuilder)
             => SetRaiseMethodCore(mdBuilder);
 
index 2609261..1e879c0 100644 (file)
@@ -22,17 +22,15 @@ namespace System.Reflection.Emit
             SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
 
-            SetCustomAttributeCore(customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
         }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
-
         public void SetOffset(int iOffset)
             => SetOffsetCore(iOffset);
 
index 3594991..dfc38ba 100644 (file)
@@ -13,17 +13,15 @@ namespace System.Reflection.Emit
 
         public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) => SetCustomAttributeCore(con, binaryAttribute);
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
 
-            SetCustomAttributeCore(customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
         }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
-
         public void SetBaseTypeConstraint([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? baseTypeConstraint) => SetBaseTypeConstraintCore(baseTypeConstraint);
 
         protected abstract void SetBaseTypeConstraintCore([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? baseTypeConstraint);
index dab5cd5..ef2c0ce 100644 (file)
@@ -56,17 +56,20 @@ namespace System.Reflection.Emit
             SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            SetCustomAttributeCore(con, binaryAttribute);
+        }
+
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
 
-            SetCustomAttributeCore(customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
         }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
-
         public void SetImplementationFlags(MethodImplAttributes attributes) => SetImplementationFlagsCore(attributes);
 
         protected abstract void SetImplementationFlagsCore(MethodImplAttributes attributes);
index 021ddd0..21f6843 100644 (file)
@@ -134,17 +134,15 @@ namespace System.Reflection.Emit
             SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
 
-            SetCustomAttributeCore(customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
         }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
-
         public abstract int GetTypeMetadataToken(Type type);
         public abstract int GetFieldMetadataToken(FieldInfo field);
         public abstract int GetMethodMetadataToken(MethodInfo method);
index 924c40f..43b948a 100644 (file)
@@ -27,15 +27,14 @@ namespace System.Reflection.Emit
             SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
-            SetCustomAttributeCore(customBuilder);
-        }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
+        }
 
         public void SetGetMethod(MethodBuilder mdBuilder)
             => SetGetMethodCore(mdBuilder);
index e715ac8..e3f3b9a 100644 (file)
@@ -277,17 +277,15 @@ namespace System.Reflection.Emit
             SetCustomAttributeCore(con, binaryAttribute);
         }
 
-        protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute);
+        protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute);
 
         public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
         {
             ArgumentNullException.ThrowIfNull(customBuilder);
 
-            SetCustomAttributeCore(customBuilder);
+            SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data);
         }
 
-        protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder);
-
         public void SetParent([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent)
             => SetParentCore(parent);
 
index 14b8c7b..f3cd61d 100644 (file)
@@ -55,9 +55,8 @@ namespace System.Reflection.Emit
         public override System.Type? GetType(string name, bool throwOnError, bool ignoreCase) { throw null; }
         public override bool IsDefined(System.Type attributeType, bool inherit) { throw null; }
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
     }
     [System.FlagsAttribute]
     public enum AssemblyBuilderAccess
@@ -91,9 +90,8 @@ namespace System.Reflection.Emit
         public override object Invoke(System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder? binder, object?[]? parameters, System.Globalization.CultureInfo? culture) { throw null; }
         public override bool IsDefined(System.Type attributeType, bool inherit) { throw null; }
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
         public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { }
         protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes);
         public override string ToString() { throw null; }
@@ -186,9 +184,8 @@ namespace System.Reflection.Emit
         public override System.Type MakeByRefType() { throw null; }
         public override System.Type MakePointerType() { throw null; }
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
     }
     public abstract partial class EventBuilder
     {
@@ -198,9 +195,8 @@ namespace System.Reflection.Emit
         public void SetAddOnMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { }
         protected abstract void SetAddOnMethodCore(System.Reflection.Emit.MethodBuilder mdBuilder);
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
         public void SetRaiseMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { }
         protected abstract void SetRaiseMethodCore(System.Reflection.Emit.MethodBuilder mdBuilder);
         public void SetRemoveOnMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { }
@@ -224,9 +220,8 @@ namespace System.Reflection.Emit
         public void SetConstant(object? defaultValue) { }
         protected abstract void SetConstantCore(object? defaultValue);
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
         public void SetOffset(int iOffset) { }
         protected abstract void SetOffsetCore(int iOffset);
         public override void SetValue(object? obj, object? val, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder? binder, System.Globalization.CultureInfo? culture) { }
@@ -327,9 +322,8 @@ namespace System.Reflection.Emit
         public void SetBaseTypeConstraint([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type? baseTypeConstraint) { }
         protected abstract void SetBaseTypeConstraintCore([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] Type? baseTypeConstraint);
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
         public void SetGenericParameterAttributes(System.Reflection.GenericParameterAttributes genericParameterAttributes) { }
         protected abstract void SetGenericParameterAttributesCore(System.Reflection.GenericParameterAttributes genericParameterAttributes);
         public void SetInterfaceConstraints(params System.Type[]? interfaceConstraints) { }
@@ -380,9 +374,8 @@ namespace System.Reflection.Emit
         [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
         public override System.Reflection.MethodInfo MakeGenericMethod(params System.Type[] typeArguments) { throw null; }
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
         public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { }
         protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes);
         public void SetParameters(params System.Type[] parameterTypes) { }
@@ -474,9 +467,8 @@ namespace System.Reflection.Emit
         [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Trimming changes metadata tokens")]
         public override System.Type ResolveType(int metadataToken, System.Type[]? genericTypeArguments, System.Type[]? genericMethodArguments) { throw null; }
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
     }
     public abstract partial class PropertyBuilder : System.Reflection.PropertyInfo
     {
@@ -503,9 +495,8 @@ namespace System.Reflection.Emit
         public void SetConstant(object? defaultValue) { }
         protected abstract void SetConstantCore(object? defaultValue);
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
         public void SetGetMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { }
         protected abstract void SetGetMethodCore(System.Reflection.Emit.MethodBuilder mdBuilder);
         public void SetSetMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { }
@@ -671,9 +662,8 @@ namespace System.Reflection.Emit
         public override System.Type MakeGenericType(params System.Type[] typeArguments) { throw null; }
         public override System.Type MakePointerType() { throw null; }
         public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, byte[] binaryAttribute);
         public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
-        protected abstract void SetCustomAttributeCore(System.Reflection.Emit.CustomAttributeBuilder customBuilder);
+        protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
         public void SetParent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type? parent) { }
         protected abstract void SetParentCore([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type? parent);
         public override string ToString() { throw null; }
index b3ad859..dcbd221 100644 (file)
   <data name="NotSupported_DynamicModule" xml:space="preserve">
     <value>The invoked member is not supported in a dynamic module.</value>
   </data>
-  <data name="Argument_InvalidName" xml:space="preserve">
-    <value>Invalid name.</value>
+  <data name="Argument_InvalidTypeArgument" xml:space="preserve">
+    <value>The type code may not be used as a type argument of a custom attribute .</value>
+  </data>
+  <data name="InvalidOperation_EmptyFieldForCustomAttribute" xml:space="preserve">
+    <value>Custom attribute '{0}' doesn't contain a field named '{1}'.</value>
+  </data>
+  <data name="Argument_InvalidCustomAttributeLength" xml:space="preserve">
+    <value>Custom attribute '{0}' data length is only '{1}'.</value>
+  </data>
+  <data name="Argument_InvalidProlog" xml:space="preserve">
+    <value>Custom attribute '{0}' prolog invalid.</value>
+  </data>
+  <data name="Argument_UnknownNamedType" xml:space="preserve">
+    <value>Custom attribute '{0}' has unknown named type '{1}'.</value>
+  </data>
+  <data name="NotImplemented_TypeForValue" xml:space="preserve">
+    <value>Type '{0}' not handled in the custom attribute value decoder.</value>
+  </data>
+  <data name="Argument_DllNameCannotBeEmpty" xml:space="preserve">
+    <value>DllName cannot be empty.</value>
   </data>
 </root>
\ No newline at end of file
index bc290ba..a647e57 100644 (file)
@@ -5,6 +5,7 @@
     <ContractTypesPartiallyMoved>true</ContractTypesPartiallyMoved>
   </PropertyGroup>
   <ItemGroup>
+    <Compile Include="System\Reflection\Emit\CustomAttributeWrapper.cs" />
     <Compile Include="System\Reflection\Emit\AssemblyBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\FieldBuilderImpl.cs" />
     <Compile Include="System\Reflection\Emit\MethodBuilderImpl.cs" />
index d8543a2..238be39 100644 (file)
@@ -17,6 +17,8 @@ namespace System.Reflection.Emit
         private ModuleBuilderImpl? _module;
         private bool _previouslySaved;
 
+        internal List<CustomAttributeWrapper>? _customAttributes;
+
         internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes)
         {
             ArgumentNullException.ThrowIfNull(name);
@@ -77,7 +79,7 @@ namespace System.Reflection.Emit
             }
 
             // Add assembly metadata
-            _metadataBuilder.AddAssembly(
+            AssemblyDefinitionHandle assemblyHandle = _metadataBuilder.AddAssembly(
                _metadataBuilder.GetOrAddString(value: _assemblyName.Name!),
                version: _assemblyName.Version ?? new Version(0, 0, 0, 0),
                culture: _assemblyName.CultureName == null ? default : _metadataBuilder.GetOrAddString(value: _assemblyName.CultureName),
@@ -88,6 +90,7 @@ namespace System.Reflection.Emit
 #pragma warning restore SYSLIB0037
                );
 
+            _module.WriteCustomAttributes(_customAttributes, assemblyHandle);
             // Add module's metadata
             _module.AppendMetadata();
 
@@ -128,8 +131,10 @@ namespace System.Reflection.Emit
             return null;
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) => throw new NotImplementedException();
-
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotImplementedException();
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            _customAttributes ??= new List<CustomAttributeWrapper>();
+            _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
+        }
     }
 }
diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs
new file mode 100644 (file)
index 0000000..2026b79
--- /dev/null
@@ -0,0 +1,177 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers.Binary;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection.Metadata;
+
+namespace System.Reflection.Emit
+{
+    internal readonly struct CustomAttributeWrapper
+    {
+        private readonly ConstructorInfo _constructorInfo;
+        private readonly byte[] _binaryAttribute;
+
+        public CustomAttributeWrapper(ConstructorInfo constructorInfo, ReadOnlySpan<byte> binaryAttribute)
+        {
+            _constructorInfo = constructorInfo;
+            _binaryAttribute = binaryAttribute.ToArray(); // TODO: Update to BlobHandle when public API public APi for MetadataBuilder.GetOrAddBlob(ReadOnlySpan<byte>) added
+        }
+
+        public ConstructorInfo Ctor => _constructorInfo;
+        public byte[] Data => _binaryAttribute;
+    }
+
+    internal struct CustomAttributeInfo
+    {
+        public ConstructorInfo _ctor;
+        public object?[] _ctorArgs;
+        public string[] _namedParamNames;
+        public object?[] _namedParamValues;
+        private const int Field = 0x53;
+        private const int EnumType = 0x55;
+        private const int NullValue = 0xff;
+        private const int OneByteMask = 0x7f;
+        private const int TwoByteMask = 0x3f;
+        private const int FourByteMask = 0x1f;
+
+        internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, ReadOnlySpan<byte> binaryAttribute)
+        {
+            int pos = 2;
+            CustomAttributeInfo info = default;
+
+            if (binaryAttribute.Length < 2)
+            {
+                throw new ArgumentException(SR.Format(SR.Argument_InvalidCustomAttributeLength, ctor.DeclaringType, binaryAttribute.Length), nameof(binaryAttribute));
+            }
+            if ((binaryAttribute[0] != 0x01) || (binaryAttribute[1] != 0x00))
+            {
+                throw new ArgumentException(SR.Format(SR.Argument_InvalidProlog, ctor.DeclaringType), nameof(binaryAttribute));
+            }
+
+            ParameterInfo[] pi = ctor.GetParameters();
+            info._ctor = ctor;
+            info._ctorArgs = new object?[pi.Length];
+            for (int i = 0; i < pi.Length; ++i)
+            {
+                info._ctorArgs[i] = DecodeCustomAttributeValue(pi[i].ParameterType, binaryAttribute, pos, out pos);
+            }
+            int numNamed = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(pos));
+            pos += 2;
+
+            info._namedParamNames = new string[numNamed];
+            info._namedParamValues = new object[numNamed];
+            for (int i = 0; i < numNamed; ++i)
+            {
+                int namedType = binaryAttribute[pos++];
+                int dataType = binaryAttribute[pos++];
+
+                if (dataType == EnumType)
+                {
+                    // skip bytes for Enum type name;
+                    int len2 = DecodeLen(binaryAttribute, pos, out pos);
+                    pos += len2;
+                }
+
+                int len = DecodeLen(binaryAttribute, pos, out pos);
+                string name = StringFromBytes(binaryAttribute, pos, len);
+                info._namedParamNames[i] = name;
+                pos += len;
+
+                if (namedType == Field)
+                {
+                    // For known pseudo custom attributes underlying Enum type is int
+                    Type fieldType = dataType == EnumType ? typeof(int) : ElementTypeToType((PrimitiveSerializationTypeCode)dataType);
+                    info._namedParamValues[i] = DecodeCustomAttributeValue(fieldType, binaryAttribute, pos, out pos); ;
+                }
+                else
+                {
+                    throw new ArgumentException(SR.Format(SR.Argument_UnknownNamedType, ctor.DeclaringType, namedType), nameof(binaryAttribute));
+                }
+            }
+
+            return info;
+        }
+
+        private static string StringFromBytes(ReadOnlySpan<byte> data, int pos, int len)
+        {
+            return Text.Encoding.UTF8.GetString(data.Slice(pos, len));
+        }
+
+        private static int DecodeLen(ReadOnlySpan<byte> data, int pos, out int rpos)
+        {
+            int len;
+            if ((data[pos] & 0x80) == 0)
+            {
+                len = (data[pos++] & OneByteMask);
+            }
+            else if ((data[pos] & 0x40) == 0)
+            {
+                len = ((data[pos] & TwoByteMask) << 8) + data[pos + 1];
+                pos += 2;
+            }
+            else
+            {
+                len = ((data[pos] & FourByteMask) << 24) + (data[pos + 1] << 16) + (data[pos + 2] << 8) + data[pos + 3];
+                pos += 4;
+            }
+            rpos = pos;
+            return len;
+        }
+
+        private static object? DecodeCustomAttributeValue(Type t, ReadOnlySpan<byte> data, int pos, out int rpos)
+        {
+            switch (Type.GetTypeCode(t))
+            {
+                case TypeCode.String:
+                    if (data[pos] == NullValue)
+                    {
+                        rpos = pos + 1;
+                        return null;
+                    }
+                    int len = DecodeLen(data, pos, out pos);
+                    rpos = pos + len;
+                    return StringFromBytes(data, pos, len);
+                case TypeCode.Int32:
+                    rpos = pos + 4;
+                    return BinaryPrimitives.ReadInt32LittleEndian(data.Slice(pos));
+                case TypeCode.Int16:
+                    rpos = pos + 2;
+                    return BinaryPrimitives.ReadInt16LittleEndian(data.Slice(pos));
+                case TypeCode.Boolean:
+                    rpos = pos + 1;
+                    return (data[pos] == 0) ? false : true;
+                case TypeCode.Object:
+                    int subtype = data[pos];
+                    pos += 1;
+
+                    if (subtype >= 0x02 && subtype <= 0x0e)
+                    {
+                        return DecodeCustomAttributeValue(ElementTypeToType((PrimitiveSerializationTypeCode)subtype), data, pos, out rpos);
+                    }
+                    break;
+            }
+
+            throw new NotImplementedException(SR.Format(SR.NotImplemented_TypeForValue, t));
+        }
+
+        private static Type ElementTypeToType(PrimitiveSerializationTypeCode elementType) =>
+            elementType switch
+            {
+                PrimitiveSerializationTypeCode.Boolean => typeof(bool),
+                PrimitiveSerializationTypeCode.Char => typeof(char),
+                PrimitiveSerializationTypeCode.SByte => typeof(sbyte),
+                PrimitiveSerializationTypeCode.Byte => typeof(byte),
+                PrimitiveSerializationTypeCode.Int16 => typeof(short),
+                PrimitiveSerializationTypeCode.UInt16 => typeof(ushort),
+                PrimitiveSerializationTypeCode.Int32 => typeof(int),
+                PrimitiveSerializationTypeCode.UInt32 => typeof(uint),
+                PrimitiveSerializationTypeCode.Int64 => typeof(long),
+                PrimitiveSerializationTypeCode.UInt64 => typeof(ulong),
+                PrimitiveSerializationTypeCode.Single => typeof(float),
+                PrimitiveSerializationTypeCode.Double => typeof(double),
+                PrimitiveSerializationTypeCode.String => typeof(string),
+                _ => throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"),
+            };
+    }
+}
index c017afa..3025818 100644 (file)
@@ -1,7 +1,14 @@
 ï»¿// Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
 
 namespace System.Reflection.Emit
 {
@@ -9,8 +16,12 @@ namespace System.Reflection.Emit
     {
         private readonly TypeBuilderImpl _typeBuilder;
         private readonly string _fieldName;
-        private readonly FieldAttributes _attributes;
         private readonly Type _fieldType;
+        private FieldAttributes _attributes;
+
+        internal MarshallingInfo? _marshallingInfo;
+        internal int _offset;
+        internal List<CustomAttributeWrapper>? _customAttributes;
 
         internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes)
         {
@@ -18,14 +29,45 @@ namespace System.Reflection.Emit
             _typeBuilder = typeBuilder;
             _fieldType = type;
             _attributes = attributes & ~FieldAttributes.ReservedMask;
+            _offset = -1;
         }
 
-        #region MemberInfo Overrides
         protected override void SetConstantCore(object? defaultValue) => throw new NotImplementedException();
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) => throw new NotImplementedException();
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            // Handle pseudo custom attributes
+            switch (con.ReflectedType!.FullName)
+            {
+                case "System.Runtime.InteropServices.FieldOffsetAttribute":
+                    Debug.Assert(binaryAttribute.Length >= 6);
+                    _offset = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(2));
+                return;
+                case "System.NonSerializedAttribute":
+#pragma warning disable SYSLIB0050 // 'FieldAttributes.NotSerialized' is obsolete: 'Formatter-based serialization is obsolete and should not be used'.
+                    _attributes |= FieldAttributes.NotSerialized;
+#pragma warning restore SYSLIB0050
+                return;
+                case "System.Runtime.CompilerServices.SpecialNameAttribute":
+                    _attributes |= FieldAttributes.SpecialName;
+                return;
+                case "System.Runtime.InteropServices.MarshalAsAttribute":
+                    _attributes |= FieldAttributes.HasFieldMarshal;
+                    _marshallingInfo = MarshallingInfo.ParseMarshallingInfo(con, binaryAttribute);
+                return;
+            }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotImplementedException();
-        protected override void SetOffsetCore(int iOffset) => throw new NotImplementedException();
+            _customAttributes ??= new List<CustomAttributeWrapper>();
+            _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
+        }
+
+        protected override void SetOffsetCore(int iOffset)
+        {
+            ArgumentOutOfRangeException.ThrowIfNegative(iOffset);
+
+            _offset = iOffset;
+        }
+
+        #region MemberInfo Overrides
 
         public override int MetadataToken => throw new NotImplementedException();
 
@@ -59,5 +101,386 @@ namespace System.Reflection.Emit
 
         public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_DynamicModule);
         #endregion
+
+        internal sealed class MarshallingInfo
+        {
+            internal UnmanagedType _marshalType;
+            private int _marshalArrayElementType;      // safe array: VarEnum; array: UnmanagedType
+            private int _marshalArrayElementCount;     // number of elements in an array, length of a string, or Unspecified
+            private int _marshalParameterIndex;        // index of parameter that specifies array size (short) or IID (int), or Unspecified
+            private object? _marshalTypeNameOrSymbol;  // custom marshaller: string or Type; safe array: element type
+            private string? _marshalCookie;
+
+            internal const int Invalid = -1;
+            private const UnmanagedType InvalidUnmanagedType = (UnmanagedType)Invalid;
+            private const VarEnum InvalidVariantType = (VarEnum)Invalid;
+            internal const int MaxMarshalInteger = 0x1fffffff;
+
+            internal BlobHandle PopulateMarshallingBlob(MetadataBuilder builder)
+            {
+                var blobBuilder = new BlobBuilder();
+                SerializeMarshallingDescriptor(blobBuilder);
+                return builder.GetOrAddBlob(blobBuilder);
+
+            }
+
+            // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs#L3543
+            internal void SerializeMarshallingDescriptor(BlobBuilder writer)
+            {
+                writer.WriteCompressedInteger((int)_marshalType);
+                switch (_marshalType)
+                {
+                    case UnmanagedType.ByValArray: // NATIVE_TYPE_FIXEDARRAY
+                        Debug.Assert(_marshalArrayElementCount >= 0);
+                        writer.WriteCompressedInteger(_marshalArrayElementCount);
+                        if (_marshalArrayElementType >= 0)
+                        {
+                            writer.WriteCompressedInteger(_marshalArrayElementType);
+                        }
+                        break;
+                    case UnmanagedType.CustomMarshaler:
+                        writer.WriteUInt16(0); // padding
+
+                        switch (_marshalTypeNameOrSymbol)
+                        {
+                            case Type type:
+                                writer.WriteSerializedString(type.FullName); // or AssemblyQualifiedName?
+                                break;
+                            case null:
+                                writer.WriteByte(0);
+                                break;
+                            default:
+                                writer.WriteSerializedString((string)_marshalTypeNameOrSymbol);
+                                break;
+                        }
+
+                        if (_marshalCookie != null)
+                        {
+                            writer.WriteSerializedString(_marshalCookie);
+                        }
+                        else
+                        {
+                            writer.WriteByte(0);
+                        }
+                        break;
+                    case UnmanagedType.LPArray: // NATIVE_TYPE_ARRAY
+                        Debug.Assert(_marshalArrayElementType >= 0);
+                        writer.WriteCompressedInteger(_marshalArrayElementType);
+                        if (_marshalParameterIndex >= 0)
+                        {
+                            writer.WriteCompressedInteger(_marshalParameterIndex);
+                            if (_marshalArrayElementCount >= 0)
+                            {
+                                writer.WriteCompressedInteger(_marshalArrayElementCount);
+                                writer.WriteByte(1); // The parameter number is valid
+                            }
+                        }
+                    else if (_marshalArrayElementCount >= 0)
+                        {
+                            writer.WriteByte(0); // Dummy parameter value emitted so that NumberOfElements can be in a known position
+                            writer.WriteCompressedInteger(_marshalArrayElementCount);
+                            writer.WriteByte(0); // The parameter number is not valid
+                        }
+                        break;
+                    case UnmanagedType.SafeArray:
+                        VarEnum safeArrayElementSubtype = (VarEnum)_marshalArrayElementType;
+                        if (safeArrayElementSubtype >= 0)
+                        {
+                            writer.WriteCompressedInteger((int)safeArrayElementSubtype);
+
+                            if (_marshalTypeNameOrSymbol is Type elementType)
+                            {
+                                writer.WriteSerializedString(elementType.FullName);
+                            }
+                        }
+                        break;
+                    case UnmanagedType.ByValTStr: // NATIVE_TYPE_FIXEDSYSSTRING
+                        writer.WriteCompressedInteger(_marshalArrayElementCount);
+                        break;
+
+                    case UnmanagedType.Interface:
+                    case UnmanagedType.IDispatch:
+                    case UnmanagedType.IUnknown:
+                        if (_marshalParameterIndex >= 0)
+                        {
+                            writer.WriteCompressedInteger(_marshalParameterIndex);
+                        }
+                        break;
+                }
+            }
+
+            internal void SetMarshalAsCustom(object typeSymbolOrName, string? cookie)
+            {
+                _marshalType = UnmanagedType.CustomMarshaler;
+                _marshalTypeNameOrSymbol = typeSymbolOrName;
+                _marshalCookie = cookie;
+            }
+
+            internal void SetMarshalAsComInterface(UnmanagedType unmanagedType, int? parameterIndex)
+            {
+                Debug.Assert(parameterIndex == null || parameterIndex >= 0 && parameterIndex <= MaxMarshalInteger);
+
+                _marshalType = unmanagedType;
+                _marshalParameterIndex = parameterIndex ?? Invalid;
+            }
+
+            internal void SetMarshalAsArray(UnmanagedType? elementType, int? elementCount, short? parameterIndex)
+            {
+                Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger);
+                Debug.Assert(parameterIndex == null || parameterIndex >= 0);
+
+                _marshalType = UnmanagedType.LPArray;
+                _marshalArrayElementType = (int)(elementType ?? (UnmanagedType)0x50);
+                _marshalArrayElementCount = elementCount ?? Invalid;
+                _marshalParameterIndex = parameterIndex ?? Invalid;
+            }
+
+            internal void SetMarshalAsFixedArray(UnmanagedType? elementType, int? elementCount)
+            {
+                Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger);
+                Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger);
+
+                _marshalType = UnmanagedType.ByValArray;
+                _marshalArrayElementType = (int)(elementType ?? InvalidUnmanagedType);
+                _marshalArrayElementCount = elementCount ?? Invalid;
+            }
+
+            internal void SetMarshalAsSafeArray(VarEnum? elementType, Type? type)
+            {
+                Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger);
+
+                _marshalType = UnmanagedType.SafeArray;
+                _marshalArrayElementType = (int)(elementType ?? InvalidVariantType);
+                _marshalTypeNameOrSymbol = type;
+            }
+
+            internal void SetMarshalAsFixedString(int elementCount)
+            {
+                Debug.Assert(elementCount >= 0 && elementCount <= MaxMarshalInteger);
+
+                _marshalType = UnmanagedType.ByValTStr;
+                _marshalArrayElementCount = elementCount;
+            }
+
+            internal void SetMarshalAsSimpleType(UnmanagedType type)
+            {
+                Debug.Assert(type >= 0 && (int)type <= MaxMarshalInteger);
+                _marshalType = type;
+            }
+
+            // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/Symbols/Attributes/MarshalAsAttributeDecoder.cs
+            internal static MarshallingInfo ParseMarshallingInfo(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+            {
+                CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute);
+                MarshallingInfo info = new();
+                UnmanagedType unmanagedType;
+
+                if (attributeInfo._ctorArgs[0] is short shortValue)
+                {
+                    unmanagedType = (UnmanagedType)shortValue;
+                }
+                else
+                {
+                    unmanagedType = (UnmanagedType)attributeInfo._ctorArgs[0]!;
+                }
+
+                switch (unmanagedType)
+                {
+                    case UnmanagedType.CustomMarshaler:
+                        DecodeMarshalAsCustom(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info);
+                        break;
+                    case UnmanagedType.Interface:
+                    case UnmanagedType.IDispatch:
+                    case UnmanagedType.IUnknown:
+                        DecodeMarshalAsComInterface(attributeInfo._namedParamNames, attributeInfo._namedParamValues, unmanagedType, info);
+                        break;
+                    case UnmanagedType.LPArray:
+                        DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: false, info);
+                        break;
+                    case UnmanagedType.ByValArray:
+                        DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: true, info);
+                        break;
+                    case UnmanagedType.SafeArray:
+                        DecodeMarshalAsSafeArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info);
+                        break;
+                    case UnmanagedType.ByValTStr:
+                        DecodeMarshalAsFixedString(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info);
+                        break;
+#pragma warning disable CS0618 // Type or member is obsolete
+                    case UnmanagedType.VBByRefStr:
+#pragma warning restore CS0618
+                        // named parameters ignored with no error
+                        info.SetMarshalAsSimpleType(unmanagedType);
+                        break;
+                    default:
+                        if ((int)unmanagedType < 0 || (int)unmanagedType > MaxMarshalInteger)
+                        {
+                            throw new ArgumentException(SR.Argument_InvalidTypeArgument, nameof(binaryAttribute));
+                        }
+                        else
+                        {
+                            // named parameters ignored with no error
+                            info.SetMarshalAsSimpleType(unmanagedType);
+                        }
+                        break;
+                }
+
+                return info;
+            }
+
+            private static void DecodeMarshalAsFixedString(string[] paramNames, object?[] values, MarshallingInfo info)
+            {
+                int elementCount = -1;
+
+                for (int i = 0; i < paramNames.Length; i++)
+                {
+                    switch (paramNames[i])
+                    {
+                        case "SizeConst":
+                            elementCount = (int)values[i]!;
+                            break;
+                        case "ArraySubType":
+                        case "SizeParamIndex":
+                            throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute");
+                        // other parameters ignored with no error
+                    }
+                }
+
+                if (elementCount < 0)
+                {
+                    // SizeConst must be specified:
+                    throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute");
+                }
+
+                info.SetMarshalAsFixedString(elementCount);
+            }
+
+            private static void DecodeMarshalAsSafeArray(string[] paramNames, object?[] values, MarshallingInfo info)
+            {
+                VarEnum? elementTypeVariant = null;
+                Type? elementType = null;
+                int symbolIndex = -1;
+
+                for (int i = 0; i < paramNames.Length; i++)
+                {
+                    switch (paramNames[i])
+                    {
+                        case "SafeArraySubType":
+                            elementTypeVariant = (VarEnum)values[i]!;
+                            break;
+                        case "SafeArrayUserDefinedSubType":
+                            elementType = (Type?)values[i];
+                            symbolIndex = i;
+                            break;
+                        case "ArraySubType":
+                        case "SizeConst":
+                        case "SizeParamIndex":
+                            throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute");
+                            // other parameters ignored with no error
+                    }
+                }
+
+                switch (elementTypeVariant)
+                {
+                    case VarEnum.VT_DISPATCH:
+                    case VarEnum.VT_UNKNOWN:
+                    case VarEnum.VT_RECORD:
+                        // only these variants accept specification of user defined subtype
+                        break;
+
+                    default:
+                        if (elementTypeVariant != null && symbolIndex >= 0)
+                        {
+                            throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute");
+                        }
+                        else
+                        {
+                            // type ignored:
+                            elementType = null;
+                        }
+                        break;
+                }
+
+                info.SetMarshalAsSafeArray(elementTypeVariant, elementType);
+            }
+
+            private static void DecodeMarshalAsArray(string[] paramNames, object?[] values, bool isFixed, MarshallingInfo info)
+            {
+                UnmanagedType? elementType = null;
+                int? elementCount = isFixed ? 1 : null;
+                short? parameterIndex = null;
+
+                for (int i = 0; i < paramNames.Length; i++)
+                {
+                    switch (paramNames[i])
+                    {
+                        case "ArraySubType":
+                            elementType = (UnmanagedType)values[i]!;
+                            break;
+                        case "SizeConst":
+                            elementCount = (int?)values[i];
+                            break;
+                        case "SizeParamIndex":
+                            if (isFixed)
+                            {
+                                goto case "SafeArraySubType";
+                            }
+                            parameterIndex = (short?)values[i];
+                            break;
+                        case "SafeArraySubType":
+                            throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute");
+                            // other parameters ignored with no error
+                    }
+                }
+
+                if (isFixed)
+                {
+                    info.SetMarshalAsFixedArray(elementType, elementCount);
+                }
+                else
+                {
+                    info.SetMarshalAsArray(elementType, elementCount, parameterIndex);
+                }
+            }
+
+            private static void DecodeMarshalAsComInterface(string[] paramNames, object?[] values, UnmanagedType unmanagedType, MarshallingInfo info)
+            {
+                int? parameterIndex = null;
+                for (int i = 0; i < paramNames.Length; i++)
+                {
+                    if (paramNames[i] == "IidParameterIndex")
+                    {
+                        parameterIndex = (int?)values[i];
+                        break;
+                    }
+                }
+                info.SetMarshalAsComInterface(unmanagedType, parameterIndex);
+            }
+
+            private static void DecodeMarshalAsCustom(string[] paramNames, object?[] values, MarshallingInfo info)
+            {
+                string? cookie = null;
+                Type? type = null;
+                string? name = null;
+                for (int i = 0; i < paramNames.Length; i++)
+                {
+                    switch (paramNames[i])
+                    {
+                        case "MarshalType":
+                            name = (string?)values[i];
+                            break;
+                        case "MarshalTypeRef":
+                            type = (Type?)values[i];
+                            break;
+                        case "MarshalCookie":
+                            cookie = (string?)values[i];
+                            break;
+                        // other parameters ignored with no error
+                    }
+                }
+
+                info.SetMarshalAsCustom((object?)name ?? type!, cookie);
+            }
+        }
     }
 }
index 1262ae5..4f45898 100644 (file)
@@ -1,9 +1,12 @@
 ï»¿// Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Buffers.Binary;
+using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Reflection.Metadata;
+using System.Runtime.InteropServices;
 
 namespace System.Reflection.Emit
 {
@@ -12,10 +15,14 @@ namespace System.Reflection.Emit
         private readonly Type _returnType;
         private readonly Type[]? _parameterTypes;
         private readonly ModuleBuilderImpl _module;
-        private readonly MethodAttributes _attributes;
         private readonly string _name;
         private readonly CallingConventions _callingConventions;
         private readonly TypeBuilderImpl _declaringType;
+        private MethodAttributes _attributes;
+        private MethodImplAttributes _methodImplFlags;
+
+        internal DllImportData? _dllImportData;
+        internal List<CustomAttributeWrapper>? _customAttributes;
 
         internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType,
             Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType)
@@ -35,6 +42,8 @@ namespace System.Reflection.Emit
                     ArgumentNullException.ThrowIfNull(_parameterTypes[i] = parameterTypes[i], nameof(parameterTypes));
                 }
             }
+
+            _methodImplFlags = MethodImplAttributes.IL;
         }
 
         internal BlobBuilder GetMethodSignatureBlob() =>
@@ -44,9 +53,44 @@ namespace System.Reflection.Emit
         protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(params string[] names) => throw new NotImplementedException();
         protected override ParameterBuilder DefineParameterCore(int position, ParameterAttributes attributes, string? strParamName) => throw new NotImplementedException();
         protected override ILGenerator GetILGeneratorCore(int size) => throw new NotImplementedException();
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) => throw new NotImplementedException();
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotImplementedException();
-        protected override void SetImplementationFlagsCore(MethodImplAttributes attributes) => throw new NotImplementedException();
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            // Handle pseudo custom attributes
+            switch (con.ReflectedType!.FullName)
+            {
+                case "System.Runtime.CompilerServices.MethodImplAttribute":
+                    int implValue = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(2));
+                    _methodImplFlags |= (MethodImplAttributes)implValue;
+                    return;
+                case "System.Runtime.InteropServices.DllImportAttribute":
+                    {
+                        _dllImportData = DllImportData.CreateDllImportData(CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute), out var preserveSig);
+                        _attributes |= MethodAttributes.PinvokeImpl;
+                        if (preserveSig)
+                        {
+                            _methodImplFlags |= MethodImplAttributes.PreserveSig;
+                        }
+                    }
+                    return;
+                case "System.Runtime.InteropServices.PreserveSigAttribute":
+                    _methodImplFlags |= MethodImplAttributes.PreserveSig;
+                    return;
+                case "System.Runtime.CompilerServices.SpecialNameAttribute":
+                    _attributes |= MethodAttributes.SpecialName;
+                    return;
+                case "System.Security.SuppressUnmanagedCodeSecurityAttribute":
+                    _attributes |= MethodAttributes.HasSecurity;
+                    break;
+            }
+
+            _customAttributes ??= new List<CustomAttributeWrapper>();
+            _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
+        }
+
+        protected override void SetImplementationFlagsCore(MethodImplAttributes attributes)
+        {
+            _methodImplFlags = attributes;
+        }
         protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes,
             Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) => throw new NotImplementedException();
         public override string Name => _name;
@@ -83,7 +127,7 @@ namespace System.Reflection.Emit
             => throw new NotImplementedException();
 
         public override MethodImplAttributes GetMethodImplementationFlags()
-            => throw new NotImplementedException();
+            => _methodImplFlags;
 
         public override ParameterInfo[] GetParameters()
             => throw new NotImplementedException();
@@ -98,4 +142,103 @@ namespace System.Reflection.Emit
         public override MethodInfo MakeGenericMethod(params System.Type[] typeArguments)
             => throw new NotImplementedException();
     }
+
+    internal sealed class DllImportData
+    {
+        private readonly string _moduleName;
+        private readonly string? _entryPoint;
+        private readonly MethodImportAttributes _flags;
+        internal DllImportData(string moduleName, string? entryPoint, MethodImportAttributes flags)
+        {
+            _moduleName = moduleName;
+            _entryPoint = entryPoint;
+            _flags = flags;
+        }
+
+        public string ModuleName => _moduleName;
+
+        public string? EntryPoint => _entryPoint;
+
+        public MethodImportAttributes Flags => _flags;
+
+        internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out bool preserveSig)
+        {
+            string? moduleName = (string?)attr._ctorArgs[0];
+            if (moduleName == null || moduleName.Length == 0)
+            {
+                throw new ArgumentException(SR.Argument_DllNameCannotBeEmpty);
+            }
+
+            MethodImportAttributes importAttributes = MethodImportAttributes.None;
+            string? entryPoint = null;
+            preserveSig = true;
+            for (int i = 0; i < attr._namedParamNames.Length; ++i)
+            {
+                string name = attr._namedParamNames[i];
+                object value = attr._namedParamValues[i]!;
+                switch (name)
+                {
+                    case "PreserveSig":
+                        preserveSig = (bool)value;
+                        break;
+                    case "CallingConvention":
+                        importAttributes |= (CallingConvention)value switch
+                        {
+                            CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl,
+                            CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall,
+                            CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall,
+                            CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall,
+                            _=> MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this
+                        };
+                        break;
+                    case "CharSet":
+                        importAttributes |= (CharSet)value switch
+                        {
+                            CharSet.Ansi => MethodImportAttributes.CharSetAnsi,
+                            CharSet.Auto => MethodImportAttributes.CharSetAuto,
+                            CharSet.Unicode => MethodImportAttributes.CharSetUnicode,
+                            _ => MethodImportAttributes.CharSetAuto
+                        };
+                        break;
+                    case "EntryPoint":
+                        entryPoint = (string?)value;
+                        break;
+                    case "ExactSpelling":
+                        if ((bool)value)
+                        {
+                            importAttributes |= MethodImportAttributes.ExactSpelling;
+                        }
+                        break;
+                    case "SetLastError":
+                        if ((bool)value)
+                        {
+                            importAttributes |= MethodImportAttributes.SetLastError;
+                        }
+                        break;
+                    case "BestFitMapping":
+                        if ((bool)value)
+                        {
+                            importAttributes |= MethodImportAttributes.BestFitMappingEnable;
+                        }
+                        else
+                        {
+                            importAttributes |= MethodImportAttributes.BestFitMappingDisable;
+                        }
+                        break;
+                    case "ThrowOnUnmappableChar":
+                        if ((bool)value)
+                        {
+                            importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharEnable;
+                        }
+                        else
+                        {
+                            importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharDisable;
+                        }
+                        break;
+                }
+            }
+
+            return new DllImportData(moduleName, entryPoint, importAttributes);
+        }
+    }
 }
index 8c17b67..775a5f6 100644 (file)
@@ -18,6 +18,9 @@ namespace System.Reflection.Emit
         private readonly Dictionary<Assembly, AssemblyReferenceHandle> _assemblyReferences = new();
         private readonly Dictionary<Type, TypeReferenceHandle> _typeReferences = new();
         private readonly List<TypeBuilderImpl> _typeDefinitions = new();
+        private readonly Dictionary<ConstructorInfo, MemberReferenceHandle> _ctorReferences = new();
+        private Dictionary<string, ModuleReferenceHandle>? _moduleReferences;
+        private List<CustomAttributeWrapper>? _customAttributes;
         private int _nextTypeDefRowId = 1;
         private int _nextMethodDefRowId = 1;
         private int _nextFieldDefRowId = 1;
@@ -34,7 +37,7 @@ namespace System.Reflection.Emit
         }
 
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Types are preserved via s_coreTypes")]
-         internal Type GetTypeFromCoreAssembly(CoreTypeId typeId)
+        internal Type GetTypeFromCoreAssembly(CoreTypeId typeId)
         {
             if (_coreTypes == null)
             {
@@ -97,7 +100,7 @@ namespace System.Reflection.Emit
         internal void AppendMetadata()
         {
             // Add module metadata
-            _metadataBuilder.AddModule(
+            ModuleDefinitionHandle moduleHandle = _metadataBuilder.AddModule(
                 generation: 0,
                 moduleName: _metadataBuilder.GetOrAddString(_name),
                 mvid: _metadataBuilder.GetOrAddGuid(Guid.NewGuid()),
@@ -111,7 +114,9 @@ namespace System.Reflection.Emit
                 name: _metadataBuilder.GetOrAddString("<Module>"),
                 baseType: default,
                 fieldList: MetadataTokens.FieldDefinitionHandle(1),
-                methodList: MetadataTokens.MethodDefinitionHandle(1)); ;
+                methodList: MetadataTokens.MethodDefinitionHandle(1));
+
+            WriteCustomAttributes(_customAttributes, moduleHandle);
 
             // Add each type definition to metadata table.
             foreach (TypeBuilderImpl typeBuilder in _typeDefinitions)
@@ -124,22 +129,100 @@ namespace System.Reflection.Emit
 
                 TypeDefinitionHandle typeDefinitionHandle = AddTypeDefinition(typeBuilder, parent, _nextMethodDefRowId, _nextFieldDefRowId);
                 Debug.Assert(typeBuilder._handle.Equals(typeDefinitionHandle));
+                WriteCustomAttributes(typeBuilder._customAttributes, typeDefinitionHandle);
 
-                // Add each method definition to metadata table.
-                foreach (MethodBuilderImpl method in typeBuilder._methodDefStore)
+                foreach (MethodBuilderImpl method in typeBuilder._methodDefinitions)
                 {
-                    AddMethodDefinition(method, method.GetMethodSignatureBlob());
+                    MethodDefinitionHandle methodHandle = AddMethodDefinition(method, method.GetMethodSignatureBlob());
+                    WriteCustomAttributes(method._customAttributes, methodHandle);
                     _nextMethodDefRowId++;
+
+                    if (method._dllImportData != null)
+                    {
+                        AddMethodImport(methodHandle, method._dllImportData.EntryPoint ?? method.Name,
+                            method._dllImportData.Flags, GetModuleReference(method._dllImportData.ModuleName));
+                    }
                 }
 
-                foreach (FieldBuilderImpl field in typeBuilder._fieldDefStore)
+                foreach (FieldBuilderImpl field in typeBuilder._fieldDefinitions)
                 {
-                    AddFieldDefinition(field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this));
+                    FieldDefinitionHandle fieldHandle = AddFieldDefinition(field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this));
+                    WriteCustomAttributes(field._customAttributes, fieldHandle);
                     _nextFieldDefRowId++;
+
+                    if (field._offset > 0 && (typeBuilder.Attributes & TypeAttributes.ExplicitLayout) != 0)
+                    {
+                        AddFieldLayout(fieldHandle, field._offset);
+                    }
+
+                    if (field._marshallingInfo != null)
+                    {
+                        AddFieldMarshalling(fieldHandle, field._marshallingInfo.PopulateMarshallingBlob(_metadataBuilder));
+                    }
                 }
             }
         }
 
+        private ModuleReferenceHandle GetModuleReference(string moduleName)
+        {
+            _moduleReferences ??= new Dictionary<string, ModuleReferenceHandle>();
+
+            if (!_moduleReferences.TryGetValue(moduleName, out var handle))
+            {
+                handle = AddModuleReference(moduleName);
+                _moduleReferences.Add(moduleName, handle);
+            }
+
+            return handle;
+        }
+
+        internal void WriteCustomAttributes(List<CustomAttributeWrapper>? customAttributes, EntityHandle parent)
+        {
+            if (customAttributes != null)
+            {
+                foreach (CustomAttributeWrapper customAttribute in customAttributes)
+                {
+                    _metadataBuilder.AddCustomAttribute(parent, GetConstructorHandle(customAttribute.Ctor),
+                        _metadataBuilder.GetOrAddBlob(customAttribute.Data));
+                }
+            }
+        }
+
+        private MemberReferenceHandle GetConstructorHandle(ConstructorInfo constructorInfo)
+        {
+            if (!_ctorReferences.TryGetValue(constructorInfo, out var constructorHandle))
+            {
+                TypeReferenceHandle parentHandle = GetTypeReference(constructorInfo.DeclaringType!);
+                constructorHandle = AddConstructorReference(parentHandle, constructorInfo);
+                _ctorReferences.Add(constructorInfo, constructorHandle);
+            }
+
+            return constructorHandle;
+        }
+
+        private TypeReferenceHandle GetTypeReference(Type type)
+        {
+            if (!_typeReferences.TryGetValue(type, out var typeHandle))
+            {
+                typeHandle = AddTypeReference(type, GetAssemblyReference(type.Assembly));
+                _typeReferences.Add(type, typeHandle);
+            }
+
+            return typeHandle;
+        }
+
+        private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly)
+        {
+            if (!_assemblyReferences.TryGetValue(assembly, out var handle))
+            {
+                AssemblyName aName = assembly.GetName();
+                handle = AddAssemblyReference(aName.Name!, aName.Version, aName.CultureName, aName.GetPublicKeyToken(), aName.Flags, aName.ContentType);
+                _assemblyReferences.Add(assembly, handle);
+            }
+
+            return handle;
+        }
+
         private FieldDefinitionHandle AddFieldDefinition(FieldBuilderImpl field, BlobBuilder fieldSignature) =>
             _metadataBuilder.AddFieldDefinition(
                 attributes: field.Attributes,
@@ -158,45 +241,48 @@ namespace System.Reflection.Emit
         private MethodDefinitionHandle AddMethodDefinition(MethodBuilderImpl method, BlobBuilder methodSignature) =>
             _metadataBuilder.AddMethodDefinition(
                 attributes: method.Attributes,
-                implAttributes: MethodImplAttributes.IL,
+                implAttributes: method.GetMethodImplementationFlags(),
                 name: _metadataBuilder.GetOrAddString(method.Name),
                 signature: _metadataBuilder.GetOrAddBlob(methodSignature),
                 bodyOffset: -1, // No body supported yet
-                parameterList: MetadataTokens.ParameterHandle(1)
-                );
+                parameterList: MetadataTokens.ParameterHandle(1));
 
         private TypeReferenceHandle AddTypeReference(Type type, AssemblyReferenceHandle parent) =>
             _metadataBuilder.AddTypeReference(
                 resolutionScope: parent,
                 @namespace: (type.Namespace == null) ? default : _metadataBuilder.GetOrAddString(type.Namespace),
-                name: _metadataBuilder.GetOrAddString(type.Name)
-        );
+                name: _metadataBuilder.GetOrAddString(type.Name));
 
-        private TypeReferenceHandle GetTypeReference(Type type)
+        private MemberReferenceHandle AddConstructorReference(TypeReferenceHandle parent, ConstructorInfo method)
         {
-            if (!_typeReferences.TryGetValue(type, out var parentHandle))
-            {
-                parentHandle = AddTypeReference(type, GetAssemblyReference(type.Assembly));
-                _typeReferences.Add(type, parentHandle);
-            }
-
-            return parentHandle;
+            var blob = MetadataSignatureHelper.ConstructorSignatureEncoder(method.GetParameters(), this);
+            return _metadataBuilder.AddMemberReference(
+                    parent: parent,
+                    name: _metadataBuilder.GetOrAddString(method.Name),
+                    signature: _metadataBuilder.GetOrAddBlob(blob));
         }
 
-        private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly)
-        {
-            if (!_assemblyReferences.TryGetValue(assembly, out var handle))
-            {
-                AssemblyName aName = assembly.GetName();
-                handle = AddAssemblyReference(aName.Name!, aName.Version, aName.CultureName, aName.GetPublicKeyToken(), aName.Flags, aName.ContentType);
-                _assemblyReferences.Add(assembly, handle);
-            }
+        private void AddMethodImport(MethodDefinitionHandle methodHandle, string name,
+            MethodImportAttributes attributes, ModuleReferenceHandle moduleHandle) =>
+            _metadataBuilder.AddMethodImport(
+                method: methodHandle,
+                attributes: attributes,
+                name: _metadataBuilder.GetOrAddString(name),
+                module: moduleHandle);
 
-            return handle;
+        private ModuleReferenceHandle AddModuleReference(string moduleName) =>
+            _metadataBuilder.AddModuleReference(moduleName: _metadataBuilder.GetOrAddString(moduleName));
+
+        private void AddFieldLayout(FieldDefinitionHandle fieldHandle, int offset) =>
+            _metadataBuilder.AddFieldLayout(field: fieldHandle, offset: offset);
+
+        private void AddFieldMarshalling(FieldDefinitionHandle fieldHandle, BlobHandle descriptor)
+        {
+            _metadataBuilder.AddMarshallingDescriptor(fieldHandle, descriptor);
         }
 
-        private AssemblyReferenceHandle AddAssemblyReference(string name, Version? version,
-            string? culture, byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) =>
+        private AssemblyReferenceHandle AddAssemblyReference(string name, Version? version, string? culture,
+            byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) =>
             _metadataBuilder.AddAssemblyReference(
                 name: _metadataBuilder.GetOrAddString(name),
                 version: version ?? new Version(0, 0, 0, 0),
@@ -233,14 +319,17 @@ namespace System.Reflection.Emit
         protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize)
         {
             TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId);
-            TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this, typeHandle);
+            TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this, typeHandle, packingSize, typesize);
             _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, byte[] binaryAttribute) => throw new NotSupportedException();
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotSupportedException();
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            _customAttributes ??= new List<CustomAttributeWrapper>();
+            _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
+        }
         public override int GetSignatureMetadataToken(SignatureHelper signature) => throw new NotImplementedException();
     }
 }
index ad1701c..7bdc5cf 100644 (file)
@@ -1,7 +1,6 @@
 ï»¿// 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;
 using System.Reflection.Metadata.Ecma335;
 
@@ -19,17 +18,37 @@ namespace System.Reflection.Emit
             return fieldSignature;
         }
 
+        internal static BlobBuilder ConstructorSignatureEncoder(ParameterInfo[]? parameters, ModuleBuilderImpl module)
+        {
+            BlobBuilder constructorSignature = new();
+
+            new BlobEncoder(constructorSignature).
+                MethodSignature(isInstanceMethod: true).
+                Parameters((parameters == null) ? 0 : parameters.Length, out ReturnTypeEncoder retType, out ParametersEncoder parameterEncoder);
+
+            retType.Void();
+
+            if (parameters != null)
+            {
+                Type[]? typeParameters = Array.ConvertAll(parameters, parameter => parameter.ParameterType);
+
+                foreach (Type parameter in typeParameters)
+                {
+                    WriteSignatureForType(parameterEncoder.AddParameter().Type(), parameter, module);
+                }
+            }
+
+            return constructorSignature;
+        }
+
         internal static BlobBuilder MethodSignatureEncoder(ModuleBuilderImpl module, Type[]? parameters, Type? returnType, bool isInstance)
         {
             // Encoding return type and parameters.
             BlobBuilder methodSignature = new();
 
-            ParametersEncoder parEncoder;
-            ReturnTypeEncoder retEncoder;
-
             new BlobEncoder(methodSignature).
                 MethodSignature(isInstanceMethod: isInstance).
-                Parameters((parameters == null) ? 0 : parameters.Length, out retEncoder, out parEncoder);
+                Parameters((parameters == null) ? 0 : parameters.Length, out ReturnTypeEncoder retEncoder, out ParametersEncoder parEncoder);
 
             if (returnType != null && returnType != module.GetTypeFromCoreAssembly(CoreTypeId.Void))
             {
index 16558aa..d2f0b5a 100644 (file)
@@ -3,8 +3,8 @@
 
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
 using System.Globalization;
+using System.Runtime.InteropServices;
 using System.Reflection.Metadata;
 using System.Reflection.Metadata.Ecma335;
 
@@ -12,22 +12,29 @@ namespace System.Reflection.Emit
 {
     internal sealed class TypeBuilderImpl : TypeBuilder
     {
-        internal List<MethodBuilderImpl> _methodDefStore = new();
-        internal List<FieldBuilderImpl> _fieldDefStore = new();
         private readonly ModuleBuilderImpl _module;
         private readonly string _name;
         private readonly string? _namespace;
-        internal readonly TypeDefinitionHandle _handle;
         [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
         private Type? _typeParent;
         private TypeAttributes _attributes;
+        private PackingSize _packingSize;
+        private int _typeSize;
+
+        internal readonly TypeDefinitionHandle _handle;
+        internal readonly List<MethodBuilderImpl> _methodDefinitions = new();
+        internal readonly List<FieldBuilderImpl> _fieldDefinitions = new();
+        internal List<CustomAttributeWrapper>? _customAttributes;
 
         internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes,
-            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module, TypeDefinitionHandle handle)
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module,
+            TypeDefinitionHandle handle, PackingSize packingSize, int typeSize)
         {
             _name = fullName;
             _module = module;
             _attributes = typeAttributes;
+            _packingSize = packingSize;
+            _typeSize = typeSize;
             SetParent(parent);
             _handle = handle;
 
@@ -41,8 +48,8 @@ namespace System.Reflection.Emit
         }
 
         internal ModuleBuilderImpl GetModuleBuilder() => _module;
-        protected override PackingSize PackingSizeCore => throw new NotImplementedException();
-        protected override int SizeCore => throw new NotImplementedException();
+        protected override PackingSize PackingSizeCore => _packingSize;
+        protected override int SizeCore => _typeSize;
         protected override void AddInterfaceImplementationCore([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type interfaceType) => throw new NotImplementedException();
         [return: DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))]
         protected override TypeInfo CreateTypeInfoCore() => throw new NotImplementedException();
@@ -52,7 +59,7 @@ namespace System.Reflection.Emit
         protected override FieldBuilder DefineFieldCore(string fieldName, Type type, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers, FieldAttributes attributes)
         {
             var field = new FieldBuilderImpl(this, fieldName, type, attributes);
-            _fieldDefStore.Add(field);
+            _fieldDefinitions.Add(field);
             return field;
         }
         protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(params string[] names) => throw new NotImplementedException();
@@ -60,7 +67,7 @@ namespace System.Reflection.Emit
         protected override MethodBuilder DefineMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers)
         {
             MethodBuilderImpl methodBuilder = new(name, attributes, callingConvention, returnType, parameterTypes, _module, this);
-            _methodDefStore.Add(methodBuilder);
+            _methodDefinitions.Add(methodBuilder);
             return methodBuilder;
         }
 
@@ -72,8 +79,85 @@ namespace System.Reflection.Emit
         protected override ConstructorBuilder DefineTypeInitializerCore() => throw new NotImplementedException();
         protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException();
         protected override bool IsCreatedCore() => throw new NotImplementedException();
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) => throw new NotImplementedException();
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotImplementedException();
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            // Handle pseudo custom attributes
+            switch (con.ReflectedType!.FullName)
+            {
+                case "System.Runtime.InteropServices.StructLayoutAttribute":
+                    ParseStructLayoutAttribute(con, binaryAttribute);
+                    return;
+                case "System.Runtime.CompilerServices.SpecialNameAttribute":
+                    _attributes |= TypeAttributes.SpecialName;
+                    return;
+                case "System.SerializableAttribute":
+#pragma warning disable SYSLIB0050 // 'TypeAttributes.Serializable' is obsolete: 'Formatter-based serialization is obsolete and should not be used'.
+                    _attributes |= TypeAttributes.Serializable;
+#pragma warning restore SYSLIB0050
+                    return;
+                case "System.Runtime.InteropServices.ComImportAttribute":
+                    _attributes |= TypeAttributes.Import;
+                    return;
+                case "System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeImportAttribute":
+                    _attributes |= TypeAttributes.WindowsRuntime;
+                    return;
+                case "System.Security.SuppressUnmanagedCodeSecurityAttribute": // It says has no effect in .NET Core, maybe remove?
+                    _attributes |= TypeAttributes.HasSecurity;
+                    break;
+            }
+
+            _customAttributes ??= new List<CustomAttributeWrapper>();
+            _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute));
+        }
+
+        private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute);
+            LayoutKind layoutKind = (LayoutKind)attributeInfo._ctorArgs[0]!;
+            _attributes &= ~TypeAttributes.LayoutMask;
+            _attributes |= layoutKind switch
+            {
+                LayoutKind.Auto => TypeAttributes.AutoLayout,
+                LayoutKind.Explicit => TypeAttributes.ExplicitLayout,
+                LayoutKind.Sequential => TypeAttributes.SequentialLayout,
+                _ => TypeAttributes.AutoLayout,
+            };
+
+            for (int i = 0; i < attributeInfo._namedParamNames.Length; ++i)
+            {
+                string name = attributeInfo._namedParamNames[i];
+                int value = (int)attributeInfo._namedParamValues[i]!;
+
+                switch (name)
+                {
+                    case "CharSet":
+                        switch ((CharSet)value)
+                        {
+                            case CharSet.None:
+                            case CharSet.Ansi:
+                                _attributes &= ~(TypeAttributes.UnicodeClass | TypeAttributes.AutoClass);
+                                break;
+                            case CharSet.Unicode:
+                                _attributes &= ~TypeAttributes.AutoClass;
+                                _attributes |= TypeAttributes.UnicodeClass;
+                                break;
+                            case CharSet.Auto:
+                                _attributes &= ~TypeAttributes.UnicodeClass;
+                                _attributes |= TypeAttributes.AutoClass;
+                                break;
+                        }
+                        break;
+                    case "Pack":
+                        _packingSize = (PackingSize)value;
+                        break;
+                    case "Size":
+                        _typeSize = value;
+                        break;
+                    default:
+                        throw new ArgumentException(SR.Format(SR.Argument_UnknownNamedType, con.DeclaringType, name), nameof(binaryAttribute));
+                }
+            }
+        }
 
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2074:DynamicallyAccessedMembers",
             Justification = "TODO: Need to figure out how to preserve System.Object public constructor")]
index e177c08..aea595e 100644 (file)
@@ -30,17 +30,6 @@ namespace System.Reflection.Emit.Tests
         }
 
         [Fact]
-        public void SetCustomAttribute_ConstructorInfo_ByteArray_NullBinaryAttribute_ThrowsArgumentNullException()
-        {
-            TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public);
-            string[] typeParamNames = new string[] { "TFirst" };
-            GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters(typeParamNames);
-            ConstructorInfo attributeConstructor = typeof(HelperAttribute).GetConstructor(new Type[0]);
-
-            AssertExtensions.Throws<ArgumentNullException>("binaryAttribute", () => typeParams[0].SetCustomAttribute(attributeConstructor, null));
-        }
-
-        [Fact]
         public void SetCustomAttribute_CustomAttributeBuilder()
         {
             TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public);
diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs
new file mode 100644 (file)
index 0000000..0f1d3c9
--- /dev/null
@@ -0,0 +1,407 @@
+// 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 System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using Xunit;
+
+namespace System.Reflection.Emit.Tests
+{
+    [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
+    public class AssemblySaveCustomAttributeTests
+    {
+        private List<CustomAttributeBuilder> _attributes = new List<CustomAttributeBuilder>
+        {
+            new CustomAttributeBuilder(s_comVisiblePair.con, s_comVisiblePair.args),
+            new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args)
+        };
+
+        private static readonly Type s_comVisibleType = typeof(ComVisibleAttribute);
+        private static readonly Type s_guideType = 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 AssemblyName PopulateAssemblyName()
+        {
+            AssemblyName assemblyName = new AssemblyName("MyDynamicAssembly");
+            assemblyName.Version = new Version("7.0.0.0");
+            assemblyName.CultureInfo = Globalization.CultureInfo.InvariantCulture;
+            return assemblyName;
+        }
+
+        [Fact]
+        public void AssemblyModuleWithCustomAttributes()
+        {
+            AssemblyName assemblyName = PopulateAssemblyName();
+
+            using (TempFile file = TempFile.Create())
+            {
+                WriteAssemblyToDisk(assemblyName, Type.EmptyTypes, file.Path, _attributes, _attributes);
+
+                Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path);
+                Module moduleFromDisk = assemblyFromDisk.Modules.First();
+
+                AssemblyTools.AssertAssemblyNameAndModule(assemblyName, assemblyFromDisk.GetName(), moduleFromDisk);
+                ValidateAttributes(assemblyFromDisk.GetCustomAttributesData());
+                ValidateAttributes(moduleFromDisk.GetCustomAttributesData());
+            }
+        }
+
+        [Fact]
+        public void MethodFieldWithCustomAttributes()
+        {
+            Type[] types = new Type[] { typeof(IMultipleMethod), typeof(IOneMethod), typeof(StructWithFields) };
+
+            using (TempFile file = TempFile.Create())
+            {
+                WriteAssemblyToDisk(PopulateAssemblyName(), types, file.Path, typeAttributes: _attributes,
+                    methodAttributes: _attributes, fieldAttributes: _attributes);
+
+                Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path);
+
+                Module moduleFromDisk = assemblyFromDisk.Modules.First();
+                Type[] typesFromDisk = moduleFromDisk.GetTypes();
+
+                Assert.Equal(types.Length, typesFromDisk.Length);
+
+                for (int i = 0; i < types.Length; i++)
+                {
+                    Type typeFromDisk = typesFromDisk[i];
+                    Type sourceType = types[i];
+                    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);
+                    ValidateAttributes(typeFromDisk.GetCustomAttributesData());
+
+                    for (int j = 0; j < methodsFromDisk.Length; j++)
+                    {
+                        ValidateAttributes(methodsFromDisk[j].GetCustomAttributesData());
+                    }
+
+                    for (int j = 0; j < fieldsFromDisk.Length; j++)
+                    {
+                        ValidateAttributes(fieldsFromDisk[j].GetCustomAttributesData());
+                    }
+                }
+            }
+        }
+
+        private void ValidateAttributes(IList<CustomAttributeData> attributesFromDisk)
+        {
+            Assert.Equal(_attributes.Count, attributesFromDisk.Count);
+
+            foreach (var attribute in attributesFromDisk)
+            {
+                if (attribute.AttributeType.Name == s_comVisibleType.Name)
+                {
+                    Assert.Equal(s_comVisiblePair.con.MetadataToken, attribute.Constructor.MetadataToken);
+                    Assert.Equal(s_comVisiblePair.args[0].GetType().FullName, attribute.ConstructorArguments[0].ArgumentType.FullName);
+                    Assert.Equal(true, attribute.ConstructorArguments[0].Value);
+                }
+                else
+                {
+                    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(s_guidPair.args[0], attribute.ConstructorArguments[0].Value);
+                }
+            }
+        }
+
+        private static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation, List<CustomAttributeBuilder>? assemblyAttributes = null,
+            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);
+            ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
+            PopulateMembersForModule(mb, types, moduleAttributes, typeAttributes, methodAttributes, fieldAttributes);
+            saveMethod.Invoke(assemblyBuilder, new object[] { fileLocation });
+        }
+
+        private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types, List<CustomAttributeBuilder>? moduleAttributes,
+            List<CustomAttributeBuilder>? typeAttributes, List<CustomAttributeBuilder>? methodAttributes, List<CustomAttributeBuilder>? fieldAttributes)
+        {
+            if (moduleAttributes != null)
+            {
+                moduleAttributes.ForEach(mb.SetCustomAttribute);
+            }
+
+            foreach (Type type in types)
+            {
+                TypeBuilder tb = mb.DefineType(type.FullName, type.Attributes, type.BaseType);
+                typeAttributes.ForEach(tb.SetCustomAttribute);
+
+                DefineMethodsAndSetAttributes(methodAttributes, tb, type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly));
+                DefineFieldsAndSetAttributes(fieldAttributes, type.GetFields(), tb);
+            }
+        }
+
+        private static void DefineFieldsAndSetAttributes(List<CustomAttributeBuilder>? fieldAttributes, FieldInfo[] fields, TypeBuilder tb)
+        {
+            foreach (FieldInfo field in fields)
+            {
+                FieldBuilder fb = tb.DefineField(field.Name, field.FieldType, field.Attributes);
+                fieldAttributes.ForEach(fb.SetCustomAttribute);
+            }
+        }
+
+        private static void DefineMethodsAndSetAttributes(List<CustomAttributeBuilder> methodAttributes, TypeBuilder tb, MethodInfo[] methods)
+        {
+            foreach (var method in methods)
+            {
+                MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, null);
+                methodAttributes.ForEach(meb.SetCustomAttribute);
+            }
+        }
+
+        [Fact]
+        public void CreateStructWithPseudoCustomAttributesTest()
+        {
+            using (TempFile file = TempFile.Create())
+            {
+                Type type = typeof(StructWithFields);
+                List<CustomAttributeBuilder> typeAttributes = new() { new CustomAttributeBuilder(typeof(SerializableAttribute).GetConstructor(Type.EmptyTypes), new object[] { }),
+                                                              new CustomAttributeBuilder(typeof(StructLayoutAttribute).GetConstructor(new Type[] { typeof(LayoutKind) }), new object[] { LayoutKind.Explicit },
+                                                                    typeof(StructLayoutAttribute).GetFields() , new object[]{32, 64, CharSet.Unicode}),
+                                                              new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args),
+                                                              new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { })
+                                                            };
+                CustomAttributeBuilder[] fieldAttributes = new[] { new CustomAttributeBuilder(typeof(NonSerializedAttribute).GetConstructor(Type.EmptyTypes), new object[] { }),
+                                                              new CustomAttributeBuilder(typeof(FieldOffsetAttribute).GetConstructor(new Type[] { typeof(int) }), new object[] { 2 }),
+                                                              new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args),
+                                                              new CustomAttributeBuilder(typeof(MarshalAsAttribute).GetConstructor(new Type[] { typeof(UnmanagedType) }), new object[] { UnmanagedType.I4}),
+                                                              new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { })
+                                                            };
+
+                AssemblyBuilder ab = AssemblyTools.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);
+                Module moduleFromDisk = assemblyFromDisk.Modules.First();
+                Type testType = moduleFromDisk.GetTypes()[0];
+                IList<CustomAttributeData> attributesFromDisk = testType.GetCustomAttributesData();
+
+                Assert.Equal(typeAttributes.Count - 3, attributesFromDisk.Count); // 3 pseudo attributes 
+                Assert.True((testType.Attributes & TypeAttributes.Serializable) != 0); // SerializableAttribute
+                Assert.True((testType.Attributes & TypeAttributes.SpecialName) != 0); // SpecialNameAttribute
+                Assert.True((testType.Attributes & TypeAttributes.ExplicitLayout) != 0); // StructLayoutAttribute
+                Assert.True((testType.Attributes & TypeAttributes.UnicodeClass) != 0);   // StructLayoutAttribute, not sure if we could test the PackingSize and Size
+
+                for (int i = 0; i < attributesFromDisk.Count; i++)
+                {
+                    switch (attributesFromDisk[i].AttributeType.Name)
+                    {
+                        case "GuidAttribute":
+                            Assert.Equal(s_guidPair.args[0], attributesFromDisk[i].ConstructorArguments[0].Value);
+                            break;
+                        default:
+                            Assert.Fail($"Not expected attribute : {attributesFromDisk[i].AttributeType.Name}");
+                            break;
+                    }
+                }
+
+                FieldInfo field = testType.GetFields()[0];
+                IList<CustomAttributeData> fieldAttributesFromDisk = field.GetCustomAttributesData();
+
+                Assert.Equal(3, fieldAttributesFromDisk.Count);
+                Assert.True((field.Attributes & FieldAttributes.NotSerialized) != 0); // NonSerializedAttribute
+                Assert.True((field.Attributes & FieldAttributes.SpecialName) != 0); // SpecialNameAttribute
+                Assert.True((field.Attributes & FieldAttributes.HasFieldMarshal) != 0); // MarshalAsAttribute
+
+                for (int i = 0; i < fieldAttributesFromDisk.Count; i++)
+                {
+                    switch (fieldAttributesFromDisk[i].AttributeType.Name)
+                    {
+                        case "FieldOffsetAttribute":
+                            Assert.Equal(2, fieldAttributesFromDisk[i].ConstructorArguments[0].Value);
+                            break;
+                        case "MarshalAsAttribute":
+                            Assert.Equal(UnmanagedType.I4, (UnmanagedType)fieldAttributesFromDisk[i].ConstructorArguments[0].Value);
+                            break;
+                        case "GuidAttribute":
+                            Assert.Equal(s_guidPair.args[0], fieldAttributesFromDisk[i].ConstructorArguments[0].Value);
+                            break;
+                        default:
+                            Assert.Fail($"Not expected attribute : {fieldAttributesFromDisk[i].AttributeType.Name}");
+                            break;
+                    }
+                }
+            }
+        }
+
+        [Fact]
+        public void InterfacesWithPseudoCustomAttributes()
+        {
+            using (TempFile file = TempFile.Create())
+            {
+                Type dllType = typeof(DllImportAttribute);
+                Type type = typeof(IMultipleMethod);
+                List<CustomAttributeBuilder> typeAttributes = new() { new CustomAttributeBuilder(typeof(ComImportAttribute).GetConstructor(Type.EmptyTypes), new object[] { }),
+                                                              new CustomAttributeBuilder(typeof(SuppressUnmanagedCodeSecurityAttribute).GetConstructor(Type.EmptyTypes), new object[] { }),
+                                                              new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args)
+                                                            };
+                CustomAttributeBuilder[] methodAttributes = new[] { new CustomAttributeBuilder(typeof(PreserveSigAttribute).GetConstructor(Type.EmptyTypes), new object[] { }),
+                                                              new CustomAttributeBuilder(typeof(SuppressUnmanagedCodeSecurityAttribute).GetConstructor(Type.EmptyTypes), new object[] { }),
+                                                              new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { }),
+                                                              new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args),
+                                                              new CustomAttributeBuilder(typeof(MethodImplAttribute).GetConstructor(new Type[] { typeof(MethodImplOptions) }),
+                                                                                            new object[] { MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization }),
+                                                              new CustomAttributeBuilder(dllType.GetConstructor(new Type[] { typeof(string) }), new object[] { "test.dll" },
+                                                                      new FieldInfo[] { dllType.GetField("CharSet"), dllType.GetField("SetLastError"), dllType.GetField("CallingConvention"), dllType.GetField("BestFitMapping"),
+                                                                          dllType.GetField("ThrowOnUnmappableChar") }, new object[]{ CharSet.Ansi, true, CallingConvention.FastCall, true, false }),
+                                                            };
+
+                AssemblyBuilder ab = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod(
+                    PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod);
+                TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes);
+                typeAttributes.ForEach(tb.SetCustomAttribute);
+                DefineMethodsAndSetAttributes(methodAttributes.ToList(), tb, type.GetMethods());
+                saveMethod.Invoke(ab, new object[] { file.Path });
+
+                Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path);
+                Type testType = assemblyFromDisk.Modules.First().GetTypes()[0];
+                IList<CustomAttributeData> attributesFromDisk = testType.GetCustomAttributesData();
+
+                Assert.Equal(typeAttributes.Count, attributesFromDisk.Count);
+                Assert.True((testType.Attributes & TypeAttributes.Import) != 0); // ComImportAttribute
+                Assert.True((testType.Attributes & TypeAttributes.HasSecurity) != 0); // SuppressUnmanagedCodeSecurityAttribute
+                for (int i = 0; i < attributesFromDisk.Count; i++)
+                {
+                    switch (attributesFromDisk[i].AttributeType.Name)
+                    {
+                        case "ComImportAttribute": // just making sure that these attributes are expected
+                        case "SuppressUnmanagedCodeSecurityAttribute":
+                            break;
+                        case "GuidAttribute":
+                            Assert.Equal(s_guidPair.args[0], attributesFromDisk[i].ConstructorArguments[0].Value);
+                            break;
+                        default:
+                            Assert.Fail($"Not expected attribute : {attributesFromDisk[i].AttributeType.Name}");
+                            break;
+                    }
+                }
+
+                foreach (var method in testType.GetMethods())
+                {
+                    IList<CustomAttributeData> methodAttributesFromDisk = method.GetCustomAttributesData();
+
+                    Assert.True((method.Attributes & MethodAttributes.HasSecurity) != 0); // SuppressUnmanagedCodeSecurityAttribute
+                    Assert.True((method.Attributes & MethodAttributes.SpecialName) != 0); // SpecialNameAttribute
+                    MethodImplAttributes methodImpl = method.GetMethodImplementationFlags();
+                    Assert.True((methodImpl & MethodImplAttributes.NoInlining) != 0); // MethodImplAttribute
+                    Assert.True((methodImpl & MethodImplAttributes.AggressiveOptimization) != 0); // MethodImplAttribute
+                    Assert.True((methodImpl & MethodImplAttributes.PreserveSig) != 0); // PreserveSigAttribute
+                    Assert.Equal(methodAttributes.Length - 2, methodAttributesFromDisk.Count);
+
+                    for (int i = 0; i < methodAttributesFromDisk.Count; i++)
+                    {
+                        switch (methodAttributesFromDisk[i].AttributeType.Name)
+                        {
+                            case "SuppressUnmanagedCodeSecurityAttribute":
+                            case "PreserveSigAttribute":
+                                break;
+                            case "GuidAttribute":
+                                Assert.Equal(s_guidPair.args[0], methodAttributesFromDisk[i].ConstructorArguments[0].Value);
+                                break;
+                            case "DllImportAttribute":
+                                {
+                                    CustomAttributeData attribute = methodAttributesFromDisk[i];
+                                    Assert.Equal("test.dll", attribute.ConstructorArguments[0].Value);
+
+                                    for (int j = 0; j < attribute.NamedArguments.Count; j++)
+                                    {
+                                        switch (attribute.NamedArguments[j].MemberName)
+                                        {
+                                            case "CharSet":
+                                                Assert.Equal(CharSet.Ansi, (CharSet)attribute.NamedArguments[j].TypedValue.Value);
+                                                break;
+                                            case "SetLastError":
+                                                Assert.True((bool)attribute.NamedArguments[j].TypedValue.Value);
+                                                break;
+                                            case "CallingConvention":
+                                                Assert.Equal(CallingConvention.FastCall, (CallingConvention)attribute.NamedArguments[j].TypedValue.Value);
+                                                break;
+                                            case "BestFitMapping":
+                                                Assert.True((bool)attribute.NamedArguments[j].TypedValue.Value);
+                                                break;
+                                            case "ThrowOnUnmappableChar":
+                                                Assert.False((bool)attribute.NamedArguments[j].TypedValue.Value);
+                                                break;
+                                        }
+                                    }
+                                }
+                                break;
+                            default:
+                                Assert.Fail($"Not expected attribute : {methodAttributesFromDisk[i].AttributeType.Name}");
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+
+        private static readonly ConstructorInfo marshalAsEnumCtor = typeof(MarshalAsAttribute).GetConstructor(new Type[] { typeof(UnmanagedType) });
+        private static readonly ConstructorInfo marshalAsShortCtor = typeof(MarshalAsAttribute).GetConstructor(new Type[] { typeof(short) });
+
+        public static IEnumerable<object[]> MarshalAsAttributeWithVariousFields()
+        {
+            yield return new object[] { new CustomAttributeBuilder(marshalAsEnumCtor, new object[] { UnmanagedType.LPStr }), UnmanagedType.LPStr };
+            yield return new object[] { new CustomAttributeBuilder(marshalAsShortCtor, new object[] { (short)21 }), UnmanagedType.LPWStr };
+            yield return new object[] { new CustomAttributeBuilder(marshalAsShortCtor, new object[] { (short)19 }), UnmanagedType.BStr };
+            yield return new object[] { new CustomAttributeBuilder(marshalAsEnumCtor, new object[] { UnmanagedType.ByValTStr },
+                new FieldInfo[] { typeof(MarshalAsAttribute).GetField("SizeConst") }, new object[] { 256 }) , UnmanagedType.ByValTStr };
+            yield return new object[] { new CustomAttributeBuilder(marshalAsEnumCtor, new object[] { UnmanagedType.CustomMarshaler },
+                new FieldInfo[] { typeof(MarshalAsAttribute).GetField("MarshalType"), typeof(MarshalAsAttribute).GetField("MarshalCookie")  },
+                new object[] { typeof(EmptyTestClass).AssemblyQualifiedName, "MyCookie" }) , UnmanagedType.CustomMarshaler };
+            // TODO: When array support added add test for LPArray/ByValArray/SafeArray
+        }
+
+        [Theory]
+        [MemberData(nameof(MarshalAsAttributeWithVariousFields))]
+        public void MarshalAsPseudoCustomAttributesTest(CustomAttributeBuilder attribute, UnmanagedType expectedType)
+        {
+            using (TempFile file = TempFile.Create())
+            {
+                Type type = typeof(StructWithFields);
+                AssemblyBuilder ab = AssemblyTools.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];
+                FieldBuilder fb = tb.DefineField(stringField.Name, stringField.FieldType, stringField.Attributes);
+                fb.SetCustomAttribute(attribute);
+                saveMethod.Invoke(ab, new object[] { file.Path });
+
+                Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path);
+                FieldInfo field = assemblyFromDisk.Modules.First().GetTypes()[0].GetFields()[0];
+                CustomAttributeData attributeFromDisk = field.GetCustomAttributesData()[0];
+
+                Assert.Equal(1, field.GetCustomAttributesData().Count);
+                Assert.True((field.Attributes & FieldAttributes.HasFieldMarshal) != 0);
+                Assert.Equal(expectedType, (UnmanagedType)attributeFromDisk.ConstructorArguments[0].Value);
+
+                switch (expectedType)
+                {
+                    case UnmanagedType.CustomMarshaler:
+                        Assert.Equal(typeof(EmptyTestClass).AssemblyQualifiedName,
+                            attributeFromDisk.NamedArguments.First(na => na.MemberName == "MarshalType").TypedValue.Value);
+                        Assert.Equal("MyCookie", attributeFromDisk.NamedArguments.First(na => na.MemberName == "MarshalCookie").TypedValue.Value);
+                        break;
+                    case UnmanagedType.ByValTStr:
+                        Assert.Equal(256, attributeFromDisk.NamedArguments.First(na => na.MemberName == "SizeConst").TypedValue.Value);
+                        break;
+                }
+            }
+        }
+    }
+}
@@ -9,7 +9,7 @@ using Xunit;
 namespace System.Reflection.Emit.Tests
 {
     [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
-    public class AssemblySaveTestsWithVariousTypes
+    public class AssemblySaveWithVariousMembersTests
     {
         private static readonly AssemblyName s_assemblyName = new AssemblyName("MyDynamicAssembly")
         {
@@ -25,23 +25,9 @@ namespace System.Reflection.Emit.Tests
             using (TempFile file = TempFile.Create())
             {
                 Assembly assemblyFromDisk = WriteAndLoadAssembly(Type.EmptyTypes, file.Path);
-                AssemblyName aNameFromDisk = assemblyFromDisk.GetName();
-
-                // Test AssemblyName properties
-                Assert.Equal(s_assemblyName.Name, aNameFromDisk.Name);
-                Assert.Equal(s_assemblyName.Version, aNameFromDisk.Version);
-                Assert.Equal(s_assemblyName.CultureInfo, aNameFromDisk.CultureInfo);
-                Assert.Equal(s_assemblyName.CultureName, aNameFromDisk.CultureName);
-                Assert.Equal(s_assemblyName.ContentType, aNameFromDisk.ContentType);
-                // Runtime assemblies adding AssemblyNameFlags.PublicKey in Assembly.GetName() overloads
-                Assert.Equal(s_assemblyName.Flags | AssemblyNameFlags.PublicKey, aNameFromDisk.Flags);
-                Assert.Empty(assemblyFromDisk.GetTypes());
-
-                Module moduleFromDisk = assemblyFromDisk.Modules.FirstOrDefault();
 
-                Assert.NotNull(moduleFromDisk);
-                Assert.Equal(s_assemblyName.Name, moduleFromDisk.ScopeName);
-                Assert.Empty(moduleFromDisk.GetTypes());
+                Assert.Empty(assemblyFromDisk.GetTypes());
+                AssemblyTools.AssertAssemblyNameAndModule(s_assemblyName, assemblyFromDisk.GetName(), assemblyFromDisk.Modules.FirstOrDefault());
             }
         }
 
@@ -86,51 +72,12 @@ namespace System.Reflection.Emit.Tests
                 Type sourceType = types[i];
                 Type typeFromDisk = typesFromDisk[i];
 
-                AssertTypeProperties(sourceType, typeFromDisk);
-                AssertMethods(sourceType.GetMethods(), typeFromDisk.GetMethods());
-                AssertFields(sourceType.GetFields(), typeFromDisk.GetFields());
-            }
-        }
-
-        private static void AssertFields(FieldInfo[] declaredFields, FieldInfo[] fieldsFromDisk)
-        {
-            Assert.Equal(declaredFields.Length, fieldsFromDisk.Length);
-
-            for (int j = 0; j < declaredFields.Length; j++)
-            {
-                FieldInfo sourceField = declaredFields[j];
-                FieldInfo fieldFromDisk = fieldsFromDisk[j];
-
-                Assert.Equal(sourceField.Name, fieldFromDisk.Name);
-                Assert.Equal(sourceField.Attributes, fieldFromDisk.Attributes);
-                Assert.Equal(sourceField.FieldType.FullName, fieldFromDisk.FieldType.FullName);
+                AssemblyTools.AssertTypeProperties(sourceType, typeFromDisk);
+                AssemblyTools.AssertMethods(sourceType.GetMethods(), typeFromDisk.GetMethods());
+                AssemblyTools.AssertFields(sourceType.GetFields(), typeFromDisk.GetFields());
             }
         }
 
-        private static void AssertMethods(MethodInfo[] sourceMethods, MethodInfo[] methodsFromDisk)
-        {
-            Assert.Equal(sourceMethods.Length, methodsFromDisk.Length);
-
-            for (int j = 0; j < sourceMethods.Length; j++)
-            {
-                MethodInfo sourceMethod = sourceMethods[j];
-                MethodInfo methodFromDisk = methodsFromDisk[j];
-
-                Assert.Equal(sourceMethod.Name, methodFromDisk.Name);
-                Assert.Equal(sourceMethod.Attributes, methodFromDisk.Attributes);
-                Assert.Equal(sourceMethod.ReturnType.FullName, methodFromDisk.ReturnType.FullName);
-            }
-        }
-
-        private static void AssertTypeProperties(Type sourceType, Type typeFromDisk)
-        {
-            Assert.Equal(sourceType.Name, typeFromDisk.Name);
-            Assert.Equal(sourceType.Namespace, typeFromDisk.Namespace);
-            Assert.Equal(sourceType.Attributes, typeFromDisk.Attributes);
-            Assert.Equal(sourceType.IsInterface, typeFromDisk.IsInterface);
-            Assert.Equal(sourceType.IsValueType, typeFromDisk.IsValueType);
-        }
-
         [Theory]
         [MemberData(nameof(VariousInterfacesStructsTestData))]
         public void WriteAssemblyWithVariousTypesToStreamAndReadBackTest(Type[] types)
@@ -149,9 +96,8 @@ namespace System.Reflection.Emit.Tests
         {
             using (TempFile file = TempFile.Create())
             {
-                MethodInfo defineDynamicAssemblyMethod = AssemblyTools.PopulateMethods(typeof(string), out MethodInfo saveMethod);
-                AssemblyBuilder assemblyBuilder = (AssemblyBuilder)defineDynamicAssemblyMethod.Invoke(null,
-                    new object[] { s_assemblyName, typeof(object).Assembly, null });
+                AssemblyBuilder assemblyBuilder = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod(
+                    s_assemblyName, null, typeof(string), out MethodInfo saveMethod);
 
                 ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module");
                 TypeBuilder tb = mb.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract);
index b07c04b..0cb5fe3 100644 (file)
@@ -3,6 +3,8 @@
 
 using System.Collections.Generic;
 using System.IO;
+using System.Runtime.InteropServices;
+using Xunit;
 
 namespace System.Reflection.Emit.Tests
 {
@@ -10,23 +12,16 @@ namespace System.Reflection.Emit.Tests
     {
         internal static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation)
         {
-            WriteAssemblyToDisk(assemblyName, types, fileLocation, null);
-        }
-
-        internal static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation, List<CustomAttributeBuilder> assemblyAttributes)
-        {
-            MethodInfo defineDynamicAssemblyMethod = PopulateMethods(typeof(string), out MethodInfo saveMethod);
+            AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilderAndSaveMethod(
+                assemblyName, null, typeof(string), out MethodInfo saveMethod);
 
-            AssemblyBuilder assemblyBuilder = (AssemblyBuilder)defineDynamicAssemblyMethod.Invoke(null,
-                new object[] { assemblyName, CoreMetadataAssemblyResolver.s_coreAssembly, assemblyAttributes });
             ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
-
-            PopulateMembersForModule(types, mb);
+            PopulateMembersForModule(mb, types);
 
             saveMethod.Invoke(assemblyBuilder, new object[] { fileLocation });
         }
 
-        private static void PopulateMembersForModule(Type[] types, ModuleBuilder mb)
+        private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types)
         {
             foreach (Type type in types)
             {
@@ -47,33 +42,27 @@ namespace System.Reflection.Emit.Tests
 
         internal static void WriteAssemblyToStream(AssemblyName assemblyName, Type[] types, Stream stream)
         {
-            WriteAssemblyToStream(assemblyName, types, stream, null);
-        }
-
-        internal static void WriteAssemblyToStream(AssemblyName assemblyName, Type[] types, Stream stream, List<CustomAttributeBuilder>? assemblyAttributes)
-        {
-            MethodInfo defineDynamicAssemblyMethod = PopulateMethods(typeof(Stream), out MethodInfo saveMethod);
-
-            AssemblyBuilder assemblyBuilder = (AssemblyBuilder)defineDynamicAssemblyMethod.Invoke(null,
-                new object[] { assemblyName, CoreMetadataAssemblyResolver.s_coreAssembly, assemblyAttributes });
+            AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilderAndSaveMethod(
+                assemblyName, null, typeof(Stream), out MethodInfo saveMethod);
 
             ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
-
-            PopulateMembersForModule(types, mb);
+            PopulateMembersForModule(mb, types);
 
             saveMethod.Invoke(assemblyBuilder, new object[] { stream });
         }
 
-        internal static MethodInfo PopulateMethods(Type parameterType, out MethodInfo saveMethod)
+        internal static AssemblyBuilder PopulateAssemblyBuilderAndSaveMethod(AssemblyName assemblyName,
+            List<CustomAttributeBuilder>? assemblyAttributes, Type parameterType, out MethodInfo saveMethod)
         {
-            Type assemblyType = Type.GetType(
-                    "System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit",
-                    throwOnError: true)!;
+            Type assemblyType = Type.GetType("System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit", throwOnError: true)!;
 
             saveMethod = assemblyType.GetMethod("Save", BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { parameterType });
 
-            return assemblyType.GetMethod("DefinePersistedAssembly", BindingFlags.NonPublic | BindingFlags.Static,
+            MethodInfo defineDynamicAssemblyMethod = assemblyType.GetMethod("DefinePersistedAssembly", BindingFlags.NonPublic | BindingFlags.Static,
                 new Type[] { typeof(AssemblyName), typeof(Assembly), typeof(List<CustomAttributeBuilder>) });
+
+            return (AssemblyBuilder)defineDynamicAssemblyMethod.Invoke(null,
+                new object[] { assemblyName, CoreMetadataAssemblyResolver.s_coreAssembly, assemblyAttributes });
         }
 
         internal static Assembly LoadAssemblyFromPath(string filePath) =>
@@ -81,6 +70,60 @@ namespace System.Reflection.Emit.Tests
 
         internal static Assembly LoadAssemblyFromStream(Stream stream) =>
             new MetadataLoadContext(new CoreMetadataAssemblyResolver()).LoadFromStream(stream);
+
+        internal static void AssertAssemblyNameAndModule(AssemblyName sourceAName, AssemblyName aNameFromDisk, Module moduleFromDisk)
+        {
+            // Runtime assemblies adding AssemblyNameFlags.PublicKey in Assembly.GetName() overloads
+            Assert.Equal(sourceAName.Flags | AssemblyNameFlags.PublicKey, aNameFromDisk.Flags);
+            Assert.Equal(sourceAName.Name, aNameFromDisk.Name);
+            Assert.Equal(sourceAName.Version, aNameFromDisk.Version);
+            Assert.Equal(sourceAName.CultureInfo, aNameFromDisk.CultureInfo);
+            Assert.Equal(sourceAName.CultureName, aNameFromDisk.CultureName);
+            Assert.Equal(sourceAName.ContentType, aNameFromDisk.ContentType);
+
+            Assert.NotNull(moduleFromDisk);
+            Assert.Equal(sourceAName.Name, moduleFromDisk.ScopeName);
+            Assert.Empty(moduleFromDisk.GetTypes());
+        }
+
+        internal static void AssertTypeProperties(Type sourceType, Type typeFromDisk)
+        {
+            Assert.Equal(sourceType.Name, typeFromDisk.Name);
+            Assert.Equal(sourceType.Namespace, typeFromDisk.Namespace);
+            Assert.Equal(sourceType.Attributes, typeFromDisk.Attributes);
+            Assert.Equal(sourceType.IsInterface, typeFromDisk.IsInterface);
+            Assert.Equal(sourceType.IsValueType, typeFromDisk.IsValueType);
+        }
+
+        internal static void AssertFields(FieldInfo[] declaredFields, FieldInfo[] fieldsFromDisk)
+        {
+            Assert.Equal(declaredFields.Length, fieldsFromDisk.Length);
+
+            for (int j = 0; j < declaredFields.Length; j++)
+            {
+                FieldInfo sourceField = declaredFields[j];
+                FieldInfo fieldFromDisk = fieldsFromDisk[j];
+
+                Assert.Equal(sourceField.Name, fieldFromDisk.Name);
+                Assert.Equal(sourceField.Attributes, fieldFromDisk.Attributes);
+                Assert.Equal(sourceField.FieldType.FullName, fieldFromDisk.FieldType.FullName);
+            }
+        }
+
+        internal static void AssertMethods(MethodInfo[] sourceMethods, MethodInfo[] methodsFromDisk)
+        {
+            Assert.Equal(sourceMethods.Length, methodsFromDisk.Length);
+
+            for (int j = 0; j < sourceMethods.Length; j++)
+            {
+                MethodInfo sourceMethod = sourceMethods[j];
+                MethodInfo methodFromDisk = methodsFromDisk[j];
+
+                Assert.Equal(sourceMethod.Name, methodFromDisk.Name);
+                Assert.Equal(sourceMethod.Attributes, methodFromDisk.Attributes);
+                Assert.Equal(sourceMethod.ReturnType.FullName, methodFromDisk.ReturnType.FullName);
+            }
+        }
     }
 
     // The resolver copied from MLC tests
index f10f0a0..f672dc3 100644 (file)
@@ -62,7 +62,8 @@
     <Compile Include="ModuleBuilder\ModuleBuilderGetArrayMethod.cs" />
     <Compile Include="ModuleBuilder\ModuleBuilderSetCustomAttribute.cs" />
     <Compile Include="ParameterBuilder\ParameterBuilderSetConstant.cs" />
-    <Compile Include="PersistableAssemblyBuilder\AssemblySaveTestsWithVariousTypes.cs" />
+    <Compile Include="PersistableAssemblyBuilder\AssemblySaveCustomAttributeTests.cs" />
+    <Compile Include="PersistableAssemblyBuilder\AssemblySaveWithVariousMembersTests.cs" />
     <Compile Include="PersistableAssemblyBuilder\AssemblyTools.cs" />
     <Compile Include="PropertyBuilder\PropertyBuilderAddOtherMethod.cs" />
     <Compile Include="PropertyBuilder\PropertyBuilderAttributes.cs" />
index 494a2bc..28cbe9e 100644 (file)
@@ -77,13 +77,12 @@ namespace System.Reflection.Emit
             return result;
         }
 
-        internal CustomAttributeBuilder(ConstructorInfo con, byte[] binaryAttribute)
+        internal CustomAttributeBuilder(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             ArgumentNullException.ThrowIfNull(con);
-            ArgumentNullException.ThrowIfNull(binaryAttribute);
 
             ctor = con;
-            data = (byte[])binaryAttribute.Clone();
+            data = binaryAttribute.ToArray();
             /* should we check that the user supplied data is correct? */
         }
 
@@ -268,7 +267,7 @@ namespace System.Reflection.Emit
         }
 
         /* helper methods */
-        internal static int decode_len(byte[] data, int pos, out int rpos)
+        internal static int decode_len(ReadOnlySpan<byte> data, int pos, out int rpos)
         {
             int len;
             if ((data[pos] & 0x80) == 0)
@@ -289,9 +288,9 @@ namespace System.Reflection.Emit
             return len;
         }
 
-        internal static string string_from_bytes(byte[] data, int pos, int len)
+        internal static string string_from_bytes(ReadOnlySpan<byte> data, int pos, int len)
         {
-            return Text.Encoding.UTF8.GetString(data, pos, len);
+            return Text.Encoding.UTF8.GetString(data.Slice(pos, len));
         }
 
         internal static string? decode_string(byte[] data, int pos, out int rpos)
@@ -454,7 +453,7 @@ namespace System.Reflection.Emit
                 _ => throw new Exception(SR.Format(SR.ArgumentException_InvalidTypeArgument, elementType)),
             };
 
-        private static object? decode_cattr_value(Type t, byte[] data, int pos, out int rpos)
+        private static object? decode_cattr_value(Type t, ReadOnlySpan<byte> data, int pos, out int rpos)
         {
             switch (Type.GetTypeCode(t))
             {
@@ -494,14 +493,19 @@ namespace System.Reflection.Emit
             public object?[] namedParamValues;
         }
 
+        internal static CustomAttributeInfo decode_cattr(CustomAttributeBuilder customBuilder)
+        {
+            byte[] data = customBuilder.Data;
+            ConstructorInfo ctor = customBuilder.Ctor;
+            return decode_cattr(ctor, data);
+        }
+
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:UnrecognizedReflectionPattern",
             Justification = "Types referenced from custom attributes are preserved")]
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
             Justification = "Types referenced from custom attributes are preserved")]
-        internal static CustomAttributeInfo decode_cattr(CustomAttributeBuilder customBuilder)
+        internal static CustomAttributeInfo decode_cattr(ConstructorInfo ctor, ReadOnlySpan<byte> data)
         {
-            byte[] data = customBuilder.Data;
-            ConstructorInfo ctor = customBuilder.Ctor;
             int pos;
 
             CustomAttributeInfo info = default;
index a5bfdaa..32cbacf 100644 (file)
@@ -297,8 +297,9 @@ namespace System.Reflection.Emit
 
         public override bool IsCollectible => access == (uint)AssemblyBuilderAccess.RunAndCollect;
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -315,11 +316,6 @@ namespace System.Reflection.Emit
             UpdateNativeCustomAttributes(this);
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         /*Warning, @typeArguments must be a mscorlib internal array. So make a copy before passing it in*/
         internal static Type MakeGenericType(Type gtd, Type[] typeArguments) =>
             new TypeBuilderInstantiation(gtd, typeArguments);
index f9939d2..83c62a8 100644 (file)
@@ -37,6 +37,7 @@
 using System.Globalization;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.InteropServices;
+using System.Buffers.Binary;
 
 namespace System.Reflection.Emit
 {
@@ -254,20 +255,16 @@ namespace System.Reflection.Emit
             return ilgen;
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
-            ArgumentNullException.ThrowIfNull(customBuilder);
-
-            string? attrname = customBuilder.Ctor.ReflectedType!.FullName;
+            string? attrname = con.ReflectedType!.FullName;
             if (attrname == "System.Runtime.CompilerServices.MethodImplAttribute")
             {
-                byte[] data = customBuilder.Data;
-                int impla; // the (stupid) ctor takes a short or an int ...
-                impla = (int)data[2];
-                impla |= ((int)data[3]) << 8;
+                int impla = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(2));
                 SetImplementationFlags((MethodImplAttributes)impla);
                 return;
             }
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -282,14 +279,6 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            ArgumentNullException.ThrowIfNull(con);
-            ArgumentNullException.ThrowIfNull(binaryAttribute);
-
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         protected override void SetImplementationFlagsCore(MethodImplAttributes attributes)
         {
             if (type.is_created)
index ab54737..b417c32 100644 (file)
@@ -447,14 +447,9 @@ namespace System.Reflection.Emit
             return SymbolType.FormCompoundType("*", this, 0)!;
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
-            _tb.SetCustomAttribute(customBuilder);
-        }
-
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
+            _tb.SetCustomAttribute(con, binaryAttribute);
         }
 
         internal override bool IsUserType
index 8ff4a3d..5cbfea6 100644 (file)
@@ -106,15 +106,16 @@ namespace System.Reflection.Emit
             remove_method = mdBuilder;
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             RejectIfCreated();
-            string? attrname = customBuilder.Ctor.ReflectedType!.FullName;
+            string? attrname = con.ReflectedType!.FullName;
             if (attrname == "System.Runtime.CompilerServices.SpecialNameAttribute")
             {
                 attrs |= EventAttributes.SpecialName;
                 return;
             }
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -129,11 +130,6 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         private void RejectIfCreated()
         {
             if (typeb.is_created)
index db8d805..68f4c7b 100644 (file)
@@ -37,6 +37,7 @@
 using System.Globalization;
 using System.Runtime.InteropServices;
 using System.Diagnostics.CodeAnalysis;
+using System.Buffers.Binary;
 
 namespace System.Reflection.Emit
 {
@@ -174,18 +175,14 @@ namespace System.Reflection.Emit
             def_value = defaultValue;
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             RejectIfCreated();
-
-            string? attrname = customBuilder.Ctor.ReflectedType!.FullName;
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
+            string? attrname = con.ReflectedType!.FullName;
             if (attrname == "System.Runtime.InteropServices.FieldOffsetAttribute")
             {
-                byte[] data = customBuilder.Data;
-                offset = (int)data[2];
-                offset |= ((int)data[3]) << 8;
-                offset |= ((int)data[4]) << 16;
-                offset |= ((int)data[5]) << 24;
+                offset = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(2));
                 return;
             }
 #pragma warning disable SYSLIB0050 // FieldAttributes.NotSerialized is obsolete
@@ -221,12 +218,6 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            RejectIfCreated();
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         protected override void SetOffsetCore(int iOffset)
         {
             RejectIfCreated();
index a1f1761..27e0d2e 100644 (file)
@@ -424,8 +424,9 @@ namespace System.Reflection.Emit
             get { return mbuilder; }
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -440,12 +441,6 @@ namespace System.Reflection.Emit
             }
         }
 
-        // FIXME: "unverified implementation"
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         private static NotSupportedException not_supported()
         {
             return new NotSupportedException();
index 80e8352..cdae183 100644 (file)
@@ -34,6 +34,7 @@
 //
 
 #if MONO_FEATURE_SRE
+using System.Buffers.Binary;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Runtime.InteropServices;
@@ -381,20 +382,17 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
-            switch (customBuilder.Ctor.ReflectedType!.FullName)
+            switch (con.ReflectedType!.FullName)
             {
                 case "System.Runtime.CompilerServices.MethodImplAttribute":
-                    byte[] data = customBuilder.Data;
-                    int impla; // the (stupid) ctor takes a short or an int ...
-                    impla = (int)data[2];
-                    impla |= ((int)data[3]) << 8;
+                    int impla = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(2));
                     iattrs |= (MethodImplAttributes)impla;
                     return;
 
                 case "System.Runtime.InteropServices.DllImportAttribute":
-                    CustomAttributeBuilder.CustomAttributeInfo attr = CustomAttributeBuilder.decode_cattr(customBuilder);
+                    CustomAttributeBuilder.CustomAttributeInfo attr = CustomAttributeBuilder.decode_cattr(con, binaryAttribute);
                     bool preserveSig = true;
 
                     /*
@@ -453,6 +451,7 @@ namespace System.Reflection.Emit
                     break;
             }
 
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -467,11 +466,6 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         protected override void SetImplementationFlagsCore(MethodImplAttributes attributes)
         {
             RejectIfCreated();
index a6ab47c..85e98ea 100644 (file)
@@ -426,8 +426,9 @@ namespace System.Reflection.Emit
             return index;
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -441,11 +442,6 @@ namespace System.Reflection.Emit
                 cattrs[0] = customBuilder;
             }
         }
-
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
         /*
                 internal ISymbolDocumentWriter? DefineDocument (string url, Guid language, Guid languageVendor, Guid documentType)
                 {
index 80c8d94..25dbb48 100644 (file)
@@ -159,16 +159,18 @@ namespace System.Reflection.Emit
             def_value = defaultValue;
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
             typeb.check_not_created();
-            string? attrname = customBuilder.Ctor.ReflectedType!.FullName;
+            string? attrname = con.ReflectedType!.FullName;
             if (attrname == "System.Runtime.CompilerServices.SpecialNameAttribute")
             {
                 attrs |= PropertyAttributes.SpecialName;
                 return;
             }
 
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
+
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -183,11 +185,6 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         protected override void SetGetMethodCore(MethodBuilder mdBuilder)
         {
             typeb.check_not_created();
index 98670c9..469f9ef 100644 (file)
@@ -34,6 +34,7 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+using System.Buffers.Binary;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
@@ -1411,15 +1412,19 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder)
+        internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
         {
-            string? attrname = customBuilder.Ctor.ReflectedType!.FullName;
+            SetCustomAttributeCore(con, binaryAttribute);
+        }
+
+        protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute)
+        {
+            string? attrname = con.ReflectedType!.FullName;
             if (attrname == "System.Runtime.InteropServices.StructLayoutAttribute")
             {
-                byte[] data = customBuilder.Data;
                 int layout_kind; /* the (stupid) ctor takes a short or an int ... */
-                layout_kind = (int)data[2];
-                layout_kind |= ((int)data[3]) << 8;
+                layout_kind = (int)binaryAttribute[2];
+                layout_kind |= ((int)binaryAttribute[3]) << 8;
                 attrs &= ~TypeAttributes.LayoutMask;
                 attrs |= ((LayoutKind)layout_kind) switch
                 {
@@ -1429,38 +1434,36 @@ namespace System.Reflection.Emit
                     _ => throw new Exception(SR.Argument_InvalidKindOfTypeForCA), // we should ignore it since it can be any value anyway...
                 };
 
-                Type ctor_type = customBuilder.Ctor is RuntimeConstructorBuilder builder ? builder.parameters![0] : customBuilder.Ctor.GetParametersInternal()[0].ParameterType;
+                Type ctor_type = con is RuntimeConstructorBuilder builder ? builder.parameters![0] : con.GetParametersInternal()[0].ParameterType;
                 int pos = 6;
                 if (ctor_type.FullName == "System.Int16")
                     pos = 4;
-                int nnamed = (int)data[pos++];
-                nnamed |= ((int)data[pos++]) << 8;
+                int nnamed = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(pos++));
+                pos++;
                 for (int i = 0; i < nnamed; ++i)
                 {
                     //byte named_type = data [pos++];
                     pos++;
-                    byte type = data[pos++];
+                    byte type = binaryAttribute[pos++];
                     int len;
                     string named_name;
 
                     if (type == 0x55)
                     {
-                        len = CustomAttributeBuilder.decode_len(data, pos, out pos);
+                        len = CustomAttributeBuilder.decode_len(binaryAttribute, pos, out pos);
                         //string named_typename =
-                        CustomAttributeBuilder.string_from_bytes(data, pos, len);
+                        CustomAttributeBuilder.string_from_bytes(binaryAttribute, pos, len);
                         pos += len;
                         // FIXME: Check that 'named_type' and 'named_typename' match, etc.
                         //        See related code/FIXME in mono/mono/metadata/reflection.c
                     }
 
-                    len = CustomAttributeBuilder.decode_len(data, pos, out pos);
-                    named_name = CustomAttributeBuilder.string_from_bytes(data, pos, len);
+                    len = CustomAttributeBuilder.decode_len(binaryAttribute, pos, out pos);
+                    named_name = CustomAttributeBuilder.string_from_bytes(binaryAttribute, pos, len);
                     pos += len;
                     /* all the fields are integers in StructLayout */
-                    int value = (int)data[pos++];
-                    value |= ((int)data[pos++]) << 8;
-                    value |= ((int)data[pos++]) << 16;
-                    value |= ((int)data[pos++]) << 24;
+                    int value = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(pos++));
+                    pos += 3;
                     switch (named_name)
                     {
                         case "CharSet":
@@ -1520,6 +1523,8 @@ namespace System.Reflection.Emit
                 is_byreflike_set = 1;
             }
 
+            CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute);
+
             if (cattrs != null)
             {
                 CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1];
@@ -1534,11 +1539,6 @@ namespace System.Reflection.Emit
             }
         }
 
-        protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute)
-        {
-            SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute));
-        }
-
         protected override EventBuilder DefineEventCore(string name, EventAttributes attributes, Type eventtype)
         {
             check_name(nameof(name), name);