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 513df134b75a5..5d242a710ae2b 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 @@ -809,7 +809,7 @@ internal static SignatureCallingConvention GetSignatureConvention(CallingConvent return convention; } - private static MemberInfo GetOriginalMemberIfConstructedType(MethodBase methodBase) + private MemberInfo GetOriginalMemberIfConstructedType(MethodBase methodBase) { Type declaringType = methodBase.DeclaringType!; if (declaringType.IsConstructedGenericType && @@ -1068,7 +1068,7 @@ internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [Dynamic internal EntityHandle TryGetFieldHandle(FieldInfo field) { - if (field is FieldBuilderImpl fb) + if (field is FieldBuilderImpl fb && Equals(fb.Module)) { return fb._handle; } @@ -1096,27 +1096,28 @@ private EntityHandle GetHandleForMember(MemberInfo member) return GetMemberReferenceHandle(member); } - private static bool IsConstructedFromTypeBuilder(Type type) + private bool IsConstructedFromTypeBuilder(Type type) { if (type.IsConstructedGenericType) { - return type.GetGenericTypeDefinition() is TypeBuilderImpl || ContainsTypeBuilder(type.GetGenericArguments()); + return (type.GetGenericTypeDefinition() is TypeBuilderImpl tb && Equals(tb.Module)) || + ContainsTypeBuilder(type.GetGenericArguments()); } - Type? elementType = type.GetElementType(); - if (elementType is not null) + if (type.HasElementType) { - return (elementType is TypeBuilderImpl) || IsConstructedFromTypeBuilder(elementType); + Type elementType = type.GetElementType()!; + return (elementType is TypeBuilderImpl tbi && Equals(tbi.Module)) || IsConstructedFromTypeBuilder(elementType); } return false; } - internal static bool ContainsTypeBuilder(Type[] genericArguments) + internal bool ContainsTypeBuilder(Type[] genericArguments) { foreach (Type type in genericArguments) { - if (type is TypeBuilderImpl || type is GenericTypeParameterBuilderImpl) + if ((type is TypeBuilderImpl tb && Equals(tb.Module)) || (type is GenericTypeParameterBuilderImpl gtb && Equals(gtb.Module))) { return true; } @@ -1154,7 +1155,7 @@ internal EntityHandle TryGetTypeHandle(Type type) internal EntityHandle TryGetConstructorHandle(ConstructorInfo constructor) { - if (constructor is ConstructorBuilderImpl cb) + if (constructor is ConstructorBuilderImpl cb && Equals(cb.Module)) { return cb._methodBuilder._handle; } @@ -1166,7 +1167,7 @@ internal EntityHandle TryGetConstructorHandle(ConstructorInfo constructor) internal EntityHandle TryGetMethodHandle(MethodInfo method) { - if (method is MethodBuilderImpl mb) + if (method is MethodBuilderImpl mb && Equals(mb.Module)) { return mb._handle; } @@ -1180,11 +1181,11 @@ internal EntityHandle TryGetMethodHandle(MethodInfo method) return GetHandleForMember(method); } - private static bool IsArrayMethodTypeIsTypeBuilder(MethodInfo method) => method is ArrayMethod arrayMethod && - arrayMethod.DeclaringType!.GetElementType() is TypeBuilderImpl; + private bool IsArrayMethodTypeIsTypeBuilder(MethodInfo method) => method is ArrayMethod arrayMethod && + arrayMethod.DeclaringType!.GetElementType() is TypeBuilderImpl tb && Equals(tb.Module); - private static bool IsConstructedFromMethodBuilderOrTypeBuilder(MethodInfo method) => method.IsConstructedGenericMethod && - (method.GetGenericMethodDefinition() is MethodBuilderImpl || ContainsTypeBuilder(method.GetGenericArguments())); + private bool IsConstructedFromMethodBuilderOrTypeBuilder(MethodInfo method) => method.IsConstructedGenericMethod && + ((method.GetGenericMethodDefinition() is MethodBuilderImpl mb && Equals(mb.Module)) || ContainsTypeBuilder(method.GetGenericArguments())); internal EntityHandle TryGetMethodHandle(MethodInfo method, Type[] optionalParameterTypes) { @@ -1194,7 +1195,7 @@ internal EntityHandle TryGetMethodHandle(MethodInfo method, Type[] optionalParam throw new InvalidOperationException(SR.InvalidOperation_NotAVarArgCallingConvention); } - if (method is MethodBuilderImpl mb) + if (method is MethodBuilderImpl mb && Equals(mb.Module)) { return mb._handle; } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 4a48349359e71..469b229b20dc7 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 @@ -212,7 +212,7 @@ private ConstructorBuilderImpl DefineDefaultConstructorInternal(MethodAttributes // Get the parent class's default constructor and add it to the IL ConstructorInfo? con; if (_typeParent!.IsConstructedGenericType && - (_typeParent.GetGenericTypeDefinition() is TypeBuilderImpl || ModuleBuilderImpl.ContainsTypeBuilder(_typeParent.GetGenericArguments()))) + (_typeParent.GetGenericTypeDefinition() is TypeBuilderImpl || _module.ContainsTypeBuilder(_typeParent.GetGenericArguments()))) { // When TypeBuilder involved need to construct the parent constructor using TypeBuilder.GetConstructor() static method con = GetConstructor(_typeParent, _typeParent.GetGenericTypeDefinition().GetConstructor( diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs index 7496a9c24193d..2b155556c5f2a 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -643,7 +643,7 @@ void Main(int a) ILGenerator il = methodMultiply.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); - il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Mul); il.Emit(OpCodes.Ret); @@ -2908,5 +2908,141 @@ public void ExplicitFieldOffsetSavesAndLoads() var assembly = AssemblyLoadContext.Default.LoadFromStream(stream); var method = assembly.GetType("ExplicitLayoutType")!; } + + [Fact] + public void ReferenceMemberInOtherGeneratedAssembly() + { + using (TempFile file = TempFile.Create()) + using (TempFile file2 = TempFile.Create()) + { + PersistedAssemblyBuilder ab1 = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly1"), typeof(object).Assembly); + ModuleBuilder mb1 = ab1.DefineDynamicModule("Module1"); + TypeBuilder type1 = mb1.DefineType("Type1", TypeAttributes.Public); + MethodBuilder method = type1.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + + ILGenerator ilGenerator = method.GetILGenerator(); + int expectedReturn = 5; + ilGenerator.Emit(OpCodes.Ldc_I4, expectedReturn); + ilGenerator.Emit(OpCodes.Ret); + type1.CreateType(); + ab1.Save(file.Path); + + PersistedAssemblyBuilder ab2 = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly2"), typeof(object).Assembly); + ModuleBuilder mb2 = ab2.DefineDynamicModule("Module2"); + TypeBuilder type2 = mb2.DefineType("Type2", TypeAttributes.Public); + MethodBuilder method2 = type2.DefineMethod("TestMethod2", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + + ILGenerator ilGenerator2 = method2.GetILGenerator(); + ilGenerator2.Emit(OpCodes.Call, method); + ilGenerator2.Emit(OpCodes.Ret); + type2.CreateType(); + ab2.Save(file2.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file2.Path).GetType("Type2"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("TestMethod2")!; + Assert.Equal(expectedReturn, methodFromDisk.Invoke(null, null)); + tlc.Unload(); + } + } + + [Fact] + public void CrossReferenceGeneratedAssemblyMembers() + { + using (TempFile file = TempFile.Create()) + using (TempFile file2 = TempFile.Create()) + { + PersistedAssemblyBuilder ab1 = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly1"), typeof(object).Assembly); + ModuleBuilder mb1 = ab1.DefineDynamicModule("Module1"); + TypeBuilder type1 = mb1.DefineType("Type1", TypeAttributes.Public); + MethodBuilder method = type1.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + int expectedReturn = 5; + + PersistedAssemblyBuilder ab2 = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly2"), typeof(object).Assembly); + ModuleBuilder mb2 = ab2.DefineDynamicModule("Module2"); + TypeBuilder type2 = mb2.DefineType("Type2", TypeAttributes.Public); + MethodBuilder method2 = type2.DefineMethod("TestMethod2", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); + FieldBuilder field = type2.DefineField("Field2", typeof(int), FieldAttributes.Public | FieldAttributes.Static); + ILGenerator staticCtorIL = type2.DefineTypeInitializer().GetILGenerator(); + staticCtorIL.Emit(OpCodes.Ldc_I4, expectedReturn); + staticCtorIL.Emit(OpCodes.Stsfld, field); + staticCtorIL.Emit(OpCodes.Ret); + ILGenerator ilGenerator2 = method2.GetILGenerator(); + ilGenerator2.Emit(OpCodes.Call, method); + ilGenerator2.Emit(OpCodes.Ret); + type2.CreateType(); + + ILGenerator ilGenerator = method.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldsfld, field); + ilGenerator.Emit(OpCodes.Ret); + type1.CreateType(); + + ab1.Save(file.Path); + ab2.Save(file2.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file2.Path).GetType("Type2"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("TestMethod2")!; + Assert.Equal(expectedReturn, methodFromDisk.Invoke(null, null)); + tlc.Unload(); + } + } + + [Fact] + public void ReferenceGenericMembersInOtherGeneratedAssembly() + { + using (TempFile file = TempFile.Create()) + using (TempFile file2 = TempFile.Create()) + { + PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters(["T"]); + ConstructorBuilder ctor = type.DefineDefaultConstructor(MethodAttributes.PrivateScope | MethodAttributes.Public | + MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); + FieldBuilder myField = type.DefineField("Field", typeParams[0], FieldAttributes.Public); + MethodBuilder genericMethod = type.DefineMethod("GM", MethodAttributes.Public | MethodAttributes.Static); + GenericTypeParameterBuilder[] methodParams = genericMethod.DefineGenericParameters("U"); + genericMethod.SetSignature(methodParams[0], null, null, new[] { methodParams[0] }, null, null); + + ILGenerator ilg = genericMethod.GetILGenerator(); + Type SampleOfU = type.MakeGenericType(methodParams[0]); + ConstructorInfo ctorOfU = TypeBuilder.GetConstructor(SampleOfU, ctor); + ilg.Emit(OpCodes.Newobj, ctorOfU); + ilg.Emit(OpCodes.Dup); + ilg.Emit(OpCodes.Ldarg_0); + FieldInfo FieldOfU = TypeBuilder.GetField(SampleOfU, myField); + ilg.Emit(OpCodes.Stfld, FieldOfU); + ilg.Emit(OpCodes.Dup); + ilg.Emit(OpCodes.Ldfld, FieldOfU); + ilg.Emit(OpCodes.Box, methodParams[0]); + MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", [typeof(object)]); + ilg.EmitCall(OpCodes.Call, writeLineObj, null); + ilg.Emit(OpCodes.Ldfld, FieldOfU); + ilg.Emit(OpCodes.Ret); + type.CreateType(); + ab.Save(file.Path); + + PersistedAssemblyBuilder ab2 = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly2"), typeof(object).Assembly); + TypeBuilder type2 = ab2.DefineDynamicModule("MyModule2").DefineType("Type2", TypeAttributes.Class | TypeAttributes.Public); + MethodBuilder method2 = type2.DefineMethod("Method2", MethodAttributes.Public | MethodAttributes.Static, typeof(string), Type.EmptyTypes); + ilg = method2.GetILGenerator(); + Type SampleOfInt = type.MakeGenericType(typeof(string)); + MethodInfo SampleOfIntGM = TypeBuilder.GetMethod(SampleOfInt, genericMethod); + MethodInfo GMOfString = SampleOfIntGM.MakeGenericMethod(typeof(string)); + ilg.Emit(OpCodes.Ldstr, "Hello, world!"); + ilg.EmitCall(OpCodes.Call, GMOfString, null); + ilg.Emit(OpCodes.Ret); + type2.CreateType(); + ab2.Save(file2.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file2.Path).GetType("Type2"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("Method2")!; + Assert.Equal("Hello, world!", methodFromDisk.Invoke(null, null)); + tlc.Unload(); + } + } } }