From f5d66386bb859dc7cde0e5e610e8becf5a0112f0 Mon Sep 17 00:00:00 2001 From: Jonas <35030845+jgh07@users.noreply.github.com> Date: Tue, 21 Oct 2025 21:41:56 +0200 Subject: [PATCH 1/3] Support function pointer types in ``System.Reflection.Emit`` (#119935) This fixes #111003. The current implementation is a **work in progress**, only **managed** function pointers with no modopts are supported. --------- Co-authored-by: Jan Kotas --- .../System/Reflection/Emit/SignatureHelper.cs | 64 +++++++ .../AssemblySaveTypeBuilderTests.cs | 160 ++++++++++++++++++ .../System.Reflection.Emit/tests/Utilities.cs | 50 ++++++ 3 files changed, 274 insertions(+) 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 625704f781faf0..0cacbcc8c3f1a7 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 @@ -223,12 +223,76 @@ private static void WriteSignatureForType(SignatureTypeEncoder signature, Type t { signature.GenericTypeParameter(type.GenericParameterPosition); } + else if (type.IsFunctionPointer) + { + WriteSignatureForFunctionPointerType(signature, type, module); + } else { WriteSimpleSignature(signature, type, module); } } + private static void WriteSignatureForFunctionPointerType(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module) + { + SignatureCallingConvention callConv = SignatureCallingConvention.Default; + FunctionPointerAttributes attribs = FunctionPointerAttributes.None; + + Type returnType = type.GetFunctionPointerReturnType(); + Type[] paramTypes = type.GetFunctionPointerParameterTypes(); + + if (type.IsUnmanagedFunctionPointer) + { + callConv = SignatureCallingConvention.Unmanaged; + + if (type.GetFunctionPointerCallingConventions() is Type[] conventions && conventions.Length == 1) + { + switch (conventions[0].FullName) + { + case "System.Runtime.CompilerServices.CallConvCdecl": + callConv = SignatureCallingConvention.CDecl; + break; + case "System.Runtime.CompilerServices.CallConvStdcall": + callConv = SignatureCallingConvention.StdCall; + break; + case "System.Runtime.CompilerServices.CallConvThiscall": + callConv = SignatureCallingConvention.ThisCall; + break; + case "System.Runtime.CompilerServices.CallConvFastcall": + callConv = SignatureCallingConvention.FastCall; + break; + } + } + } + + MethodSignatureEncoder sigEncoder = signature.FunctionPointer(callConv, attribs); + sigEncoder.Parameters(paramTypes.Length, out ReturnTypeEncoder retTypeEncoder, out ParametersEncoder paramsEncoder); + + CustomModifiersEncoder retModifiersEncoder = retTypeEncoder.CustomModifiers(); + + if (returnType.GetOptionalCustomModifiers() is Type[] retModOpts) + WriteCustomModifiers(retModifiersEncoder, retModOpts, isOptional: true, module); + + if (returnType.GetRequiredCustomModifiers() is Type[] retModReqs) + WriteCustomModifiers(retModifiersEncoder, retModReqs, isOptional: false, module); + + WriteSignatureForType(retTypeEncoder.Type(), returnType, module); + + foreach (Type paramType in paramTypes) + { + ParameterTypeEncoder paramEncoder = paramsEncoder.AddParameter(); + CustomModifiersEncoder paramModifiersEncoder = paramEncoder.CustomModifiers(); + + if (paramType.GetOptionalCustomModifiers() is Type[] paramModOpts) + WriteCustomModifiers(paramModifiersEncoder, paramModOpts, isOptional: true, module); + + if (paramType.GetRequiredCustomModifiers() is Type[] paramModReqs) + WriteCustomModifiers(paramModifiersEncoder, paramModReqs, isOptional: false, module); + + WriteSignatureForType(paramEncoder.Type(), paramType, module); + } + } + private static void WriteSimpleSignature(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module) { CoreTypeId? typeId = module.GetTypeIdFromCoreTypes(type); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 1894d8d5f35b86..4645dae1b7a92c 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -5,8 +5,11 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Numerics; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Xunit; namespace System.Reflection.Emit.Tests @@ -789,6 +792,155 @@ public void CreateGenericTypeFromMetadataLoadContextSignatureTypes() Assert.Equal("ValueTypeChildren", fields[1].Name); Assert.True(fields[1].FieldType.GetGenericArguments()[0].IsValueType); } + + [Fact] + public void SaveFunctionPointerFields() + { + using TempFile file = TempFile.Create(); + using MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver()); + + PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder mb); + TypeBuilder tb = mb.DefineType("TestType", TypeAttributes.Public | TypeAttributes.Class); + + // delegate* + Type funcPtr1 = typeof(delegate*); + tb.DefineField("FuncPtr1", funcPtr1, FieldAttributes.Public | FieldAttributes.Static); + + // delegate* unmanaged[Cdecl] + Type funcPtr4 = new ModifiedTypeHelpers.FunctionPointer( + typeof(delegate* unmanaged[Cdecl]), + [typeof(CallConvCdecl)]); + tb.DefineField("FuncPtr2", funcPtr4, FieldAttributes.Public | FieldAttributes.Static); + + // delegate* unmanaged[Stdcall] + Type funcPtr5 = new ModifiedTypeHelpers.FunctionPointer( + typeof(delegate* unmanaged[Stdcall]), + [typeof(CallConvStdcall)], + customParameterTypes: [typeof(string), new ModifiedTypeHelpers.ModifiedType(typeof(int).MakeByRefType(), [typeof(InAttribute)], [])]); + tb.DefineField("FuncPtr3", funcPtr5, FieldAttributes.Public | FieldAttributes.Static); + + tb.CreateType(); + ab.Save(file.Path); + + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type testType = assemblyFromDisk.Modules.First().GetType("TestType"); + Assert.NotNull(testType); + + FieldInfo field1 = testType.GetField("FuncPtr1"); + Assert.NotNull(field1); + Assert.True(field1.FieldType.IsFunctionPointer); + Assert.False(field1.FieldType.IsUnmanagedFunctionPointer); + Type[] paramTypes1 = field1.FieldType.GetFunctionPointerParameterTypes(); + Assert.Equal(1, paramTypes1.Length); + Assert.Equal(typeof(int).FullName, paramTypes1[0].FullName); + Assert.Equal(typeof(int).FullName, field1.FieldType.GetFunctionPointerReturnType().FullName); + + FieldInfo field2 = testType.GetField("FuncPtr2"); + Type field2Type = field2.GetModifiedFieldType(); + Assert.NotNull(field2); + Assert.True(field2Type.IsFunctionPointer); + Assert.True(field2Type.IsUnmanagedFunctionPointer); + Type[] paramTypes2 = field2Type.GetFunctionPointerParameterTypes(); + Assert.Equal(2, paramTypes2.Length); + Assert.Equal(typeof(int).FullName, paramTypes2[0].FullName); + Assert.Equal(typeof(float).FullName, paramTypes2[1].FullName); + Assert.Equal(typeof(double).FullName, field2Type.GetFunctionPointerReturnType().FullName); + Type[] callingConventions2 = field2Type.GetFunctionPointerCallingConventions(); + Assert.Contains(callingConventions2, t => t.FullName == typeof(CallConvCdecl).FullName); + + FieldInfo field3 = testType.GetField("FuncPtr3"); + Type field3Type = field3.GetModifiedFieldType(); + Assert.NotNull(field3); + Assert.True(field3Type.IsFunctionPointer); + Assert.True(field3Type.IsUnmanagedFunctionPointer); + Type[] paramTypes3 = field3Type.GetFunctionPointerParameterTypes(); + Assert.Equal(2, paramTypes3.Length); + Assert.Equal(typeof(string).FullName, paramTypes3[0].FullName); + Assert.Equal(typeof(int).MakeByRefType().FullName, paramTypes3[1].FullName); + Assert.Contains(paramTypes3[1].GetRequiredCustomModifiers(), t => t.FullName == typeof(InAttribute).FullName); + Assert.Equal(typeof(void).FullName, field3Type.GetFunctionPointerReturnType().FullName); + Type[] callingConventions3 = field3Type.GetFunctionPointerCallingConventions(); + Assert.Contains(callingConventions3, t => t.FullName == typeof(CallConvStdcall).FullName); + } + + [Fact] + public void ConsumeFunctionPointerFields() + { + // public unsafe class Container + // { + // public static delegate* Method; + // + // public static int Add(int a, int b) => a + b; + // public static void Init() => Method = &Add; + // } + + TempFile assembly1Path = TempFile.Create(); + PersistedAssemblyBuilder assembly1 = new(new AssemblyName("Assembly1"), typeof(object).Assembly); + ModuleBuilder mod1 = assembly1.DefineDynamicModule("Module1"); + TypeBuilder containerType = mod1.DefineType("Container", TypeAttributes.Public | TypeAttributes.Class); + FieldBuilder methodField = containerType.DefineField("Method", typeof(delegate*), FieldAttributes.Public | FieldAttributes.Static); + MethodBuilder addMethod = containerType.DefineMethod("Add", MethodAttributes.Public | MethodAttributes.Static); + addMethod.SetParameters(typeof(int), typeof(int)); + addMethod.SetReturnType(typeof(int)); + ILGenerator addMethodIL = addMethod.GetILGenerator(); + addMethodIL.Emit(OpCodes.Ldarg_0); + addMethodIL.Emit(OpCodes.Ldarg_1); + addMethodIL.Emit(OpCodes.Add); + addMethodIL.Emit(OpCodes.Ret); + MethodBuilder initMethod = containerType.DefineMethod("Init", MethodAttributes.Public | MethodAttributes.Static); + initMethod.SetReturnType(typeof(void)); + ILGenerator initMethodIL = initMethod.GetILGenerator(); + initMethodIL.Emit(OpCodes.Ldftn, addMethod); + initMethodIL.Emit(OpCodes.Stsfld, methodField); + initMethodIL.Emit(OpCodes.Ret); + containerType.CreateType(); + assembly1.Save(assembly1Path.Path); + + // class Program + // { + // public static int Main() + // { + // Container.Init(); + // return Container.Method(2, 3); + // } + // } + + TestAssemblyLoadContext context = new(); + + TempFile assembly2Path = TempFile.Create(); + Assembly assembly1FromDisk = context.LoadFromAssemblyPath(assembly1Path.Path); + PersistedAssemblyBuilder assembly2 = new(new AssemblyName("Assembly2"), typeof(object).Assembly); + ModuleBuilder mod2 = assembly2.DefineDynamicModule("Module2"); + TypeBuilder programType = mod2.DefineType("Program"); + MethodBuilder mainMethod = programType.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static); + mainMethod.SetReturnType(typeof(int)); + ILGenerator il = mainMethod.GetILGenerator(); + il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field1")); + il.Emit(OpCodes.Pop); + // References to fields with unmanaged calling convention are broken + // [ActiveIssue("https://github.com/dotnet/runtime/issues/120909")] + // il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field2")); + // il.Emit(OpCodes.Pop); + // il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field3")); + // il.Emit(OpCodes.Pop); + // il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field4")); + // il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, assembly1FromDisk.GetType("Container").GetMethod("Init")); + il.Emit(OpCodes.Ldc_I4_2); + il.Emit(OpCodes.Ldc_I4_3); + il.Emit(OpCodes.Ldsfld, assembly1FromDisk.GetType("Container").GetField("Method")); + il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(int), [typeof(int), typeof(int)], null); + il.Emit(OpCodes.Ret); + programType.CreateType(); + assembly2.Save(assembly2Path.Path); + + Assembly assembly2FromDisk = context.LoadFromAssemblyPath(assembly2Path.Path); + int result = (int)assembly2FromDisk.GetType("Program").GetMethod("Main").Invoke(null, null); + Assert.Equal(5, result); + + assembly1Path.Dispose(); + assembly2Path.Dispose(); + } } // Test Types @@ -836,4 +988,12 @@ public class ClassWithFields : EmptyTestClass public EmptyTestClass field1; public byte field2; } + + public unsafe class ClassWithFunctionPointerFields + { + public static delegate* field1; + public static delegate* unmanaged field2; + public static delegate* unmanaged[Cdecl] field3; + public static delegate* unmanaged[Cdecl, SuppressGCTransition], Vector> field4; + } } diff --git a/src/libraries/System.Reflection.Emit/tests/Utilities.cs b/src/libraries/System.Reflection.Emit/tests/Utilities.cs index 4baf34722a49d9..446b33f8628aa9 100644 --- a/src/libraries/System.Reflection.Emit/tests/Utilities.cs +++ b/src/libraries/System.Reflection.Emit/tests/Utilities.cs @@ -164,4 +164,54 @@ public static string GetFullName(string name) return name; } } + + public static class ModifiedTypeHelpers + { + public class FunctionPointer : TypeDelegator + { + private readonly Type[] callingConventions; + private readonly Type returnType; + private readonly Type[] parameterTypes; + private readonly Type[] requiredModifiers; + private readonly Type[] optionalModifiers; + + public FunctionPointer( + Type baseFunctionPointerType, + Type[] conventions = null, + Type customReturnType = null, + Type[] customParameterTypes = null, + Type[] fnPtrRequiredMods = null, + Type[] fnPtrOptionalMods = null) + : base(baseFunctionPointerType) + { + callingConventions = conventions ?? []; + returnType = customReturnType ?? baseFunctionPointerType.GetFunctionPointerReturnType(); + parameterTypes = customParameterTypes ?? baseFunctionPointerType.GetFunctionPointerParameterTypes(); + requiredModifiers = fnPtrRequiredMods ?? []; + optionalModifiers = fnPtrOptionalMods ?? []; + } + + public override Type[] GetFunctionPointerCallingConventions() => callingConventions; + public override Type GetFunctionPointerReturnType() => returnType; + public override Type[] GetFunctionPointerParameterTypes() => parameterTypes; + public override Type[] GetRequiredCustomModifiers() => requiredModifiers; + public override Type[] GetOptionalCustomModifiers() => optionalModifiers; + } + + public class ModifiedType : TypeDelegator + { + private readonly Type[] requiredModifiers; + private readonly Type[] optionalModifiers; + + public ModifiedType(Type delegatingType, Type[] requiredMods = null, Type[] optionalMods = null) + : base(delegatingType) + { + requiredModifiers = requiredMods ?? []; + optionalModifiers = optionalMods ?? []; + } + + public override Type[] GetRequiredCustomModifiers() => requiredModifiers; + public override Type[] GetOptionalCustomModifiers() => optionalModifiers; + } + } } From e0584c6daf1fe1398bde903c7b8c26c0317137aa Mon Sep 17 00:00:00 2001 From: Jonas <35030845+jgh07@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:38:48 +0100 Subject: [PATCH 2/3] Support references to unmanaged function pointers in Reflection.Emit (#121128) This fixes #120909. --- .../Emit/FieldOnTypeBuilderInstantiation.cs | 1 + .../Emit/MethodOnTypeBuilderInstantiation.cs | 2 +- .../src/System/Reflection/ModifiedType.cs | 2 + .../Reflection/Emit/FieldBuilderImpl.cs | 2 + .../Reflection/Emit/ModuleBuilderImpl.cs | 28 +++------- .../Reflection/Emit/ParameterBuilderImpl.cs | 5 +- .../System/Reflection/Emit/SignatureHelper.cs | 24 ++++++++- .../AssemblySaveTypeBuilderAPIsTests.cs | 8 +-- .../AssemblySaveTypeBuilderTests.cs | 51 ++++++++++++++----- 9 files changed, 81 insertions(+), 42 deletions(-) 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 006795ce3692e5..b269d598a01d2e 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 @@ -56,6 +56,7 @@ internal FieldOnTypeBuilderInstantiation(FieldInfo field, TypeBuilderInstantiati #region Public Abstract\Virtual Members public override Type[] GetRequiredCustomModifiers() { return _field.GetRequiredCustomModifiers(); } public override Type[] GetOptionalCustomModifiers() { return _field.GetOptionalCustomModifiers(); } + public override Type GetModifiedFieldType() => _field.GetModifiedFieldType(); public override void SetValueDirect(TypedReference obj, object value) { throw new NotImplementedException(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs index bfa9ef0359c502..5e0b8c317ad770 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs @@ -118,7 +118,7 @@ public override MethodInfo MakeGenericMethod(params Type[] typeArgs) #region Public Abstract\Virtual Members public override Type ReturnType => _method.ReturnType; - public override ParameterInfo ReturnParameter => throw new NotSupportedException(); + public override ParameterInfo ReturnParameter => _method.ReturnParameter; public override ICustomAttributeProvider ReturnTypeCustomAttributes => throw new NotSupportedException(); public override MethodInfo GetBaseDefinition() { throw new NotSupportedException(); } #endregion diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedType.cs index 70ea199dc47368..89d5401a50b1bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedType.cs @@ -172,8 +172,10 @@ public override Type[] GetOptionalCustomModifiers() public override bool IsEnum => _unmodifiedType.IsEnum; protected override bool IsPrimitiveImpl() => _unmodifiedType.IsPrimitive; protected override bool IsByRefImpl() => _unmodifiedType.IsByRef; + public override bool IsGenericParameter => _unmodifiedType.IsGenericParameter; public override bool IsGenericTypeParameter => _unmodifiedType.IsGenericTypeParameter; public override bool IsGenericMethodParameter => _unmodifiedType.IsGenericMethodParameter; + public override int GenericParameterPosition => _unmodifiedType.GenericParameterPosition; protected override bool IsPointerImpl() => _unmodifiedType.IsPointer; protected override bool IsValueTypeImpl() => _unmodifiedType.IsValueType; protected override bool IsCOMObjectImpl() => _unmodifiedType.IsCOMObject; 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 8d0561d15be47b..f5ef7912f19b39 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 @@ -113,6 +113,8 @@ public override void SetValue(object? obj, object? val, BindingFlags invokeAttr, public override Type[] GetOptionalCustomModifiers() => _optionalCustomModifiers ?? Type.EmptyTypes; + public override Type GetModifiedFieldType() => FieldType; + #endregion #region ICustomAttributeProvider Implementation 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 26d43dbaa1083e..e87fa61926efb9 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 @@ -184,7 +184,8 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuil } // Now write all generic parameters in order - genericParams.Sort((x, y) => { + genericParams.Sort((x, y) => + { int primary = CodedIndex.TypeOrMethodDef(x._parentHandle).CompareTo(CodedIndex.TypeOrMethodDef(y._parentHandle)); if (primary != 0) return primary; @@ -690,6 +691,8 @@ internal void WriteCustomAttributes(List? customAttribut private EntityHandle GetTypeReferenceOrSpecificationHandle(Type type) { + type = type.UnderlyingSystemType; + if (!_typeReferences.TryGetValue(type, out var typeHandle)) { if (type.HasElementType || type.IsGenericParameter || @@ -740,7 +743,7 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo) declaringType = declaringType.MakeGenericType(declaringType.GetGenericArguments()); } - Type fieldType = ((FieldInfo)GetOriginalMemberIfConstructedType(field)).FieldType; + Type fieldType = ((FieldInfo)GetOriginalMemberIfConstructedType(field)).GetModifiedFieldType(); memberHandle = AddMemberReference(field.Name, GetTypeHandle(declaringType), MetadataSignatureHelper.GetFieldSignature(fieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); @@ -791,7 +794,7 @@ private EntityHandle GetMethodReference(MethodInfo methodInfo, Type[] optionalPa } private BlobBuilder GetMethodSignature(MethodInfo method, Type[]? optionalParameterTypes) => - MetadataSignatureHelper.GetMethodSignature(this, ParameterTypes(method.GetParameters()), method.ReturnType, + MetadataSignatureHelper.GetMethodSignature(this, MetadataSignatureHelper.GetParameterTypes(method.GetParameters()), method.ReturnParameter.GetModifiedParameterType(), GetSignatureConvention(method.CallingConvention), method.GetGenericArguments().Length, !method.IsStatic, optionalParameterTypes); private BlobBuilder GetMethodArrayMethodSignature(ArrayMethod method) => MetadataSignatureHelper.GetMethodSignature( @@ -829,23 +832,6 @@ private MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo) return memberInfo; } - private static Type[] ParameterTypes(ParameterInfo[] parameterInfos) - { - if (parameterInfos.Length == 0) - { - return Type.EmptyTypes; - } - - Type[] parameterTypes = new Type[parameterInfos.Length]; - - for (int i = 0; i < parameterInfos.Length; i++) - { - parameterTypes[i] = parameterInfos[i].ParameterType; - } - - return parameterTypes; - } - private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly) { if (!_assemblyReferences.TryGetValue(assembly, out var handle)) @@ -861,7 +847,7 @@ private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly) } else { - publicKeyOrToken = aName.GetPublicKeyToken(); + publicKeyOrToken = aName.GetPublicKeyToken(); } handle = AddAssemblyReference(aName.Name, aName.Version, aName.CultureName, publicKeyOrToken, assemblyFlags); _assemblyReferences.Add(assembly, handle); diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ParameterBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ParameterBuilderImpl.cs index f1b6446be8e0f8..d2e504fd73b583 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ParameterBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ParameterBuilderImpl.cs @@ -68,8 +68,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan internal sealed class ParameterInfoWrapper : ParameterInfo { private readonly ParameterBuilderImpl _pb; - private readonly Type _type -; + private readonly Type _type; public ParameterInfoWrapper(ParameterBuilderImpl pb, Type type) { _pb = pb; @@ -87,5 +86,7 @@ public ParameterInfoWrapper(ParameterBuilderImpl pb, Type type) public override bool HasDefaultValue => _pb._defaultValue != DBNull.Value; public override object? DefaultValue => HasDefaultValue ? _pb._defaultValue : null; + + public override Type GetModifiedParameterType() => ParameterType; } } 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 0cacbcc8c3f1a7..2ec00b90743525 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 @@ -48,7 +48,7 @@ internal static BlobBuilder GetConstructorSignature(ParameterInfo[]? parameters, retType.Void(); - WriteParametersSignature(module, Array.ConvertAll(parameters, p => p.ParameterType), parameterEncoder); + WriteParametersSignature(module, GetParameterTypes(parameters), parameterEncoder); return constructorSignature; } @@ -106,6 +106,23 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? return methodSignature; } + internal static Type[] GetParameterTypes(ParameterInfo[] parameterInfos) + { + if (parameterInfos.Length == 0) + { + return Type.EmptyTypes; + } + + Type[] parameterTypes = new Type[parameterInfos.Length]; + + for (int i = 0; i < parameterInfos.Length; i++) + { + parameterTypes[i] = parameterInfos[i].GetModifiedParameterType(); + } + + return parameterTypes; + } + private static void WriteReturnTypeCustomModifiers(CustomModifiersEncoder encoder, Type[]? requiredModifiers, Type[]? optionalModifiers, ModuleBuilderImpl module) { @@ -122,8 +139,10 @@ private static void WriteReturnTypeCustomModifiers(CustomModifiersEncoder encode private static void WriteCustomModifiers(CustomModifiersEncoder encoder, Type[] customModifiers, bool isOptional, ModuleBuilderImpl module) { - foreach (Type modifier in customModifiers) + // GetOptionalCustomModifiers and GetRequiredCustomModifiers return modifiers in reverse order + for (int i = customModifiers.Length - 1; i >= 0; i--) { + Type modifier = customModifiers[i]; encoder.AddModifier(module.GetTypeHandle(modifier), isOptional); } } @@ -295,6 +314,7 @@ private static void WriteSignatureForFunctionPointerType(SignatureTypeEncoder si private static void WriteSimpleSignature(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module) { + type = type.UnderlyingSystemType; CoreTypeId? typeId = module.GetTypeIdFromCoreTypes(type); switch (typeId) diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index d9d1e1e50269b5..8063964e4473b7 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -633,13 +633,13 @@ public void ReturnTypeAndParameterRequiredOptionalCustomModifiers() Type[] par0RequiredMods = allModMethod.GetParameters()[0].GetRequiredCustomModifiers(); Type[] par0OptionalMods = allModMethod.GetParameters()[0].GetOptionalCustomModifiers(); Assert.Equal(2, returnReqMods.Length); - Assert.Equal(mlc.CoreAssembly.GetType(typeof(short).FullName), returnReqMods[0]); - Assert.Equal(mlc.CoreAssembly.GetType(typeof(int).FullName), returnReqMods[1]); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(int).FullName), returnReqMods[0]); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(short).FullName), returnReqMods[1]); Assert.Equal(1, returnOptMods.Length); Assert.Equal(mlc.CoreAssembly.GetType(typeof(Version).FullName), returnOptMods[0]); Assert.Equal(cmodsReq1.Length, par0RequiredMods.Length); - Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[1].FullName), par0RequiredMods[0]); - Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[0].FullName), par0RequiredMods[1]); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[0].FullName), par0RequiredMods[0]); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[1].FullName), par0RequiredMods[1]); Assert.Equal(cmodsOpt1.Length, par0OptionalMods.Length); Assert.Equal(mlc.CoreAssembly.GetType(cmodsOpt1[0].FullName), par0OptionalMods[0]); Assert.Equal(cmodsReq2.Length, allModMethod.GetParameters()[1].GetRequiredCustomModifiers().Length); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 4645dae1b7a92c..5b7911bed07f88 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -864,7 +864,8 @@ public void SaveFunctionPointerFields() } [Fact] - public void ConsumeFunctionPointerFields() + [ActiveIssue("https://github.com/dotnet/runtime/issues/2383", TestRuntimes.Mono)] + public void ConsumeFunctionPointerMembers() { // public unsafe class Container // { @@ -915,16 +916,29 @@ public void ConsumeFunctionPointerFields() MethodBuilder mainMethod = programType.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static); mainMethod.SetReturnType(typeof(int)); ILGenerator il = mainMethod.GetILGenerator(); - il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field1")); + il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerMembers).GetField("field1")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerMembers).GetField("field2")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerMembers).GetField("field3")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerMembers).GetField("field4")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerMembers).GetField("field5")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, typeof(ClassWithFunctionPointerMembers).GetMethod("Method1")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, typeof(ClassWithFunctionPointerMembers).GetMethod("Method2")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, typeof(ClassWithFunctionPointerMembers).GetMethod("Method3")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, typeof(ClassWithFunctionPointerMembers).GetMethod("Method4")); + il.Emit(OpCodes.Call, typeof(ClassWithFunctionPointerMembers).GetMethod("Method5")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Ldsfld, typeof(GenericClassWithFunctionPointerMembers).GetField("Field")); + il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, typeof(GenericClassWithFunctionPointerMembers).GetMethod("Method").MakeGenericMethod(typeof(string))); il.Emit(OpCodes.Pop); - // References to fields with unmanaged calling convention are broken - // [ActiveIssue("https://github.com/dotnet/runtime/issues/120909")] - // il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field2")); - // il.Emit(OpCodes.Pop); - // il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field3")); - // il.Emit(OpCodes.Pop); - // il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointerFields).GetField("field4")); - // il.Emit(OpCodes.Pop); il.Emit(OpCodes.Call, assembly1FromDisk.GetType("Container").GetMethod("Init")); il.Emit(OpCodes.Ldc_I4_2); il.Emit(OpCodes.Ldc_I4_3); @@ -989,11 +1003,24 @@ public class ClassWithFields : EmptyTestClass public byte field2; } - public unsafe class ClassWithFunctionPointerFields + public unsafe class ClassWithFunctionPointerMembers { - public static delegate* field1; + public static delegate* field1; public static delegate* unmanaged field2; public static delegate* unmanaged[Cdecl] field3; public static delegate* unmanaged[Cdecl, SuppressGCTransition], Vector> field4; + public static List*[]> field5; + + public static delegate* Method1() => null; + public static delegate* unmanaged Method2() => null; + public static delegate* unmanaged[Fastcall] Method3() => null; + public static delegate* unmanaged[Cdecl], Guid> Method4() => null; + public static delegate* unmanaged[Cdecl] Method5(delegate* unmanaged[Cdecl], Guid> funcPtr) => null; + } + + public unsafe class GenericClassWithFunctionPointerMembers + { + public static delegate* unmanaged[Cdecl] Field; + public static delegate* unmanaged[Fastcall, MemberFunction] Method() => null; } } From eefcb2292ad595daade90f5644ce4cfa64614415 Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis Date: Tue, 17 Feb 2026 23:29:41 +0200 Subject: [PATCH 3/3] `PersistedAssemblyBuilder`: Fix encoding of custom modifiers. (#123925) Fixes #123857. --------- Co-authored-by: Jan Kotas --- .../System/Reflection/Emit/SignatureHelper.cs | 68 ++++--------------- .../AssemblySaveILGeneratorTests.cs | 4 +- .../AssemblySaveTypeBuilderTests.cs | 61 ++++++++++++++++- .../tests/System.Reflection.Emit.Tests.csproj | 1 + .../System/Reflection/ModifiedType.Mono.cs | 33 +++++---- .../System/Reflection/RuntimeParameterInfo.cs | 2 +- 6 files changed, 92 insertions(+), 77 deletions(-) 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 2ec00b90743525..b6f465eecd4374 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 @@ -30,8 +30,7 @@ internal static BlobBuilder GetFieldSignature(Type fieldType, Type[] requiredCus { BlobBuilder fieldSignature = new(); FieldTypeEncoder encoder = new BlobEncoder(fieldSignature).Field(); - WriteReturnTypeCustomModifiers(encoder.CustomModifiers(), requiredCustomModifiers, optionalCustomModifiers, module); - WriteSignatureForType(encoder.Type(), fieldType, module); + WriteSignatureForType(encoder.Type(), fieldType, module, requiredCustomModifiers, optionalCustomModifiers); return fieldSignature; } @@ -85,16 +84,8 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? new BlobEncoder(methodSignature).MethodSignature(convention, genParamCount, isInstance). Parameters(paramsLength, out ReturnTypeEncoder retEncoder, out ParametersEncoder parEncoder); - WriteReturnTypeCustomModifiers(retEncoder.CustomModifiers(), returnTypeRequiredModifiers, returnTypeOptionalModifiers, module); - - if (returnType != null && returnType != module.GetTypeFromCoreAssembly(CoreTypeId.Void)) - { - WriteSignatureForType(retEncoder.Type(), returnType, module); - } - else - { - retEncoder.Void(); - } + returnType ??= module.GetTypeFromCoreAssembly(CoreTypeId.Void); + WriteSignatureForType(retEncoder.Type(), returnType, module, returnTypeRequiredModifiers, returnTypeOptionalModifiers); WriteParametersSignature(module, parameters, parEncoder, parameterRequiredModifiers, parameterOptionalModifiers); @@ -123,20 +114,6 @@ internal static Type[] GetParameterTypes(ParameterInfo[] parameterInfos) return parameterTypes; } - private static void WriteReturnTypeCustomModifiers(CustomModifiersEncoder encoder, - Type[]? requiredModifiers, Type[]? optionalModifiers, ModuleBuilderImpl module) - { - if (requiredModifiers != null) - { - WriteCustomModifiers(encoder, requiredModifiers, isOptional: false, module); - } - - if (optionalModifiers != null) - { - WriteCustomModifiers(encoder, optionalModifiers, isOptional: true, module); - } - } - private static void WriteCustomModifiers(CustomModifiersEncoder encoder, Type[] customModifiers, bool isOptional, ModuleBuilderImpl module) { // GetOptionalCustomModifiers and GetRequiredCustomModifiers return modifiers in reverse order @@ -156,17 +133,10 @@ private static void WriteParametersSignature(ModuleBuilderImpl module, Type[]? p { ParameterTypeEncoder encoder = parameterEncoder.AddParameter(); - if (requiredModifiers != null && requiredModifiers.Length > i && requiredModifiers[i] != null) - { - WriteCustomModifiers(encoder.CustomModifiers(), requiredModifiers[i], isOptional: false, module); - } - - if (optionalModifiers != null && optionalModifiers.Length > i && optionalModifiers[i] != null) - { - WriteCustomModifiers(encoder.CustomModifiers(), optionalModifiers[i], isOptional: true, module); - } + Type[]? modreqs = (requiredModifiers != null && requiredModifiers.Length > i) ? requiredModifiers[i] : null; + Type[]? modopts = (optionalModifiers != null && optionalModifiers.Length > i) ? optionalModifiers[i] : null; - WriteSignatureForType(encoder.Type(), parameters[i], module); + WriteSignatureForType(encoder.Type(), parameters[i], module, modreqs, modopts); } } } @@ -179,15 +149,16 @@ internal static BlobBuilder GetPropertySignature(PropertyBuilderImpl property, M PropertySignature(isInstanceProperty: property.CallingConventions.HasFlag(CallingConventions.HasThis)). Parameters(property.ParameterTypes == null ? 0 : property.ParameterTypes.Length, out ReturnTypeEncoder retType, out ParametersEncoder paramEncoder); - WriteReturnTypeCustomModifiers(retType.CustomModifiers(), property._returnTypeRequiredCustomModifiers, property._returnTypeOptionalCustomModifiers, module); - WriteSignatureForType(retType.Type(), property.PropertyType, module); + WriteSignatureForType(retType.Type(), property.PropertyType, module, property._returnTypeRequiredCustomModifiers, property._returnTypeOptionalCustomModifiers); WriteParametersSignature(module, property.ParameterTypes, paramEncoder, property._parameterTypeRequiredCustomModifiers, property._parameterTypeOptionalCustomModifiers); return propertySignature; } - private static void WriteSignatureForType(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module) + private static void WriteSignatureForType(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module, Type[]? requiredModifiers = null, Type[]? optionalModifiers = null) { + WriteCustomModifiers(signature.CustomModifiers(), requiredModifiers ?? type.GetRequiredCustomModifiers(), isOptional: false, module); + WriteCustomModifiers(signature.CustomModifiers(), optionalModifiers ?? type.GetOptionalCustomModifiers(), isOptional: true, module); if (type.IsArray) { Type elementType = type.GetElementType()!; @@ -199,8 +170,8 @@ private static void WriteSignatureForType(SignatureTypeEncoder signature, Type t else { signature.Array(out SignatureTypeEncoder elTypeSignature, out ArrayShapeEncoder arrayEncoder); - WriteSimpleSignature(elTypeSignature, elementType, module); - arrayEncoder.Shape(type.GetArrayRank(), ImmutableArray.Create(), ImmutableArray.Create(new int[rank])); + WriteSignatureForType(elTypeSignature, elementType, module); + arrayEncoder.Shape(type.GetArrayRank(), [], default); } } else if (type.IsPointer) @@ -287,26 +258,11 @@ private static void WriteSignatureForFunctionPointerType(SignatureTypeEncoder si MethodSignatureEncoder sigEncoder = signature.FunctionPointer(callConv, attribs); sigEncoder.Parameters(paramTypes.Length, out ReturnTypeEncoder retTypeEncoder, out ParametersEncoder paramsEncoder); - CustomModifiersEncoder retModifiersEncoder = retTypeEncoder.CustomModifiers(); - - if (returnType.GetOptionalCustomModifiers() is Type[] retModOpts) - WriteCustomModifiers(retModifiersEncoder, retModOpts, isOptional: true, module); - - if (returnType.GetRequiredCustomModifiers() is Type[] retModReqs) - WriteCustomModifiers(retModifiersEncoder, retModReqs, isOptional: false, module); - WriteSignatureForType(retTypeEncoder.Type(), returnType, module); foreach (Type paramType in paramTypes) { ParameterTypeEncoder paramEncoder = paramsEncoder.AddParameter(); - CustomModifiersEncoder paramModifiersEncoder = paramEncoder.CustomModifiers(); - - if (paramType.GetOptionalCustomModifiers() is Type[] paramModOpts) - WriteCustomModifiers(paramModifiersEncoder, paramModOpts, isOptional: true, module); - - if (paramType.GetRequiredCustomModifiers() is Type[] paramModReqs) - WriteCustomModifiers(paramModifiersEncoder, paramModReqs, isOptional: false, module); WriteSignatureForType(paramEncoder.Type(), paramType, module); } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs index f12da03bfb067d..b1f02f3ce95734 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -2637,9 +2637,9 @@ public void ReferenceNestedGenericTypeWithConstructedTypeBuilderParameterInIL() TypeBuilder nestedItem = type.DefineNestedType("ItemInfo", TypeAttributes.NestedPublic); GenericTypeParameterBuilder itemParam = nestedItem.DefineGenericParameters(genParams)[0]; TypeBuilder nestedSector = type.DefineNestedType("Sector", TypeAttributes.NestedPublic); - GenericTypeParameterBuilder nestedParam = nestedSector.DefineGenericParameters(genParams)[0]; + GenericTypeParameterBuilder sectorParam = nestedSector.DefineGenericParameters(genParams)[0]; - Type nestedOfT = nestedItem.MakeGenericType(nestedParam); + Type nestedOfT = nestedItem.MakeGenericType(sectorParam); Type parent = typeof(HashSet<>).MakeGenericType(nestedOfT); nestedSector.SetParent(parent); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 5b7911bed07f88..fd20ead73a8f11 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -626,6 +626,59 @@ public void SaveGenericTypeSignatureWithGenericParameter() } } + [Fact] + public void SaveInterfaceOverrideWithCustomModifier() + { + using (TempFile file = TempFile.Create()) + { + AssemblyName name = new("TestAssembly"); + PersistedAssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(name); + ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); + + TypeBuilder tb = mb.DefineType("IMethodWithModifiersImpl", TypeAttributes.Class | TypeAttributes.Public); + tb.AddInterfaceImplementation(typeof(IMethodWithModifiers)); + MethodInfo mRun = typeof(IMethodWithModifiers).GetMethod(nameof(IMethodWithModifiers.Run)); + MethodBuilder m = tb.DefineMethod("IMethodWithModifiers.Run", + MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + CallingConventions.Standard, + returnType: mRun.ReturnParameter.GetModifiedParameterType(), + returnTypeRequiredCustomModifiers: null, + returnTypeOptionalCustomModifiers: null, + // The first parameter will have modreqs specified from parameterTypeRequiredCustomModifiers, and the second from parameterTypes. + parameterTypes: mRun.GetParameters().Select((x, i) => i == 0 ? x.ParameterType : x.GetModifiedParameterType()).ToArray(), + parameterTypeRequiredCustomModifiers: [[typeof(InAttribute)], null], + parameterTypeOptionalCustomModifiers: null); + tb.DefineMethodOverride(m, mRun); + ParameterBuilder pb = m.DefineParameter(1, ParameterAttributes.In, "x"); + pb.SetCustomAttribute(new CustomAttributeBuilder(typeof(IsReadOnlyAttribute).GetConstructor(types: []), [])); + m.GetILGenerator().Emit(OpCodes.Ret); + MethodInfo mRun2 = typeof(IMethodWithModifiers).GetMethod(nameof(IMethodWithModifiers.Run2)); + MethodBuilder m2 = tb.DefineMethod("IMethodWithModifiers.Run2", + MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + CallingConventions.Standard, + returnType: mRun2.ReturnParameter.GetModifiedParameterType(), + returnTypeRequiredCustomModifiers: null, + returnTypeOptionalCustomModifiers: null, + parameterTypes: mRun2.GetParameters().Select(x => x.GetModifiedParameterType()).ToArray(), + // Test that passing null gets modreqs from the parameter types. + parameterTypeRequiredCustomModifiers: null, + parameterTypeOptionalCustomModifiers: null); + tb.DefineMethodOverride(m2, mRun2); + ParameterBuilder pb2 = m2.DefineParameter(1, ParameterAttributes.In, "x"); + pb2.SetCustomAttribute(new CustomAttributeBuilder(typeof(IsReadOnlyAttribute).GetConstructor(types: []), [])); + m2.GetILGenerator().Emit(OpCodes.Ret); + + tb.CreateType(); + assemblyBuilder.Save(file.Path); + + TestAssemblyLoadContext context = new(); + // Load the assembly and check that loading the type does not throw. + Assembly loadedAsm = context.LoadFromAssemblyPath(file.Path); + _ = loadedAsm.GetType(tb.Name, throwOnError: true); + context.Unload(); + } + } + [Fact] public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() { @@ -870,7 +923,7 @@ public void ConsumeFunctionPointerMembers() // public unsafe class Container // { // public static delegate* Method; - // + // // public static int Add(int a, int b) => a + b; // public static void Init() => Method = &Add; // } @@ -983,6 +1036,12 @@ public interface IOneMethod object Func(string a, short b); } + public interface IMethodWithModifiers + { + unsafe void Run(in int x, delegate* f); + void Run2(in int x); + } + public struct EmptyStruct { } 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 0552a0ae8f6473..39e12cd5e07f24 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 @@ -135,6 +135,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/ModifiedType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/ModifiedType.Mono.cs index 81a0d098d04682..334e768098f9f3 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/ModifiedType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/ModifiedType.Mono.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; + namespace System.Reflection { internal partial class ModifiedType @@ -19,11 +21,13 @@ internal partial class ModifiedType /// volatile delegate* unmanaged[Cdecl]<int> fptrField2; /// NOTE: In scenario 3) the SignatureHolderInfo has higher priority for retrieving field data (like custom modifiers) /// - internal struct TypeSignature + internal readonly struct TypeSignature { - internal readonly RuntimeType? SignatureHolderType; - internal readonly object? SignatureHolderInfo; - internal int ParameterIndex; + public readonly RuntimeType? SignatureHolderType; + public readonly object? SignatureHolderInfo; + // The index of the function pointer (starting from 1, 0 being the return type) + // or generic parameter for which we are retrieving modifiers. + public readonly int ParameterIndex; internal TypeSignature(RuntimeType signatureHolderType, int parameterIndex) { @@ -39,8 +43,9 @@ internal TypeSignature(object signatureHolderInfo, int parameterIndex) ParameterIndex = parameterIndex; } - internal TypeSignature(RuntimeType signatureHolderType, object signatureHolderInfo, int parameterIndex) + internal TypeSignature(RuntimeType? signatureHolderType, object? signatureHolderInfo, int parameterIndex) { + Debug.Assert(signatureHolderType is not null || signatureHolderInfo is not null); SignatureHolderType = signatureHolderType; SignatureHolderInfo = signatureHolderInfo; ParameterIndex = parameterIndex; @@ -88,15 +93,15 @@ internal bool TryGetCustomModifiersFromSignatureHolderType(bool required, out Ty } } - internal static Type Create(Type sourceType, object sourceTypeInfo, int parameterIndex = 0) + internal static Type Create(Type sourceType, object sourceTypeInfo) { var unmodifiedType = (RuntimeType)sourceType; TypeSignature typeSignature; if (unmodifiedType.IsFunctionPointer) - typeSignature = new TypeSignature(unmodifiedType, sourceTypeInfo, parameterIndex); + typeSignature = new TypeSignature(unmodifiedType, sourceTypeInfo, -1); else - typeSignature = new TypeSignature(sourceTypeInfo, parameterIndex); + typeSignature = new TypeSignature(sourceTypeInfo, -1); return Create(unmodifiedType, typeSignature); } @@ -118,18 +123,12 @@ internal Type GetTypeParameter(Type unmodifiedType, int index) } else { + var parentSignatureHolderInfo = _typeSignature.SignatureHolderInfo; if (parentUnmodifiedType.IsFunctionPointer) { - var parentSignatureHolderType = _typeSignature.SignatureHolderType ?? - throw new Exception($"Parent's {nameof(_typeSignature.SignatureHolderType)} cannot be null"); - childTypeSignature = new TypeSignature(parentSignatureHolderType, index); - } - else - { - var parentSignatureHolderInfo = _typeSignature.SignatureHolderInfo ?? - throw new Exception($"Parent's {nameof(_typeSignature.SignatureHolderInfo)} cannot be null"); - childTypeSignature = new TypeSignature(parentSignatureHolderInfo, index); + parentSignatureHolderInfo = null; } + childTypeSignature = new TypeSignature(_typeSignature.SignatureHolderType, parentSignatureHolderInfo, index); } return Create(childUnmodifiedType, childTypeSignature); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs index d28cdfc3469c1b..3d5bbec0bcf732 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs @@ -425,6 +425,6 @@ internal void SetAttributes(ParameterAttributes attributes) internal Type[] GetCustomModifiersFromModifiedType(bool optional, int genericArgumentPosition) => GetTypeModifiers(ParameterType, Member, Position, optional, genericArgumentPosition) ?? Type.EmptyTypes; - public override Type GetModifiedParameterType() => ModifiedType.Create(ParameterType, this, PositionImpl + 1); + public override Type GetModifiedParameterType() => ModifiedType.Create(ParameterType, this); } }