diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 931354e6fe184..c916e0ad2cc95 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -4,6 +4,7 @@ c5ed3c1d-b572-46f1-8f96-522a85ce1179 System.Private.CoreLib.Strings true + true diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorOnTypeBuilderInstantiation.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorOnTypeBuilderInstantiation.cs index cc697cdc10a56..c4e80153cf1a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorOnTypeBuilderInstantiation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorOnTypeBuilderInstantiation.cs @@ -42,22 +42,7 @@ internal override Type[] GetParameterTypes() public override object[] GetCustomAttributes(bool inherit) { return _ctor.GetCustomAttributes(inherit); } public override object[] GetCustomAttributes(Type attributeType, bool inherit) { return _ctor.GetCustomAttributes(attributeType, inherit); } public override bool IsDefined(Type attributeType, bool inherit) { return _ctor.IsDefined(attributeType, inherit); } - public override int MetadataToken - { - get - { - ConstructorBuilder? cb = _ctor as ConstructorBuilder; - - if (cb != null) - { - return cb.MetadataToken; - } - else - { - return _ctor.MetadataToken; - } - } - } + public override int MetadataToken => _ctor.MetadataToken; public override Module Module => _ctor.Module; #endregion diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldOnTypeBuilderInstantiation.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldOnTypeBuilderInstantiation.cs index 1a9d8cc992169..006795ce3692e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldOnTypeBuilderInstantiation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/FieldOnTypeBuilderInstantiation.cs @@ -49,22 +49,7 @@ internal FieldOnTypeBuilderInstantiation(FieldInfo field, TypeBuilderInstantiati public override object[] GetCustomAttributes(bool inherit) { return _field.GetCustomAttributes(inherit); } public override object[] GetCustomAttributes(Type attributeType, bool inherit) { return _field.GetCustomAttributes(attributeType, inherit); } public override bool IsDefined(Type attributeType, bool inherit) { return _field.IsDefined(attributeType, inherit); } - public override int MetadataToken - { - get - { - FieldBuilder? fb = _field as FieldBuilder; - - if (fb != null) - { - return fb.MetadataToken; - } - else - { - return _field.MetadataToken; - } - } - } + public override int MetadataToken => _field.MetadataToken; public override Module Module => _field.Module; #endregion diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs index eec5d2eb32550..6c0867dbe3158 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs @@ -32,10 +32,12 @@ internal MethodBuilderInstantiation(MethodInfo method, Type[] inst) } #endregion +#if SYSTEM_PRIVATE_CORELIB internal override Type[] GetParameterTypes() { return _method.GetParameterTypes(); } +#endif #region MemberBase public override MemberTypes MemberType => _method.MemberType; @@ -82,6 +84,7 @@ public override bool ContainsGenericParameters } } + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] [RequiresUnreferencedCode("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 MethodInfo MakeGenericMethod(params Type[] arguments) { diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index c0f82bed38998..6572d94d71d67 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -225,4 +225,7 @@ Should not specify exception type for catch clause for filter block. + + {0} is not a GenericMethodDefinition. MakeGenericMethod may only be called on a method for which MethodBase.IsGenericMethodDefinition is true. + \ No newline at end of file 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 8580d8bacb8a6..ede82acce0a97 100644 --- a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj +++ b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj @@ -8,6 +8,7 @@ + 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 0a06117c21e91..510cb8e680119 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 @@ -293,7 +293,7 @@ public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? bind [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] [RequiresUnreferencedCode("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 MethodInfo MakeGenericMethod(params Type[] typeArguments) - => throw new NotImplementedException(); + public override MethodInfo MakeGenericMethod(params Type[] typeArguments) => + MethodBuilderInstantiation.MakeGenericMethod(this, typeArguments); } } 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 bcc533954e926..68d839986bd32 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 @@ -18,7 +18,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly AssemblyBuilderImpl _assemblyBuilder; private readonly Dictionary _assemblyReferences = new(); private readonly Dictionary _typeReferences = new(); - private readonly Dictionary _memberReferences = new(); + private readonly Dictionary _memberReferences = new(); private readonly List _typeDefinitions = new(); private readonly Dictionary _ctorReferences = new(); private Dictionary? _moduleReferences; @@ -372,13 +372,26 @@ private EntityHandle GetTypeReferenceOrSpecificationHandle(Type type) private TypeSpecificationHandle AddTypeSpecification(Type type) => _metadataBuilder.AddTypeSpecification( - signature: _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetTypeSignature(type, this))); + signature: _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetTypeSpecificationSignature(type, this))); - private MemberReferenceHandle GetMemberReference(MemberInfo member) + private MethodSpecificationHandle AddMethodSpecification(EntityHandle methodHandle, Type[] genericArgs) => + _metadataBuilder.AddMethodSpecification( + method: methodHandle, + instantiation: _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetMethodSpecificationSignature(genericArgs, this))); + + private EntityHandle GetMemberReference(MemberInfo member) { if (!_memberReferences.TryGetValue(member, out var memberHandle)) { - memberHandle = AddMemberReference(member.Name, GetTypeReferenceOrSpecificationHandle(member.DeclaringType!), GetMemberSignature(member)); + if (member is MethodInfo method && method.IsConstructedGenericMethod) + { + memberHandle = AddMethodSpecification(GetMemberReference(method.GetGenericMethodDefinition()), method.GetGenericArguments()); + } + else + { + memberHandle = AddMemberReference(member.Name, GetTypeHandle(member.DeclaringType!), GetMemberSignature(member)); + } + _memberReferences.Add(member, memberHandle); } 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 4d0cea3ecac7a..c84052be93741 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 @@ -56,12 +56,25 @@ internal static BlobBuilder ConstructorSignatureEncoder(ParameterInfo[]? paramet return constructorSignature; } - internal static BlobBuilder GetTypeSignature(Type type, ModuleBuilderImpl module) + internal static BlobBuilder GetTypeSpecificationSignature(Type type, ModuleBuilderImpl module) { - BlobBuilder typeSignature = new(); - WriteSignatureForType(new BlobEncoder(typeSignature).TypeSpecificationSignature(), type, module); + BlobBuilder typeSpecSignature = new(); + WriteSignatureForType(new BlobEncoder(typeSpecSignature).TypeSpecificationSignature(), type, module); - return typeSignature; + return typeSpecSignature; + } + + internal static BlobBuilder GetMethodSpecificationSignature(Type[] genericArguments, ModuleBuilderImpl module) + { + BlobBuilder methodSpecSignature = new(); + GenericTypeArgumentsEncoder encoder = new BlobEncoder(methodSpecSignature).MethodSpecificationSignature(genericArguments.Length); + + foreach (Type argument in genericArguments) + { + WriteSignatureForType(encoder.AddArgument(), argument, module); + } + + return methodSpecSignature; } internal static BlobBuilder MethodSignatureEncoder(ModuleBuilderImpl module, Type[]? parameters, diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs index fccd631f7e311..a5d646811c76d 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -653,9 +653,7 @@ void Main(int a) } } - - - /*[Fact] // TODO: Resolve MethodBuilderInstantiation access + [Fact] public void ReferenceConstructedGenericMethod() { using (TempFile file = TempFile.Create()) @@ -672,7 +670,6 @@ public void ReferenceConstructedGenericMethod() ilg.Emit(OpCodes.Ret); MethodBuilder mainMethod = type.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static); ilg = mainMethod.GetILGenerator(); - //MethodInfo SampleOfGM = TypeBuilder.GetMethod(type, genericMethod); MethodInfo GMOfString = genericMethod.MakeGenericMethod(typeof(string)); ilg.Emit(OpCodes.Ldstr, "Hello, world!"); ilg.EmitCall(OpCodes.Call, GMOfString, null); @@ -681,11 +678,16 @@ public void ReferenceConstructedGenericMethod() saveMethod.Invoke(ab, new[] { file.Path }); Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type myTypeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - Assert.True(myTypeFromDisk.IsGenericType); - Assert.True(myTypeFromDisk.IsGenericTypeDefinition); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodInfo genericMethodFromDisk = typeFromDisk.GetMethod("GM"); + Assert.True(genericMethodFromDisk.IsGenericMethod); + Assert.True(genericMethodFromDisk.IsGenericMethodDefinition); + byte[] ilBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, ilBytes[0]); + Assert.Equal(OpCodes.Call.Value, ilBytes[5]); + Assert.Equal(OpCodes.Ret.Value, ilBytes[10]); } - }*/ + } [Fact] public void ReferenceConstructedGenericMethodFieldOfConstructedType() @@ -727,7 +729,7 @@ public void ReferenceConstructedGenericMethodFieldOfConstructedType() ilg.EmitCall(OpCodes.Call, GMOfString, null); ilg.Emit(OpCodes.Ret); dummy.CreateType(); -/* TODO: verify 'MyType.GM("HelloWorld");' emitted correctly after 'MethodBuilderImpl.GetParameters()' implemented +/* Generated IL would like this in C#: public class MyType { public T Field; @@ -744,15 +746,37 @@ internal class Dummy { public static void Main() { - MyType.GM("HelloWorld"); + MyType.GM("HelloWorld"); } } */ saveMethod.Invoke(ab, new[] { file.Path }); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type myTypeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + Module module = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First(); + Type myTypeFromDisk = module.GetType("MyType"); Assert.True(myTypeFromDisk.IsGenericType); Assert.True(myTypeFromDisk.IsGenericTypeDefinition); + Assert.Equal("T", myTypeFromDisk.GetGenericArguments()[0].Name); + Assert.Equal("T", myTypeFromDisk.GetField("Field").FieldType.Name); + MethodInfo genericMethodFromDisk = myTypeFromDisk.GetMethod("GM"); + Assert.True(genericMethodFromDisk.IsGenericMethod); + Assert.True(genericMethodFromDisk.IsGenericMethodDefinition); + Assert.Equal(1, genericMethodFromDisk.GetMethodBody().LocalVariables.Count); + Assert.Equal("MyType[U]", genericMethodFromDisk.GetMethodBody().LocalVariables[0].LocalType.ToString()); + byte[] gmIlBytes = genericMethodFromDisk.GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Newobj.Value, gmIlBytes[0]); + Assert.Equal(OpCodes.Stloc_0.Value, gmIlBytes[5]); + Assert.Equal(OpCodes.Ldloc_0.Value, gmIlBytes[6]); + Assert.Equal(OpCodes.Ldarg_0.Value, gmIlBytes[7]); + Assert.Equal(OpCodes.Stfld.Value, gmIlBytes[8]); + Assert.Equal(OpCodes.Ldloc_0.Value, gmIlBytes[13]); + Assert.Equal(OpCodes.Ldfld.Value, gmIlBytes[14]); + Assert.Equal(OpCodes.Box.Value, gmIlBytes[19]); + Assert.Equal(OpCodes.Call.Value, gmIlBytes[24]); + Assert.Equal(OpCodes.Ret.Value, gmIlBytes[29]); + byte[] ilBytes = module.GetType("Dummy").GetMethod("Main").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, ilBytes[0]); + Assert.Equal(OpCodes.Call.Value, ilBytes[5]); + Assert.Equal(OpCodes.Ret.Value, ilBytes[10]); } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs index 65c9f005b754a..6f92cc3a6e855 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs @@ -137,8 +137,6 @@ internal static void AssertMethods(MethodInfo[] sourceMethods, MethodInfo[] meth { MethodInfo sourceMethod = sourceMethods[j]; MethodInfo methodFromDisk = methodsFromDisk[j]; - if (methodFromDisk.Name == ".ctor") - continue; Assert.Equal(sourceMethod.Name, methodFromDisk.Name); Assert.Equal(sourceMethod.Attributes, methodFromDisk.Attributes);