Save interfaces implementations and nested types (#85596)
authorBuyaa Namnan <bunamnan@microsoft.com>
Mon, 1 May 2023 20:52:13 +0000 (13:52 -0700)
committerGitHub <noreply@github.com>
Mon, 1 May 2023 20:52:13 +0000 (13:52 -0700)
* Save interfaces implemented and nested type

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs
src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs
src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveWithVariousMembersTests.cs

index f18aace..8d2600d 100644 (file)
@@ -132,6 +132,25 @@ namespace System.Reflection.Emit
                 Debug.Assert(typeBuilder._handle.Equals(typeDefinitionHandle));
                 WriteCustomAttributes(typeBuilder._customAttributes, typeDefinitionHandle);
 
+                if ((typeBuilder.Attributes & TypeAttributes.ExplicitLayout) != 0)
+                {
+                    _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 @@ namespace System.Reflection.Emit
 
             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 <Unknown> for modules with no file path")]
         public override string Name => "<In Memory Module>";
@@ -347,10 +374,11 @@ namespace System.Reflection.Emit
         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;
         }
index d2f0b5a..4e5fbaf 100644 (file)
@@ -17,6 +17,7 @@ namespace System.Reflection.Emit
         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 @@ namespace System.Reflection.Emit
         internal readonly TypeDefinitionHandle _handle;
         internal readonly List<MethodBuilderImpl> _methodDefinitions = new();
         internal readonly List<FieldBuilderImpl> _fieldDefinitions = new();
+        internal List<Type>? _interfaces;
         internal List<CustomAttributeWrapper>? _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 @@ namespace System.Reflection.Emit
             _typeSize = typeSize;
             SetParent(parent);
             _handle = handle;
+            _declaringType = enclosingType;
 
             // Extract namespace from fullName
             int idx = _name.LastIndexOf('.');
@@ -45,12 +48,28 @@ namespace System.Reflection.Emit
                 _namespace = _name[..idx];
                 _name = _name[(idx + 1)..];
             }
+
+            if (interfaces != null)
+            {
+                _interfaces = new List<Type>();
+                for (int i = 0; i < interfaces.Length; i++)
+                {
+                    Type @interface = interfaces[i];
+                    // cannot contain null in the interface list
+                    ArgumentNullException.ThrowIfNull(@interface, nameof(interfaces));
+                    _interfaces.Add(@interface);
+                }
+            }
         }
 
         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<Type>();
+            _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 +91,12 @@ namespace System.Reflection.Emit
         }
 
         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 +215,8 @@ namespace System.Reflection.Emit
             }
         }
         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();
@@ -245,7 +271,8 @@ namespace System.Reflection.Emit
         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)]
index df89812..7307ebd 100644 (file)
@@ -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 @@ namespace System.Reflection.Emit.Tests
                 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