From f4b02db254c0db60aaab6b76d83b1006d617747e Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 6 Apr 2023 22:55:18 -0700 Subject: [PATCH 01/11] Initial custom attibutes handling --- .../Reflection/Emit/CustomAttributeBuilder.cs | 6 +- .../Reflection/Emit/RuntimeAssemblyBuilder.cs | 11 - .../Emit/RuntimeConstructorBuilder.cs | 5 - .../Reflection/Emit/RuntimeEnumBuilder.cs | 9 - .../Reflection/Emit/RuntimeEventBuilder.cs | 7 - .../Reflection/Emit/RuntimeFieldBuilder.cs | 7 - .../RuntimeGenericTypeParameterBuilder.cs | 5 - .../Reflection/Emit/RuntimeMethodBuilder.cs | 11 +- .../Reflection/Emit/RuntimeModuleBuilder.cs | 5 - .../Reflection/Emit/RuntimePropertyBuilder.cs | 8 - .../Reflection/Emit/RuntimeTypeBuilder.cs | 5 - .../Reflection/Emit/CustomAttributeBuilder.cs | 5 + .../System/Reflection/Emit/AssemblyBuilder.cs | 4 +- .../Reflection/Emit/ConstructorBuilder.cs | 13 +- .../src/System/Reflection/Emit/EnumBuilder.cs | 4 +- .../System/Reflection/Emit/EventBuilder.cs | 4 +- .../System/Reflection/Emit/FieldBuilder.cs | 4 +- .../Emit/GenericTypeParameterBuilder.cs | 4 +- .../System/Reflection/Emit/MethodBuilder.cs | 4 +- .../System/Reflection/Emit/ModuleBuilder.cs | 4 +- .../System/Reflection/Emit/PropertyBuilder.cs | 5 +- .../src/System/Reflection/Emit/TypeBuilder.cs | 4 +- .../ref/System.Reflection.Emit.cs | 10 - .../src/System.Reflection.Emit.csproj | 1 + .../Reflection/Emit/AssemblyBuilderImpl.cs | 13 +- .../Reflection/Emit/CustomAttributeWrapper.cs | 17 ++ .../Reflection/Emit/FieldBuilderImpl.cs | 12 +- .../System/Reflection/Emit/MetadataHelper.cs | 10 + .../Reflection/Emit/MethodBuilderImpl.cs | 9 +- .../Reflection/Emit/ModuleBuilderImpl.cs | 49 +++- .../System/Reflection/Emit/SignatureHelper.cs | 30 ++- .../System/Reflection/Emit/TypeBuilderImpl.cs | 29 ++- .../AssemblySaveTestsWithVariousTypes.cs | 68 +---- .../AssemblyTools.cs | 96 +++++-- .../CustomAttributeTesting.cs | 234 ++++++++++++++++++ .../tests/System.Reflection.Emit.Tests.csproj | 1 + .../Emit/CustomAttributeBuilder.Mono.cs | 9 + .../Emit/RuntimeAssemblyBuilder.Mono.cs | 8 +- .../Emit/RuntimeConstructorBuilder.Mono.cs | 17 +- .../Emit/RuntimeEnumBuilder.Mono.cs | 7 +- .../Emit/RuntimeEventBuilder.Mono.cs | 10 +- .../Emit/RuntimeFieldBuilder.Mono.cs | 17 +- .../RuntimeGenericTypeParameterBuilder.cs | 10 +- .../Emit/RuntimeMethodBuilder.Mono.cs | 14 +- .../Emit/RuntimeModuleBuilder.Mono.cs | 8 +- .../Emit/RuntimePropertyBuilder.Mono.cs | 11 +- .../Emit/RuntimeTypeBuilder.Mono.cs | 15 +- 47 files changed, 533 insertions(+), 306 deletions(-) create mode 100644 src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs create mode 100644 src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs index b740d461df3f8e..842ebaf3c3c97d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs @@ -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) : diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs index b2d82487f02321..5f699ca4114580 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs @@ -299,16 +299,5 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar binaryAttribute); } } - - /// - /// Use this function if client wishes to build CustomAttribute using CustomAttributeBuilder. - /// - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) - { - lock (SyncRoot) - { - customBuilder.CreateCustomAttribute(_manifestModuleBuilder, AssemblyDefToken); - } - } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs index 86b20be8e7ff09..4a7bfdfd7f6d28 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs @@ -162,11 +162,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar 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); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs index 3e81b8ed9fc874..49ccbe7e0d6f4b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs @@ -274,18 +274,11 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) } // Use this function if client decides to form the custom attribute blob themselves - protected override void SetCustomAttributeCore(ConstructorInfo con, 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 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil 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 @@ public override Type MakeArrayType(int rank) return SymbolType.FormCompoundType(s, this, 0)!; } - // Constructs a EnumBuilder. // EnumBuilder can only be a top-level (not nested) enum type. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2064:UnrecognizedReflectionPattern", diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs index 41befaa64ce550..787a706cabd114 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs @@ -91,13 +91,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar 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; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs index da3341e744410a..457654fcc3735d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs @@ -160,13 +160,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar m_fieldTok, moduleBuilder.GetMethodMetadataToken(con), binaryAttribute); } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) - { - m_typeBuilder.ThrowIfCreated(); - - customBuilder.CreateCustomAttribute((RuntimeModuleBuilder)m_typeBuilder.Module, m_fieldTok); - } - #endregion } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs index 6301e2ab4c5098..c58b304576e6c1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs @@ -220,11 +220,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar 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); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs index 6b2cdad3cc2582..f8b6503c96e7db 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs @@ -678,7 +678,7 @@ private void ThrowIfShouldNotHaveBody() m_isDllImport) { // cannot attach method body if methodimpl is marked not marked as managed IL - // + // supress throw new InvalidOperationException(SR.InvalidOperation_ShouldNotHaveMethodBody); } } @@ -706,15 +706,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar 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) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs index 637f7cd6c7e95f..7b0c5db735f108 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs @@ -1298,11 +1298,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar binaryAttribute); } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) - { - customBuilder.CreateCustomAttribute(this, 1); // This is hard coding the module token to 1 - } - #endregion #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs index ee71d32ceb710f..8d71956e435bc1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs @@ -97,7 +97,6 @@ protected override void AddOtherMethodCore(MethodBuilder mdBuilder) } // Use this function if client decides to form the custom attribute blob themselves - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { m_containingType.ThrowIfCreated(); @@ -108,13 +107,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar 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) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs index 02108de51c1c52..01abe24e064385 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs @@ -1863,11 +1863,6 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar DefineCustomAttribute(m_module, m_tdType, m_module.GetMethodMetadataToken(con), binaryAttribute); } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) - { - customBuilder.CreateCustomAttribute(m_module, m_tdType); - } - #endregion #endregion diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs index 5a80b4318eddb1..818dccc95dceb3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs @@ -24,5 +24,10 @@ public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, Pro { 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 } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 3abc6925b7a4af..6ff03e95bb613a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -45,11 +45,9 @@ 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs index 3daac6f42875b0..55609291d05271 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs @@ -31,14 +31,21 @@ public ILGenerator GetILGenerator(int streamSize) 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); 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs index f3cdb7f848fb32..c23ff08a405dd2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs @@ -38,8 +38,6 @@ public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) - => SetCustomAttributeCore(customBuilder); - - protected abstract void SetCustomAttributeCore(CustomAttributeBuilder customBuilder); + => SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs index 4c4a8f09f6ed49..cfa7c5d219d9f4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs @@ -33,11 +33,9 @@ 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs index 26092610945625..14727f3d8cf935 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs @@ -28,11 +28,9 @@ 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs index 3594991999021b..1b7cd888b3bd2e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs @@ -19,11 +19,9 @@ 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs index dab5cd525e029a..b46178667529a6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs @@ -62,11 +62,9 @@ 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs index 021ddd08ecd86e..0b6cc3bb3e9a94 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs @@ -140,11 +140,9 @@ 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs index 924c40f948a128..30dc61b4e29c20 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs @@ -32,10 +32,9 @@ public void SetCustomAttribute(ConstructorInfo con, 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs index e715ac81fbb9d5..d64b2138797713 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs @@ -283,11 +283,9 @@ 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); diff --git a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs index 14b8c7b36d9c17..473ce04e1db82c 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs @@ -57,7 +57,6 @@ protected AssemblyBuilder() { } 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); } [System.FlagsAttribute] public enum AssemblyBuilderAccess @@ -93,7 +92,6 @@ protected ConstructorBuilder() { } 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); public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { } protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes); public override string ToString() { throw null; } @@ -188,7 +186,6 @@ protected EnumBuilder() { } 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); } public abstract partial class EventBuilder { @@ -200,7 +197,6 @@ public void SetAddOnMethod(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); 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) { } @@ -226,7 +222,6 @@ public void SetConstant(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); 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) { } @@ -329,7 +324,6 @@ public void SetBaseTypeConstraint([System.Diagnostics.CodeAnalysis.DynamicallyAc 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); public void SetGenericParameterAttributes(System.Reflection.GenericParameterAttributes genericParameterAttributes) { } protected abstract void SetGenericParameterAttributesCore(System.Reflection.GenericParameterAttributes genericParameterAttributes); public void SetInterfaceConstraints(params System.Type[]? interfaceConstraints) { } @@ -382,7 +376,6 @@ protected MethodBuilder() { } 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); public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { } protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes); public void SetParameters(params System.Type[] parameterTypes) { } @@ -476,7 +469,6 @@ public void CreateGlobalFunctions() { } 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); } public abstract partial class PropertyBuilder : System.Reflection.PropertyInfo { @@ -505,7 +497,6 @@ public void SetConstant(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); 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) { } @@ -673,7 +664,6 @@ public void DefineMethodOverride(System.Reflection.MethodInfo methodInfoBody, Sy 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); 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; } diff --git a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj index 7da2c955d52e60..649d37be78ec3b 100644 --- a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj +++ b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj @@ -5,6 +5,7 @@ true + diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs index ea7ec68d9d7e1d..e4041fc13bd1f8 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs @@ -16,6 +16,8 @@ internal sealed class AssemblyBuilderImpl : AssemblyBuilder private readonly Assembly _coreAssembly; private ModuleBuilderImpl? _module; + internal List _customAttributes = new(); + internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable? assemblyAttributes) { ArgumentNullException.ThrowIfNull(name); @@ -77,7 +79,7 @@ internal void Save(Stream stream) // Add assembly metadata var metadata = new MetadataBuilder(); - metadata.AddAssembly( + AssemblyDefinitionHandle assemblyHandle = metadata.AddAssembly( metadata.GetOrAddString(value: _assemblyName.Name!), version: _assemblyName.Version ?? new Version(0, 0, 0, 0), culture: _assemblyName.CultureName == null ? default : metadata.GetOrAddString(value: _assemblyName.CultureName), @@ -89,7 +91,7 @@ internal void Save(Stream stream) ); // Add module's metadata - _module.AppendMetadata(metadata); + _module.AppendMetadata(metadata, assemblyHandle, _customAttributes); var ilBuilder = new BlobBuilder(); WritePEImage(stream, metadata, ilBuilder); @@ -128,8 +130,9 @@ protected override ModuleBuilder DefineDynamicModuleCore(string name) 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, byte[] binaryAttribute) + { + _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 index 00000000000000..abef65561c53aa --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + internal sealed class CustomAttributeWrapper + { + internal ConstructorInfo constructorInfo; + internal byte[] binaryAttribute; + + public CustomAttributeWrapper(ConstructorInfo constructorInfo, byte[] binaryAttribute) + { + this.constructorInfo = constructorInfo; + this.binaryAttribute = binaryAttribute; + } + } +} diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index c017afa9f3580d..13db36cfe8b991 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Globalization; namespace System.Reflection.Emit @@ -12,6 +13,8 @@ internal sealed class FieldBuilderImpl : FieldBuilder private readonly FieldAttributes _attributes; private readonly Type _fieldType; + internal List _customAttributes = new(); + internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes) { _fieldName = fieldName; @@ -20,13 +23,16 @@ internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type ty _attributes = attributes & ~FieldAttributes.ReservedMask; } - #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, byte[] binaryAttribute) + { + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotImplementedException(); protected override void SetOffsetCore(int iOffset) => throw new NotImplementedException(); + #region MemberInfo Overrides + public override int MetadataToken => throw new NotImplementedException(); public override Module Module => _typeBuilder.Module; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs index 869c5ef3f11130..ce1d7c77fd8af4 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs @@ -74,5 +74,15 @@ internal static FieldDefinitionHandle AddFieldDefinition(MetadataBuilder metadat name: metadata.GetOrAddString(field.Name), signature: metadata.GetOrAddBlob(fieldSignatureBlob)); } + + internal static MemberReferenceHandle AddConstructorReference(MetadataBuilder metadata, TypeReferenceHandle parent, MethodBase method) + { + var blob = MetadataSignatureHelper.ConstructorSignatureEncoder(method.GetParameters(), (ModuleBuilderImpl)method.Module); + return metadata.AddMemberReference( + parent: parent, + name: metadata.GetOrAddString(method.Name), + signature: metadata.GetOrAddBlob(blob) + ); + } } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index c63e4c26e5d47c..6f56a7d2351385 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection.Metadata; @@ -17,6 +18,8 @@ internal sealed class MethodBuilderImpl : MethodBuilder private readonly CallingConventions _callingConventions; private readonly TypeBuilderImpl _declaringType; + internal List _customAttributes = new(); + internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType, Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType) { @@ -44,8 +47,10 @@ internal BlobBuilder GetMethodSignatureBlob() => 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 SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + { + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } protected override void SetImplementationFlagsCore(MethodImplAttributes attributes) => throw new NotImplementedException(); protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) => throw new NotImplementedException(); diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 07dde6a0746ee6..15ba6cb7778dd4 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -15,8 +15,10 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly string _name; private Type?[]? _coreTypes; private readonly Dictionary _assemblyRefStore = new(); + private readonly Dictionary _constructorRefStore = new(); private readonly Dictionary _typeRefStore = new(); private readonly List _typeDefStore = new(); + private readonly List _customAttributes = new(); private int _nextMethodDefRowId = 1; private int _nextFieldDefRowId = 1; private bool _coreTypesFullPopulated; @@ -90,10 +92,10 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MetadataBuilder metadata) + internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle assemlbyHandle, List assemblyAttributes) { // Add module metadata - metadata.AddModule( + ModuleDefinitionHandle moduleHandle = metadata.AddModule( generation: 0, moduleName: metadata.GetOrAddString(_name), mvid: metadata.GetOrAddGuid(Guid.NewGuid()), @@ -107,7 +109,10 @@ internal void AppendMetadata(MetadataBuilder metadata) name: metadata.GetOrAddString(""), baseType: default, fieldList: MetadataTokens.FieldDefinitionHandle(1), - methodList: MetadataTokens.MethodDefinitionHandle(1)); ; + methodList: MetadataTokens.MethodDefinitionHandle(1)); + + WriteCustomAttributes(metadata, assemblyAttributes, assemlbyHandle); + WriteCustomAttributes(metadata, _customAttributes, moduleHandle); // Add each type definition to metadata table. foreach (TypeBuilderImpl typeBuilder in _typeDefStore) @@ -120,22 +125,47 @@ internal void AppendMetadata(MetadataBuilder metadata) } TypeDefinitionHandle typeDefinitionHandle = MetadataHelper.AddTypeDefinition(metadata, typeBuilder, parent, _nextMethodDefRowId, _nextFieldDefRowId); + WriteCustomAttributes(metadata, typeBuilder._customAttributes, typeDefinitionHandle); - // Add each method definition to metadata table. foreach (MethodBuilderImpl method in typeBuilder._methodDefStore) { - MetadataHelper.AddMethodDefinition(metadata, method, method.GetMethodSignatureBlob()); + MethodDefinitionHandle methodHandle = MetadataHelper.AddMethodDefinition(metadata, method, method.GetMethodSignatureBlob()); + WriteCustomAttributes(metadata, method._customAttributes, methodHandle); _nextMethodDefRowId++; } foreach (FieldBuilderImpl field in typeBuilder._fieldDefStore) { - MetadataHelper.AddFieldDefinition(metadata, field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this)); + FieldDefinitionHandle fieldHandle = MetadataHelper.AddFieldDefinition(metadata, field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this)); + WriteCustomAttributes(metadata, field._customAttributes, fieldHandle); _nextFieldDefRowId++; } } } + private void WriteCustomAttributes(MetadataBuilder metadata, List customAttributes, EntityHandle parent) + { + foreach (CustomAttributeWrapper customAttribute in customAttributes) + { + metadata.AddCustomAttribute(parent, GetConstructorHandle(metadata, customAttribute.constructorInfo), + metadata.GetOrAddBlob(customAttribute.binaryAttribute)); + } + } + + private MemberReferenceHandle GetConstructorHandle(MetadataBuilder metadata, ConstructorInfo constructorInfo) + { + if (_constructorRefStore.TryGetValue(constructorInfo, out var constructorHandle)) + { + return constructorHandle; + } + + TypeReferenceHandle parentHandle = GetTypeReference(metadata, constructorInfo.DeclaringType!); + + constructorHandle = MetadataHelper.AddConstructorReference(metadata, parentHandle, constructorInfo); + _constructorRefStore.Add(constructorInfo, constructorHandle); + return constructorHandle; + } + private TypeReferenceHandle GetTypeReference(MetadataBuilder metadata, Type type) { if (!_typeRefStore.TryGetValue(type, out var parentHandle)) @@ -156,6 +186,7 @@ private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly, Metadata return MetadataHelper.AddAssemblyReference(assembly, metadata); } + [RequiresAssemblyFiles("Returns for modules with no file path")] public override string Name => ""; public override string ScopeName => _name; @@ -179,8 +210,10 @@ protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, } 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, byte[] binaryAttribute) + { + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } public override int GetSignatureMetadataToken(SignatureHelper signature) => throw new NotImplementedException(); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs index ad950b15152ac3..474d7e08c65001 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs @@ -19,17 +19,37 @@ internal static BlobBuilder FieldSignatureEncoder(Type fieldType, ModuleBuilderI 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) + { + WriteSignatureTypeForReflectionType(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 parameterEncoder); if (returnType != null && returnType != module.GetTypeFromCoreAssembly(CoreTypeId.Void)) { @@ -44,7 +64,7 @@ internal static BlobBuilder MethodSignatureEncoder(ModuleBuilderImpl module, Typ { foreach (Type parameter in parameters) { - WriteSignatureTypeForReflectionType(parEncoder.AddParameter().Type(), parameter, module); + WriteSignatureTypeForReflectionType(parameterEncoder.AddParameter().Type(), parameter, module); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 71548abab22ef7..c4f16cd0ebb175 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -10,8 +10,6 @@ namespace System.Reflection.Emit { internal sealed class TypeBuilderImpl : TypeBuilder { - internal List _methodDefStore = new(); - internal List _fieldDefStore = new(); private readonly ModuleBuilderImpl _module; private readonly string _name; private readonly string? _namespace; @@ -19,6 +17,10 @@ internal sealed class TypeBuilderImpl : TypeBuilder private Type? _typeParent; private TypeAttributes _attributes; + internal List _methodDefStore = new(); + internal List _fieldDefStore = new(); + internal List _customAttributes = new(); + internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module) { @@ -68,8 +70,27 @@ protected override MethodBuilder DefineMethodCore(string name, MethodAttributes 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, byte[] binaryAttribute) + { + if (con.DeclaringType == null) + { + throw new ArgumentException("Attribute constructor has no type."); + } + + // We check whether the custom attribute is actually a pseudo-custom attribute. + // (We have only done ComImport for the prototype, eventually all pseudo-custom attributes will be hard-coded.) + // If it is, simply alter the TypeAttributes. + // We want to handle this before the type metadata is generated. + + if (con.DeclaringType.Name.Equals("ComImportAttribute")) + { + _attributes |= TypeAttributes.Import; + } + else + { + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } + } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2074:DynamicallyAccessedMembers", Justification = "TODO: Need to figure out how to preserve System.Object public constructor")] diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs index c459a9be15a56c..3ced5f70542bf2 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs @@ -25,23 +25,9 @@ public void EmptyAssemblyAndModuleTest() 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()); } } @@ -87,51 +73,12 @@ private static void AssertTypesAndTypeMembers(Type[] types, Type[] typesFromDisk 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) @@ -150,9 +97,8 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest() { 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); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs index 16d16ff84d7ec4..03d7fd2533122a 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblyTools.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; +using Xunit; namespace System.Reflection.Emit.Tests { @@ -11,23 +12,16 @@ internal static class AssemblyTools { 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 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) { @@ -48,33 +42,27 @@ private static void PopulateMembersForModule(Type[] types, ModuleBuilder mb) 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? 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? 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) }); + + return (AssemblyBuilder)defineDynamicAssemblyMethod.Invoke(null, + new object[] { assemblyName, CoreMetadataAssemblyResolver.s_coreAssembly, assemblyAttributes }); } internal static Assembly LoadAssemblyFromPath(string filePath) => @@ -82,6 +70,60 @@ internal static Assembly LoadAssemblyFromPath(string filePath) => 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 diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs new file mode 100644 index 00000000000000..21ebe492e85277 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs @@ -0,0 +1,234 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.AccessControl; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + public class CustomAttributeTesting + { + // Add three custom attributes to two types. One is pseudo custom attribute. + private List _attributesWithPseudo = new List + { + new CustomAttributeBuilder(s_comVisiblePair.con, s_comVisiblePair.args), + new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args), + new CustomAttributeBuilder(s_comImportPair.con, s_comImportPair.args) + }; + + private List _attributes = new List + { + 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 Type s_comImportType = typeof(ComImportAttribute); + private static readonly (ConstructorInfo con, object[] args) s_comVisiblePair = (s_comVisibleType.GetConstructor(new Type[] { typeof(bool) }), new object[] { true }); + private static readonly (ConstructorInfo con, object[] args) s_guidPair = (s_guideType.GetConstructor(new Type[] { typeof(string) }), new object[] { "9ED54F84-A89D-4fcd-A854-44251E925F09" }); + private static readonly (ConstructorInfo con, object[] args) s_comImportPair = (s_comImportType.GetConstructor(Type.EmptyTypes), new object[] { }); + + 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); + + IList assemblyAttributesFromDisk = assemblyFromDisk.GetCustomAttributesData(); + IList moduleAttributesFromDisk = moduleFromDisk.GetCustomAttributesData(); + + Assert.Equal(_attributes.Count, assemblyAttributesFromDisk.Count); + Assert.Equal(_attributes.Count, moduleAttributesFromDisk.Count); + + foreach (var attribute in assemblyAttributesFromDisk) + { + ValidateAttributes(attribute); + } + + foreach (var attribute in moduleAttributesFromDisk) + { + ValidateAttributes(attribute); + } + } + } + + [Fact] + public void MethodFieldWithCustomAttributes() + { + AssemblyName assemblyName = PopulateAssemblyName(); + Type[] types = new Type[] { typeof(IMultipleMethod), typeof(IOneMethod), typeof(StructWithField) }; + + using (TempFile file = TempFile.Create()) + { + WriteAssemblyToDisk(assemblyName, types, file.Path, typeAttributes: _attributesWithPseudo, + 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(); + + IList typeAttributesFromDisk = typeFromDisk.GetCustomAttributesData(); + + Assert.Equal(_attributesWithPseudo.Count, typeAttributesFromDisk.Count); + Assert.Equal(sourceType.Attributes | TypeAttributes.Import, typeFromDisk.Attributes); // Pseudo-custom attributes are added to core TypeAttributes. + AssemblyTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); + AssemblyTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); + + foreach (var attribute in typeAttributesFromDisk) + { + if (attribute.AttributeType.Name == "ComImportAttribute") + { + Assert.Equal(s_comImportPair.con.MetadataToken, attribute.Constructor.MetadataToken); + Assert.Empty(attribute.ConstructorArguments); + } + else + { + ValidateAttributes(attribute); + } + } + + for (int j = 0; j < methodsFromDisk.Length; j++) + { + IList attributesFromDisk = methodsFromDisk[j].GetCustomAttributesData(); + + Assert.Equal(_attributes.Count, attributesFromDisk.Count); + + foreach (var attribute in attributesFromDisk) + { + ValidateAttributes(attribute); + } + } + + for (int j = 0; j < fieldsFromDisk.Length; j++) + { + IList attributesFromDisk = fieldsFromDisk[j].GetCustomAttributesData(); + + Assert.Equal(_attributes.Count, attributesFromDisk.Count); + + foreach (var attribute in attributesFromDisk) + { + ValidateAttributes(attribute); + } + } + } + } + } + + private void ValidateAttributes(CustomAttributeData customAttribute) + { + if (customAttribute.AttributeType.Name == s_comVisibleType.Name) + { + Assert.Equal(s_comVisiblePair.con.MetadataToken, customAttribute.Constructor.MetadataToken); + + Assert.Equal(s_comVisiblePair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); + Assert.Equal(true, customAttribute.ConstructorArguments[0].Value); + } + else + { + Assert.Equal(s_guidPair.con.MetadataToken, customAttribute.Constructor.MetadataToken); + + Assert.Equal(s_guidPair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); + Assert.Equal(customAttribute.AttributeType.Name, s_guideType.Name); + Assert.Equal(s_guidPair.args[0], customAttribute.ConstructorArguments[0].Value); + } + } + + private static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation, List? assemblyAttributes = null, + List? moduleAttributes = null, List? typeAttributes = null, + List? methodAttributes = null, List? 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? moduleAttributes, + List? typeAttributes, List? methodAttributes, List? fieldAttributes) + { + if (moduleAttributes != null) + { + foreach (var attribute in moduleAttributes) + { + mb.SetCustomAttribute(attribute); + } + } + + foreach (Type type in types) + { + TypeBuilder tb = mb.DefineType(type.FullName, type.Attributes, type.BaseType); + + if (typeAttributes != null) + { + foreach (CustomAttributeBuilder typeAttribute in typeAttributes) + { + tb.SetCustomAttribute(typeAttribute); + } + } + + MethodInfo[] methods = type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly); + foreach (var method in methods) + { + MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, null); + + if (methodAttributes != null) + { + foreach (CustomAttributeBuilder typeAttribute in methodAttributes) + { + meb.SetCustomAttribute(typeAttribute); + } + } + } + + foreach (FieldInfo field in type.GetFields()) + { + FieldBuilder fb = tb.DefineField(field.Name, field.FieldType, field.Attributes); + + if (fieldAttributes != null) + { + foreach (var attribute in fieldAttributes) + { + fb.SetCustomAttribute(attribute); + } + } + } + } + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index f10f0a08c4e4bc..1623bee3c273e9 100644 --- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -64,6 +64,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs index 494a2bc3b86f6f..38e4ecb6f0affd 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs @@ -502,6 +502,15 @@ internal static CustomAttributeInfo decode_cattr(CustomAttributeBuilder customBu { 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(ConstructorInfo ctor, byte[] data) + { int pos; CustomAttributeInfo info = default; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs index a5bfdaabee1f33..b30ea819a47093 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs @@ -297,8 +297,9 @@ internal static AssemblyBuilder InternalDefineDynamicAssembly( public override bool IsCollectible => access == (uint)AssemblyBuilderAccess.RunAndCollect; - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) { CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1]; @@ -315,11 +316,6 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil 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); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs index f9939d27b3183b..a6a3dfe737dd20 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs @@ -254,20 +254,19 @@ protected override ILGenerator GetILGeneratorCore(int streamSize) return ilgen; } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, 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; + byte[] data = binaryAttribute; int impla; // the (stupid) ctor takes a short or an int ... impla = (int)data[2]; impla |= ((int)data[3]) << 8; SetImplementationFlags((MethodImplAttributes)impla); return; } + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) { CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1]; @@ -282,14 +281,6 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil } } - 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) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs index ab54737e986dcb..f2b2e0d6e114b9 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs @@ -447,14 +447,9 @@ public override Type MakePointerType() return SymbolType.FormCompoundType("*", this, 0)!; } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) - { - _tb.SetCustomAttribute(customBuilder); - } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { - SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute)); + _tb.SetCustomAttribute(con, binaryAttribute); } internal override bool IsUserType diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs index 8ff4a3df89eac5..9d3261f1e0472f 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs @@ -106,15 +106,16 @@ protected override void SetRemoveOnMethodCore(MethodBuilder mdBuilder) remove_method = mdBuilder; } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, 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 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) - { - SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute)); - } - private void RejectIfCreated() { if (typeb.is_created) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs index fcd8d18f8e96b5..abd4420e42f3ea 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs @@ -178,10 +178,17 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil { RejectIfCreated(); - string? attrname = customBuilder.Ctor.ReflectedType!.FullName; + + } + + protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + { + RejectIfCreated(); + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); + string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.InteropServices.FieldOffsetAttribute") { - byte[] data = customBuilder.Data; + byte[] data = binaryAttribute; offset = (int)data[2]; offset |= ((int)data[3]) << 8; offset |= ((int)data[4]) << 16; @@ -219,12 +226,6 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) - { - RejectIfCreated(); - SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute)); - } - protected override void SetOffsetCore(int iOffset) { RejectIfCreated(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs index a1f176127f97f8..4fa4209ac25ff8 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs @@ -424,8 +424,10 @@ public override MethodBase? DeclaringMethod get { return mbuilder; } } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + // FIXME: "unverified implementation" + protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) { CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1]; @@ -440,12 +442,6 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil } } - // FIXME: "unverified implementation" - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) - { - SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute)); - } - private static NotSupportedException not_supported() { return new NotSupportedException(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs index 80e8352d04b40e..347f61a8874356 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs @@ -381,12 +381,12 @@ internal void ResolveUserTypes() } } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { - switch (customBuilder.Ctor.ReflectedType!.FullName) + switch (con.ReflectedType!.FullName) { case "System.Runtime.CompilerServices.MethodImplAttribute": - byte[] data = customBuilder.Data; + byte[] data = binaryAttribute; int impla; // the (stupid) ctor takes a short or an int ... impla = (int)data[2]; impla |= ((int)data[3]) << 8; @@ -394,7 +394,7 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil 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 +453,7 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil break; } + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) { CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1]; @@ -467,11 +468,6 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) - { - SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute)); - } - protected override void SetImplementationFlagsCore(MethodImplAttributes attributes) { RejectIfCreated(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs index a6ab47cae28d3e..1646950b3c45b5 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs @@ -426,8 +426,9 @@ internal int get_next_table_index(int table, int count) return index; } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) { CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1]; @@ -441,11 +442,6 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil 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) { diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs index 80c8d942ccd68f..275438573237d3 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs @@ -159,16 +159,18 @@ protected override void SetConstantCore(object? defaultValue) def_value = defaultValue; } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, 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 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) - { - SetCustomAttributeCore(new CustomAttributeBuilder(con, binaryAttribute)); - } - protected override void SetGetMethodCore(MethodBuilder mdBuilder) { typeb.check_not_created(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs index 18bc362c0e6e7e..6050d39e61a656 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs @@ -1411,12 +1411,12 @@ public override RuntimeTypeHandle TypeHandle } } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) + protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { - string? attrname = customBuilder.Ctor.ReflectedType!.FullName; + string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.InteropServices.StructLayoutAttribute") { - byte[] data = customBuilder.Data; + byte[] data = binaryAttribute; int layout_kind; /* the (stupid) ctor takes a short or an int ... */ layout_kind = (int)data[2]; layout_kind |= ((int)data[3]) << 8; @@ -1429,7 +1429,7 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil _ => 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; @@ -1518,6 +1518,8 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil is_byreflike_set = 1; } + CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); + if (cattrs != null) { CustomAttributeBuilder[] new_array = new CustomAttributeBuilder[cattrs.Length + 1]; @@ -1532,11 +1534,6 @@ protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuil } } - 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); From fa87dd862e5f5b44447b7b5f3cbb7bc7c2567879 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 10 Apr 2023 10:26:04 -0700 Subject: [PATCH 02/11] Pseudo custom attributes handling and tests --- .../Reflection/Emit/RuntimeMethodBuilder.cs | 2 +- .../src/Resources/Strings.resx | 24 ++ .../Reflection/Emit/CustomAttributeWrapper.cs | 156 +++++++ .../Reflection/Emit/FieldBuilderImpl.cs | 34 +- .../System/Reflection/Emit/MetadataHelper.cs | 6 +- .../Reflection/Emit/MethodBuilderImpl.cs | 81 +++- .../Reflection/Emit/ModuleBuilderImpl.cs | 8 +- .../System/Reflection/Emit/TypeBuilderImpl.cs | 93 ++++- .../AssemblySaveCustomAttributeTests.cs | 391 ++++++++++++++++++ ...=> AssemblySaveWithVariousMembersTests.cs} | 2 +- .../CustomAttributeTesting.cs | 234 ----------- .../tests/System.Reflection.Emit.Tests.csproj | 4 +- .../Emit/CustomAttributeBuilder.Mono.cs | 4 - .../Emit/RuntimeFieldBuilder.Mono.cs | 7 - .../RuntimeGenericTypeParameterBuilder.cs | 1 - 15 files changed, 769 insertions(+), 278 deletions(-) create mode 100644 src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs rename src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/{AssemblySaveTestsWithVariousTypes.cs => AssemblySaveWithVariousMembersTests.cs} (99%) delete mode 100644 src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs index f8b6503c96e7db..db9aff767c940e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs @@ -678,7 +678,7 @@ private void ThrowIfShouldNotHaveBody() m_isDllImport) { // cannot attach method body if methodimpl is marked not marked as managed IL - // supress + // throw new InvalidOperationException(SR.InvalidOperation_ShouldNotHaveMethodBody); } } diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index b3ad859f052538..6f406180cfb9a2 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -144,4 +144,28 @@ Invalid name. + + The type '{0}' may not be used as a type argument. + + + This type cannot be represented as a custom attribute. + + + Custom attribute type '{0}' doesn't contain a field named '{1}'. + + + Custom attribute length is only '{0}'. + + + Prolog invalid. + + + Unknown named type '{0}'. + + + Type '{0}' not yet handled in DecodeCustomAttributeValue. + + + Subtype '{0}' of type object not yet handled. + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs index abef65561c53aa..b3ccb2060017f1 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -1,6 +1,8 @@ // 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; + namespace System.Reflection.Emit { internal sealed class CustomAttributeWrapper @@ -14,4 +16,158 @@ public CustomAttributeWrapper(ConstructorInfo constructorInfo, byte[] binaryAttr this.binaryAttribute = binaryAttribute; } } + + internal struct CustomAttributeInfo + { + public ConstructorInfo _ctor; + public object?[] _ctorArgs; + public string[] _namedParamNames; + public object?[] _namedParamValues; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String)'", + Justification = "The 'enumTypeName' only available at runtime")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicFields', 'DynamicallyAccessedMemberTypes.NonPublicFields' in call to 'System.Type.GetField(String, BindingFlags)'", + Justification = "Could not propagate attribute into 'ctor.DeclaringType' only available at runtime")] + internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, byte[] data) + { + int pos; + CustomAttributeInfo info = default; + + // Prolog + if (data.Length < 2) + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidCustomAttributeLength, data.Length)); + if ((data[0] != 0x1) || (data[1] != 0x00)) + throw new InvalidOperationException(SR.InvalidOperation_InvalidProlog); + pos = 2; + + 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, data, pos, out pos); + + int numNamed = data[pos] + (data[pos + 1] * 256); + pos += 2; + + info._namedParamNames = new string[numNamed]; + info._namedParamValues = new object[numNamed]; + for (int i = 0; i < numNamed; ++i) + { + int namedType = data[pos++]; + int dataType = data[pos++]; + string? enumTypeName = null; + + if (dataType == 0x55) + { + int len2 = DecodeLen(data, pos, out pos); + enumTypeName = StringFromBytes(data, pos, len2); + pos += len2; + } + + int len = DecodeLen(data, pos, out pos); + string name = StringFromBytes(data, pos, len); + info._namedParamNames[i] = name; + pos += len; + + if (namedType == 0x53) + { + /* Field */ + FieldInfo? fi = ctor.DeclaringType!.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (fi == null) + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_EmptyFieldForCustomAttributeType, ctor.DeclaringType, name)); + + object? val = DecodeCustomAttributeValue(fi.FieldType, data, pos, out pos); + if (enumTypeName != null) + { + Type enumType = Type.GetType(enumTypeName)!; + val = Enum.ToObject(enumType, val!); + } + + info._namedParamValues[i] = val; + } + else + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, namedType)); + } + + return info; + } + + private static string StringFromBytes(byte[] data, int pos, int len) + { + return Text.Encoding.UTF8.GetString(data, pos, len); + } + + private static int DecodeLen(byte[] data, int pos, out int rpos) + { + int len; + if ((data[pos] & 0x80) == 0) + { + len = (int)(data[pos++] & 0x7f); + } + else if ((data[pos] & 0x40) == 0) + { + len = ((data[pos] & 0x3f) << 8) + data[pos + 1]; + pos += 2; + } + else + { + len = ((data[pos] & 0x1f) << 24) + (data[pos + 1] << 16) + (data[pos + 2] << 8) + data[pos + 3]; + pos += 4; + } + rpos = pos; + return len; + } + + private static object? DecodeCustomAttributeValue(Type t, byte[] data, int pos, out int rpos) + { + switch (Type.GetTypeCode(t)) + { + case TypeCode.String: + if (data[pos] == 0xff) + { + 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 data[pos] + (data[pos + 1] << 8) + (data[pos + 2] << 16) + (data[pos + 3] << 24); + 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(subtype), data, pos, out rpos); + else + throw new NotImplementedException(SR.NotImplemented_UnhandledSubType); + default: + throw new NotImplementedException(SR.Format(SR.NotImplemented_TypeForValueNotHandled, t)); + } + } + + private static Type ElementTypeToType(int elementType) => + /* Partition II, section 23.1.16 */ + elementType switch + { + 0x02 => typeof(bool), + 0x03 => typeof(char), + 0x04 => typeof(sbyte), + 0x05 => typeof(byte), + 0x06 => typeof(short), + 0x07 => typeof(ushort), + 0x08 => typeof(int), + 0x09 => typeof(uint), + 0x0a => typeof(long), + 0x0b => typeof(ulong), + 0x0c => typeof(float), + 0x0d => typeof(double), + 0x0e => typeof(string), + _ => throw new ArgumentException(SR.Format(SR.ArgumentException_InvalidTypeArgument, elementType)), + }; + } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index 13db36cfe8b991..7fb6d79c8aed3e 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -10,7 +10,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder { private readonly TypeBuilderImpl _typeBuilder; private readonly string _fieldName; - private readonly FieldAttributes _attributes; + private FieldAttributes _attributes; private readonly Type _fieldType; internal List _customAttributes = new(); @@ -26,7 +26,37 @@ internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type ty protected override void SetConstantCore(object? defaultValue) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { - _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + if (!IsPseudoAttribute(con.ReflectedType!.FullName!)) + { + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } + } + + private bool IsPseudoAttribute(string attributeName) + { + switch (attributeName) + { + case "System.Runtime.InteropServices.FieldOffsetAttribute": + /* TODO: Not sure how to apply this + * byte[] data = customBuilder.Data; + offset = (int)data[2]; + offset |= ((int)data[3]) << 8; + offset |= ((int)data[4]) << 16; + offset |= ((int)data[5]) << 24;*/ + break; + case "System.NonSerializedAttribute": + _attributes |= FieldAttributes.NotSerialized; + break; + case "System.Runtime.CompilerServices.SpecialNameAttribute": + _attributes |= FieldAttributes.SpecialName; + break; + case "System.Runtime.InteropServices.MarshalAsAttribute": + _attributes |= FieldAttributes.HasFieldMarshal; + return false; + default: return false; + } + + return true; } protected override void SetOffsetCore(int iOffset) => throw new NotImplementedException(); diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs index ce1d7c77fd8af4..41b61d2ad725e1 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs @@ -59,7 +59,7 @@ internal static MethodDefinitionHandle AddMethodDefinition(MetadataBuilder metad { return metadata.AddMethodDefinition( attributes: methodBuilder.Attributes, - implAttributes: MethodImplAttributes.IL, + implAttributes: methodBuilder.GetMethodImplementationFlags(), name: metadata.GetOrAddString(methodBuilder.Name), signature: metadata.GetOrAddBlob(methodSignatureBlob), bodyOffset: -1, // No body supported yet @@ -75,9 +75,9 @@ internal static FieldDefinitionHandle AddFieldDefinition(MetadataBuilder metadat signature: metadata.GetOrAddBlob(fieldSignatureBlob)); } - internal static MemberReferenceHandle AddConstructorReference(MetadataBuilder metadata, TypeReferenceHandle parent, MethodBase method) + internal static MemberReferenceHandle AddConstructorReference(ModuleBuilderImpl moduleBuilder, MetadataBuilder metadata, TypeReferenceHandle parent, MethodBase method) { - var blob = MetadataSignatureHelper.ConstructorSignatureEncoder(method.GetParameters(), (ModuleBuilderImpl)method.Module); + var blob = MetadataSignatureHelper.ConstructorSignatureEncoder(method.GetParameters(), moduleBuilder); return metadata.AddMemberReference( parent: parent, name: metadata.GetOrAddString(method.Name), diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 6f56a7d2351385..1eeb6188dc12be 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -13,7 +13,8 @@ internal sealed class MethodBuilderImpl : MethodBuilder private readonly Type _returnType; private readonly Type[]? _parameterTypes; private readonly ModuleBuilderImpl _module; - private readonly MethodAttributes _attributes; + private MethodAttributes _attributes; + private MethodImplAttributes _methodImplFlags; private readonly string _name; private readonly CallingConventions _callingConventions; private readonly TypeBuilderImpl _declaringType; @@ -38,6 +39,8 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv ArgumentNullException.ThrowIfNull(_parameterTypes[i] = parameterTypes[i], nameof(parameterTypes)); } } + + _methodImplFlags = MethodImplAttributes.IL; } internal BlobBuilder GetMethodSignatureBlob() => @@ -49,9 +52,79 @@ internal BlobBuilder GetMethodSignatureBlob() => protected override ILGenerator GetILGeneratorCore(int size) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { - _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) + { + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } + } + + private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, byte[] data) + { + switch (attributeName) + { + case "System.Runtime.CompilerServices.MethodImplAttribute": + int impla = data[2]; + impla |= data[3] << 8; + _methodImplFlags |= (MethodImplAttributes)impla; + break; + + case "System.Runtime.InteropServices.DllImportAttribute": + CustomAttributeInfo attr = CustomAttributeInfo.DecodeCustomAttribute(con, data); + bool preserveSig = true; + + /* TODO + * pi_dll = (string?)attr._ctorArgs[0]; + if (pi_dll == null || pi_dll.Length == 0) + throw new ArgumentException(SR.Arg_DllNameCannotBeEmpty); + + native_cc = Runtime.InteropServices.CallingConvention.Winapi;*/ + + for (int i = 0; i < attr._namedParamNames.Length; ++i) + { + string name = attr._namedParamNames[i]; + object? value = attr._namedParamValues[i]; + + if (name == "PreserveSig") + preserveSig = (bool)value!; + /*else if (name == "CallingConvention") // TODO: this values might need to be covered + native_cc = (CallingConvention)value!; + else if (name == "CharSet") + charset = (CharSet)value!; + else if (name == "EntryPoint") + pi_entry = (string)value!; + else if (name == "ExactSpelling") + ExactSpelling = (bool)value!; + else if (name == "SetLastError") + SetLastError = (bool)value!; + else if (name == "BestFitMapping") + BestFitMapping = (bool)value!; + else if (name == "ThrowOnUnmappableChar") + ThrowOnUnmappableChar = (bool)value!;*/ + } + + _attributes |= MethodAttributes.PinvokeImpl; + if (preserveSig) + _methodImplFlags |= MethodImplAttributes.PreserveSig; + break; + case "System.Runtime.InteropServices.PreserveSigAttribute": + _methodImplFlags |= MethodImplAttributes.PreserveSig; + break; + case "System.Runtime.CompilerServices.SpecialNameAttribute": + _attributes |= MethodAttributes.SpecialName; + break; + case "System.Security.SuppressUnmanagedCodeSecurityAttribute": + _attributes |= MethodAttributes.HasSecurity; + return false; + default: return false; + } + + return true; + } + + protected override void SetImplementationFlagsCore(MethodImplAttributes attributes) + { + _methodImplFlags = attributes; } - protected override void SetImplementationFlagsCore(MethodImplAttributes attributes) => throw new NotImplementedException(); protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) => throw new NotImplementedException(); public override string Name => _name; @@ -88,7 +161,7 @@ public override int GetHashCode() => throw new NotImplementedException(); public override MethodImplAttributes GetMethodImplementationFlags() - => throw new NotImplementedException(); + => _methodImplFlags; public override ParameterInfo[] GetParameters() => throw new NotImplementedException(); diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 15ba6cb7778dd4..5cb37356fb51b4 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -92,7 +92,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle assemlbyHandle, List assemblyAttributes) + internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle assemblyHandle, List assemblyAttributes) { // Add module metadata ModuleDefinitionHandle moduleHandle = metadata.AddModule( @@ -111,7 +111,7 @@ internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle fieldList: MetadataTokens.FieldDefinitionHandle(1), methodList: MetadataTokens.MethodDefinitionHandle(1)); - WriteCustomAttributes(metadata, assemblyAttributes, assemlbyHandle); + WriteCustomAttributes(metadata, assemblyAttributes, assemblyHandle); WriteCustomAttributes(metadata, _customAttributes, moduleHandle); // Add each type definition to metadata table. @@ -161,7 +161,7 @@ private MemberReferenceHandle GetConstructorHandle(MetadataBuilder metadata, Con TypeReferenceHandle parentHandle = GetTypeReference(metadata, constructorInfo.DeclaringType!); - constructorHandle = MetadataHelper.AddConstructorReference(metadata, parentHandle, constructorInfo); + constructorHandle = MetadataHelper.AddConstructorReference(this, metadata, parentHandle, constructorInfo); _constructorRefStore.Add(constructorInfo, constructorHandle); return constructorHandle; } @@ -204,7 +204,7 @@ private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly, Metadata protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize) { - TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this); + TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this, packingSize, typesize); _typeDefStore.Add(_type); return _type; } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index c4f16cd0ebb175..b92622f70194fe 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; using System.Globalization; +using System.Runtime.InteropServices; namespace System.Reflection.Emit { @@ -16,17 +16,21 @@ internal sealed class TypeBuilderImpl : TypeBuilder [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private Type? _typeParent; private TypeAttributes _attributes; + private PackingSize _packingSize; + private int _typeSize; internal List _methodDefStore = new(); internal List _fieldDefStore = new(); internal List _customAttributes = new(); internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module, PackingSize packingSize, int typeSize) { _name = fullName; _module = module; _attributes = typeAttributes; + _packingSize = packingSize; + _typeSize = typeSize; SetParent(parent); // Extract namespace from fullName @@ -39,8 +43,8 @@ internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, } 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(); @@ -72,23 +76,82 @@ protected override MethodBuilder DefineMethodCore(string name, MethodAttributes protected override bool IsCreatedCore() => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { - if (con.DeclaringType == null) + if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) { - throw new ArgumentException("Attribute constructor has no type."); + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } + } - // We check whether the custom attribute is actually a pseudo-custom attribute. - // (We have only done ComImport for the prototype, eventually all pseudo-custom attributes will be hard-coded.) - // If it is, simply alter the TypeAttributes. - // We want to handle this before the type metadata is generated. - - if (con.DeclaringType.Name.Equals("ComImportAttribute")) + private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, byte[] data) + { + switch (attributeName) { - _attributes |= TypeAttributes.Import; + case "System.Runtime.InteropServices.StructLayoutAttribute": + ParseStructLayoutAttribute(con, data); + break; + case "System.Runtime.CompilerServices.SpecialNameAttribute": + _attributes |= TypeAttributes.SpecialName; + break; + case "System.SerializableAttribute": + _attributes |= TypeAttributes.Serializable; + break; + case "System.Runtime.InteropServices.ComImportAttribute": + _attributes |= TypeAttributes.Import; + break; + case "System.Security.SuppressUnmanagedCodeSecurityAttribute": + _attributes |= TypeAttributes.HasSecurity; + return false; + default: return false; } - else + return true; + } + + private void ParseStructLayoutAttribute(ConstructorInfo con, byte[] data) + { + CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, data); + LayoutKind layoutKind = (LayoutKind)attributeInfo._ctorArgs[0]!; + _attributes &= ~TypeAttributes.LayoutMask; + _attributes |= layoutKind switch { - _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + LayoutKind.Auto => TypeAttributes.AutoLayout, + LayoutKind.Explicit => TypeAttributes.ExplicitLayout, + LayoutKind.Sequential => TypeAttributes.SequentialLayout, + _ => throw new ArgumentException(SR.Argument_InvalidKindOfTypeForCA), + }; + + 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 InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, name)); + } } } 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 index 00000000000000..4aa692826e3bf2 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -0,0 +1,391 @@ +// 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 _attributes = new List + { + 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); + + IList assemblyAttributesFromDisk = assemblyFromDisk.GetCustomAttributesData(); + IList moduleAttributesFromDisk = moduleFromDisk.GetCustomAttributesData(); + + Assert.Equal(_attributes.Count, assemblyAttributesFromDisk.Count); + Assert.Equal(_attributes.Count, moduleAttributesFromDisk.Count); + + foreach (var attribute in assemblyAttributesFromDisk) + { + ValidateAttributes(attribute); + } + + foreach (var attribute in moduleAttributesFromDisk) + { + ValidateAttributes(attribute); + } + } + } + + [Fact] + public void MethodFieldWithCustomAttributes() + { + Type[] types = new Type[] { typeof(IMultipleMethod), typeof(IOneMethod), typeof(StructWithField) }; + + 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(); + + IList typeAttributesFromDisk = typeFromDisk.GetCustomAttributesData(); + + Assert.Equal(_attributes.Count, typeAttributesFromDisk.Count); + AssemblyTools.AssertTypeProperties(sourceType, typeFromDisk); + AssemblyTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); + AssemblyTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); + + foreach (var attribute in typeAttributesFromDisk) + { + ValidateAttributes(attribute); + } + + for (int j = 0; j < methodsFromDisk.Length; j++) + { + IList attributesFromDisk = methodsFromDisk[j].GetCustomAttributesData(); + + Assert.Equal(_attributes.Count, attributesFromDisk.Count); + + foreach (var attribute in attributesFromDisk) + { + ValidateAttributes(attribute); + } + } + + for (int j = 0; j < fieldsFromDisk.Length; j++) + { + IList attributesFromDisk = fieldsFromDisk[j].GetCustomAttributesData(); + + Assert.Equal(_attributes.Count, attributesFromDisk.Count); + + foreach (var attribute in attributesFromDisk) + { + ValidateAttributes(attribute); + } + } + } + } + } + + private void ValidateAttributes(CustomAttributeData customAttribute) + { + if (customAttribute.AttributeType.Name == s_comVisibleType.Name) + { + Assert.Equal(s_comVisiblePair.con.MetadataToken, customAttribute.Constructor.MetadataToken); + + Assert.Equal(s_comVisiblePair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); + Assert.Equal(true, customAttribute.ConstructorArguments[0].Value); + } + else + { + Assert.Equal(s_guidPair.con.MetadataToken, customAttribute.Constructor.MetadataToken); + + Assert.Equal(s_guidPair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); + Assert.Equal(customAttribute.AttributeType.Name, s_guideType.Name); + Assert.Equal(s_guidPair.args[0], customAttribute.ConstructorArguments[0].Value); + } + } + + private static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation, List? assemblyAttributes = null, + List? moduleAttributes = null, List? typeAttributes = null, + List? methodAttributes = null, List? 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? moduleAttributes, + List? typeAttributes, List? methodAttributes, List? fieldAttributes) + { + if (moduleAttributes != null) + { + foreach (var attribute in moduleAttributes) + { + mb.SetCustomAttribute(attribute); + } + } + + foreach (Type type in types) + { + TypeBuilder tb = mb.DefineType(type.FullName, type.Attributes, type.BaseType); + + if (typeAttributes != null) + { + foreach (CustomAttributeBuilder typeAttribute in typeAttributes) + { + tb.SetCustomAttribute(typeAttribute); + } + } + + DefineMethodsAndSetAttributes(methodAttributes, tb, type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly)); + DefineFieldsAndSetAttributes(fieldAttributes, type.GetFields(), tb); + } + } + + private static void DefineFieldsAndSetAttributes(List? fieldAttributes, FieldInfo[] fields, TypeBuilder tb) + { + foreach (FieldInfo field in fields) + { + FieldBuilder fb = tb.DefineField(field.Name, field.FieldType, field.Attributes); + + if (fieldAttributes != null) + { + foreach (var attribute in fieldAttributes) + { + fb.SetCustomAttribute(attribute); + } + } + } + } + + private static void DefineMethodsAndSetAttributes(List methodAttributes, TypeBuilder tb, MethodInfo[] methods) + { + foreach (var method in methods) + { + MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, null); + + if (methodAttributes != null) + { + foreach (CustomAttributeBuilder attribute in methodAttributes) + { + meb.SetCustomAttribute(attribute); + } + } + } + } + + [Fact] + public void CreateStructWithPseudoCustomAttributesTest() + { + using (TempFile file = TempFile.Create()) + { + Type type = typeof(StructWithField); + CustomAttributeBuilder[] attributes = 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[] { 0 }), + 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); + DefineFieldsAndSetAttributes(fieldAttributes.ToList(), type.GetFields(), tb); + foreach (CustomAttributeBuilder attribute in attributes) + { + tb.SetCustomAttribute(attribute); + } + + saveMethod.Invoke(ab, new object[] { file.Path }); + + Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Module moduleFromDisk = assemblyFromDisk.Modules.First(); + Type testType = moduleFromDisk.GetTypes()[0]; + + IList attributesFromDisk = testType.GetCustomAttributesData(); + + Assert.Equal(attributes.Length - 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 fieldAttributesFromDisk = field.GetCustomAttributesData(); + + 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": + // TODO: Assert.Equal(0, methodAttributesFromDisk[i].ConstructorArguments[0].Value); + break; + /*case "MarshalAsAttribute": // TODO: Need to support the UnmanagedType type + Assert.Equal(UnmanagedType.I4, methodAttributesFromDisk[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 type = typeof(IMultipleMethod); + CustomAttributeBuilder[] attributes = 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 }), + }; + + AssemblyBuilder ab = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( + PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes); + DefineMethodsAndSetAttributes(methodAttributes.ToList(), tb, type.GetMethods()); + foreach (CustomAttributeBuilder attribute in attributes) + { + tb.SetCustomAttribute(attribute); + } + + saveMethod.Invoke(ab, new object[] { file.Path }); + + Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; + IList attributesFromDisk = testType.GetCustomAttributesData(); + + Assert.Equal(attributes.Length, attributesFromDisk.Count); + + for (int i = 0; i < attributesFromDisk.Count; i++) + { + switch (attributesFromDisk[i].AttributeType.Name) + { + case "ComImportAttribute": + Assert.True((testType.Attributes & TypeAttributes.Import) != 0); + break; + case "SuppressUnmanagedCodeSecurityAttribute": + Assert.True((testType.Attributes & TypeAttributes.HasSecurity) != 0); + 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 methodAttributesFromDisk = method.GetCustomAttributesData(); + + Assert.True((method.Attributes & MethodAttributes.HasSecurity) != 0); // SuppressUnmanagedCodeSecurityAttribute + Assert.True((method.Attributes & MethodAttributes.SpecialName) != 0); // SpecialNameAttribute + Assert.True((method.GetMethodImplementationFlags() & MethodImplAttributes.NoInlining) != 0); // MethodImplAttribute + Assert.True((method.GetMethodImplementationFlags() & MethodImplAttributes.AggressiveOptimization) != 0); // MethodImplAttribute + Assert.True((method.GetMethodImplementationFlags() & 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": + break; + case "PreserveSigAttribute": + break; + case "GuidAttribute": + Assert.Equal(s_guidPair.args[0], methodAttributesFromDisk[i].ConstructorArguments[0].Value); + break; + default: + Assert.Fail($"Not expected attribute : {methodAttributesFromDisk[i].AttributeType.Name}"); + break; + } + } + } + } + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs similarity index 99% rename from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs rename to src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs index 3ced5f70542bf2..a4e2bf9a130ad3 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTestsWithVariousTypes.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs @@ -9,7 +9,7 @@ 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") { diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs deleted file mode 100644 index 21ebe492e85277..00000000000000 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/CustomAttributeTesting.cs +++ /dev/null @@ -1,234 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.AccessControl; -using Xunit; - -namespace System.Reflection.Emit.Tests -{ - public class CustomAttributeTesting - { - // Add three custom attributes to two types. One is pseudo custom attribute. - private List _attributesWithPseudo = new List - { - new CustomAttributeBuilder(s_comVisiblePair.con, s_comVisiblePair.args), - new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args), - new CustomAttributeBuilder(s_comImportPair.con, s_comImportPair.args) - }; - - private List _attributes = new List - { - 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 Type s_comImportType = typeof(ComImportAttribute); - private static readonly (ConstructorInfo con, object[] args) s_comVisiblePair = (s_comVisibleType.GetConstructor(new Type[] { typeof(bool) }), new object[] { true }); - private static readonly (ConstructorInfo con, object[] args) s_guidPair = (s_guideType.GetConstructor(new Type[] { typeof(string) }), new object[] { "9ED54F84-A89D-4fcd-A854-44251E925F09" }); - private static readonly (ConstructorInfo con, object[] args) s_comImportPair = (s_comImportType.GetConstructor(Type.EmptyTypes), new object[] { }); - - 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); - - IList assemblyAttributesFromDisk = assemblyFromDisk.GetCustomAttributesData(); - IList moduleAttributesFromDisk = moduleFromDisk.GetCustomAttributesData(); - - Assert.Equal(_attributes.Count, assemblyAttributesFromDisk.Count); - Assert.Equal(_attributes.Count, moduleAttributesFromDisk.Count); - - foreach (var attribute in assemblyAttributesFromDisk) - { - ValidateAttributes(attribute); - } - - foreach (var attribute in moduleAttributesFromDisk) - { - ValidateAttributes(attribute); - } - } - } - - [Fact] - public void MethodFieldWithCustomAttributes() - { - AssemblyName assemblyName = PopulateAssemblyName(); - Type[] types = new Type[] { typeof(IMultipleMethod), typeof(IOneMethod), typeof(StructWithField) }; - - using (TempFile file = TempFile.Create()) - { - WriteAssemblyToDisk(assemblyName, types, file.Path, typeAttributes: _attributesWithPseudo, - 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(); - - IList typeAttributesFromDisk = typeFromDisk.GetCustomAttributesData(); - - Assert.Equal(_attributesWithPseudo.Count, typeAttributesFromDisk.Count); - Assert.Equal(sourceType.Attributes | TypeAttributes.Import, typeFromDisk.Attributes); // Pseudo-custom attributes are added to core TypeAttributes. - AssemblyTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); - AssemblyTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); - - foreach (var attribute in typeAttributesFromDisk) - { - if (attribute.AttributeType.Name == "ComImportAttribute") - { - Assert.Equal(s_comImportPair.con.MetadataToken, attribute.Constructor.MetadataToken); - Assert.Empty(attribute.ConstructorArguments); - } - else - { - ValidateAttributes(attribute); - } - } - - for (int j = 0; j < methodsFromDisk.Length; j++) - { - IList attributesFromDisk = methodsFromDisk[j].GetCustomAttributesData(); - - Assert.Equal(_attributes.Count, attributesFromDisk.Count); - - foreach (var attribute in attributesFromDisk) - { - ValidateAttributes(attribute); - } - } - - for (int j = 0; j < fieldsFromDisk.Length; j++) - { - IList attributesFromDisk = fieldsFromDisk[j].GetCustomAttributesData(); - - Assert.Equal(_attributes.Count, attributesFromDisk.Count); - - foreach (var attribute in attributesFromDisk) - { - ValidateAttributes(attribute); - } - } - } - } - } - - private void ValidateAttributes(CustomAttributeData customAttribute) - { - if (customAttribute.AttributeType.Name == s_comVisibleType.Name) - { - Assert.Equal(s_comVisiblePair.con.MetadataToken, customAttribute.Constructor.MetadataToken); - - Assert.Equal(s_comVisiblePair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); - Assert.Equal(true, customAttribute.ConstructorArguments[0].Value); - } - else - { - Assert.Equal(s_guidPair.con.MetadataToken, customAttribute.Constructor.MetadataToken); - - Assert.Equal(s_guidPair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); - Assert.Equal(customAttribute.AttributeType.Name, s_guideType.Name); - Assert.Equal(s_guidPair.args[0], customAttribute.ConstructorArguments[0].Value); - } - } - - private static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation, List? assemblyAttributes = null, - List? moduleAttributes = null, List? typeAttributes = null, - List? methodAttributes = null, List? 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? moduleAttributes, - List? typeAttributes, List? methodAttributes, List? fieldAttributes) - { - if (moduleAttributes != null) - { - foreach (var attribute in moduleAttributes) - { - mb.SetCustomAttribute(attribute); - } - } - - foreach (Type type in types) - { - TypeBuilder tb = mb.DefineType(type.FullName, type.Attributes, type.BaseType); - - if (typeAttributes != null) - { - foreach (CustomAttributeBuilder typeAttribute in typeAttributes) - { - tb.SetCustomAttribute(typeAttribute); - } - } - - MethodInfo[] methods = type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly); - foreach (var method in methods) - { - MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, null); - - if (methodAttributes != null) - { - foreach (CustomAttributeBuilder typeAttribute in methodAttributes) - { - meb.SetCustomAttribute(typeAttribute); - } - } - } - - foreach (FieldInfo field in type.GetFields()) - { - FieldBuilder fb = tb.DefineField(field.Name, field.FieldType, field.Attributes); - - if (fieldAttributes != null) - { - foreach (var attribute in fieldAttributes) - { - fb.SetCustomAttribute(attribute); - } - } - } - } - } - } -} diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index 1623bee3c273e9..f672dc37d9e828 100644 --- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -62,9 +62,9 @@ - + + - diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs index 38e4ecb6f0affd..cecc02c49dda8e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs @@ -494,10 +494,6 @@ internal struct CustomAttributeInfo public object?[] namedParamValues; } - [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) { byte[] data = customBuilder.Data; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs index abd4420e42f3ea..4b58d08ce94e1b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs @@ -174,13 +174,6 @@ protected override void SetConstantCore(object? defaultValue) def_value = defaultValue; } - protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) - { - RejectIfCreated(); - - - } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { RejectIfCreated(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs index 4fa4209ac25ff8..3859a4cb69b60b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs @@ -424,7 +424,6 @@ public override MethodBase? DeclaringMethod get { return mbuilder; } } - // FIXME: "unverified implementation" protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) { CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); From 79dad590bf962a088ea6fa2cd2369882158774e4 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 12 Apr 2023 09:59:41 -0700 Subject: [PATCH 03/11] Use ReadOnlySpan for CustomAttribute binaryData --- .../Reflection/Emit/RuntimeAssemblyBuilder.cs | 2 +- .../Emit/RuntimeConstructorBuilder.cs | 2 +- .../Reflection/Emit/RuntimeEnumBuilder.cs | 2 +- .../Reflection/Emit/RuntimeEventBuilder.cs | 2 +- .../Reflection/Emit/RuntimeFieldBuilder.cs | 2 +- .../RuntimeGenericTypeParameterBuilder.cs | 2 +- .../Reflection/Emit/RuntimeMethodBuilder.cs | 2 +- .../Reflection/Emit/RuntimeModuleBuilder.cs | 2 +- .../Reflection/Emit/RuntimePropertyBuilder.cs | 2 +- .../Reflection/Emit/RuntimeTypeBuilder.cs | 28 +++++++-------- .../System/Reflection/Emit/AssemblyBuilder.cs | 2 +- .../Reflection/Emit/ConstructorBuilder.cs | 2 +- .../src/System/Reflection/Emit/EnumBuilder.cs | 2 +- .../System/Reflection/Emit/EventBuilder.cs | 2 +- .../System/Reflection/Emit/FieldBuilder.cs | 2 +- .../Emit/GenericTypeParameterBuilder.cs | 2 +- .../System/Reflection/Emit/MethodBuilder.cs | 7 +++- .../System/Reflection/Emit/ModuleBuilder.cs | 2 +- .../System/Reflection/Emit/PropertyBuilder.cs | 2 +- .../src/System/Reflection/Emit/TypeBuilder.cs | 2 +- .../ref/System.Reflection.Emit.cs | 20 +++++------ .../Reflection/Emit/AssemblyBuilderImpl.cs | 2 +- .../Reflection/Emit/CustomAttributeWrapper.cs | 14 ++++---- .../Reflection/Emit/FieldBuilderImpl.cs | 4 ++- .../Reflection/Emit/MethodBuilderImpl.cs | 4 +-- .../Reflection/Emit/ModuleBuilderImpl.cs | 4 +-- .../System/Reflection/Emit/TypeBuilderImpl.cs | 8 +++-- ...cTypeParameterBuilderSetCustomAttribute.cs | 11 ------ .../Emit/CustomAttributeBuilder.Mono.cs | 15 ++++---- .../Emit/RuntimeAssemblyBuilder.Mono.cs | 2 +- .../Emit/RuntimeConstructorBuilder.Mono.cs | 7 ++-- .../Emit/RuntimeEnumBuilder.Mono.cs | 2 +- .../Emit/RuntimeEventBuilder.Mono.cs | 2 +- .../Emit/RuntimeFieldBuilder.Mono.cs | 11 +++--- .../RuntimeGenericTypeParameterBuilder.cs | 2 +- .../Emit/RuntimeMethodBuilder.Mono.cs | 7 ++-- .../Emit/RuntimeModuleBuilder.Mono.cs | 2 +- .../Emit/RuntimePropertyBuilder.Mono.cs | 2 +- .../Emit/RuntimeTypeBuilder.Mono.cs | 34 +++++++++++-------- 39 files changed, 109 insertions(+), 115 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs index 5f699ca4114580..dd6c84158a01fe 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.cs @@ -288,7 +288,7 @@ public override Assembly GetSatelliteAssembly(CultureInfo culture, Version? vers /// /// 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 binaryAttribute) { lock (SyncRoot) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs index 4a7bfdfd7f6d28..bb1c16bf4fe3c4 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.cs @@ -157,7 +157,7 @@ internal override Type GetReturnType() return m_methodBuilder.ReturnType; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { m_methodBuilder.SetCustomAttribute(con, binaryAttribute); } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs index 49ccbe7e0d6f4b..2e14270c0d0265 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.cs @@ -274,7 +274,7 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) } // 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 binaryAttribute) { m_typeBuilder.SetCustomAttribute(con, binaryAttribute); } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs index 787a706cabd114..cf3da5e026c41a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.cs @@ -80,7 +80,7 @@ protected override void AddOtherMethodCore(MethodBuilder mdBuilder) // 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 binaryAttribute) { m_type.ThrowIfCreated(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs index 457654fcc3735d..e607b1523d7653 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.cs @@ -150,7 +150,7 @@ protected override void SetConstantCore(object? defaultValue) 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 binaryAttribute) { RuntimeModuleBuilder moduleBuilder = (RuntimeModuleBuilder)m_typeBuilder.Module; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs index c58b304576e6c1..4245e409b28863 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs @@ -215,7 +215,7 @@ public override Type MakeArrayType(int rank) #endregion #region Protected Members Overrides - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { m_type.SetGenParamCustomAttribute(con, binaryAttribute); } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs index db9aff767c940e..b824378c665e6a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.cs @@ -695,7 +695,7 @@ internal Module GetModule() return GetModuleBuilder(); } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { ThrowIfGeneric(); RuntimeTypeBuilder.DefineCustomAttribute(m_module, MetadataToken, diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs index 7b0c5db735f108..496c24bb105ae9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs @@ -1289,7 +1289,7 @@ internal int GetSignatureToken(byte[] sigBytes, int sigLength) #region Other - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { RuntimeTypeBuilder.DefineCustomAttribute( this, diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs index 8d71956e435bc1..eccafa7da3f2f6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.cs @@ -97,7 +97,7 @@ protected override void AddOtherMethodCore(MethodBuilder mdBuilder) } // 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 binaryAttribute) { m_containingType.ThrowIfCreated(); RuntimeTypeBuilder.DefineCustomAttribute( diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs index 01abe24e064385..d550f656c32455 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.cs @@ -111,13 +111,12 @@ private sealed class CustAttr private readonly byte[]? m_binaryAttribute; private readonly CustomAttributeBuilder? m_customBuilder; - public CustAttr(ConstructorInfo con, byte[] binaryAttribute) + public CustAttr(ConstructorInfo con, ReadOnlySpan 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 @@ private static partial void SetMethodIL(QCallModule module, int tk, [MarshalAs(U [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeBuilder_DefineCustomAttribute")] private static partial void DefineCustomAttribute(QCallModule module, int tkAssociate, int tkConstructor, - byte[]? attr, int attrLength); + ReadOnlySpan attr, int attrLength); internal static void DefineCustomAttribute(RuntimeModuleBuilder module, int tkAssociate, int tkConstructor, - byte[]? attr) + ReadOnlySpan 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 @@ internal void SetGenParamAttributes(GenericParameterAttributes genericParameterA m_genParamAttributes = genericParameterAttributes; } - internal void SetGenParamCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + internal void SetGenParamCustomAttribute(ConstructorInfo con, ReadOnlySpan binaryAttribute) { CustAttr ca = new CustAttr(con, binaryAttribute); @@ -1858,7 +1849,12 @@ internal int TypeToken } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + SetCustomAttributeCore(con, binaryAttribute); + } + + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { DefineCustomAttribute(m_module, m_tdType, m_module.GetMethodMetadataToken(con), binaryAttribute); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index 6ff03e95bb613a..2ce19ba7a7546b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -39,7 +39,7 @@ 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs index 55609291d05271..82d95cb8c4f42c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs @@ -38,7 +38,7 @@ 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs index c23ff08a405dd2..8017aabdcdb52c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs @@ -35,7 +35,7 @@ public FieldBuilder DefineLiteral(string literalName, object? literalValue) 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) => SetCustomAttributeCore(customBuilder.Ctor, customBuilder.Data); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs index cfa7c5d219d9f4..75b1a9e3606687 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs @@ -27,7 +27,7 @@ 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs index 14727f3d8cf935..1e879c09da292e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs @@ -22,7 +22,7 @@ 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs index 1b7cd888b3bd2e..dfc38ba67ab088 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs @@ -13,7 +13,7 @@ protected GenericTypeParameterBuilder() 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs index b46178667529a6..ef2c0cea25ab07 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs @@ -56,7 +56,12 @@ public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) SetCustomAttributeCore(con, binaryAttribute); } - protected abstract void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute); + internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + SetCustomAttributeCore(con, binaryAttribute); + } + + protected abstract void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs index 0b6cc3bb3e9a94..21f68439ee3927 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs @@ -134,7 +134,7 @@ 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs index 30dc61b4e29c20..43b948ac9b9449 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs @@ -27,7 +27,7 @@ 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs index d64b2138797713..e3f3b9aa55b5d7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs @@ -277,7 +277,7 @@ 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 binaryAttribute); public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { diff --git a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs index 473ce04e1db82c..8c7e3d984c0ed4 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs @@ -55,7 +55,7 @@ protected AssemblyBuilder() { } 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } } [System.FlagsAttribute] @@ -90,7 +90,7 @@ protected ConstructorBuilder() { } 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { } protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes); @@ -184,7 +184,7 @@ protected EnumBuilder() { } 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } } public abstract partial class EventBuilder @@ -195,7 +195,7 @@ public void AddOtherMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { } 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } public void SetRaiseMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { } protected abstract void SetRaiseMethodCore(System.Reflection.Emit.MethodBuilder mdBuilder); @@ -220,7 +220,7 @@ protected FieldBuilder() { } 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } public void SetOffset(int iOffset) { } protected abstract void SetOffsetCore(int iOffset); @@ -322,7 +322,7 @@ protected GenericTypeParameterBuilder() { } 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } public void SetGenericParameterAttributes(System.Reflection.GenericParameterAttributes genericParameterAttributes) { } protected abstract void SetGenericParameterAttributesCore(System.Reflection.GenericParameterAttributes genericParameterAttributes); @@ -374,7 +374,7 @@ protected MethodBuilder() { } [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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { } protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes); @@ -467,7 +467,7 @@ public void CreateGlobalFunctions() { } [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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } } public abstract partial class PropertyBuilder : System.Reflection.PropertyInfo @@ -495,7 +495,7 @@ public void AddOtherMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { } 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } public void SetGetMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { } protected abstract void SetGetMethodCore(System.Reflection.Emit.MethodBuilder mdBuilder); @@ -662,7 +662,7 @@ public void DefineMethodOverride(System.Reflection.MethodInfo methodInfoBody, Sy 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); + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } 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); diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs index e4041fc13bd1f8..3fa7a9792f85c8 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs @@ -130,7 +130,7 @@ protected override ModuleBuilder DefineDynamicModuleCore(string name) return null; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { _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 index b3ccb2060017f1..e575439f3daae7 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -10,10 +10,10 @@ internal sealed class CustomAttributeWrapper internal ConstructorInfo constructorInfo; internal byte[] binaryAttribute; - public CustomAttributeWrapper(ConstructorInfo constructorInfo, byte[] binaryAttribute) + public CustomAttributeWrapper(ConstructorInfo constructorInfo, ReadOnlySpan binaryAttribute) { this.constructorInfo = constructorInfo; - this.binaryAttribute = binaryAttribute; + this.binaryAttribute = binaryAttribute.ToArray(); } } @@ -28,7 +28,7 @@ internal struct CustomAttributeInfo Justification = "The 'enumTypeName' only available at runtime")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicFields', 'DynamicallyAccessedMemberTypes.NonPublicFields' in call to 'System.Type.GetField(String, BindingFlags)'", Justification = "Could not propagate attribute into 'ctor.DeclaringType' only available at runtime")] - internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, byte[] data) + internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, ReadOnlySpan data) { int pos; CustomAttributeInfo info = default; @@ -92,12 +92,12 @@ internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, return info; } - private static string StringFromBytes(byte[] data, int pos, int len) + private static string StringFromBytes(ReadOnlySpan data, int pos, int len) { - return Text.Encoding.UTF8.GetString(data, pos, len); + return Text.Encoding.UTF8.GetString(data.Slice(pos, len)); } - private static int DecodeLen(byte[] data, int pos, out int rpos) + private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) { int len; if ((data[pos] & 0x80) == 0) @@ -118,7 +118,7 @@ private static int DecodeLen(byte[] data, int pos, out int rpos) return len; } - private static object? DecodeCustomAttributeValue(Type t, byte[] data, int pos, out int rpos) + private static object? DecodeCustomAttributeValue(Type t, ReadOnlySpan data, int pos, out int rpos) { switch (Type.GetTypeCode(t)) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index 7fb6d79c8aed3e..3c428a6a9f7cff 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -24,7 +24,7 @@ internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type ty } protected override void SetConstantCore(object? defaultValue) => throw new NotImplementedException(); - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { if (!IsPseudoAttribute(con.ReflectedType!.FullName!)) { @@ -45,7 +45,9 @@ private bool IsPseudoAttribute(string attributeName) offset |= ((int)data[5]) << 24;*/ break; 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 break; case "System.Runtime.CompilerServices.SpecialNameAttribute": _attributes |= FieldAttributes.SpecialName; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 1eeb6188dc12be..dd6e66537a267f 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -50,7 +50,7 @@ internal BlobBuilder GetMethodSignatureBlob() => 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) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) { @@ -58,7 +58,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar } } - private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, byte[] data) + private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, ReadOnlySpan data) { switch (attributeName) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 5cb37356fb51b4..88da0224f0e575 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -32,7 +32,7 @@ internal ModuleBuilderImpl(string name, Assembly coreAssembly) } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Types are preserved via s_coreTypes")] - internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) + internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) { if (_coreTypes == null) { @@ -210,7 +210,7 @@ protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, } 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) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index b92622f70194fe..9ac8b2c4a1a5cc 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -74,7 +74,7 @@ protected override MethodBuilder DefineMethodCore(string name, MethodAttributes 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) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) { @@ -82,7 +82,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar } } - private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, byte[] data) + private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, ReadOnlySpan data) { switch (attributeName) { @@ -93,7 +93,9 @@ private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, _attributes |= TypeAttributes.SpecialName; break; 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 break; case "System.Runtime.InteropServices.ComImportAttribute": _attributes |= TypeAttributes.Import; @@ -106,7 +108,7 @@ private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, return true; } - private void ParseStructLayoutAttribute(ConstructorInfo con, byte[] data) + private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan data) { CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, data); LayoutKind layoutKind = (LayoutKind)attributeInfo._ctorArgs[0]!; diff --git a/src/libraries/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderSetCustomAttribute.cs b/src/libraries/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderSetCustomAttribute.cs index e177c08dab26a9..aea595e0761c68 100644 --- a/src/libraries/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderSetCustomAttribute.cs +++ b/src/libraries/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderSetCustomAttribute.cs @@ -29,17 +29,6 @@ public void SetCustomAttribute_ConstructorInfo_ByteArray_NullAttributeConstructo AssertExtensions.Throws("con", () => typeParams[0].SetCustomAttribute(null, new byte[128])); } - [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("binaryAttribute", () => typeParams[0].SetCustomAttribute(attributeConstructor, null)); - } - [Fact] public void SetCustomAttribute_CustomAttributeBuilder() { diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs index cecc02c49dda8e..28cbe9e7cb2a84 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.Mono.cs @@ -77,13 +77,12 @@ internal object Invoke() return result; } - internal CustomAttributeBuilder(ConstructorInfo con, byte[] binaryAttribute) + internal CustomAttributeBuilder(ConstructorInfo con, ReadOnlySpan 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 @@ private void Initialize(ConstructorInfo con, object?[] constructorArgs, } /* helper methods */ - internal static int decode_len(byte[] data, int pos, out int rpos) + internal static int decode_len(ReadOnlySpan data, int pos, out int rpos) { int len; if ((data[pos] & 0x80) == 0) @@ -289,9 +288,9 @@ internal static int decode_len(byte[] data, int pos, out int rpos) return len; } - internal static string string_from_bytes(byte[] data, int pos, int len) + internal static string string_from_bytes(ReadOnlySpan 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 @@ private static Type elementTypeToType(int elementType) => _ => 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 data, int pos, out int rpos) { switch (Type.GetTypeCode(t)) { @@ -505,7 +504,7 @@ internal static CustomAttributeInfo decode_cattr(CustomAttributeBuilder customBu 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(ConstructorInfo ctor, byte[] data) + internal static CustomAttributeInfo decode_cattr(ConstructorInfo ctor, ReadOnlySpan data) { int pos; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs index b30ea819a47093..32cbacfc6a4df4 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.Mono.cs @@ -297,7 +297,7 @@ internal static AssemblyBuilder InternalDefineDynamicAssembly( public override bool IsCollectible => access == (uint)AssemblyBuilderAccess.RunAndCollect; - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs index a6a3dfe737dd20..280e51e8ee3150 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs @@ -254,15 +254,14 @@ protected override ILGenerator GetILGeneratorCore(int streamSize) return ilgen; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.CompilerServices.MethodImplAttribute") { - byte[] data = binaryAttribute; int impla; // the (stupid) ctor takes a short or an int ... - impla = (int)data[2]; - impla |= ((int)data[3]) << 8; + impla = (int)binaryAttribute[2]; + impla |= ((int)binaryAttribute[3]) << 8; SetImplementationFlags((MethodImplAttributes)impla); return; } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs index f2b2e0d6e114b9..b417c32f06abae 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEnumBuilder.Mono.cs @@ -447,7 +447,7 @@ public override Type MakePointerType() return SymbolType.FormCompoundType("*", this, 0)!; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { _tb.SetCustomAttribute(con, binaryAttribute); } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs index 9d3261f1e0472f..5cbfea669f365d 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.Mono.cs @@ -106,7 +106,7 @@ protected override void SetRemoveOnMethodCore(MethodBuilder mdBuilder) remove_method = mdBuilder; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { RejectIfCreated(); string? attrname = con.ReflectedType!.FullName; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs index c9c227f1c5f3fb..936dd55272dfa8 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs @@ -174,18 +174,17 @@ protected override void SetConstantCore(object? defaultValue) def_value = defaultValue; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { RejectIfCreated(); CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.InteropServices.FieldOffsetAttribute") { - byte[] data = binaryAttribute; - offset = (int)data[2]; - offset |= ((int)data[3]) << 8; - offset |= ((int)data[4]) << 16; - offset |= ((int)data[5]) << 24; + offset = (int)binaryAttribute[2]; + offset |= ((int)binaryAttribute[3]) << 8; + offset |= ((int)binaryAttribute[4]) << 16; + offset |= ((int)binaryAttribute[5]) << 24; return; } #pragma warning disable SYSLIB0050 // FieldAttributes.NotSerialized is obsolete diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs index 3859a4cb69b60b..27e0d2e9975561 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeGenericTypeParameterBuilder.cs @@ -424,7 +424,7 @@ public override MethodBase? DeclaringMethod get { return mbuilder; } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs index 347f61a8874356..84857a839c570e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs @@ -381,15 +381,14 @@ internal void ResolveUserTypes() } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { switch (con.ReflectedType!.FullName) { case "System.Runtime.CompilerServices.MethodImplAttribute": - byte[] data = binaryAttribute; int impla; // the (stupid) ctor takes a short or an int ... - impla = (int)data[2]; - impla |= ((int)data[3]) << 8; + impla = (int)binaryAttribute[2]; + impla |= ((int)binaryAttribute[3]) << 8; iattrs |= (MethodImplAttributes)impla; return; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs index 1646950b3c45b5..85e98ea5b84a4c 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.Mono.cs @@ -426,7 +426,7 @@ internal int get_next_table_index(int table, int count) return index; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { CustomAttributeBuilder customBuilder = new CustomAttributeBuilder(con, binaryAttribute); if (cattrs != null) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs index 275438573237d3..25dbb485d23c3e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.Mono.cs @@ -159,7 +159,7 @@ protected override void SetConstantCore(object? defaultValue) def_value = defaultValue; } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { typeb.check_not_created(); string? attrname = con.ReflectedType!.FullName; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs index 98badc2c0c82b6..e6f748dc080926 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs @@ -1411,15 +1411,19 @@ public override RuntimeTypeHandle TypeHandle } } - protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) + internal void SetCustomAttribute(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + SetCustomAttributeCore(con, binaryAttribute); + } + + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.InteropServices.StructLayoutAttribute") { - byte[] data = binaryAttribute; 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 { @@ -1433,34 +1437,34 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binar int pos = 6; if (ctor_type.FullName == "System.Int16") pos = 4; - int nnamed = (int)data[pos++]; - nnamed |= ((int)data[pos++]) << 8; + int nnamed = (int)binaryAttribute[pos++]; + nnamed |= ((int)binaryAttribute[pos++]) << 8; 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 = (int)binaryAttribute[pos++]; + value |= ((int)binaryAttribute[pos++]) << 8; + value |= ((int)binaryAttribute[pos++]) << 16; + value |= ((int)binaryAttribute[pos++]) << 24; switch (named_name) { case "CharSet": From 33855966c0ef84042f3a2d40bba541e6caa18032 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 13 Apr 2023 17:02:12 -0700 Subject: [PATCH 04/11] Update some pseudo attributes handling and apply feedback --- .../src/Resources/Strings.resx | 26 ++- .../Reflection/Emit/AssemblyBuilderImpl.cs | 4 +- .../Reflection/Emit/CustomAttributeWrapper.cs | 34 ++-- .../Reflection/Emit/FieldBuilderImpl.cs | 23 ++- .../System/Reflection/Emit/MetadataHelper.cs | 70 ++++---- .../Reflection/Emit/MethodBuilderImpl.cs | 150 +++++++++++++---- .../Reflection/Emit/ModuleBuilderImpl.cs | 68 +++++--- .../System/Reflection/Emit/SignatureHelper.cs | 1 - .../System/Reflection/Emit/TypeBuilderImpl.cs | 14 +- .../AssemblySaveCustomAttributeTests.cs | 157 ++++++++---------- 10 files changed, 319 insertions(+), 228 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 6f406180cfb9a2..2e04318a970b1e 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -141,31 +141,25 @@ The invoked member is not supported in a dynamic module. - - Invalid name. - - The type '{0}' may not be used as a type argument. - - - This type cannot be represented as a custom attribute. + The type code '{0}' may not be used as a type argument of a custom attribute . - - Custom attribute type '{0}' doesn't contain a field named '{1}'. + + Custom attribute '{0}' doesn't contain a field named '{1}'. - Custom attribute length is only '{0}'. + Custom attribute '{0}' data length is only '{1}'. - Prolog invalid. + Custom attribute '{0}' prolog invalid. - Unknown named type '{0}'. + Custom attribute '{0}' has unknown named type '{1}'. - - Type '{0}' not yet handled in DecodeCustomAttributeValue. + + Type '{0}' not handled in the custom attribute value decoder. - - Subtype '{0}' of type object not yet handled. + + DllName cannot be empty. \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs index 3fa7a9792f85c8..c0458c0dc57ddc 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs @@ -130,9 +130,7 @@ protected override ModuleBuilder DefineDynamicModuleCore(string name) return null; } - protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) - { + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) => _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 index e575439f3daae7..490ff8123420bb 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -30,22 +30,25 @@ internal struct CustomAttributeInfo Justification = "Could not propagate attribute into 'ctor.DeclaringType' only available at runtime")] internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, ReadOnlySpan data) { - int pos; + int pos = 2; CustomAttributeInfo info = default; - // Prolog if (data.Length < 2) - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidCustomAttributeLength, data.Length)); + { + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidCustomAttributeLength, ctor.DeclaringType, data.Length)); + } if ((data[0] != 0x1) || (data[1] != 0x00)) - throw new InvalidOperationException(SR.InvalidOperation_InvalidProlog); - pos = 2; + { + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidProlog, ctor.DeclaringType)); + } 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, data, pos, out pos); - + } int numNamed = data[pos] + (data[pos + 1] * 256); pos += 2; @@ -74,9 +77,11 @@ internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, /* Field */ FieldInfo? fi = ctor.DeclaringType!.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (fi == null) - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_EmptyFieldForCustomAttributeType, ctor.DeclaringType, name)); - + { + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_EmptyFieldForCustomAttribute, ctor.DeclaringType, name)); + } object? val = DecodeCustomAttributeValue(fi.FieldType, data, pos, out pos); + if (enumTypeName != null) { Type enumType = Type.GetType(enumTypeName)!; @@ -86,7 +91,9 @@ internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, info._namedParamValues[i] = val; } else - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, namedType)); + { + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, ctor.DeclaringType, namedType)); + } } return info; @@ -142,12 +149,13 @@ private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) pos += 1; if (subtype >= 0x02 && subtype <= 0x0e) + { return DecodeCustomAttributeValue(ElementTypeToType(subtype), data, pos, out rpos); - else - throw new NotImplementedException(SR.NotImplemented_UnhandledSubType); - default: - throw new NotImplementedException(SR.Format(SR.NotImplemented_TypeForValueNotHandled, t)); + } + break; } + + throw new NotImplementedException(SR.Format(SR.NotImplemented_TypeForValue, t)); } private static Type ElementTypeToType(int elementType) => diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index 3c428a6a9f7cff..0b6bb7be4b9f66 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -10,6 +10,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder { private readonly TypeBuilderImpl _typeBuilder; private readonly string _fieldName; + internal int _offset; private FieldAttributes _attributes; private readonly Type _fieldType; @@ -21,28 +22,27 @@ internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type ty _typeBuilder = typeBuilder; _fieldType = type; _attributes = attributes & ~FieldAttributes.ReservedMask; + _offset = -1; } protected override void SetConstantCore(object? defaultValue) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { - if (!IsPseudoAttribute(con.ReflectedType!.FullName!)) + if (!IsPseudoAttribute(con.ReflectedType!.FullName!, binaryAttribute)) { _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } } - private bool IsPseudoAttribute(string attributeName) + private bool IsPseudoAttribute(string attributeName, ReadOnlySpan binaryAttribute) { switch (attributeName) { case "System.Runtime.InteropServices.FieldOffsetAttribute": - /* TODO: Not sure how to apply this - * byte[] data = customBuilder.Data; - offset = (int)data[2]; - offset |= ((int)data[3]) << 8; - offset |= ((int)data[4]) << 16; - offset |= ((int)data[5]) << 24;*/ + _offset = (int)binaryAttribute[2]; + _offset |= ((int)binaryAttribute[3]) << 8; + _offset |= ((int)binaryAttribute[4]) << 16; + _offset |= ((int)binaryAttribute[5]) << 24; break; case "System.NonSerializedAttribute": #pragma warning disable SYSLIB0050 // 'FieldAttributes.NotSerialized' is obsolete: 'Formatter-based serialization is obsolete and should not be used'. @@ -61,7 +61,12 @@ private bool IsPseudoAttribute(string attributeName) return true; } - protected override void SetOffsetCore(int iOffset) => throw new NotImplementedException(); + protected override void SetOffsetCore(int iOffset) + { + ArgumentOutOfRangeException.ThrowIfNegative(iOffset); + + _offset = iOffset; + } #region MemberInfo Overrides diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs index 41b61d2ad725e1..e57031d9aa4643 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MetadataHelper.cs @@ -18,46 +18,35 @@ internal static AssemblyReferenceHandle AddAssemblyReference(Assembly assembly, } internal static AssemblyReferenceHandle AddAssemblyReference(MetadataBuilder metadata, string name, Version? version, - string? culture, byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) - { - return metadata.AddAssemblyReference( + string? culture, byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) => + metadata.AddAssemblyReference( name: metadata.GetOrAddString(name), version: version ?? new Version(0, 0, 0, 0), culture: (culture == null) ? default : metadata.GetOrAddString(value: culture), publicKeyOrToken: (publicKeyToken == null) ? default : metadata.GetOrAddBlob(publicKeyToken), // reference has token, not full public key flags: (AssemblyFlags)((int)contentType << 9) | ((flags & AssemblyNameFlags.Retargetable) != 0 ? AssemblyFlags.Retargetable : 0), hashValue: default); // .file directive assemblies not supported, no need to handle this value. - } - internal static TypeDefinitionHandle AddTypeDefinition(MetadataBuilder metadata, TypeBuilderImpl typeBuilder, EntityHandle baseType, int methodToken, int fieldToken) - { - // Add type metadata - return metadata.AddTypeDefinition( + internal static TypeDefinitionHandle AddTypeDefinition(MetadataBuilder metadata, TypeBuilderImpl typeBuilder, + EntityHandle baseType, int methodToken, int fieldToken) => + metadata.AddTypeDefinition( attributes: typeBuilder.Attributes, @namespace: (typeBuilder.Namespace == null) ? default : metadata.GetOrAddString(typeBuilder.Namespace), name: metadata.GetOrAddString(typeBuilder.Name), baseType: baseType, fieldList: MetadataTokens.FieldDefinitionHandle(fieldToken), - methodList: MetadataTokens.MethodDefinitionHandle(methodToken)); - } - - internal static TypeReferenceHandle AddTypeReference(MetadataBuilder metadata, Type type, AssemblyReferenceHandle parent) - { - return AddTypeReference(metadata, parent, type.Name, type.Namespace); - } + methodList: MetadataTokens.MethodDefinitionHandle(methodToken) + ); - internal static TypeReferenceHandle AddTypeReference(MetadataBuilder metadata, AssemblyReferenceHandle parent, string name, string? nameSpace) - { - return metadata.AddTypeReference( + internal static TypeReferenceHandle AddTypeReference(MetadataBuilder metadata, Type type, AssemblyReferenceHandle parent) => + metadata.AddTypeReference( resolutionScope: parent, - @namespace: (nameSpace == null) ? default : metadata.GetOrAddString(nameSpace), - name: metadata.GetOrAddString(name) + @namespace: (type.Namespace == null) ? default : metadata.GetOrAddString(type.Namespace), + name: metadata.GetOrAddString(type.Name) ); - } - internal static MethodDefinitionHandle AddMethodDefinition(MetadataBuilder metadata, MethodBuilderImpl methodBuilder, BlobBuilder methodSignatureBlob) - { - return metadata.AddMethodDefinition( + internal static MethodDefinitionHandle AddMethodDefinition(MetadataBuilder metadata, MethodBuilderImpl methodBuilder, BlobBuilder methodSignatureBlob) => + metadata.AddMethodDefinition( attributes: methodBuilder.Attributes, implAttributes: methodBuilder.GetMethodImplementationFlags(), name: metadata.GetOrAddString(methodBuilder.Name), @@ -65,24 +54,37 @@ internal static MethodDefinitionHandle AddMethodDefinition(MetadataBuilder metad bodyOffset: -1, // No body supported yet parameterList: MetadataTokens.ParameterHandle(1) ); - } - internal static FieldDefinitionHandle AddFieldDefinition(MetadataBuilder metadata, FieldInfo field, BlobBuilder fieldSignatureBlob) - { - return metadata.AddFieldDefinition( + internal static FieldDefinitionHandle AddFieldDefinition(MetadataBuilder metadata, FieldInfo field, BlobBuilder fieldSignatureBlob) => + metadata.AddFieldDefinition( attributes: field.Attributes, name: metadata.GetOrAddString(field.Name), - signature: metadata.GetOrAddBlob(fieldSignatureBlob)); - } + signature: metadata.GetOrAddBlob(fieldSignatureBlob) + ); internal static MemberReferenceHandle AddConstructorReference(ModuleBuilderImpl moduleBuilder, MetadataBuilder metadata, TypeReferenceHandle parent, MethodBase method) { var blob = MetadataSignatureHelper.ConstructorSignatureEncoder(method.GetParameters(), moduleBuilder); return metadata.AddMemberReference( - parent: parent, - name: metadata.GetOrAddString(method.Name), - signature: metadata.GetOrAddBlob(blob) - ); + parent: parent, + name: metadata.GetOrAddString(method.Name), + signature: metadata.GetOrAddBlob(blob) + ); } + + internal static void AddMethodImport(MetadataBuilder metadata, MethodDefinitionHandle methodHandle, + string name, MethodImportAttributes attributes, ModuleReferenceHandle moduleHandle) => + metadata.AddMethodImport( + method: methodHandle, + attributes: attributes, + name: metadata.GetOrAddString(name), + module: moduleHandle + ); + + internal static ModuleReferenceHandle AddModuleReference(MetadataBuilder metadata, string moduleName) => + metadata.AddModuleReference(moduleName: metadata.GetOrAddString(moduleName)); + + internal static void AddFieldLayout(MetadataBuilder metadata, FieldDefinitionHandle fieldHandle, int offset) => + metadata.AddFieldLayout(field: fieldHandle, offset: offset); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index dd6e66537a267f..35411413f5a129 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection.Metadata; +using System.Runtime.InteropServices; namespace System.Reflection.Emit { @@ -18,6 +19,7 @@ internal sealed class MethodBuilderImpl : MethodBuilder private readonly string _name; private readonly CallingConventions _callingConventions; private readonly TypeBuilderImpl _declaringType; + internal DllImportData? _dllImportData; internal List _customAttributes = new(); @@ -67,44 +69,15 @@ private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, impla |= data[3] << 8; _methodImplFlags |= (MethodImplAttributes)impla; break; - case "System.Runtime.InteropServices.DllImportAttribute": - CustomAttributeInfo attr = CustomAttributeInfo.DecodeCustomAttribute(con, data); - bool preserveSig = true; - - /* TODO - * pi_dll = (string?)attr._ctorArgs[0]; - if (pi_dll == null || pi_dll.Length == 0) - throw new ArgumentException(SR.Arg_DllNameCannotBeEmpty); - - native_cc = Runtime.InteropServices.CallingConvention.Winapi;*/ - - for (int i = 0; i < attr._namedParamNames.Length; ++i) { - string name = attr._namedParamNames[i]; - object? value = attr._namedParamValues[i]; - - if (name == "PreserveSig") - preserveSig = (bool)value!; - /*else if (name == "CallingConvention") // TODO: this values might need to be covered - native_cc = (CallingConvention)value!; - else if (name == "CharSet") - charset = (CharSet)value!; - else if (name == "EntryPoint") - pi_entry = (string)value!; - else if (name == "ExactSpelling") - ExactSpelling = (bool)value!; - else if (name == "SetLastError") - SetLastError = (bool)value!; - else if (name == "BestFitMapping") - BestFitMapping = (bool)value!; - else if (name == "ThrowOnUnmappableChar") - ThrowOnUnmappableChar = (bool)value!;*/ + _dllImportData = DllImportData.CreateDllImportData(CustomAttributeInfo.DecodeCustomAttribute(con, data), out var preserveSig); + _attributes |= MethodAttributes.PinvokeImpl; + if (preserveSig) + { + _methodImplFlags |= MethodImplAttributes.PreserveSig; + } } - - _attributes |= MethodAttributes.PinvokeImpl; - if (preserveSig) - _methodImplFlags |= MethodImplAttributes.PreserveSig; break; case "System.Runtime.InteropServices.PreserveSigAttribute": _methodImplFlags |= MethodImplAttributes.PreserveSig; @@ -176,4 +149,111 @@ public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? bind public override MethodInfo MakeGenericMethod(params System.Type[] typeArguments) => throw new NotImplementedException(); } + + internal sealed class DllImportData + { + private readonly string _moduleName; + private readonly string? _entryPoint; + private readonly MethodImportAttributes _flags; + internal DllImportData(string moduleName, string? enntryPoint, MethodImportAttributes flags) + { + _moduleName = moduleName; + _entryPoint = enntryPoint; + _flags = flags; + } + + public string ModuleName + { + get { return _moduleName; } + } + + public string? EntryPoint + { + get { return _entryPoint; } + } + + public MethodImportAttributes Flags + { + get { return _flags; } + } + + internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out bool preserveSig) + { + string? moduleName = (string?)attr._ctorArgs[0]; + if (moduleName == null || moduleName.Length == 0) + { + throw new ArgumentException(SR.Argument_DllNameCannotBeEmpty); + } + + MethodImportAttributes importAttributes = MethodImportAttributes.None; + string? entryPoint = null; + preserveSig = true; + for (int i = 0; i < attr._namedParamNames.Length; ++i) + { + string name = attr._namedParamNames[i]; + object value = attr._namedParamValues[i]!; + switch (name) + { + case "PreserveSig": + preserveSig = (bool)value; break; + case "CallingConvention": + importAttributes |= (CallingConvention)value switch + { + CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl, + CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall, + CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall, + CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall, + _=> MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this + }; + break; + case "CharSet": + importAttributes |= (CharSet)value switch + { + CharSet.Ansi => MethodImportAttributes.CharSetAnsi, + CharSet.Auto => MethodImportAttributes.CharSetAuto, + CharSet.Unicode => MethodImportAttributes.CharSetUnicode, + _ => MethodImportAttributes.CharSetAuto + }; + break; + case "EntryPoint": + entryPoint = (string?)value; + break; + case "ExactSpelling": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.ExactSpelling; + } + break; + case "SetLastError": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.SetLastError; + } + break; + case "BestFitMapping": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.BestFitMappingEnable; + } + else + { + importAttributes |= MethodImportAttributes.BestFitMappingDisable; + } + break; + case "ThrowOnUnmappableChar": + if ((bool)value) + { + importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharEnable; + } + else + { + importAttributes |= MethodImportAttributes.ThrowOnUnmappableCharDisable; + } + break; + } + } + + return new DllImportData(moduleName, entryPoint, importAttributes); + } + } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 88da0224f0e575..05bd4d31e65bc9 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -14,10 +14,11 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly Assembly _coreAssembly; private readonly string _name; private Type?[]? _coreTypes; - private readonly Dictionary _assemblyRefStore = new(); - private readonly Dictionary _constructorRefStore = new(); - private readonly Dictionary _typeRefStore = new(); - private readonly List _typeDefStore = new(); + private readonly Dictionary _assemblyReferences = new(); + private readonly Dictionary _memberReferences = new(); + private readonly Dictionary _typeReferences = new(); + private Dictionary? _moduleReferences; + private readonly List _typeDefinitions = new(); private readonly List _customAttributes = new(); private int _nextMethodDefRowId = 1; private int _nextFieldDefRowId = 1; @@ -115,7 +116,7 @@ internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle WriteCustomAttributes(metadata, _customAttributes, moduleHandle); // Add each type definition to metadata table. - foreach (TypeBuilderImpl typeBuilder in _typeDefStore) + foreach (TypeBuilderImpl typeBuilder in _typeDefinitions) { TypeReferenceHandle parent = default; if (typeBuilder.BaseType is not null) @@ -127,22 +128,46 @@ internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle TypeDefinitionHandle typeDefinitionHandle = MetadataHelper.AddTypeDefinition(metadata, typeBuilder, parent, _nextMethodDefRowId, _nextFieldDefRowId); WriteCustomAttributes(metadata, typeBuilder._customAttributes, typeDefinitionHandle); - foreach (MethodBuilderImpl method in typeBuilder._methodDefStore) + foreach (MethodBuilderImpl method in typeBuilder._methodDefinitions) { MethodDefinitionHandle methodHandle = MetadataHelper.AddMethodDefinition(metadata, method, method.GetMethodSignatureBlob()); WriteCustomAttributes(metadata, method._customAttributes, methodHandle); _nextMethodDefRowId++; + + if (method._dllImportData != null) + { + MetadataHelper.AddMethodImport(metadata, methodHandle, method._dllImportData.EntryPoint ?? method.Name, + method._dllImportData.Flags, GetModuleReference(metadata, method._dllImportData.ModuleName)); + } } - foreach (FieldBuilderImpl field in typeBuilder._fieldDefStore) + foreach (FieldBuilderImpl field in typeBuilder._fieldDefinitions) { FieldDefinitionHandle fieldHandle = MetadataHelper.AddFieldDefinition(metadata, field, MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType, this)); WriteCustomAttributes(metadata, field._customAttributes, fieldHandle); _nextFieldDefRowId++; + + if (field._offset > 0 && (typeBuilder.Attributes & TypeAttributes.ExplicitLayout) != 0) + { + MetadataHelper.AddFieldLayout(metadata, fieldHandle, field._offset); + } } } } + private ModuleReferenceHandle GetModuleReference(MetadataBuilder metadata, string moduleName) + { + _moduleReferences ??= new(); + + if (!_moduleReferences.TryGetValue(moduleName, out var handle)) + { + handle = MetadataHelper.AddModuleReference(metadata, moduleName); + _moduleReferences.Add(moduleName, handle); + } + + return handle; + } + private void WriteCustomAttributes(MetadataBuilder metadata, List customAttributes, EntityHandle parent) { foreach (CustomAttributeWrapper customAttribute in customAttributes) @@ -154,24 +179,22 @@ private void WriteCustomAttributes(MetadataBuilder metadata, List for modules with no file path")] @@ -205,15 +229,13 @@ private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly, Metadata protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize) { TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this, packingSize, typesize); - _typeDefStore.Add(_type); + _typeDefinitions.Add(_type); return _type; } protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException(); protected override MethodInfo GetArrayMethodCore(Type arrayClass, string methodName, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException(); - protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) - { + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) => _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); - } public override int GetSignatureMetadataToken(SignatureHelper signature) => throw new NotImplementedException(); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs index 474d7e08c65001..7119424aaa1e36 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs @@ -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; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 9ac8b2c4a1a5cc..a59045a7cbc85f 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -19,8 +19,8 @@ internal sealed class TypeBuilderImpl : TypeBuilder private PackingSize _packingSize; private int _typeSize; - internal List _methodDefStore = new(); - internal List _fieldDefStore = new(); + internal List _methodDefinitions = new(); + internal List _fieldDefinitions = new(); internal List _customAttributes = new(); internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, @@ -54,7 +54,7 @@ internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, 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(); @@ -62,7 +62,7 @@ protected override FieldBuilder DefineFieldCore(string fieldName, Type type, Typ 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; } @@ -100,7 +100,7 @@ private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, case "System.Runtime.InteropServices.ComImportAttribute": _attributes |= TypeAttributes.Import; break; - case "System.Security.SuppressUnmanagedCodeSecurityAttribute": + case "System.Security.SuppressUnmanagedCodeSecurityAttribute": // It says has no effect in .NET Core, maybe remove? _attributes |= TypeAttributes.HasSecurity; return false; default: return false; @@ -118,7 +118,7 @@ private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan LayoutKind.Auto => TypeAttributes.AutoLayout, LayoutKind.Explicit => TypeAttributes.ExplicitLayout, LayoutKind.Sequential => TypeAttributes.SequentialLayout, - _ => throw new ArgumentException(SR.Argument_InvalidKindOfTypeForCA), + _ => TypeAttributes.AutoLayout, }; for (int i = 0; i < attributeInfo._namedParamNames.Length; ++i) @@ -152,7 +152,7 @@ private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan _typeSize = value; break; default: - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, name)); + throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, con.DeclaringType, name)); } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs index 4aa692826e3bf2..c3a63c27911d02 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -46,22 +46,8 @@ public void AssemblyModuleWithCustomAttributes() Module moduleFromDisk = assemblyFromDisk.Modules.First(); AssemblyTools.AssertAssemblyNameAndModule(assemblyName, assemblyFromDisk.GetName(), moduleFromDisk); - - IList assemblyAttributesFromDisk = assemblyFromDisk.GetCustomAttributesData(); - IList moduleAttributesFromDisk = moduleFromDisk.GetCustomAttributesData(); - - Assert.Equal(_attributes.Count, assemblyAttributesFromDisk.Count); - Assert.Equal(_attributes.Count, moduleAttributesFromDisk.Count); - - foreach (var attribute in assemblyAttributesFromDisk) - { - ValidateAttributes(attribute); - } - - foreach (var attribute in moduleAttributesFromDisk) - { - ValidateAttributes(attribute); - } + ValidateAttributes(assemblyFromDisk.GetCustomAttributesData()); + ValidateAttributes(moduleFromDisk.GetCustomAttributesData()); } } @@ -91,59 +77,43 @@ public void MethodFieldWithCustomAttributes() IList typeAttributesFromDisk = typeFromDisk.GetCustomAttributesData(); - Assert.Equal(_attributes.Count, typeAttributesFromDisk.Count); AssemblyTools.AssertTypeProperties(sourceType, typeFromDisk); AssemblyTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); AssemblyTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); - - foreach (var attribute in typeAttributesFromDisk) - { - ValidateAttributes(attribute); - } + ValidateAttributes(typeAttributesFromDisk); for (int j = 0; j < methodsFromDisk.Length; j++) { - IList attributesFromDisk = methodsFromDisk[j].GetCustomAttributesData(); - - Assert.Equal(_attributes.Count, attributesFromDisk.Count); - - foreach (var attribute in attributesFromDisk) - { - ValidateAttributes(attribute); - } + ValidateAttributes(methodsFromDisk[j].GetCustomAttributesData()); } for (int j = 0; j < fieldsFromDisk.Length; j++) { - IList attributesFromDisk = fieldsFromDisk[j].GetCustomAttributesData(); - - Assert.Equal(_attributes.Count, attributesFromDisk.Count); - - foreach (var attribute in attributesFromDisk) - { - ValidateAttributes(attribute); - } + ValidateAttributes(fieldsFromDisk[j].GetCustomAttributesData()); } } } } - private void ValidateAttributes(CustomAttributeData customAttribute) + private void ValidateAttributes(IList attributesFromDisk) { - if (customAttribute.AttributeType.Name == s_comVisibleType.Name) - { - Assert.Equal(s_comVisiblePair.con.MetadataToken, customAttribute.Constructor.MetadataToken); + Assert.Equal(_attributes.Count, attributesFromDisk.Count); - Assert.Equal(s_comVisiblePair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); - Assert.Equal(true, customAttribute.ConstructorArguments[0].Value); - } - else + foreach (var attribute in attributesFromDisk) { - Assert.Equal(s_guidPair.con.MetadataToken, customAttribute.Constructor.MetadataToken); - - Assert.Equal(s_guidPair.args[0].GetType().FullName, customAttribute.ConstructorArguments[0].ArgumentType.FullName); - Assert.Equal(customAttribute.AttributeType.Name, s_guideType.Name); - Assert.Equal(s_guidPair.args[0], customAttribute.ConstructorArguments[0].Value); + 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); + } } } @@ -151,13 +121,9 @@ private static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, List? moduleAttributes = null, List? typeAttributes = null, List? methodAttributes = null, List? fieldAttributes = null) { - AssemblyBuilder assemblyBuilder = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( - assemblyName, assemblyAttributes, typeof(string), out MethodInfo saveMethod); - + AssemblyBuilder assemblyBuilder = 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 }); } @@ -166,10 +132,7 @@ private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types, Lis { if (moduleAttributes != null) { - foreach (var attribute in moduleAttributes) - { - mb.SetCustomAttribute(attribute); - } + moduleAttributes.ForEach(mb.SetCustomAttribute); } foreach (Type type in types) @@ -178,10 +141,7 @@ private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types, Lis if (typeAttributes != null) { - foreach (CustomAttributeBuilder typeAttribute in typeAttributes) - { - tb.SetCustomAttribute(typeAttribute); - } + typeAttributes.ForEach(tb.SetCustomAttribute); } DefineMethodsAndSetAttributes(methodAttributes, tb, type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly)); @@ -197,10 +157,7 @@ private static void DefineFieldsAndSetAttributes(List? f if (fieldAttributes != null) { - foreach (var attribute in fieldAttributes) - { - fb.SetCustomAttribute(attribute); - } + fieldAttributes.ForEach(fb.SetCustomAttribute); } } } @@ -213,10 +170,7 @@ private static void DefineMethodsAndSetAttributes(List m if (methodAttributes != null) { - foreach (CustomAttributeBuilder attribute in methodAttributes) - { - meb.SetCustomAttribute(attribute); - } + methodAttributes.ForEach(meb.SetCustomAttribute); } } } @@ -227,15 +181,16 @@ public void CreateStructWithPseudoCustomAttributesTest() using (TempFile file = TempFile.Create()) { Type type = typeof(StructWithField); - CustomAttributeBuilder[] attributes = new[] { new CustomAttributeBuilder(typeof(SerializableAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + List 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[] { 0 }), + new CustomAttributeBuilder(typeof(FieldOffsetAttribute).GetConstructor(new Type[] { typeof(int) }), new object[] { 2 }), new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args), + // TODO: Need to support the UnmanagedType type //new CustomAttributeBuilder(typeof(MarshalAsAttribute).GetConstructor(new Type[] { typeof(UnmanagedType) }), new object[] { UnmanagedType.I4}), new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { }) }; @@ -244,10 +199,7 @@ public void CreateStructWithPseudoCustomAttributesTest() PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes); DefineFieldsAndSetAttributes(fieldAttributes.ToList(), type.GetFields(), tb); - foreach (CustomAttributeBuilder attribute in attributes) - { - tb.SetCustomAttribute(attribute); - } + typeAttributes.ForEach(tb.SetCustomAttribute); saveMethod.Invoke(ab, new object[] { file.Path }); @@ -257,7 +209,7 @@ public void CreateStructWithPseudoCustomAttributesTest() IList attributesFromDisk = testType.GetCustomAttributesData(); - Assert.Equal(attributes.Length - 3, attributesFromDisk.Count); // 3 pseudo attributes + 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 @@ -278,6 +230,7 @@ public void CreateStructWithPseudoCustomAttributesTest() FieldInfo field = testType.GetFields()[0]; IList fieldAttributesFromDisk = field.GetCustomAttributesData(); + Assert.Equal(2, fieldAttributesFromDisk.Count); Assert.True((field.Attributes & FieldAttributes.NotSerialized) != 0); // NonSerializedAttribute Assert.True((field.Attributes & FieldAttributes.SpecialName) != 0); // SpecialNameAttribute @@ -288,7 +241,7 @@ public void CreateStructWithPseudoCustomAttributesTest() switch (fieldAttributesFromDisk[i].AttributeType.Name) { case "FieldOffsetAttribute": - // TODO: Assert.Equal(0, methodAttributesFromDisk[i].ConstructorArguments[0].Value); + Assert.Equal(2, fieldAttributesFromDisk[i].ConstructorArguments[0].Value); break; /*case "MarshalAsAttribute": // TODO: Need to support the UnmanagedType type Assert.Equal(UnmanagedType.I4, methodAttributesFromDisk[i].ConstructorArguments[0].Value); @@ -309,8 +262,9 @@ public void InterfacesWithPseudoCustomAttributes() { using (TempFile file = TempFile.Create()) { + Type dllType = typeof(DllImportAttribute); Type type = typeof(IMultipleMethod); - CustomAttributeBuilder[] attributes = new[] { new CustomAttributeBuilder(typeof(ComImportAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), + List typeAttributes = new() { new CustomAttributeBuilder(typeof(ComImportAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), new CustomAttributeBuilder(typeof(SuppressUnmanagedCodeSecurityAttribute).GetConstructor(Type.EmptyTypes), new object[] { }), new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args) }; @@ -318,17 +272,18 @@ public void InterfacesWithPseudoCustomAttributes() 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(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(attr => tb.SetCustomAttribute(attr)); DefineMethodsAndSetAttributes(methodAttributes.ToList(), tb, type.GetMethods()); - foreach (CustomAttributeBuilder attribute in attributes) - { - tb.SetCustomAttribute(attribute); - } saveMethod.Invoke(ab, new object[] { file.Path }); @@ -336,7 +291,7 @@ public void InterfacesWithPseudoCustomAttributes() Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; IList attributesFromDisk = testType.GetCustomAttributesData(); - Assert.Equal(attributes.Length, attributesFromDisk.Count); + Assert.Equal(typeAttributes.Count, attributesFromDisk.Count); for (int i = 0; i < attributesFromDisk.Count; i++) { @@ -379,6 +334,34 @@ public void InterfacesWithPseudoCustomAttributes() 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; From 899efeef8d571632c9d87d5d8231b971ec48b247 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 13 Apr 2023 17:32:40 -0700 Subject: [PATCH 05/11] Apply suggestions from code review Co-authored-by: Aaron Robinson --- .../src/System/Reflection/Emit/FieldBuilderImpl.cs | 1 + .../src/System/Reflection/Emit/MethodBuilderImpl.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index 0b6bb7be4b9f66..3d8da2dbb50c97 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -39,6 +39,7 @@ private bool IsPseudoAttribute(string attributeName, ReadOnlySpan binaryAt switch (attributeName) { case "System.Runtime.InteropServices.FieldOffsetAttribute": + Debug.Assert(binaryAttribute.Length == 6); _offset = (int)binaryAttribute[2]; _offset |= ((int)binaryAttribute[3]) << 8; _offset |= ((int)binaryAttribute[4]) << 16; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 35411413f5a129..66517c1fc524f6 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -155,10 +155,10 @@ internal sealed class DllImportData private readonly string _moduleName; private readonly string? _entryPoint; private readonly MethodImportAttributes _flags; - internal DllImportData(string moduleName, string? enntryPoint, MethodImportAttributes flags) + internal DllImportData(string moduleName, string? entryPoint, MethodImportAttributes flags) { _moduleName = moduleName; - _entryPoint = enntryPoint; + _entryPoint = entryPoint; _flags = flags; } From 02536ba141ade74355dcfef8a8d52a2b7aaa2f54 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sun, 16 Apr 2023 16:35:13 -0700 Subject: [PATCH 06/11] Fix build failure and few other updates --- .../Reflection/Emit/AssemblyBuilderImpl.cs | 7 +++++-- .../Reflection/Emit/CustomAttributeWrapper.cs | 13 +++++++----- .../Reflection/Emit/FieldBuilderImpl.cs | 6 ++++-- .../Reflection/Emit/MethodBuilderImpl.cs | 3 ++- .../Reflection/Emit/ModuleBuilderImpl.cs | 20 ++++++++++++------- .../System/Reflection/Emit/TypeBuilderImpl.cs | 3 ++- 6 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs index c0458c0dc57ddc..d2686d513a058c 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs @@ -16,7 +16,7 @@ internal sealed class AssemblyBuilderImpl : AssemblyBuilder private readonly Assembly _coreAssembly; private ModuleBuilderImpl? _module; - internal List _customAttributes = new(); + internal List? _customAttributes; internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable? assemblyAttributes) { @@ -130,7 +130,10 @@ protected override ModuleBuilder DefineDynamicModuleCore(string name) return null; } - protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) => + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + _customAttributes ??= new List(); _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 index 490ff8123420bb..796f0bf93c0ba3 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -5,16 +5,19 @@ namespace System.Reflection.Emit { - internal sealed class CustomAttributeWrapper + internal readonly struct CustomAttributeWrapper { - internal ConstructorInfo constructorInfo; - internal byte[] binaryAttribute; + private readonly ConstructorInfo _constructorInfo; + private readonly byte[] _binaryAttribute; public CustomAttributeWrapper(ConstructorInfo constructorInfo, ReadOnlySpan binaryAttribute) { - this.constructorInfo = constructorInfo; - this.binaryAttribute = binaryAttribute.ToArray(); + _constructorInfo = constructorInfo; + _binaryAttribute = binaryAttribute.ToArray(); } + + public ConstructorInfo Ctor => _constructorInfo; + public byte[] Data => _binaryAttribute; } internal struct CustomAttributeInfo diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index 3d8da2dbb50c97..d6d188aaf7dc62 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; namespace System.Reflection.Emit @@ -14,7 +15,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder private FieldAttributes _attributes; private readonly Type _fieldType; - internal List _customAttributes = new(); + internal List? _customAttributes; internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes) { @@ -30,6 +31,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan { if (!IsPseudoAttribute(con.ReflectedType!.FullName!, binaryAttribute)) { + _customAttributes ??= new List(); _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } } @@ -39,7 +41,7 @@ private bool IsPseudoAttribute(string attributeName, ReadOnlySpan binaryAt switch (attributeName) { case "System.Runtime.InteropServices.FieldOffsetAttribute": - Debug.Assert(binaryAttribute.Length == 6); + Debug.Assert(binaryAttribute.Length >= 6); _offset = (int)binaryAttribute[2]; _offset |= ((int)binaryAttribute[3]) << 8; _offset |= ((int)binaryAttribute[4]) << 16; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 66517c1fc524f6..c45cab2899b65f 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -21,7 +21,7 @@ internal sealed class MethodBuilderImpl : MethodBuilder private readonly TypeBuilderImpl _declaringType; internal DllImportData? _dllImportData; - internal List _customAttributes = new(); + internal List? _customAttributes; internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType, Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType) @@ -56,6 +56,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan { if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) { + _customAttributes ??= new List(); _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 05bd4d31e65bc9..961a1a83ad8d34 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -19,7 +19,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly Dictionary _typeReferences = new(); private Dictionary? _moduleReferences; private readonly List _typeDefinitions = new(); - private readonly List _customAttributes = new(); + private List? _customAttributes; private int _nextMethodDefRowId = 1; private int _nextFieldDefRowId = 1; private bool _coreTypesFullPopulated; @@ -93,7 +93,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle assemblyHandle, List assemblyAttributes) + internal void AppendMetadata(MetadataBuilder metadata, AssemblyDefinitionHandle assemblyHandle, List? assemblyAttributes) { // Add module metadata ModuleDefinitionHandle moduleHandle = metadata.AddModule( @@ -168,12 +168,15 @@ private ModuleReferenceHandle GetModuleReference(MetadataBuilder metadata, strin return handle; } - private void WriteCustomAttributes(MetadataBuilder metadata, List customAttributes, EntityHandle parent) + private void WriteCustomAttributes(MetadataBuilder metadata, List? customAttributes, EntityHandle parent) { - foreach (CustomAttributeWrapper customAttribute in customAttributes) + if (customAttributes != null) { - metadata.AddCustomAttribute(parent, GetConstructorHandle(metadata, customAttribute.constructorInfo), - metadata.GetOrAddBlob(customAttribute.binaryAttribute)); + foreach (CustomAttributeWrapper customAttribute in customAttributes) + { + metadata.AddCustomAttribute(parent, GetConstructorHandle(metadata, customAttribute.Ctor), + metadata.GetOrAddBlob(customAttribute.Data)); + } } } @@ -234,8 +237,11 @@ protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, } protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException(); protected override MethodInfo GetArrayMethodCore(Type arrayClass, string methodName, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException(); - protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) => + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + _customAttributes ??= new List(); _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); + } public override int GetSignatureMetadataToken(SignatureHelper signature) => throw new NotImplementedException(); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index a59045a7cbc85f..a7dda5a0b24352 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -21,7 +21,7 @@ internal sealed class TypeBuilderImpl : TypeBuilder internal List _methodDefinitions = new(); internal List _fieldDefinitions = new(); - internal List _customAttributes = new(); + internal List? _customAttributes; internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module, PackingSize packingSize, int typeSize) @@ -78,6 +78,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan { if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) { + _customAttributes ??= new List(); _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } } From f43f7fc8d8476c612c8f2a1fbb771f47d94dd9a1 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 17 Apr 2023 17:58:51 -0700 Subject: [PATCH 07/11] Add one more pseudo attribute and a few small changes --- .../ref/System.Reflection.Emit.cs | 20 +++++------ .../Reflection/Emit/ModuleBuilderImpl.cs | 8 ++--- .../System/Reflection/Emit/TypeBuilderImpl.cs | 7 ++-- .../AssemblySaveCustomAttributeTests.cs | 33 +++++-------------- 4 files changed, 27 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs index 8c7e3d984c0ed4..f3cd61d33e8964 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs @@ -55,8 +55,8 @@ protected AssemblyBuilder() { } 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); } [System.FlagsAttribute] public enum AssemblyBuilderAccess @@ -90,8 +90,8 @@ protected ConstructorBuilder() { } 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { } protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes); public override string ToString() { throw null; } @@ -184,8 +184,8 @@ protected EnumBuilder() { } 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); } public abstract partial class EventBuilder { @@ -195,8 +195,8 @@ public void AddOtherMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { } 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan 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) { } @@ -220,8 +220,8 @@ protected FieldBuilder() { } 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan 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) { } @@ -322,8 +322,8 @@ protected GenericTypeParameterBuilder() { } 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetGenericParameterAttributes(System.Reflection.GenericParameterAttributes genericParameterAttributes) { } protected abstract void SetGenericParameterAttributesCore(System.Reflection.GenericParameterAttributes genericParameterAttributes); public void SetInterfaceConstraints(params System.Type[]? interfaceConstraints) { } @@ -374,8 +374,8 @@ protected MethodBuilder() { } [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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); public void SetImplementationFlags(System.Reflection.MethodImplAttributes attributes) { } protected abstract void SetImplementationFlagsCore(System.Reflection.MethodImplAttributes attributes); public void SetParameters(params System.Type[] parameterTypes) { } @@ -467,8 +467,8 @@ public void CreateGlobalFunctions() { } [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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); } public abstract partial class PropertyBuilder : System.Reflection.PropertyInfo { @@ -495,8 +495,8 @@ public void AddOtherMethod(System.Reflection.Emit.MethodBuilder mdBuilder) { } 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan 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) { } @@ -662,8 +662,8 @@ public void DefineMethodOverride(System.Reflection.MethodInfo methodInfoBody, Sy 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, System.ReadOnlySpan binaryAttribute); public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } + protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan 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; } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 961a1a83ad8d34..4d5db583ec6e44 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -15,10 +15,10 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly string _name; private Type?[]? _coreTypes; private readonly Dictionary _assemblyReferences = new(); - private readonly Dictionary _memberReferences = new(); + private readonly Dictionary _ctorReferences = new(); private readonly Dictionary _typeReferences = new(); - private Dictionary? _moduleReferences; private readonly List _typeDefinitions = new(); + private Dictionary? _moduleReferences; private List? _customAttributes; private int _nextMethodDefRowId = 1; private int _nextFieldDefRowId = 1; @@ -182,11 +182,11 @@ private void WriteCustomAttributes(MetadataBuilder metadata, List _methodDefinitions = new(); - internal List _fieldDefinitions = new(); + internal readonly List _methodDefinitions = new(); + internal readonly List _fieldDefinitions = new(); internal List? _customAttributes; internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, @@ -101,6 +101,9 @@ private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, case "System.Runtime.InteropServices.ComImportAttribute": _attributes |= TypeAttributes.Import; break; + case "System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeImportAttribute": + _attributes |= TypeAttributes.WindowsRuntime; + break; case "System.Security.SuppressUnmanagedCodeSecurityAttribute": // It says has no effect in .NET Core, maybe remove? _attributes |= TypeAttributes.HasSecurity; return false; diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs index c3a63c27911d02..0425c6637104ad 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -75,12 +75,10 @@ public void MethodFieldWithCustomAttributes() MethodInfo[] methodsFromDisk = typeFromDisk.IsValueType ? typeFromDisk.GetMethods(BindingFlags.DeclaredOnly) : typeFromDisk.GetMethods(); FieldInfo[] fieldsFromDisk = typeFromDisk.GetFields(); - IList typeAttributesFromDisk = typeFromDisk.GetCustomAttributesData(); - AssemblyTools.AssertTypeProperties(sourceType, typeFromDisk); AssemblyTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); AssemblyTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); - ValidateAttributes(typeAttributesFromDisk); + ValidateAttributes(typeFromDisk.GetCustomAttributesData()); for (int j = 0; j < methodsFromDisk.Length; j++) { @@ -138,11 +136,7 @@ private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types, Lis foreach (Type type in types) { TypeBuilder tb = mb.DefineType(type.FullName, type.Attributes, type.BaseType); - - if (typeAttributes != null) - { - typeAttributes.ForEach(tb.SetCustomAttribute); - } + typeAttributes.ForEach(tb.SetCustomAttribute); DefineMethodsAndSetAttributes(methodAttributes, tb, type.IsInterface ? type.GetMethods() : type.GetMethods(BindingFlags.DeclaredOnly)); DefineFieldsAndSetAttributes(fieldAttributes, type.GetFields(), tb); @@ -154,11 +148,7 @@ private static void DefineFieldsAndSetAttributes(List? f foreach (FieldInfo field in fields) { FieldBuilder fb = tb.DefineField(field.Name, field.FieldType, field.Attributes); - - if (fieldAttributes != null) - { - fieldAttributes.ForEach(fb.SetCustomAttribute); - } + fieldAttributes.ForEach(fb.SetCustomAttribute); } } @@ -167,11 +157,7 @@ private static void DefineMethodsAndSetAttributes(List m foreach (var method in methods) { MethodBuilder meb = tb.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType, null); - - if (methodAttributes != null) - { - methodAttributes.ForEach(meb.SetCustomAttribute); - } + methodAttributes.ForEach(meb.SetCustomAttribute); } } @@ -282,7 +268,7 @@ public void InterfacesWithPseudoCustomAttributes() AssemblyBuilder ab = AssemblyTools.PopulateAssemblyBuilderAndSaveMethod( PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes); - typeAttributes.ForEach(attr => tb.SetCustomAttribute(attr)); + typeAttributes.ForEach(tb.SetCustomAttribute); DefineMethodsAndSetAttributes(methodAttributes.ToList(), tb, type.GetMethods()); saveMethod.Invoke(ab, new object[] { file.Path }); @@ -292,16 +278,14 @@ public void InterfacesWithPseudoCustomAttributes() IList 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": - Assert.True((testType.Attributes & TypeAttributes.Import) != 0); - break; + case "ComImportAttribute": // just making sure that these attributes are expected case "SuppressUnmanagedCodeSecurityAttribute": - Assert.True((testType.Attributes & TypeAttributes.HasSecurity) != 0); break; case "GuidAttribute": Assert.Equal(s_guidPair.args[0], attributesFromDisk[i].ConstructorArguments[0].Value); @@ -328,7 +312,6 @@ public void InterfacesWithPseudoCustomAttributes() switch (methodAttributesFromDisk[i].AttributeType.Name) { case "SuppressUnmanagedCodeSecurityAttribute": - break; case "PreserveSigAttribute": break; case "GuidAttribute": From a7ed5ce7ef03dacb7662e58bead008d118f7125f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 20 Apr 2023 18:51:15 -0700 Subject: [PATCH 08/11] Apply feedbacks --- .../src/Resources/Strings.resx | 2 +- .../Reflection/Emit/CustomAttributeWrapper.cs | 45 ++++++++++--------- .../Reflection/Emit/FieldBuilderImpl.cs | 6 +-- .../Reflection/Emit/MethodBuilderImpl.cs | 6 +-- .../Emit/RuntimeConstructorBuilder.Mono.cs | 5 +-- .../Emit/RuntimeFieldBuilder.Mono.cs | 6 +-- .../Emit/RuntimeMethodBuilder.Mono.cs | 5 +-- .../Emit/RuntimeTypeBuilder.Mono.cs | 11 +++-- 8 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 2e04318a970b1e..baaf8219f740f0 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -142,7 +142,7 @@ The invoked member is not supported in a dynamic module. - The type code '{0}' may not be used as a type argument of a custom attribute . + The type code may not be used as a type argument of a custom attribute . Custom attribute '{0}' doesn't contain a field named '{1}'. diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs index 796f0bf93c0ba3..8372c506260eef 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -1,7 +1,9 @@ // 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 { @@ -13,7 +15,7 @@ internal readonly struct CustomAttributeWrapper public CustomAttributeWrapper(ConstructorInfo constructorInfo, ReadOnlySpan binaryAttribute) { _constructorInfo = constructorInfo; - _binaryAttribute = binaryAttribute.ToArray(); + _binaryAttribute = binaryAttribute.ToArray(); // TODO: Update to BlobHandle when public API public APi for MetadataBuilder.GetOrAddBlob(ReadOnlySpan) added } public ConstructorInfo Ctor => _constructorInfo; @@ -143,7 +145,7 @@ private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) return StringFromBytes(data, pos, len); case TypeCode.Int32: rpos = pos + 4; - return data[pos] + (data[pos + 1] << 8) + (data[pos + 2] << 16) + (data[pos + 3] << 24); + return BinaryPrimitives.ReadInt32LittleEndian(data.Slice(pos)); case TypeCode.Boolean: rpos = pos + 1; return (data[pos] == 0) ? false : true; @@ -153,7 +155,7 @@ private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) if (subtype >= 0x02 && subtype <= 0x0e) { - return DecodeCustomAttributeValue(ElementTypeToType(subtype), data, pos, out rpos); + return DecodeCustomAttributeValue(ElementTypeToType((PrimitiveSerializationTypeCode)subtype), data, pos, out rpos); } break; } @@ -161,24 +163,23 @@ private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) throw new NotImplementedException(SR.Format(SR.NotImplemented_TypeForValue, t)); } - private static Type ElementTypeToType(int elementType) => - /* Partition II, section 23.1.16 */ - elementType switch - { - 0x02 => typeof(bool), - 0x03 => typeof(char), - 0x04 => typeof(sbyte), - 0x05 => typeof(byte), - 0x06 => typeof(short), - 0x07 => typeof(ushort), - 0x08 => typeof(int), - 0x09 => typeof(uint), - 0x0a => typeof(long), - 0x0b => typeof(ulong), - 0x0c => typeof(float), - 0x0d => typeof(double), - 0x0e => typeof(string), - _ => throw new ArgumentException(SR.Format(SR.ArgumentException_InvalidTypeArgument, elementType)), - }; + 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.ArgumentException_InvalidTypeArgument), + }; } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index d6d188aaf7dc62..a97a8f8b736d6c 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -1,6 +1,7 @@ // 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; @@ -42,10 +43,7 @@ private bool IsPseudoAttribute(string attributeName, ReadOnlySpan binaryAt { case "System.Runtime.InteropServices.FieldOffsetAttribute": Debug.Assert(binaryAttribute.Length >= 6); - _offset = (int)binaryAttribute[2]; - _offset |= ((int)binaryAttribute[3]) << 8; - _offset |= ((int)binaryAttribute[4]) << 16; - _offset |= ((int)binaryAttribute[5]) << 24; + _offset = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(2)); break; case "System.NonSerializedAttribute": #pragma warning disable SYSLIB0050 // 'FieldAttributes.NotSerialized' is obsolete: 'Formatter-based serialization is obsolete and should not be used'. diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index c45cab2899b65f..715c7fabe8758c 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -1,6 +1,7 @@ // 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; @@ -66,9 +67,8 @@ private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, switch (attributeName) { case "System.Runtime.CompilerServices.MethodImplAttribute": - int impla = data[2]; - impla |= data[3] << 8; - _methodImplFlags |= (MethodImplAttributes)impla; + int implValue = BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(2)); + _methodImplFlags |= (MethodImplAttributes)implValue; break; case "System.Runtime.InteropServices.DllImportAttribute": { diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs index 280e51e8ee3150..83c62a8e9817ac 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeConstructorBuilder.Mono.cs @@ -37,6 +37,7 @@ using System.Globalization; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using System.Buffers.Binary; namespace System.Reflection.Emit { @@ -259,9 +260,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.CompilerServices.MethodImplAttribute") { - int impla; // the (stupid) ctor takes a short or an int ... - impla = (int)binaryAttribute[2]; - impla |= ((int)binaryAttribute[3]) << 8; + int impla = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(2)); SetImplementationFlags((MethodImplAttributes)impla); return; } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs index 936dd55272dfa8..68f4c7b2bbf74b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.Mono.cs @@ -37,6 +37,7 @@ using System.Globalization; using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis; +using System.Buffers.Binary; namespace System.Reflection.Emit { @@ -181,10 +182,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan string? attrname = con.ReflectedType!.FullName; if (attrname == "System.Runtime.InteropServices.FieldOffsetAttribute") { - offset = (int)binaryAttribute[2]; - offset |= ((int)binaryAttribute[3]) << 8; - offset |= ((int)binaryAttribute[4]) << 16; - offset |= ((int)binaryAttribute[5]) << 24; + offset = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(2)); return; } #pragma warning disable SYSLIB0050 // FieldAttributes.NotSerialized is obsolete diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs index 84857a839c570e..cdae1832367f8c 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.Mono.cs @@ -34,6 +34,7 @@ // #if MONO_FEATURE_SRE +using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; @@ -386,9 +387,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan switch (con.ReflectedType!.FullName) { case "System.Runtime.CompilerServices.MethodImplAttribute": - int impla; // the (stupid) ctor takes a short or an int ... - impla = (int)binaryAttribute[2]; - impla |= ((int)binaryAttribute[3]) << 8; + int impla = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(2)); iattrs |= (MethodImplAttributes)impla; return; diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs index e6f748dc080926..469f9efebb744b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs @@ -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; @@ -1437,8 +1438,8 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan int pos = 6; if (ctor_type.FullName == "System.Int16") pos = 4; - int nnamed = (int)binaryAttribute[pos++]; - nnamed |= ((int)binaryAttribute[pos++]) << 8; + int nnamed = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(pos++)); + pos++; for (int i = 0; i < nnamed; ++i) { //byte named_type = data [pos++]; @@ -1461,10 +1462,8 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan named_name = CustomAttributeBuilder.string_from_bytes(binaryAttribute, pos, len); pos += len; /* all the fields are integers in StructLayout */ - int value = (int)binaryAttribute[pos++]; - value |= ((int)binaryAttribute[pos++]) << 8; - value |= ((int)binaryAttribute[pos++]) << 16; - value |= ((int)binaryAttribute[pos++]) << 24; + int value = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(pos++)); + pos += 3; switch (named_name) { case "CharSet": From 18f01ea3b9f9c09c4393987021274951d36f8fcd Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sun, 23 Apr 2023 22:30:27 -0700 Subject: [PATCH 09/11] Move pseudo attributes handling --- .../Reflection/Emit/FieldBuilderImpl.cs | 30 ++++------- .../Reflection/Emit/MethodBuilderImpl.cs | 51 +++++++------------ .../Reflection/Emit/ModuleBuilderImpl.cs | 2 +- .../System/Reflection/Emit/TypeBuilderImpl.cs | 33 +++++------- 4 files changed, 42 insertions(+), 74 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index a97a8f8b736d6c..6dfd7e454df03f 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -12,10 +12,10 @@ internal sealed class FieldBuilderImpl : FieldBuilder { private readonly TypeBuilderImpl _typeBuilder; private readonly string _fieldName; - internal int _offset; - private FieldAttributes _attributes; private readonly Type _fieldType; + private FieldAttributes _attributes; + internal int _offset; internal List? _customAttributes; internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes) @@ -30,36 +30,28 @@ internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type ty protected override void SetConstantCore(object? defaultValue) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { - if (!IsPseudoAttribute(con.ReflectedType!.FullName!, binaryAttribute)) - { - _customAttributes ??= new List(); - _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); - } - } - - private bool IsPseudoAttribute(string attributeName, ReadOnlySpan binaryAttribute) - { - switch (attributeName) + // Handle pseudo custom attributes + switch (con.ReflectedType!.FullName) { case "System.Runtime.InteropServices.FieldOffsetAttribute": Debug.Assert(binaryAttribute.Length >= 6); - _offset = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(2)); - break; + _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 - break; + return; case "System.Runtime.CompilerServices.SpecialNameAttribute": _attributes |= FieldAttributes.SpecialName; - break; + return; case "System.Runtime.InteropServices.MarshalAsAttribute": _attributes |= FieldAttributes.HasFieldMarshal; - return false; - default: return false; + break; } - return true; + _customAttributes ??= new List(); + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } protected override void SetOffsetCore(int iOffset) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 7fc970e4b863b0..4f45898d62890d 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -15,13 +15,13 @@ internal sealed class MethodBuilderImpl : MethodBuilder private readonly Type _returnType; private readonly Type[]? _parameterTypes; private readonly ModuleBuilderImpl _module; - private MethodAttributes _attributes; - private MethodImplAttributes _methodImplFlags; private readonly string _name; private readonly CallingConventions _callingConventions; private readonly TypeBuilderImpl _declaringType; - internal DllImportData? _dllImportData; + private MethodAttributes _attributes; + private MethodImplAttributes _methodImplFlags; + internal DllImportData? _dllImportData; internal List? _customAttributes; internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType, @@ -55,44 +55,36 @@ internal BlobBuilder GetMethodSignatureBlob() => protected override ILGenerator GetILGeneratorCore(int size) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { - if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) - { - _customAttributes ??= new List(); - _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); - } - } - - private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, ReadOnlySpan data) - { - switch (attributeName) + // Handle pseudo custom attributes + switch (con.ReflectedType!.FullName) { case "System.Runtime.CompilerServices.MethodImplAttribute": - int implValue = BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(2)); + int implValue = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(2)); _methodImplFlags |= (MethodImplAttributes)implValue; - break; + return; case "System.Runtime.InteropServices.DllImportAttribute": { - _dllImportData = DllImportData.CreateDllImportData(CustomAttributeInfo.DecodeCustomAttribute(con, data), out var preserveSig); + _dllImportData = DllImportData.CreateDllImportData(CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute), out var preserveSig); _attributes |= MethodAttributes.PinvokeImpl; if (preserveSig) { _methodImplFlags |= MethodImplAttributes.PreserveSig; } } - break; + return; case "System.Runtime.InteropServices.PreserveSigAttribute": _methodImplFlags |= MethodImplAttributes.PreserveSig; - break; + return; case "System.Runtime.CompilerServices.SpecialNameAttribute": _attributes |= MethodAttributes.SpecialName; - break; + return; case "System.Security.SuppressUnmanagedCodeSecurityAttribute": _attributes |= MethodAttributes.HasSecurity; - return false; - default: return false; + break; } - return true; + _customAttributes ??= new List(); + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } protected override void SetImplementationFlagsCore(MethodImplAttributes attributes) @@ -163,20 +155,11 @@ internal DllImportData(string moduleName, string? entryPoint, MethodImportAttrib _flags = flags; } - public string ModuleName - { - get { return _moduleName; } - } + public string ModuleName => _moduleName; - public string? EntryPoint - { - get { return _entryPoint; } - } + public string? EntryPoint => _entryPoint; - public MethodImportAttributes Flags - { - get { return _flags; } - } + public MethodImportAttributes Flags => _flags; internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out bool preserveSig) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 5399fbcddeb017..93a24a8ad1700e 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -160,7 +160,7 @@ internal void AppendMetadata() private ModuleReferenceHandle GetModuleReference(string moduleName) { - _moduleReferences ??= new(); + _moduleReferences ??= new Dictionary(); if (!_moduleReferences.TryGetValue(moduleName, out var handle)) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 97b993998b41ab..119a7c73c21ff0 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -15,13 +15,13 @@ internal sealed class TypeBuilderImpl : TypeBuilder 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 _methodDefinitions = new(); internal readonly List _fieldDefinitions = new(); internal List? _customAttributes; @@ -81,40 +81,33 @@ protected override MethodBuilder DefineMethodCore(string name, MethodAttributes protected override bool IsCreatedCore() => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { - if (!IsPseudoCustomAttribute(con.ReflectedType!.FullName!, con, binaryAttribute)) - { - _customAttributes ??= new List(); - _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); - } - } - - private bool IsPseudoCustomAttribute(string attributeName, ConstructorInfo con, ReadOnlySpan data) - { - switch (attributeName) + // Handle pseudo custom attributes + switch (con.ReflectedType!.FullName) { case "System.Runtime.InteropServices.StructLayoutAttribute": - ParseStructLayoutAttribute(con, data); - break; + ParseStructLayoutAttribute(con, binaryAttribute); + return; case "System.Runtime.CompilerServices.SpecialNameAttribute": _attributes |= TypeAttributes.SpecialName; - break; + 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 - break; + return; case "System.Runtime.InteropServices.ComImportAttribute": _attributes |= TypeAttributes.Import; - break; + return; case "System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeImportAttribute": _attributes |= TypeAttributes.WindowsRuntime; - break; + return; case "System.Security.SuppressUnmanagedCodeSecurityAttribute": // It says has no effect in .NET Core, maybe remove? _attributes |= TypeAttributes.HasSecurity; - return false; - default: return false; + break; } - return true; + + _customAttributes ??= new List(); + _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan data) From bffca633f29362fd8d9c8f3c21afcb6a4ef1889c Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 24 Apr 2023 15:39:40 -0700 Subject: [PATCH 10/11] Remove type loading logic from CustomAttributes parsing, use constants for some values --- .../Reflection/Emit/CustomAttributeWrapper.cs | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs index 8372c506260eef..7cd3dc38cd97de 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -28,11 +28,13 @@ internal struct CustomAttributeInfo 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; - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String)'", - Justification = "The 'enumTypeName' only available at runtime")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicFields', 'DynamicallyAccessedMemberTypes.NonPublicFields' in call to 'System.Type.GetField(String, BindingFlags)'", - Justification = "Could not propagate attribute into 'ctor.DeclaringType' only available at runtime")] internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, ReadOnlySpan data) { int pos = 2; @@ -42,7 +44,7 @@ internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, { throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidCustomAttributeLength, ctor.DeclaringType, data.Length)); } - if ((data[0] != 0x1) || (data[1] != 0x00)) + if ((data[0] != 0x01) || (data[1] != 0x00)) { throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidProlog, ctor.DeclaringType)); } @@ -63,12 +65,11 @@ internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, { int namedType = data[pos++]; int dataType = data[pos++]; - string? enumTypeName = null; - if (dataType == 0x55) + if (dataType == EnumType) { + // skip bytes for Enum type name; int len2 = DecodeLen(data, pos, out pos); - enumTypeName = StringFromBytes(data, pos, len2); pos += len2; } @@ -77,23 +78,10 @@ internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, info._namedParamNames[i] = name; pos += len; - if (namedType == 0x53) + if (namedType == Field) { - /* Field */ - FieldInfo? fi = ctor.DeclaringType!.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (fi == null) - { - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_EmptyFieldForCustomAttribute, ctor.DeclaringType, name)); - } - object? val = DecodeCustomAttributeValue(fi.FieldType, data, pos, out pos); - - if (enumTypeName != null) - { - Type enumType = Type.GetType(enumTypeName)!; - val = Enum.ToObject(enumType, val!); - } - - info._namedParamValues[i] = val; + Type fieldType = dataType == EnumType ? typeof(int) : ElementTypeToType((PrimitiveSerializationTypeCode)dataType); + info._namedParamValues[i] = DecodeCustomAttributeValue(fieldType, data, pos, out pos); ; } else { @@ -114,16 +102,16 @@ private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) int len; if ((data[pos] & 0x80) == 0) { - len = (int)(data[pos++] & 0x7f); + len = (data[pos++] & OneByteMask); } else if ((data[pos] & 0x40) == 0) { - len = ((data[pos] & 0x3f) << 8) + data[pos + 1]; + len = ((data[pos] & TwoByteMask) << 8) + data[pos + 1]; pos += 2; } else { - len = ((data[pos] & 0x1f) << 24) + (data[pos + 1] << 16) + (data[pos + 2] << 8) + data[pos + 3]; + len = ((data[pos] & FourByteMask) << 24) + (data[pos + 1] << 16) + (data[pos + 2] << 8) + data[pos + 3]; pos += 4; } rpos = pos; @@ -135,7 +123,7 @@ private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) switch (Type.GetTypeCode(t)) { case TypeCode.String: - if (data[pos] == 0xff) + if (data[pos] == NullValue) { rpos = pos + 1; return null; From 0d56695c67820b62c8558bbb8351b679d1e5b689 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 25 Apr 2023 22:33:27 -0700 Subject: [PATCH 11/11] Add MarshalAsAttribute handling and apply other feedbacks --- .../src/Resources/Strings.resx | 8 +- .../Reflection/Emit/CustomAttributeWrapper.cs | 34 +- .../Reflection/Emit/FieldBuilderImpl.cs | 393 +++++++++++++++++- .../Reflection/Emit/ModuleBuilderImpl.cs | 10 + .../System/Reflection/Emit/TypeBuilderImpl.cs | 6 +- .../AssemblySaveCustomAttributeTests.cs | 75 +++- 6 files changed, 488 insertions(+), 38 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index baaf8219f740f0..dcbd221db545cb 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -141,19 +141,19 @@ The invoked member is not supported in a dynamic module. - + The type code may not be used as a type argument of a custom attribute . Custom attribute '{0}' doesn't contain a field named '{1}'. - + Custom attribute '{0}' data length is only '{1}'. - + Custom attribute '{0}' prolog invalid. - + Custom attribute '{0}' has unknown named type '{1}'. diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs index 7cd3dc38cd97de..2026b79c74e263 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/CustomAttributeWrapper.cs @@ -35,18 +35,18 @@ internal struct CustomAttributeInfo private const int TwoByteMask = 0x3f; private const int FourByteMask = 0x1f; - internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, ReadOnlySpan data) + internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, ReadOnlySpan binaryAttribute) { int pos = 2; CustomAttributeInfo info = default; - if (data.Length < 2) + if (binaryAttribute.Length < 2) { - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidCustomAttributeLength, ctor.DeclaringType, data.Length)); + throw new ArgumentException(SR.Format(SR.Argument_InvalidCustomAttributeLength, ctor.DeclaringType, binaryAttribute.Length), nameof(binaryAttribute)); } - if ((data[0] != 0x01) || (data[1] != 0x00)) + if ((binaryAttribute[0] != 0x01) || (binaryAttribute[1] != 0x00)) { - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_InvalidProlog, ctor.DeclaringType)); + throw new ArgumentException(SR.Format(SR.Argument_InvalidProlog, ctor.DeclaringType), nameof(binaryAttribute)); } ParameterInfo[] pi = ctor.GetParameters(); @@ -54,38 +54,39 @@ internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, info._ctorArgs = new object?[pi.Length]; for (int i = 0; i < pi.Length; ++i) { - info._ctorArgs[i] = DecodeCustomAttributeValue(pi[i].ParameterType, data, pos, out pos); + info._ctorArgs[i] = DecodeCustomAttributeValue(pi[i].ParameterType, binaryAttribute, pos, out pos); } - int numNamed = data[pos] + (data[pos + 1] * 256); + 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 = data[pos++]; - int dataType = data[pos++]; + int namedType = binaryAttribute[pos++]; + int dataType = binaryAttribute[pos++]; if (dataType == EnumType) { // skip bytes for Enum type name; - int len2 = DecodeLen(data, pos, out pos); + int len2 = DecodeLen(binaryAttribute, pos, out pos); pos += len2; } - int len = DecodeLen(data, pos, out pos); - string name = StringFromBytes(data, pos, len); + 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, data, pos, out pos); ; + info._namedParamValues[i] = DecodeCustomAttributeValue(fieldType, binaryAttribute, pos, out pos); ; } else { - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, ctor.DeclaringType, namedType)); + throw new ArgumentException(SR.Format(SR.Argument_UnknownNamedType, ctor.DeclaringType, namedType), nameof(binaryAttribute)); } } @@ -134,6 +135,9 @@ private static int DecodeLen(ReadOnlySpan data, int pos, out int rpos) 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; @@ -167,7 +171,7 @@ private static Type ElementTypeToType(PrimitiveSerializationTypeCode elementType PrimitiveSerializationTypeCode.Single => typeof(float), PrimitiveSerializationTypeCode.Double => typeof(double), PrimitiveSerializationTypeCode.String => typeof(string), - _ => throw new ArgumentException(SR.ArgumentException_InvalidTypeArgument), + _ => throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"), }; } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index 6dfd7e454df03f..3025818ef1a001 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -5,6 +5,10 @@ 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 { @@ -15,6 +19,7 @@ internal sealed class FieldBuilderImpl : FieldBuilder private readonly Type _fieldType; private FieldAttributes _attributes; + internal MarshallingInfo? _marshallingInfo; internal int _offset; internal List? _customAttributes; @@ -34,8 +39,8 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan switch (con.ReflectedType!.FullName) { case "System.Runtime.InteropServices.FieldOffsetAttribute": - Debug.Assert(binaryAttribute.Length >= 6); - _offset = BinaryPrimitives.ReadInt32LittleEndian(binaryAttribute.Slice(2)); + 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'. @@ -47,7 +52,8 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan return; case "System.Runtime.InteropServices.MarshalAsAttribute": _attributes |= FieldAttributes.HasFieldMarshal; - break; + _marshallingInfo = MarshallingInfo.ParseMarshallingInfo(con, binaryAttribute); + return; } _customAttributes ??= new List(); @@ -95,5 +101,386 @@ public override void SetValue(object? obj, object? val, BindingFlags invokeAttr, public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_DynamicModule); #endregion + + internal sealed class MarshallingInfo + { + internal UnmanagedType _marshalType; + private int _marshalArrayElementType; // safe array: VarEnum; array: UnmanagedType + private int _marshalArrayElementCount; // number of elements in an array, length of a string, or Unspecified + private int _marshalParameterIndex; // index of parameter that specifies array size (short) or IID (int), or Unspecified + private object? _marshalTypeNameOrSymbol; // custom marshaller: string or Type; safe array: element type + private string? _marshalCookie; + + internal const int Invalid = -1; + private const UnmanagedType InvalidUnmanagedType = (UnmanagedType)Invalid; + private const VarEnum InvalidVariantType = (VarEnum)Invalid; + internal const int MaxMarshalInteger = 0x1fffffff; + + internal BlobHandle PopulateMarshallingBlob(MetadataBuilder builder) + { + var blobBuilder = new BlobBuilder(); + SerializeMarshallingDescriptor(blobBuilder); + return builder.GetOrAddBlob(blobBuilder); + + } + + // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs#L3543 + internal void SerializeMarshallingDescriptor(BlobBuilder writer) + { + writer.WriteCompressedInteger((int)_marshalType); + switch (_marshalType) + { + case UnmanagedType.ByValArray: // NATIVE_TYPE_FIXEDARRAY + Debug.Assert(_marshalArrayElementCount >= 0); + writer.WriteCompressedInteger(_marshalArrayElementCount); + if (_marshalArrayElementType >= 0) + { + writer.WriteCompressedInteger(_marshalArrayElementType); + } + break; + case UnmanagedType.CustomMarshaler: + writer.WriteUInt16(0); // padding + + switch (_marshalTypeNameOrSymbol) + { + case Type type: + writer.WriteSerializedString(type.FullName); // or AssemblyQualifiedName? + break; + case null: + writer.WriteByte(0); + break; + default: + writer.WriteSerializedString((string)_marshalTypeNameOrSymbol); + break; + } + + if (_marshalCookie != null) + { + writer.WriteSerializedString(_marshalCookie); + } + else + { + writer.WriteByte(0); + } + break; + case UnmanagedType.LPArray: // NATIVE_TYPE_ARRAY + Debug.Assert(_marshalArrayElementType >= 0); + writer.WriteCompressedInteger(_marshalArrayElementType); + if (_marshalParameterIndex >= 0) + { + writer.WriteCompressedInteger(_marshalParameterIndex); + if (_marshalArrayElementCount >= 0) + { + writer.WriteCompressedInteger(_marshalArrayElementCount); + writer.WriteByte(1); // The parameter number is valid + } + } + else if (_marshalArrayElementCount >= 0) + { + writer.WriteByte(0); // Dummy parameter value emitted so that NumberOfElements can be in a known position + writer.WriteCompressedInteger(_marshalArrayElementCount); + writer.WriteByte(0); // The parameter number is not valid + } + break; + case UnmanagedType.SafeArray: + VarEnum safeArrayElementSubtype = (VarEnum)_marshalArrayElementType; + if (safeArrayElementSubtype >= 0) + { + writer.WriteCompressedInteger((int)safeArrayElementSubtype); + + if (_marshalTypeNameOrSymbol is Type elementType) + { + writer.WriteSerializedString(elementType.FullName); + } + } + break; + case UnmanagedType.ByValTStr: // NATIVE_TYPE_FIXEDSYSSTRING + writer.WriteCompressedInteger(_marshalArrayElementCount); + break; + + case UnmanagedType.Interface: + case UnmanagedType.IDispatch: + case UnmanagedType.IUnknown: + if (_marshalParameterIndex >= 0) + { + writer.WriteCompressedInteger(_marshalParameterIndex); + } + break; + } + } + + internal void SetMarshalAsCustom(object typeSymbolOrName, string? cookie) + { + _marshalType = UnmanagedType.CustomMarshaler; + _marshalTypeNameOrSymbol = typeSymbolOrName; + _marshalCookie = cookie; + } + + internal void SetMarshalAsComInterface(UnmanagedType unmanagedType, int? parameterIndex) + { + Debug.Assert(parameterIndex == null || parameterIndex >= 0 && parameterIndex <= MaxMarshalInteger); + + _marshalType = unmanagedType; + _marshalParameterIndex = parameterIndex ?? Invalid; + } + + internal void SetMarshalAsArray(UnmanagedType? elementType, int? elementCount, short? parameterIndex) + { + Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger); + Debug.Assert(parameterIndex == null || parameterIndex >= 0); + + _marshalType = UnmanagedType.LPArray; + _marshalArrayElementType = (int)(elementType ?? (UnmanagedType)0x50); + _marshalArrayElementCount = elementCount ?? Invalid; + _marshalParameterIndex = parameterIndex ?? Invalid; + } + + internal void SetMarshalAsFixedArray(UnmanagedType? elementType, int? elementCount) + { + Debug.Assert(elementCount == null || elementCount >= 0 && elementCount <= MaxMarshalInteger); + Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger); + + _marshalType = UnmanagedType.ByValArray; + _marshalArrayElementType = (int)(elementType ?? InvalidUnmanagedType); + _marshalArrayElementCount = elementCount ?? Invalid; + } + + internal void SetMarshalAsSafeArray(VarEnum? elementType, Type? type) + { + Debug.Assert(elementType == null || elementType >= 0 && (int)elementType <= MaxMarshalInteger); + + _marshalType = UnmanagedType.SafeArray; + _marshalArrayElementType = (int)(elementType ?? InvalidVariantType); + _marshalTypeNameOrSymbol = type; + } + + internal void SetMarshalAsFixedString(int elementCount) + { + Debug.Assert(elementCount >= 0 && elementCount <= MaxMarshalInteger); + + _marshalType = UnmanagedType.ByValTStr; + _marshalArrayElementCount = elementCount; + } + + internal void SetMarshalAsSimpleType(UnmanagedType type) + { + Debug.Assert(type >= 0 && (int)type <= MaxMarshalInteger); + _marshalType = type; + } + + // The logic imported from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/Symbols/Attributes/MarshalAsAttributeDecoder.cs + internal static MarshallingInfo ParseMarshallingInfo(ConstructorInfo con, ReadOnlySpan binaryAttribute) + { + CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute); + MarshallingInfo info = new(); + UnmanagedType unmanagedType; + + if (attributeInfo._ctorArgs[0] is short shortValue) + { + unmanagedType = (UnmanagedType)shortValue; + } + else + { + unmanagedType = (UnmanagedType)attributeInfo._ctorArgs[0]!; + } + + switch (unmanagedType) + { + case UnmanagedType.CustomMarshaler: + DecodeMarshalAsCustom(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); + break; + case UnmanagedType.Interface: + case UnmanagedType.IDispatch: + case UnmanagedType.IUnknown: + DecodeMarshalAsComInterface(attributeInfo._namedParamNames, attributeInfo._namedParamValues, unmanagedType, info); + break; + case UnmanagedType.LPArray: + DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: false, info); + break; + case UnmanagedType.ByValArray: + DecodeMarshalAsArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, isFixed: true, info); + break; + case UnmanagedType.SafeArray: + DecodeMarshalAsSafeArray(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); + break; + case UnmanagedType.ByValTStr: + DecodeMarshalAsFixedString(attributeInfo._namedParamNames, attributeInfo._namedParamValues, info); + break; +#pragma warning disable CS0618 // Type or member is obsolete + case UnmanagedType.VBByRefStr: +#pragma warning restore CS0618 + // named parameters ignored with no error + info.SetMarshalAsSimpleType(unmanagedType); + break; + default: + if ((int)unmanagedType < 0 || (int)unmanagedType > MaxMarshalInteger) + { + throw new ArgumentException(SR.Argument_InvalidTypeArgument, nameof(binaryAttribute)); + } + else + { + // named parameters ignored with no error + info.SetMarshalAsSimpleType(unmanagedType); + } + break; + } + + return info; + } + + private static void DecodeMarshalAsFixedString(string[] paramNames, object?[] values, MarshallingInfo info) + { + int elementCount = -1; + + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "SizeConst": + elementCount = (int)values[i]!; + break; + case "ArraySubType": + case "SizeParamIndex": + throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); + // other parameters ignored with no error + } + } + + if (elementCount < 0) + { + // SizeConst must be specified: + throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); + } + + info.SetMarshalAsFixedString(elementCount); + } + + private static void DecodeMarshalAsSafeArray(string[] paramNames, object?[] values, MarshallingInfo info) + { + VarEnum? elementTypeVariant = null; + Type? elementType = null; + int symbolIndex = -1; + + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "SafeArraySubType": + elementTypeVariant = (VarEnum)values[i]!; + break; + case "SafeArrayUserDefinedSubType": + elementType = (Type?)values[i]; + symbolIndex = i; + break; + case "ArraySubType": + case "SizeConst": + case "SizeParamIndex": + throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); + // other parameters ignored with no error + } + } + + switch (elementTypeVariant) + { + case VarEnum.VT_DISPATCH: + case VarEnum.VT_UNKNOWN: + case VarEnum.VT_RECORD: + // only these variants accept specification of user defined subtype + break; + + default: + if (elementTypeVariant != null && symbolIndex >= 0) + { + throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); + } + else + { + // type ignored: + elementType = null; + } + break; + } + + info.SetMarshalAsSafeArray(elementTypeVariant, elementType); + } + + private static void DecodeMarshalAsArray(string[] paramNames, object?[] values, bool isFixed, MarshallingInfo info) + { + UnmanagedType? elementType = null; + int? elementCount = isFixed ? 1 : null; + short? parameterIndex = null; + + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "ArraySubType": + elementType = (UnmanagedType)values[i]!; + break; + case "SizeConst": + elementCount = (int?)values[i]; + break; + case "SizeParamIndex": + if (isFixed) + { + goto case "SafeArraySubType"; + } + parameterIndex = (short?)values[i]; + break; + case "SafeArraySubType": + throw new ArgumentException(SR.Argument_InvalidTypeArgument, "binaryAttribute"); + // other parameters ignored with no error + } + } + + if (isFixed) + { + info.SetMarshalAsFixedArray(elementType, elementCount); + } + else + { + info.SetMarshalAsArray(elementType, elementCount, parameterIndex); + } + } + + private static void DecodeMarshalAsComInterface(string[] paramNames, object?[] values, UnmanagedType unmanagedType, MarshallingInfo info) + { + int? parameterIndex = null; + for (int i = 0; i < paramNames.Length; i++) + { + if (paramNames[i] == "IidParameterIndex") + { + parameterIndex = (int?)values[i]; + break; + } + } + info.SetMarshalAsComInterface(unmanagedType, parameterIndex); + } + + private static void DecodeMarshalAsCustom(string[] paramNames, object?[] values, MarshallingInfo info) + { + string? cookie = null; + Type? type = null; + string? name = null; + for (int i = 0; i < paramNames.Length; i++) + { + switch (paramNames[i]) + { + case "MarshalType": + name = (string?)values[i]; + break; + case "MarshalTypeRef": + type = (Type?)values[i]; + break; + case "MarshalCookie": + cookie = (string?)values[i]; + break; + // other parameters ignored with no error + } + } + + info.SetMarshalAsCustom((object?)name ?? type!, cookie); + } + } } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 93a24a8ad1700e..775a5f647a5328 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -154,6 +154,11 @@ internal void AppendMetadata() { AddFieldLayout(fieldHandle, field._offset); } + + if (field._marshallingInfo != null) + { + AddFieldMarshalling(fieldHandle, field._marshallingInfo.PopulateMarshallingBlob(_metadataBuilder)); + } } } } @@ -271,6 +276,11 @@ private ModuleReferenceHandle AddModuleReference(string moduleName) => private void AddFieldLayout(FieldDefinitionHandle fieldHandle, int offset) => _metadataBuilder.AddFieldLayout(field: fieldHandle, offset: offset); + private void AddFieldMarshalling(FieldDefinitionHandle fieldHandle, BlobHandle descriptor) + { + _metadataBuilder.AddMarshallingDescriptor(fieldHandle, descriptor); + } + private AssemblyReferenceHandle AddAssemblyReference(string name, Version? version, string? culture, byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) => _metadataBuilder.AddAssemblyReference( diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 119a7c73c21ff0..d2f0b5aaf1651d 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -110,9 +110,9 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } - private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan data) + private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan binaryAttribute) { - CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, data); + CustomAttributeInfo attributeInfo = CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute); LayoutKind layoutKind = (LayoutKind)attributeInfo._ctorArgs[0]!; _attributes &= ~TypeAttributes.LayoutMask; _attributes |= layoutKind switch @@ -154,7 +154,7 @@ private void ParseStructLayoutAttribute(ConstructorInfo con, ReadOnlySpan _typeSize = value; break; default: - throw new InvalidOperationException(SR.Format(SR.InvalidOperation_UnknownNamedType, con.DeclaringType, name)); + throw new ArgumentException(SR.Format(SR.Argument_UnknownNamedType, con.DeclaringType, name), nameof(binaryAttribute)); } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs index 0dfba3182e5b41..0f1d3c929b3e36 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -176,23 +176,20 @@ public void CreateStructWithPseudoCustomAttributesTest() 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), - // TODO: Need to support the UnmanagedType type - //new CustomAttributeBuilder(typeof(MarshalAsAttribute).GetConstructor(new Type[] { typeof(UnmanagedType) }), new object[] { UnmanagedType.I4}), + 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); + 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 attributesFromDisk = testType.GetCustomAttributesData(); Assert.Equal(typeAttributes.Count - 3, attributesFromDisk.Count); // 3 pseudo attributes @@ -216,11 +213,11 @@ public void CreateStructWithPseudoCustomAttributesTest() FieldInfo field = testType.GetFields()[0]; IList fieldAttributesFromDisk = field.GetCustomAttributesData(); - Assert.Equal(2, fieldAttributesFromDisk.Count); + 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 + Assert.True((field.Attributes & FieldAttributes.HasFieldMarshal) != 0); // MarshalAsAttribute for (int i = 0; i < fieldAttributesFromDisk.Count; i++) { @@ -229,9 +226,9 @@ public void CreateStructWithPseudoCustomAttributesTest() case "FieldOffsetAttribute": Assert.Equal(2, fieldAttributesFromDisk[i].ConstructorArguments[0].Value); break; - /*case "MarshalAsAttribute": // TODO: Need to support the UnmanagedType type - Assert.Equal(UnmanagedType.I4, methodAttributesFromDisk[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; @@ -270,9 +267,8 @@ public void InterfacesWithPseudoCustomAttributes() 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 }); - Console.WriteLine(file.Path); + Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; IList attributesFromDisk = testType.GetCustomAttributesData(); @@ -306,7 +302,7 @@ public void InterfacesWithPseudoCustomAttributes() Assert.True((methodImpl & MethodImplAttributes.NoInlining) != 0); // MethodImplAttribute Assert.True((methodImpl & MethodImplAttributes.AggressiveOptimization) != 0); // MethodImplAttribute Assert.True((methodImpl & MethodImplAttributes.PreserveSig) != 0); // PreserveSigAttribute - Assert.Equal(methodAttributes.Length-2, methodAttributesFromDisk.Count); + Assert.Equal(methodAttributes.Length - 2, methodAttributesFromDisk.Count); for (int i = 0; i < methodAttributesFromDisk.Count; i++) { @@ -354,5 +350,58 @@ public void InterfacesWithPseudoCustomAttributes() } } } + + 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 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; + } + } + } } }