From 15aa7dae55bb8b45b317989d0d99a9a0a1bd5b00 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sun, 30 Apr 2023 22:04:38 -0700 Subject: [PATCH 1/3] Save interfaces implemented and nested type --- .../Reflection/Emit/ModuleBuilderImpl.cs | 32 +++++++++++++++-- .../System/Reflection/Emit/TypeBuilderImpl.cs | 31 ++++++++++++++-- .../AssemblySaveWithVariousMembersTests.cs | 35 +++++++++++++++++-- 3 files changed, 91 insertions(+), 7 deletions(-) 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 f18aace4f14afe..303db9d126e4a3 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 @@ -132,6 +132,25 @@ internal void AppendMetadata() Debug.Assert(typeBuilder._handle.Equals(typeDefinitionHandle)); WriteCustomAttributes(typeBuilder._customAttributes, typeDefinitionHandle); + if (typeBuilder.Attributes.HasFlag(TypeAttributes.ExplicitLayout)) + { + _metadataBuilder.AddTypeLayout(typeDefinitionHandle, (ushort)typeBuilder.PackingSize, (uint)typeBuilder.Size); + } + + if (typeBuilder._interfaces != null) + { + foreach(Type iface in typeBuilder._interfaces) + { + _metadataBuilder.AddInterfaceImplementation(typeDefinitionHandle, GetTypeHandle(iface)); + // TODO: need to add interface mapping between interface method and implemented method + } + } + + if (typeBuilder.DeclaringType != null) + { + _metadataBuilder.AddNestedType(typeDefinitionHandle, (TypeDefinitionHandle)GetTypeHandle(typeBuilder.DeclaringType)); + } + foreach (MethodBuilderImpl method in typeBuilder._methodDefinitions) { MethodDefinitionHandle methodHandle = AddMethodDefinition(method, method.GetMethodSignatureBlob(), _nextParameterRowId); @@ -331,6 +350,14 @@ internal EntityHandle GetTypeHandle(Type type) return GetTypeReference(type); } + internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, + Type[]? interfaces, PackingSize packingSize, int typesize, TypeBuilderImpl? enclosingType) + { + TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId); + TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this, typeHandle, interfaces, packingSize, typesize, enclosingType); + _typeDefinitions.Add(_type); + return _type; + } [RequiresAssemblyFiles("Returns for modules with no file path")] public override string Name => ""; @@ -347,10 +374,11 @@ internal EntityHandle GetTypeHandle(Type type) protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) => throw new NotImplementedException(); [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); - protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize) + protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize) { TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId); - TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this, typeHandle, packingSize, typesize); + TypeBuilderImpl _type = new TypeBuilderImpl(name, attr, parent, this, typeHandle, interfaces, packingSize, typesize, null); _typeDefinitions.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 d2f0b5aaf1651d..26108beb752ac6 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 @@ -17,6 +17,7 @@ internal sealed class TypeBuilderImpl : TypeBuilder private readonly string? _namespace; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private Type? _typeParent; + private readonly TypeBuilderImpl? _declaringType; private TypeAttributes _attributes; private PackingSize _packingSize; private int _typeSize; @@ -24,11 +25,12 @@ internal sealed class TypeBuilderImpl : TypeBuilder internal readonly TypeDefinitionHandle _handle; internal readonly List _methodDefinitions = new(); internal readonly List _fieldDefinitions = new(); + internal List? _interfaces; internal List? _customAttributes; internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module, - TypeDefinitionHandle handle, PackingSize packingSize, int typeSize) + TypeDefinitionHandle handle, Type[]? interfaces, PackingSize packingSize, int typeSize, TypeBuilderImpl? enclosingType) { _name = fullName; _module = module; @@ -37,6 +39,7 @@ internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, _typeSize = typeSize; SetParent(parent); _handle = handle; + _declaringType = enclosingType; // Extract namespace from fullName int idx = _name.LastIndexOf('.'); @@ -45,12 +48,27 @@ internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, _namespace = _name[..idx]; _name = _name[(idx + 1)..]; } + + if (interfaces != null) + { + _interfaces = new List(); + for (int i = 0; i < interfaces.Length; i++) + { + // cannot contain null in the interface list + ArgumentNullException.ThrowIfNull(interfaces[i], nameof(interfaces)); + _interfaces.Add(interfaces[i]); + } + } } internal ModuleBuilderImpl GetModuleBuilder() => _module; protected override PackingSize PackingSizeCore => _packingSize; protected override int SizeCore => _typeSize; - protected override void AddInterfaceImplementationCore([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type interfaceType) => throw new NotImplementedException(); + protected override void AddInterfaceImplementationCore([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes.All))] Type interfaceType) + { + _interfaces ??= new List(); + _interfaces.Add(interfaceType); + } [return: DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] protected override TypeInfo CreateTypeInfoCore() => throw new NotImplementedException(); protected override ConstructorBuilder DefineConstructorCore(MethodAttributes attributes, CallingConventions callingConvention, Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers) => throw new NotImplementedException(); @@ -72,7 +90,12 @@ protected override MethodBuilder DefineMethodCore(string name, MethodAttributes } protected override void DefineMethodOverrideCore(MethodInfo methodInfoBody, MethodInfo methodInfoDeclaration) => throw new NotImplementedException(); - protected override TypeBuilder DefineNestedTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type? parent, Type[]? interfaces, Emit.PackingSize packSize, int typeSize) => throw new NotImplementedException(); + protected override TypeBuilder DefineNestedTypeCore(string name, TypeAttributes attr, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, Type[]? interfaces, PackingSize packSize, int typeSize) + { + return _module.DefineNestedType(name, attr, parent, interfaces, packSize, typeSize, this); + } + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); protected override PropertyBuilder DefinePropertyCore(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) => throw new NotImplementedException(); @@ -191,6 +214,8 @@ protected override void SetParentCore([DynamicallyAccessedMembers(DynamicallyAcc } } public override string Name => _name; + public override Type? DeclaringType => _declaringType; + public override Type? ReflectedType => _declaringType; public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); public override object[] GetCustomAttributes(bool inherit) => throw new NotImplementedException(); public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotImplementedException(); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs index df89812f1c62db..2f4ead727bd50e 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.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; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -122,6 +123,36 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest() Assert.Equal("System.Void", method.ReturnType.FullName); } } + + [Fact] + public void AddInterfaceImplementationTest() + { + using (TempFile file = TempFile.Create()) + { + 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, null, new Type[] { typeof(IOneMethod)}); + tb.AddInterfaceImplementation(typeof(INoMethod)); + tb.DefineNestedType("NestedType", TypeAttributes.Interface | TypeAttributes.Abstract); + saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + + Assembly assemblyFromDisk = AssemblyTools.LoadAssemblyFromPath(file.Path); + Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; + Type[] interfaces = testType.GetInterfaces(); + + Assert.Equal("TestInterface", testType.Name); + Assert.Equal(2, interfaces.Length); + + Type iOneMethod = testType.GetInterface("IOneMethod"); + Type iNoMethod = testType.GetInterface("INoMethod"); + Type[] nt = testType.GetNestedTypes(); + Assert.Equal(1, iOneMethod.GetMethods().Length); + Assert.Empty(iNoMethod.GetMethods()); + Assert.NotNull(testType.GetNestedType("NestedType", BindingFlags.NonPublic)); + } + } } // Test Types @@ -129,7 +160,7 @@ public interface INoMethod { } - public interface IMultipleMethod + public interface IMultipleMethod : IEnumerable { string Func(int a, string b); IOneMethod MoreFunc(); @@ -137,7 +168,7 @@ public interface IMultipleMethod void BuildAPerpetualMotionMachine(); } - internal interface IAccess + internal interface IAccess : IOneMethod { public Version BuildAI(double field); public int DisableRogueAI(); From 4b10300666452d089290fb335d15a6906bdcf12d Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 1 May 2023 09:20:22 -0700 Subject: [PATCH 2/3] Apply suggestions from code review --- .../AssemblySaveWithVariousMembersTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs index 2f4ead727bd50e..7307ebd0cdf225 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs @@ -160,7 +160,7 @@ public interface INoMethod { } - public interface IMultipleMethod : IEnumerable + public interface IMultipleMethod { string Func(int a, string b); IOneMethod MoreFunc(); @@ -168,7 +168,7 @@ public interface IMultipleMethod : IEnumerable void BuildAPerpetualMotionMachine(); } - internal interface IAccess : IOneMethod + internal interface IAccess { public Version BuildAI(double field); public int DisableRogueAI(); From ddc8dec789dae800186860e1166b6ecf2fa3100c Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 1 May 2023 10:11:55 -0700 Subject: [PATCH 3/3] Apply feedback --- .../src/System/Reflection/Emit/ModuleBuilderImpl.cs | 2 +- .../src/System/Reflection/Emit/TypeBuilderImpl.cs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) 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 303db9d126e4a3..8d2600d84542e2 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 @@ -132,7 +132,7 @@ internal void AppendMetadata() Debug.Assert(typeBuilder._handle.Equals(typeDefinitionHandle)); WriteCustomAttributes(typeBuilder._customAttributes, typeDefinitionHandle); - if (typeBuilder.Attributes.HasFlag(TypeAttributes.ExplicitLayout)) + if ((typeBuilder.Attributes & TypeAttributes.ExplicitLayout) != 0) { _metadataBuilder.AddTypeLayout(typeDefinitionHandle, (ushort)typeBuilder.PackingSize, (uint)typeBuilder.Size); } 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 26108beb752ac6..4e5fbaf7125828 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 @@ -54,9 +54,10 @@ internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, _interfaces = new List(); for (int i = 0; i < interfaces.Length; i++) { + Type @interface = interfaces[i]; // cannot contain null in the interface list - ArgumentNullException.ThrowIfNull(interfaces[i], nameof(interfaces)); - _interfaces.Add(interfaces[i]); + ArgumentNullException.ThrowIfNull(@interface, nameof(interfaces)); + _interfaces.Add(@interface); } } } @@ -270,7 +271,8 @@ protected override bool IsCOMObjectImpl() public override Type? GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(); [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] - public override Type[] GetInterfaces() => throw new NotSupportedException(); + public override Type[] GetInterfaces() => _interfaces == null ? EmptyTypes : _interfaces.ToArray(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(); [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]