Skip to content

Commit

Permalink
Support generic type parameter when created with TypeBuilder (dotnet#…
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter authored Feb 21, 2025
1 parent 36fb5c9 commit 931d75f
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3568,18 +3568,18 @@ public override Type[] GetGenericArguments()
}

[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 Type MakeGenericType(Type[] instantiation)
public override Type MakeGenericType(Type[] typeArguments)
{
ArgumentNullException.ThrowIfNull(instantiation);
ArgumentNullException.ThrowIfNull(typeArguments);

if (!IsGenericTypeDefinition)
throw new InvalidOperationException(SR.Format(SR.Arg_NotGenericTypeDefinition, this));

RuntimeType[] genericParameters = GetGenericArgumentsInternal();
if (genericParameters.Length != instantiation.Length)
throw new ArgumentException(SR.Argument_GenericArgsCount, nameof(instantiation));
if (genericParameters.Length != typeArguments.Length)
throw new ArgumentException(SR.Argument_GenericArgsCount, nameof(typeArguments));

if (instantiation.Length == 1 && instantiation[0] is RuntimeType rt)
if (typeArguments.Length == 1 && typeArguments[0] is RuntimeType rt)
{
ThrowIfTypeNeverValidGenericArgument(rt);
try
Expand All @@ -3593,13 +3593,13 @@ public override Type MakeGenericType(Type[] instantiation)
}
}

RuntimeType[] instantiationRuntimeType = new RuntimeType[instantiation.Length];
RuntimeType[] instantiationRuntimeType = new RuntimeType[typeArguments.Length];

bool foundSigType = false;
bool foundNonRuntimeType = false;
for (int i = 0; i < instantiation.Length; i++)
for (int i = 0; i < typeArguments.Length; i++)
{
Type instantiationElem = instantiation[i] ?? throw new ArgumentNullException();
Type instantiationElem = typeArguments[i] ?? throw new ArgumentNullException();
RuntimeType? rtInstantiationElem = instantiationElem as RuntimeType;

if (rtInstantiationElem == null)
Expand All @@ -3617,9 +3617,9 @@ public override Type MakeGenericType(Type[] instantiation)
if (foundNonRuntimeType)
{
if (foundSigType)
return new SignatureConstructedGenericType(this, instantiation);
return new SignatureConstructedGenericType(this, typeArguments);

return Reflection.Emit.TypeBuilderInstantiation.MakeGenericType(this, (Type[])(instantiation.Clone()));
return Reflection.Emit.TypeBuilderInstantiation.MakeGenericType(this, (Type[])(typeArguments.Clone()));
}

SanityCheckGenericArguments(instantiationRuntimeType, genericParameters);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.Diagnostics;
using System.Text;

namespace System.Reflection
Expand All @@ -11,8 +12,9 @@ internal sealed class SignatureConstructedGenericType : SignatureType
// intended user of this constructor.
internal SignatureConstructedGenericType(Type genericTypeDefinition, Type[] typeArguments)
{
ArgumentNullException.ThrowIfNull(genericTypeDefinition);
ArgumentNullException.ThrowIfNull(typeArguments);
Debug.Assert(genericTypeDefinition != null);
Debug.Assert(typeArguments != null);
Debug.Assert(genericTypeDefinition.IsGenericTypeDefinition);

typeArguments = (Type[])(typeArguments.Clone());
for (int i = 0; i < typeArguments.Length; i++)
Expand All @@ -30,6 +32,7 @@ internal SignatureConstructedGenericType(Type genericTypeDefinition, Type[] type
protected sealed override bool IsArrayImpl() => false;
protected sealed override bool IsByRefImpl() => false;
public sealed override bool IsByRefLike => _genericTypeDefinition.IsByRefLike;
public sealed override bool IsEnum => _genericTypeDefinition.IsEnum;
protected sealed override bool IsPointerImpl() => false;
public sealed override bool IsSZArray => false;
public sealed override bool IsVariableBoundArray => false;
Expand All @@ -50,6 +53,7 @@ public sealed override bool ContainsGenericParameters
}
}

protected sealed override bool IsValueTypeImpl() => _genericTypeDefinition.IsValueType;
internal sealed override SignatureType? ElementType => null;
public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
public sealed override Type GetGenericTypeDefinition() => _genericTypeDefinition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ protected SignatureGenericParameterType(int position)
protected sealed override bool IsArrayImpl() => false;
protected sealed override bool IsByRefImpl() => false;
public sealed override bool IsByRefLike => false;
public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsPointerImpl() => false;
public sealed override bool IsSZArray => false;
public sealed override bool IsVariableBoundArray => false;
public sealed override bool IsConstructedGenericType => false;
public sealed override bool IsGenericParameter => true;
public abstract override bool IsGenericMethodParameter { get; }
public sealed override bool ContainsGenericParameters => true;
protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);

internal sealed override SignatureType? ElementType => null;
public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protected SignatureHasElementType(SignatureType elementType)
protected abstract override bool IsArrayImpl();
protected abstract override bool IsByRefImpl();
public sealed override bool IsByRefLike => false;
public sealed override bool IsEnum => false;
protected abstract override bool IsPointerImpl();
public abstract override bool IsSZArray { get; }
public abstract override bool IsVariableBoundArray { get; }
Expand All @@ -27,6 +28,7 @@ protected SignatureHasElementType(SignatureType elementType)
public sealed override bool IsGenericTypeParameter => false;
public sealed override bool IsGenericMethodParameter => false;
public sealed override bool ContainsGenericParameters => _elementType.ContainsGenericParameters;
protected sealed override bool IsValueTypeImpl() => false;

internal sealed override SignatureType? ElementType => _elementType;
public abstract override int GetArrayRank();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public sealed override Type MakeArrayType(int rank)
public sealed override Type[] FindInterfaces(TypeFilter filter, object? filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsContextfulImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType);
public abstract override bool IsEnum { get; }
public sealed override bool IsEquivalentTo([NotNullWhen(true)] Type? other) => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override bool IsInstanceOfType([NotNullWhen(true)] object? o) => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsMarshalByRefImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
Expand All @@ -198,7 +198,7 @@ public sealed override Type MakeArrayType(int rank)
[Obsolete(Obsoletions.LegacyFormatterMessage, DiagnosticId = Obsoletions.LegacyFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public sealed override bool IsSerializable => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override bool IsSubclassOf(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected abstract override bool IsValueTypeImpl();

public sealed override StructLayoutAttribute StructLayoutAttribute => throw new NotSupportedException(SR.NotSupported_SignatureType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public TypeDelegator([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.
public override string? AssemblyQualifiedName => typeImpl.AssemblyQualifiedName;
public override Type? BaseType => typeImpl.BaseType;

public override int GetArrayRank() => typeImpl.GetArrayRank();

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder,
CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers)
Expand Down
11 changes: 10 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,16 @@ public virtual Array GetEnumValues()

public virtual Type MakePointerType() => throw new NotSupportedException();

public static Type MakeGenericSignatureType(Type genericTypeDefinition, params Type[] typeArguments) => new SignatureConstructedGenericType(genericTypeDefinition, typeArguments);
public static Type MakeGenericSignatureType(Type genericTypeDefinition, params Type[] typeArguments)
{
ArgumentNullException.ThrowIfNull(genericTypeDefinition);
ArgumentNullException.ThrowIfNull(typeArguments);

if (!genericTypeDefinition.IsGenericTypeDefinition)
throw new ArgumentException(SR.Format(SR.Arg_NotGenericTypeDefinition, genericTypeDefinition), nameof(genericTypeDefinition));

return new SignatureConstructedGenericType(genericTypeDefinition, typeArguments);
}

public static Type MakeGenericMethodParameter(int position)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,8 +733,6 @@ public void MethodBuilderGetParametersReturnParameterTest()
Assert.True(method3.ReturnParameter.IsRetval);
}

public class BaseType<T> { }

[Fact]
public void GenericTypeWithTypeBuilderGenericParameter_UsedAsParent()
{
Expand All @@ -749,9 +747,53 @@ public void GenericTypeWithTypeBuilderGenericParameter_UsedAsParent()

Assert.NotNull(type.GetConstructor(Type.EmptyTypes)); // Default constructor created
}

[Fact]
public void CreateGenericTypeFromMetadataLoadContextSignatureTypes()
{
using TempFile file = TempFile.Create();

PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder module);

TypeBuilder childType = module.DefineType("Child");
TypeBuilder parentType = module.DefineType("Parent");

// Get List<T> from MLC and make both reference and value type fields from that.
using MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver());
Type listOfTType = mlc.CoreAssembly.GetType(typeof(List<>).FullName!);

// Currently MakeGenericSignatureType() must be used instead of MakeGenericType() for
// generic type parameters created with TypeBuilder.
Assert.Throws<ArgumentException>(() => listOfTType.MakeGenericType(childType));
Type listOfReferenceTypes = Type.MakeGenericSignatureType(listOfTType, childType);
parentType.DefineField("ReferenceTypeChildren", listOfReferenceTypes, FieldAttributes.Public);

// Pre-existing types can use MakeGenericType().
Type int32Type = mlc.CoreAssembly.GetType(typeof(int).FullName);
Type listOfValueTypes = listOfTType.MakeGenericType(int32Type);
parentType.DefineField("ValueTypeChildren", listOfValueTypes, FieldAttributes.Public);

parentType.CreateType();
childType.CreateType();

// Save and load the dynamically created assembly.
ab.Save(file.Path);
Module mlcModule = mlc.LoadFromAssemblyPath(file.Path).Modules.First();

Assert.Equal("Child", mlcModule.GetTypes()[0].Name);
Assert.Equal("Parent", mlcModule.GetTypes()[1].Name);

FieldInfo[] fields = mlcModule.GetTypes()[1].GetFields(BindingFlags.Public | BindingFlags.Instance);
Assert.Equal("ReferenceTypeChildren", fields[0].Name);
Assert.False(fields[0].FieldType.GetGenericArguments()[0].IsValueType);
Assert.Equal("ValueTypeChildren", fields[1].Name);
Assert.True(fields[1].FieldType.GetGenericArguments()[0].IsValueType);
}
}

// Test Types
public class BaseType<T> { }

public interface INoMethod
{
}
Expand Down
Loading

0 comments on commit 931d75f

Please sign in to comment.