Skip to content

Commit

Permalink
Fix IL reference tokens to another generated assembly members (#107691)
Browse files Browse the repository at this point in the history
Co-authored-by: Buyaa Namnan <bunamnan@microsoft.com>
  • Loading branch information
github-actions[bot] and buyaa-n authored Sep 11, 2024
1 parent 3b4a3e8 commit 8dc3e06
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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)
{
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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();
}
}
}
}

0 comments on commit 8dc3e06

Please sign in to comment.