From e87d5d975b47f96ef1d82841178eee48e32f74a0 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 8 Feb 2024 15:50:01 -0800 Subject: [PATCH] Add implementations for MakePointerType(), MakeArrayType() for GenericTypeParameterBuilder (#97350) * Add implementations for MakePointerType(), MakeArrayType() for GenericTypeParameterBuilder * Fix issue in loading constructed generic method, apply feedbacks * Fix valdiations bugs, issue in constructed generic constuctor reference * Fix more issues related to generic method/type validations and reference, add more test and other implementations * Fix bug with setting constant value for a enum field * Add MethodBuilder.GetArrayMethodCore(...) implementation * Fix bug in Array method, MethodOnTypeBuilderInstantiation and ConstructorInfo reference, remove abstract method validation * Fix token issue with nested generic type, add test for derived type returning different type on method override * Apply suggestions from code review Co-authored-by: Aaron Robinson * Use ActiveIssue attribute for failing test on mono, add comment for enum type case * Rename Get***Handle methods into TryGet***Handle --------- Co-authored-by: Aaron Robinson --- .../ConstructorOnTypeBuilderInstantiation.cs | 2 +- .../Emit/MethodOnTypeBuilderInstantiation.cs | 22 +- .../src/System/Reflection/Emit/SymbolType.cs | 25 +- .../src/Resources/Strings.resx | 12 + .../src/System.Reflection.Emit.csproj | 2 + .../src/System/Reflection/Emit/ArrayMethod.cs | 90 +++++++ .../System/Reflection/Emit/EnumBuilderImpl.cs | 2 +- .../Reflection/Emit/FieldBuilderImpl.cs | 8 +- .../Emit/GenericTypeParameterBuilderImpl.cs | 16 +- .../System/Reflection/Emit/ILGeneratorImpl.cs | 16 +- .../Reflection/Emit/MethodBuilderImpl.cs | 2 +- .../Reflection/Emit/ModuleBuilderImpl.cs | 150 +++++++++--- .../Reflection/Emit/ParameterBuilderImpl.cs | 8 +- .../System/Reflection/Emit/TypeBuilderImpl.cs | 180 +++++++------- .../AssemblySaveCustomAttributeTests.cs | 4 - .../AssemblySaveEnumBuilderTests.cs | 48 ++++ .../AssemblySaveILGeneratorTests.cs | 197 ++++++++++++++++ .../AssemblySaveModuleBuilderTests.cs | 67 ++++++ .../AssemblySaveTypeBuilderAPIsTests.cs | 219 +++++++++++++++--- .../AssemblySaveTypeBuilderTests.cs | 30 +++ 20 files changed, 932 insertions(+), 168 deletions(-) create mode 100644 src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ArrayMethod.cs 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 c4e80153cf1a3..49c9835211a2f 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 @@ -58,7 +58,7 @@ public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? bind public override CallingConventions CallingConvention => _ctor.CallingConvention; public override Type[] GetGenericArguments() { return _ctor.GetGenericArguments(); } public override bool IsGenericMethodDefinition => false; - public override bool ContainsGenericParameters => false; + public override bool ContainsGenericParameters => _type.ContainsGenericParameters; public override bool IsGenericMethod => false; #endregion 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 1afed496f67be..a1256cf7faab6 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 @@ -79,12 +79,26 @@ public override bool ContainsGenericParameters { get { - if (_method.ContainsGenericParameters) + if (_method.ContainsGenericParameters || _type.ContainsGenericParameters) + { return true; - if (!_method.IsGenericMethodDefinition) - throw new NotSupportedException(); + } - return _method.ContainsGenericParameters; + if (!IsGenericMethod) + { + return false; + } + + Type[] args = GetGenericArguments(); + for (int i = 0; i < args.Length; i++) + { + if (args[i].ContainsGenericParameters) + { + return true; + } + } + + return false; } } [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.")] diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs index ecf9a39418232..278d15f5fe610 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs @@ -271,18 +271,30 @@ public override Type MakeByRefType() return FormCompoundType(_format + "&", _baseType, 0)!; } + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public override Type MakeArrayType() { return FormCompoundType(_format + "[]", _baseType, 0)!; } + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public override Type MakeArrayType(int rank) { - string s = GetRankString(rank); + string s = FormatRank(rank); SymbolType? st = FormCompoundType(_format + s, _baseType, 0) as SymbolType; return st!; } + internal static string FormatRank(int rank) + { + if (rank <= 0) + { + throw new IndexOutOfRangeException(); + } + + return rank == 1 ? "[*]" : "[" + new string(',', rank - 1) + "]"; + } + public override int GetArrayRank() { if (!IsArray) @@ -440,18 +452,25 @@ public override Type GetNestedType(string name, BindingFlags bindingAttr) throw new NotSupportedException(SR.NotSupported_NonReflectedType); } - [DynamicallyAccessedMembers(GetAllMembers)] + [DynamicallyAccessedMembers(GetAllMembersInternal)] public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) { throw new NotSupportedException(SR.NotSupported_NonReflectedType); } - [DynamicallyAccessedMembers(GetAllMembers)] + [DynamicallyAccessedMembers(GetAllMembersInternal)] public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { throw new NotSupportedException(SR.NotSupported_NonReflectedType); } + private const DynamicallyAccessedMemberTypes GetAllMembersInternal = DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) { throw new NotSupportedException(SR.NotSupported_NonReflectedType); diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 7a27f2807a39e..0688551573229 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -288,4 +288,16 @@ MetadataToken for the member is not generated until the assembly saved. + + Incorrect signature format. + + + Must be an array type. + + + Not supported in a non-reflected type. + + + Not supported in an array method of a type definition that is not complete. + \ 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 b17d705bb3b02..3b2f29ec3672f 100644 --- a/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj +++ b/src/libraries/System.Reflection.Emit/src/System.Reflection.Emit.csproj @@ -9,7 +9,9 @@ + + diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ArrayMethod.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ArrayMethod.cs new file mode 100644 index 0000000000000..e619c24ca112d --- /dev/null +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ArrayMethod.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System.Reflection.Emit +{ + internal sealed class ArrayMethod : MethodInfo + { + #region Private Data Members + private readonly ModuleBuilder _module; + private readonly Type _containingType; + private readonly string _name; + private readonly CallingConventions _callingConvention; + private readonly Type _returnType; + private readonly Type[] _parameterTypes; + #endregion + + #region Constructor + // This is a kind of MethodInfo to represent methods for array type of unbaked type + internal ArrayMethod(ModuleBuilder module, Type arrayClass, string methodName, + CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) + { + _returnType = returnType ?? typeof(void); + if (parameterTypes != null) + { + _parameterTypes = new Type[parameterTypes.Length]; + for (int i = 0; i < parameterTypes.Length; i++) + { + ArgumentNullException.ThrowIfNull(_parameterTypes[i] = parameterTypes[i], nameof(parameterTypes)); + } + } + else + { + _parameterTypes = Type.EmptyTypes; + } + + _module = module; + _containingType = arrayClass; + _name = methodName; + _callingConvention = callingConvention; + } + #endregion + + #region Internal Members + internal Type[] ParameterTypes => _parameterTypes; + #endregion + + #region MemberInfo Overrides + public override Module Module => _module; + + public override Type? ReflectedType => _containingType; + + public override string Name => _name; + + public override Type? DeclaringType => _containingType; + #endregion + + #region MethodBase Overrides + public override ParameterInfo[] GetParameters() => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + + public override MethodImplAttributes GetMethodImplementationFlags() => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + + public override MethodAttributes Attributes => MethodAttributes.PrivateScope; + + public override CallingConventions CallingConvention => _callingConvention; + + public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + #endregion + + #region MethodInfo Overrides + public override Type ReturnType => _returnType; + + public override ICustomAttributeProvider ReturnTypeCustomAttributes => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + + public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + + public override MethodInfo GetBaseDefinition() => this; + #endregion + + #region ICustomAttributeProvider Implementation + public override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + + public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SymbolMethod); + #endregion + } +} diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs index edb95e889846f..1d8854bb43d9a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/EnumBuilderImpl.cs @@ -32,7 +32,7 @@ protected override FieldBuilder DefineLiteralCore(string literalName, object? li { FieldBuilder fieldBuilder = _typeBuilder.DefineField( literalName, - _typeBuilder, + this, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal); fieldBuilder.SetConstant(literalValue); return fieldBuilder; 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 35b8e40784d0e..f9f85f90b2aa5 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 @@ -74,8 +74,14 @@ internal static void ValidateDefaultValueType(object? defaultValue, Type destina { underlyingType = enumBldr.GetEnumUnderlyingType(); - if (sourceType != enumBldr._typeBuilder.UnderlyingSystemType && sourceType != underlyingType) + if (sourceType != enumBldr._typeBuilder.UnderlyingSystemType && + sourceType != underlyingType && + // If the source type is an enum, should not throw when the underlying types match + sourceType.IsEnum && + sourceType.GetEnumUnderlyingType() != underlyingType) + { throw new ArgumentException(SR.Argument_ConstantDoesntMatch); + } } else if (destinationType is TypeBuilderImpl typeBldr) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs index fccd56de74e79..046043177986b 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs @@ -83,7 +83,7 @@ public override Type[] GetGenericParameterConstraints() => public override bool IsGenericType => false; public override bool IsGenericParameter => true; public override bool IsConstructedGenericType => false; - public override bool ContainsGenericParameters => _type.ContainsGenericParameters; + public override bool ContainsGenericParameters => false; public override MethodBase? DeclaringMethod => _type.DeclaringMethod; public override Type? BaseType => _parent; public override RuntimeTypeHandle TypeHandle => throw new NotSupportedException(); @@ -137,5 +137,19 @@ public override Type[] GetGenericParameterConstraints() => public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(); [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public override object InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) => throw new NotSupportedException(); + + public override Type MakePointerType() => + SymbolType.FormCompoundType("*", this, 0)!; + + public override Type MakeByRefType() => + SymbolType.FormCompoundType("&", this, 0)!; + + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] + public override Type MakeArrayType() => + SymbolType.FormCompoundType("[]", this, 0)!; + + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] + public override Type MakeArrayType(int rank) => + SymbolType.FormCompoundType(SymbolType.FormatRank(rank), this, 0)!; } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index 2862b8774e295..dcb92654e124b 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -418,7 +418,7 @@ public override void Emit(OpCode opcode, ConstructorInfo con) EmitOpcode(opcode); UpdateStackSize(stackChange); - WriteOrReserveToken(_moduleBuilder.GetConstructorHandle(con), con); + WriteOrReserveToken(_moduleBuilder.TryGetConstructorHandle(con), con); } private void WriteOrReserveToken(EntityHandle handle, object member) @@ -553,7 +553,7 @@ public override void Emit(OpCode opcode, FieldInfo field) ArgumentNullException.ThrowIfNull(field); EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetFieldHandle(field), field); + WriteOrReserveToken(_moduleBuilder.TryGetFieldHandle(field), field); } public override void Emit(OpCode opcode, MethodInfo meth) @@ -567,7 +567,7 @@ public override void Emit(OpCode opcode, MethodInfo meth) else { EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetMethodHandle(meth), meth); + WriteOrReserveToken(_moduleBuilder.TryGetMethodHandle(meth), meth); } } @@ -576,7 +576,7 @@ public override void Emit(OpCode opcode, Type cls) ArgumentNullException.ThrowIfNull(cls); EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetTypeHandle(cls), cls); + WriteOrReserveToken(_moduleBuilder.TryGetTypeHandle(cls), cls); } public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes) @@ -592,11 +592,11 @@ public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? opti UpdateStackSize(GetStackChange(opcode, methodInfo, optionalParameterTypes)); if (optionalParameterTypes == null || optionalParameterTypes.Length == 0) { - WriteOrReserveToken(_moduleBuilder.GetMethodHandle(methodInfo), methodInfo); + WriteOrReserveToken(_moduleBuilder.TryGetMethodHandle(methodInfo), methodInfo); } else { - WriteOrReserveToken(_moduleBuilder.GetMethodHandle(methodInfo, optionalParameterTypes), + WriteOrReserveToken(_moduleBuilder.TryGetMethodHandle(methodInfo, optionalParameterTypes), new KeyValuePair(methodInfo, optionalParameterTypes)); } } @@ -616,6 +616,10 @@ private static int GetStackChange(OpCode opcode, MethodInfo methodInfo, Type[]? { stackChange -= builder.ParameterCount; } + else if (methodInfo is ArrayMethod sm) + { + stackChange -= sm.ParameterTypes.Length; + } else { stackChange -= methodInfo.GetParameters().Length; 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 a20e067e6b006..fe6b7c5e3f1b3 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 @@ -243,7 +243,7 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq public override CallingConventions CallingConvention => _callingConventions; public override Type? DeclaringType => _declaringType._isHiddenGlobalType ? null : _declaringType; public override Module Module => _module; - public override bool ContainsGenericParameters => throw new NotSupportedException(); + public override bool ContainsGenericParameters => _typeParameters != null; public override bool IsGenericMethod => _typeParameters != null; public override bool IsGenericMethodDefinition => _typeParameters != null; public override bool IsSecurityCritical => true; 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 cf0725c9673e0..de6cb832bb96e 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 @@ -519,15 +519,22 @@ private MethodSpecificationHandle AddMethodSpecification(EntityHandle methodHand method: methodHandle, instantiation: _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetMethodSpecificationSignature(genericArgs, this))); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:RequiresDynamicCode", Justification = "Test")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresUnreferencedCode", Justification = "Test")] private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo) { if (!_memberReferences.TryGetValue(memberInfo, out var memberHandle)) { - MemberInfo member = GetOriginalMemberIfConstructedType(memberInfo); - switch (member) + switch (memberInfo) { case FieldInfo field: - memberHandle = AddMemberReference(field.Name, GetTypeHandle(memberInfo.DeclaringType!), + Type declaringType = field.DeclaringType!; + if (field.DeclaringType!.IsGenericTypeDefinition) + { + //The type of the field has to be fully instantiated type. + declaringType = declaringType.MakeGenericType(declaringType.GetGenericArguments()); + } + memberHandle = AddMemberReference(field.Name, GetTypeHandle(declaringType), MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); break; case ConstructorInfo ctor: @@ -538,8 +545,13 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo) { memberHandle = AddMethodSpecification(GetMemberHandle(method.GetGenericMethodDefinition()), method.GetGenericArguments()); } + else if (method is ArrayMethod sm) + { + memberHandle = AddMemberReference(sm.Name, GetTypeHandle(sm.DeclaringType!), GetMethodArrayMethodSignature(sm)); + } else { + method = (MethodInfo)GetOriginalMemberIfConstructedType(method); memberHandle = AddMemberReference(method.Name, GetTypeHandle(memberInfo.DeclaringType!), GetMethodSignature(method, null)); } break; @@ -573,6 +585,12 @@ private BlobBuilder GetMethodSignature(MethodInfo method, Type[]? optionalParame MetadataSignatureHelper.GetMethodSignature(this, ParameterTypes(method.GetParameters()), method.ReturnType, GetSignatureConvention(method.CallingConvention), method.GetGenericArguments().Length, !method.IsStatic, optionalParameterTypes); + private BlobBuilder GetMethodArrayMethodSignature(ArrayMethod method) => MetadataSignatureHelper.GetMethodSignature( + this, method.ParameterTypes, method.ReturnType, GetSignatureConvention(method.CallingConvention), isInstance: IsInstance(method.CallingConvention)); + + private static bool IsInstance(CallingConventions callingConvention) => + callingConvention.HasFlag(CallingConventions.HasThis) || callingConvention.HasFlag(CallingConventions.ExplicitThis) ? true : false; + internal static SignatureCallingConvention GetSignatureConvention(CallingConventions callingConvention) { SignatureCallingConvention convention = SignatureCallingConvention.Default; @@ -585,15 +603,16 @@ internal static SignatureCallingConvention GetSignatureConvention(CallingConvent return convention; } - private static MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo) + private static MemberInfo GetOriginalMemberIfConstructedType(MethodBase methodBase) { - Type declaringType = memberInfo.DeclaringType!; - if (declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl) + Type declaringType = methodBase.DeclaringType!; + if (declaringType.IsConstructedGenericType && !methodBase.ContainsGenericParameters && + declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl) { - return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo); + return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(methodBase); } - return memberInfo; + return methodBase; } private static Type[] ParameterTypes(ParameterInfo[] parameterInfos) @@ -652,8 +671,20 @@ private void AddGenericTypeParametersAndConstraintsCustomAttributes(EntityHandle } } - private void AddDefaultValue(EntityHandle parentHandle, object? defaultValue) => + private void AddDefaultValue(EntityHandle parentHandle, object? defaultValue) + { + if (defaultValue != null) + { + Type type = defaultValue.GetType(); + if (type.IsEnum) + { + // ECMA spec II.22.9: in case of enum the constant type shall match the underlying type of that enum. + defaultValue = Convert.ChangeType(defaultValue, type.GetEnumUnderlyingType()); + } + } + _metadataBuilder.AddConstant(parent: parentHandle, value: defaultValue); + } private void AddMethodSemantics(EntityHandle parentHandle, MethodSemanticsAttributes attribute, MethodDefinitionHandle methodHandle) => _metadataBuilder.AddMethodSemantics( @@ -756,11 +787,15 @@ internal EntityHandle GetTypeHandle(Type type) { if (type is TypeBuilderImpl tb && Equals(tb.Module)) { + Debug.Assert(tb.IsCreated()); + return tb._handle; } if (type is EnumBuilderImpl eb && Equals(eb.Module)) { + Debug.Assert(eb._typeBuilder.IsCreated()); + return eb._typeBuilder._handle; } @@ -774,6 +809,11 @@ internal EntityHandle GetMemberHandle(MemberInfo member) return tb._handle; } + if (member is EnumBuilderImpl en && Equals(en.Module)) + { + return en._typeBuilder._handle; + } + if (member is Type type) { return GetTypeReferenceOrSpecificationHandle(type); @@ -789,7 +829,7 @@ internal EntityHandle GetMemberHandle(MemberInfo member) return ctor._methodBuilder._handle; } - if (member is FieldBuilderImpl fb && Equals(fb.Module)) + if (member is FieldBuilderImpl fb && Equals(fb.Module) && !fb.DeclaringType!.IsGenericTypeDefinition) { return fb._handle; } @@ -799,11 +839,6 @@ internal EntityHandle GetMemberHandle(MemberInfo member) return prop._handle; } - if (member is EnumBuilderImpl en && Equals(en.Module)) - { - return en._typeBuilder._handle; - } - return GetMemberReferenceHandle(member); } @@ -822,9 +857,9 @@ internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [Dynamic public override Guid ModuleVersionId => _moduleVersionId; public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); - public override int GetFieldMetadataToken(FieldInfo field) => GetTokenForHandle(GetFieldHandle(field)); + public override int GetFieldMetadataToken(FieldInfo field) => GetTokenForHandle(TryGetFieldHandle(field)); - internal EntityHandle GetFieldHandle(FieldInfo field) + internal EntityHandle TryGetFieldHandle(FieldInfo field) { if (field is FieldBuilderImpl fb) { @@ -855,11 +890,50 @@ private EntityHandle GetHandleForMember(MemberInfo member) } private static bool IsConstructedFromNotBakedTypeBuilder(Type type) => type.IsConstructedGenericType && - type.GetGenericTypeDefinition() is TypeBuilderImpl tb && tb._handle == default; + (type.GetGenericTypeDefinition() is TypeBuilderImpl tb && tb._handle == default || + ContainsNotBakedTypeBuilder(type.GetGenericArguments())); + + private static bool ContainsNotBakedTypeBuilder(Type[] genericArguments) + { + foreach (Type type in genericArguments) + { + if (type is TypeBuilderImpl tb && tb._handle == default) + { + return true; + } + + if (IsConstructedFromNotBakedTypeBuilder(type)) + { + return true; + } + } - public override int GetMethodMetadataToken(ConstructorInfo constructor) => GetTokenForHandle(GetConstructorHandle(constructor)); + return false; + } - internal EntityHandle GetConstructorHandle(ConstructorInfo constructor) + internal EntityHandle TryGetTypeHandle(Type type) + { + if (type is TypeBuilderImpl tb && Equals(tb.Module)) + { + return tb._handle; + } + + if (type is EnumBuilderImpl eb && Equals(eb.Module)) + { + return eb._typeBuilder._handle; + } + + if (IsConstructedFromNotBakedTypeBuilder(type)) + { + return default; + } + + return GetTypeReferenceOrSpecificationHandle(type); + } + + public override int GetMethodMetadataToken(ConstructorInfo constructor) => GetTokenForHandle(TryGetConstructorHandle(constructor)); + + internal EntityHandle TryGetConstructorHandle(ConstructorInfo constructor) { if (constructor is ConstructorBuilderImpl cb) { @@ -869,22 +943,31 @@ internal EntityHandle GetConstructorHandle(ConstructorInfo constructor) return GetHandleForMember(constructor); } - public override int GetMethodMetadataToken(MethodInfo method) => GetTokenForHandle(GetMethodHandle(method)); + public override int GetMethodMetadataToken(MethodInfo method) => GetTokenForHandle(TryGetMethodHandle(method)); - internal EntityHandle GetMethodHandle(MethodInfo method) + internal EntityHandle TryGetMethodHandle(MethodInfo method) { if (method is MethodBuilderImpl mb) { return mb._handle; } + if (IsConstructedMethodFromNotBakedMethodBuilder(method) || + IsArrayMethodFromNotBakedTypeBuilder(method)) + { + return default; + } + return GetHandleForMember(method); } + private static bool IsArrayMethodFromNotBakedTypeBuilder(MethodInfo method) => method is ArrayMethod arrayMethod && + arrayMethod.DeclaringType!.GetElementType() is TypeBuilderImpl tb && tb._handle == default; + private static bool IsConstructedMethodFromNotBakedMethodBuilder(MethodInfo method) => - method.IsConstructedGenericMethod && method.GetGenericMethodDefinition() is MethodBuilderImpl mb2 && mb2._handle == default; + method.IsConstructedGenericMethod && method.GetGenericMethodDefinition() is MethodBuilderImpl mb && mb._handle == default; - internal EntityHandle GetMethodHandle(MethodInfo method, Type[] optionalParameterTypes) + internal EntityHandle TryGetMethodHandle(MethodInfo method, Type[] optionalParameterTypes) { if ((method.CallingConvention & CallingConventions.VarArgs) == 0) { @@ -922,7 +1005,7 @@ internal EntityHandle GetMethodHandle(MethodInfo method, Type[] optionalParamete public override int GetStringMetadataToken(string stringConstant) => MetadataTokens.GetToken(_metadataBuilder.GetOrAddUserString(stringConstant)); - public override int GetTypeMetadataToken(Type type) => GetTokenForHandle(GetTypeHandle(type)); + public override int GetTypeMetadataToken(Type type) => GetTokenForHandle(TryGetTypeHandle(type)); protected override void CreateGlobalFunctionsCore() { @@ -1011,7 +1094,22 @@ protected override FieldBuilder DefineUninitializedDataCore(string name, int siz return field; } - protected override MethodInfo GetArrayMethodCore(Type arrayClass, string methodName, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException(); + protected override MethodInfo GetArrayMethodCore(Type arrayClass, string methodName, + CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) + { + if (!arrayClass.IsArray) + { + throw new ArgumentException(SR.Argument_HasToBeArrayClass); + } + + // GetArrayMethod is useful when you have an array of a type whose definition has not been completed and + // you want to access methods defined on Array. For example, you might define a type and want to define a + // method that takes an array of the type as a parameter. In order to access the elements of the array, + // you will need to call methods of the Array class. + + return new ArrayMethod(this, arrayClass, methodName, callingConvention, returnType, parameterTypes); + } + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { _customAttributes ??= new List(); 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 f5b165b62cba8..93040c155f207 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 @@ -30,7 +30,13 @@ public ParameterBuilderImpl(MethodBuilderImpl methodBuilder, int sequence, Param public override int Position => _position; - public override void SetConstant(object? defaultValue) => _defaultValue = defaultValue; + public override void SetConstant(object? defaultValue) + { + Type parameterType = _position == 0 ? _methodBuilder.ReturnType : _methodBuilder.ParameterTypes![_position - 1]; + FieldBuilderImpl.ValidateDefaultValueType(defaultValue, parameterType); + _defaultValue = defaultValue; + _attributes |= ParameterAttributes.HasDefault; + } protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { 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 176a04a217dec..4aea4ab3ee2c6 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 @@ -121,11 +121,6 @@ protected override TypeInfo CreateTypeInfoCore() ValidateMethods(); _isCreated = true; - if (!IsAbstract) - { - ValidateAllAbstractMethodsAreImplemented(); - } - return this; } @@ -165,84 +160,6 @@ private void ValidateMethods() } } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:DynamicallyAccessedMembers", Justification = "Methods are loaded from this TypeBuilder")] - private void ValidateAllAbstractMethodsAreImplemented() - { - if (_interfaces != null) - { - CheckInterfaces(_interfaces.ToArray()); - } - - if (_typeParent != null && _typeParent.IsAbstract) - { - foreach (MethodInfo method in _typeParent.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) - { - if (method.IsAbstract) - { - MethodInfo? targetMethod = GetMethodImpl(method.Name, GetBindingFlags(method), null, method.CallingConvention, GetParameterTypes(method.GetParameters()), null); - - if ((targetMethod == null || targetMethod.IsAbstract) && !FoundInInterfaceMapping(method)) - { - throw new TypeLoadException(SR.Format(SR.TypeLoad_MissingMethod, method, FullName)); - } - } - } - } - } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2065:DynamicallyAccessedMembers", Justification = "Methods are loaded from this TypeBuilder. The interface methods should be available at this point")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:DynamicallyAccessedMembers", Justification = "Methods are loaded from this TypeBuilder")] - private void CheckInterfaces(Type[] _interfaces) - { - foreach (Type interfaceType in _interfaces) - { -#pragma warning disable IL2075 // Analyzer produces a different warning code than illink. The IL2065 suppression takes care of illink: https://github.com/dotnet/runtime/issues/96646 - MethodInfo[] interfaceMethods = interfaceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); -#pragma warning restore IL2075 - for (int i = 0; i < interfaceMethods.Length; i++) - { - MethodInfo interfaceMethod = interfaceMethods[i]; - if (!interfaceMethod.IsAbstract) - { - continue; - } - - MethodInfo? implementedMethod = GetMethodImpl(interfaceMethod.Name, GetBindingFlags(interfaceMethod), null, interfaceMethod.CallingConvention, GetParameterTypes(interfaceMethod.GetParameters()), null); - - if ((implementedMethod == null || implementedMethod.IsAbstract) && !FoundInInterfaceMapping(interfaceMethod)) - { - throw new TypeLoadException(SR.Format(SR.TypeLoad_MissingMethod, interfaceMethod, FullName)); - } - } - - // Check parent interfaces too -#pragma warning disable IL2075 // Analyzer produces a different warning code than illink. The IL2065 suppression takes care of illink: https://github.com/dotnet/runtime/issues/96646 - CheckInterfaces(interfaceType.GetInterfaces()); -#pragma warning restore IL2075 - } - } - - private bool FoundInInterfaceMapping(MethodInfo abstractMethod) - { - if (_methodOverrides == null) - { - return false; - } - - if (_methodOverrides.TryGetValue(abstractMethod.DeclaringType!, out List<(MethodInfo ifaceMethod, MethodInfo targetMethod)>? mapping)) - { - foreach ((MethodInfo ifaceMethod, MethodInfo targetMethod) pair in mapping) - { - if (abstractMethod.Equals(pair.ifaceMethod)) - { - return true; - } - } - } - - return false; - } - internal void ThrowIfCreated() { if (_isCreated) @@ -475,7 +392,7 @@ private void ValidateImplementedMethod(MethodInfo methodInfoBody, MethodInfo met { if ((methodInfoBody.IsVirtual || methodInfoBody.IsStatic) && (methodInfoDeclaration.IsAbstract || methodInfoDeclaration.IsVirtual) && - methodInfoBody.ReturnType.Equals(methodInfoDeclaration.ReturnType)) + methodInfoDeclaration.ReturnType.IsAssignableFrom(methodInfoBody.ReturnType)) { ParameterInfo[] bodyParameters = methodInfoBody.GetParameters(); ParameterInfo[] declarationParameters = methodInfoDeclaration.GetParameters(); @@ -483,7 +400,23 @@ private void ValidateImplementedMethod(MethodInfo methodInfoBody, MethodInfo met { for (int i = 0; i < bodyParameters.Length; i++) { - if (!bodyParameters[i].ParameterType.Equals(declarationParameters[i].ParameterType)) + Type? bodyType = bodyParameters[i].ParameterType; + Type? declType = declarationParameters[i].ParameterType; + + if (bodyType.IsArray != declType.IsArray || + bodyType.IsByRef != declType.IsByRef || + bodyType.IsPointer != declType.IsPointer) + { + throw ArgumentExceptionInvalidMethodOverride(methodInfoDeclaration.Name); + } + + if (bodyType.HasElementType || declType.HasElementType) + { + bodyType = bodyType.GetElementType(); + declType = declType.GetElementType(); + } + + if (bodyType == null || !bodyType.Equals(declType)) { throw ArgumentExceptionInvalidMethodOverride(methodInfoDeclaration.Name); } @@ -820,8 +753,6 @@ internal static BindingFlags GetBindingFlags(MethodInfo method) private static bool MatchesTheFilter(MethodBuilderImpl method, BindingFlags methodFlags, BindingFlags bindingFlags, CallingConventions callConv, Type[]? argumentTypes) { - bindingFlags ^= BindingFlags.DeclaredOnly; - if ((bindingFlags & methodFlags) != methodFlags) { return false; @@ -854,7 +785,23 @@ private static bool MatchesTheFilter(MethodBuilderImpl method, BindingFlags meth for (int i = 0; i < parameterTypes.Length; i++) { - if (argumentTypes[i] != parameterTypes[i]) + Type? argType = argumentTypes[i]; + Type? paramType = parameterTypes[i]; + + if (argType.IsArray != paramType.IsArray || + argType.IsByRef != paramType.IsByRef || + argType.IsPointer != argType.IsPointer) + { + return false; + } + + if (argType.HasElementType || paramType.HasElementType) + { + argType = argType.GetElementType(); + paramType = paramType.GetElementType(); + } + + if (argType == null || !argType.Equals(paramType)) { return false; } @@ -945,10 +892,63 @@ public override MethodInfo[] GetMethods(BindingFlags bindingAttr) } [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] - public override FieldInfo? GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(); + public override FieldInfo? GetField(string name, BindingFlags bindingFlags) + { + ArgumentNullException.ThrowIfNull(name); + ThrowIfNotCreated(); + + FieldInfo? match = null; + StringComparison compare = (bindingFlags & BindingFlags.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + foreach (FieldBuilderImpl fieldInfo in _fieldDefinitions) + { + BindingFlags fieldFlags = GetBindingFlags(fieldInfo); + if (name.Equals(fieldInfo.Name, compare) && (bindingFlags & fieldFlags) == fieldFlags) + { + if (match != null && ReferenceEquals(fieldInfo.DeclaringType, match.DeclaringType)) + { + // TypeBuilder doesn't validate for duplicates when fields are defined, throw if duplicates found. + throw new AmbiguousMatchException(SR.Format(SR.AmbiguousMatch_MemberInfo, fieldInfo.DeclaringType, fieldInfo.Name)); + } + + match = fieldInfo; + } + } + + if (match == null && !bindingFlags.HasFlag(BindingFlags.DeclaredOnly) && _typeParent != null) + { + match = _typeParent.GetField(name, bindingFlags); + } + + return match; + } + + private static BindingFlags GetBindingFlags(FieldBuilderImpl field) + { + BindingFlags bindingFlags = (field.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public ? + BindingFlags.Public : BindingFlags.NonPublic; + + bindingFlags |= (field.Attributes & FieldAttributes.Static) != 0 ? BindingFlags.Static : BindingFlags.Instance; + + return bindingFlags; + } [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] - public override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(); + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + ThrowIfNotCreated(); + + List candidates = new List(_fieldDefinitions.Count); + foreach (FieldBuilderImpl fieldInfo in _fieldDefinitions) + { + BindingFlags fieldFlags = GetBindingFlags(fieldInfo); + if ((bindingAttr & fieldFlags) == fieldFlags) + { + candidates.Add(fieldInfo); + } + } + + return candidates.ToArray(); + } [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs index e2f51e70dab22..35f04f3776cce 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -171,10 +171,6 @@ private static void DefineMethodsAndSetAttributes(List m { ParameterBuilder pb = meb.DefineParameter(param.Position + 1, param.Attributes, param.Name); paramAttributes.ForEach(pb.SetCustomAttribute); - if (param.ParameterType.Equals(typeof(string))) - { - pb.SetConstant("Hello"); - } } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs index 9ea6560c5aea0..2711ba48db97f 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs @@ -200,5 +200,53 @@ private void AssertPointerType(Type testType) Assert.True(testType.IsPointer); Assert.Equal("TestEnum*", testType.Name); } + + public enum Test + { + First, + Second, + Third + } + + [Fact] + public void EnumTypeField_DefaultValueShouldMatchUnderlyingType() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(PopulateAssemblyName()); + ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); + TypeBuilder tb = mb.DefineType("TestType", TypeAttributes.Class | TypeAttributes.Public); + EnumBuilder eb = mb.DefineEnum("TestEnum", TypeAttributes.Public, typeof(int)); + FieldBuilder literal = eb.DefineLiteral("FieldOne", 1); + FieldBuilder literal2 = eb.DefineLiteral("FieldTwo", 2); + + FieldBuilder field = tb.DefineField("EnumField1", eb, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal); + field.SetConstant(2); + + FieldBuilder field2 = tb.DefineField("EnumField2", typeof(Test), FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal); + field2.SetConstant(Test.Third); + + tb.CreateType(); + eb.CreateType(); + + assemblyBuilder.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Module moduleFromDisk = mlc.LoadFromAssemblyPath(file.Path).Modules.First(); + Type testType = moduleFromDisk.GetType("TestType"); + Type enumType = moduleFromDisk.GetType("TestEnum"); + FieldInfo testField1 = testType.GetField("EnumField1"); + FieldInfo testField2 = testType.GetField("EnumField2"); + + Assert.True(testField1.FieldType.IsEnum); + Assert.True(testField2.FieldType.IsEnum); + Assert.Equal(enumType.FullName, testField1.FieldType.FullName); + Assert.Equal(typeof(Test).FullName, testField2.FieldType.FullName); + Assert.Equal(2, testField1.GetRawConstantValue()); + Assert.Equal(Test.Third, (Test)testField2.GetRawConstantValue()); + } + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs index b47aca11628bd..c2fd512b85e82 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -2306,5 +2306,202 @@ public void CallOpenGenericMembersFromConstructedGenericType() tlc.Unload(); } } + + [Fact] + public void ReferenceMethodsOfDictionaryFieldInGenericTypeWorks() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + TypeBuilder tb = ab.GetDynamicModule("MyModule").DefineType("EnumNameCache", TypeAttributes.NotPublic); + GenericTypeParameterBuilder[] param = tb.DefineGenericParameters(["TEnum"]); + Type fieldType = typeof(Dictionary<,>).MakeGenericType(param[0], typeof(string)); + FieldBuilder field = tb.DefineField("Cache", fieldType, FieldAttributes.Public | FieldAttributes.Static); + ILGenerator staticCtorIL = tb.DefineTypeInitializer().GetILGenerator(); + staticCtorIL.Emit(OpCodes.Newobj, TypeBuilder.GetConstructor( + typeof(Dictionary<,>).MakeGenericType(param[0], typeof(string)), typeof(Dictionary<,>).GetConstructor(Type.EmptyTypes))); + staticCtorIL.Emit(OpCodes.Stsfld, field); + staticCtorIL.Emit(OpCodes.Ret); + + MethodBuilder method = type.DefineMethod("Append", MethodAttributes.Public, typeof(string), null); + GenericTypeParameterBuilder[] methParam = method.DefineGenericParameters(["T"]); + method.SetParameters(methParam[0], typeof(string)); + Type typeOfT = tb.MakeGenericType(methParam); + FieldInfo fieldOfT = TypeBuilder.GetField(typeOfT, field); + ILGenerator ilGenerator = method.GetILGenerator(); + LocalBuilder str = ilGenerator.DeclareLocal(typeof(string)); + Label labelFalse = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Ldsfld, fieldOfT); + ilGenerator.Emit(OpCodes.Ldarg_1); + ilGenerator.Emit(OpCodes.Ldloca_S, 0); + ilGenerator.Emit(OpCodes.Callvirt, TypeBuilder.GetMethod( + typeof(Dictionary<,>).MakeGenericType(methParam[0], typeof(string)), typeof(Dictionary<,>).GetMethod("TryGetValue"))); + ilGenerator.Emit(OpCodes.Brfalse_S, labelFalse); + ilGenerator.Emit(OpCodes.Ldloc_0); + ilGenerator.Emit(OpCodes.Ret); + ilGenerator.MarkLabel(labelFalse); + ilGenerator.Emit(OpCodes.Ldsfld, fieldOfT); + ilGenerator.Emit(OpCodes.Ldarg_1); + ilGenerator.Emit(OpCodes.Ldarg_2); + ilGenerator.Emit(OpCodes.Callvirt, TypeBuilder.GetMethod( + typeof(Dictionary<,>).MakeGenericType(methParam[0], typeof(string)), typeof(Dictionary<,>).GetMethod("Add"))); + ilGenerator.Emit(OpCodes.Ldstr, "Added"); + ilGenerator.Emit(OpCodes.Ret); + + type.CreateType(); + tb.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("Append"); + MethodInfo genericMethod = methodFromDisk.MakeGenericMethod(typeof(int)); + object obj = Activator.CreateInstance(typeFromDisk); + Assert.Equal("Added", genericMethod.Invoke(obj, [1, "hello"])); + Assert.Equal("hello", genericMethod.Invoke(obj, [1, "next"])); + tlc.Unload(); + } + } + + [Fact] + public void ANestedTypeUsedAsGenericArgumentWorks() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + TypeBuilder nested = type.DefineNestedType("Nested", TypeAttributes.NestedPrivate); + Type nestedFType = typeof(Dictionary<,>).MakeGenericType(typeof(Type), nested); + FieldBuilder nestedField = nested.DefineField("Helpers", nestedFType, FieldAttributes.Static | FieldAttributes.Private); + MethodBuilder nestedMethod = nested.DefineMethod("TryGet", MethodAttributes.Public | MethodAttributes.Static, + typeof(bool), [typeof(Type)]); + ParameterBuilder param1 = nestedMethod.DefineParameter(1, ParameterAttributes.None, "type"); + ConstructorBuilder constructor = nested.DefineDefaultConstructor(MethodAttributes.Public); + ILGenerator nestedMILGen = nestedMethod.GetILGenerator(); + nestedMILGen.DeclareLocal(nested); + Label label = nestedMILGen.DefineLabel(); + nestedMILGen.Emit(OpCodes.Ldsfld, nestedField); + nestedMILGen.Emit(OpCodes.Ldarg_0); + nestedMILGen.Emit(OpCodes.Ldloca_S, 0); + nestedMILGen.Emit(OpCodes.Callvirt, TypeBuilder.GetMethod(nestedFType, typeof(Dictionary<,>).GetMethod("TryGetValue"))); + nestedMILGen.Emit(OpCodes.Brfalse_S, label); + nestedMILGen.Emit(OpCodes.Ldc_I4_1); + nestedMILGen.Emit(OpCodes.Ret); + nestedMILGen.MarkLabel(label); + nestedMILGen.Emit(OpCodes.Ldsfld, nestedField); + nestedMILGen.Emit(OpCodes.Ldarg_0); + nestedMILGen.Emit(OpCodes.Newobj, constructor); + nestedMILGen.Emit(OpCodes.Callvirt, TypeBuilder.GetMethod(nestedFType, typeof(Dictionary<,>).GetMethod("Add"))); + nestedMILGen.Emit(OpCodes.Ldc_I4_0); + nestedMILGen.Emit(OpCodes.Ret); + + ILGenerator nestedStaticCtorIL = nested.DefineTypeInitializer().GetILGenerator(); + nestedStaticCtorIL.Emit(OpCodes.Newobj, TypeBuilder.GetConstructor( + typeof(Dictionary<,>).MakeGenericType(typeof(Type), nested), typeof(Dictionary<,>).GetConstructor(Type.EmptyTypes))); + nestedStaticCtorIL.Emit(OpCodes.Stsfld, nestedField); + nestedStaticCtorIL.Emit(OpCodes.Ret); + + MethodBuilder test = type.DefineMethod("TestNested", MethodAttributes.Public | MethodAttributes.Static, typeof(bool), [typeof(Type)]); + test.DefineParameter(1, ParameterAttributes.None, "type"); + ILGenerator testIl = test.GetILGenerator(); + testIl.Emit(OpCodes.Ldarg_0); + testIl.Emit(OpCodes.Call, nestedMethod); + testIl.Emit(OpCodes.Ret); + nested.CreateType(); + type.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("TestNested"); + object obj = Activator.CreateInstance(typeFromDisk); + Assert.Equal(false, methodFromDisk.Invoke(null, [typeof(int)])); + Assert.Equal(true, methodFromDisk.Invoke(null, [typeof(int)])); + tlc.Unload(); + } + } + + [Fact] + public void ReferenceNestedGenericCollectionsWithTypeBuilderParameterInIL() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + TypeBuilder nestedType = type.DefineNestedType("NestedType", TypeAttributes.NestedPublic); + + Type returnType = typeof(List<>).MakeGenericType(typeof(Dictionary<,>).MakeGenericType(nestedType, typeof(bool))); + MethodBuilder nestedMethod = nestedType.DefineMethod("M1", MethodAttributes.Public, returnType, null); + ILGenerator nestedIL = nestedMethod.GetILGenerator(); + nestedIL.Emit(OpCodes.Ldc_I4_4); + nestedIL.Emit(OpCodes.Newobj, TypeBuilder.GetConstructor(returnType, typeof(List<>).GetConstructor([typeof(int)]))); + nestedIL.Emit(OpCodes.Ret); + + nestedType.CreateType(); + type.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + Type nestedFromDisk = typeFromDisk.GetNestedType("NestedType"); + MethodInfo methodFromDisk = nestedFromDisk.GetMethod("M1"); + object obj = Activator.CreateInstance(nestedFromDisk); + object result = methodFromDisk.Invoke(obj, null); + Assert.NotNull(result); + Type listType = result.GetType(); + Type expectedType = typeof(List<>).MakeGenericType(typeof(Dictionary<,>).MakeGenericType(nestedFromDisk, typeof(bool))); + Assert.Equal(expectedType, listType); + tlc.Unload(); + } + } + + [Fact] + public void ReferenceNestedGenericTypeWithConstructedTypeBuilderParameterInIL() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + string[] genParams = new string[] { "T" }; + GenericTypeParameterBuilder[] param = type.DefineGenericParameters(genParams); + 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]; + + Type nestedOfT = nestedItem.MakeGenericType(nestedParam); + Type parent = typeof(HashSet<>).MakeGenericType(nestedOfT); + nestedSector.SetParent(parent); + + Type hashSetOf = typeof(HashSet<>); + Type tFromHashSetOf = hashSetOf.GetGenericArguments()[0]; + Type iEqCompOf = typeof(IEqualityComparer<>).MakeGenericType(tFromHashSetOf); + Type paramType = typeof(IEqualityComparer<>).MakeGenericType(nestedOfT); + ConstructorBuilder nestedCtor = nestedSector.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, [paramType]); + ILGenerator ctorIL = nestedCtor.GetILGenerator(); + ctorIL.Emit(OpCodes.Ldarg_0); + ctorIL.Emit(OpCodes.Ldarg_1); + ctorIL.Emit(OpCodes.Call, TypeBuilder.GetConstructor(parent, hashSetOf.GetConstructor([iEqCompOf]))); + ctorIL.Emit(OpCodes.Ret); + + MethodBuilder nestedMethod = nestedSector.DefineMethod("Test", MethodAttributes.Public | MethodAttributes.Static); + ILGenerator methodIL = nestedMethod.GetILGenerator(); + methodIL.Emit(OpCodes.Initobj, parent); + methodIL.Emit(OpCodes.Ret); + + type.CreateType(); + nestedItem.CreateType(); + nestedSector.CreateType(); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + Type ntfdSector = typeFromDisk.GetNestedType("Sector"); + MethodInfo methodFromDisk = ntfdSector.GetMethod("Test"); + byte[] body = methodFromDisk.GetMethodBody().GetILAsByteArray(); + Assert.Equal(0xFE, body[0]); // Initobj instruction occupies 2 bytes 0xfe15 + Assert.Equal(0x15, body[1]); + } + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs index e6b5405ed81cb..7a009e638f589 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs @@ -287,5 +287,72 @@ public void GetABCMetadataToken_Validations() Assert.True(signatureToken > 0); Assert.True(stringToken > 0); } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/98013", TestRuntimes.Mono)] + public static void GetArrayMethodTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); + + ModuleBuilder mb = ab.DefineDynamicModule("MyModule"); + TypeBuilder tb = mb.DefineType("TestClass", TypeAttributes.Public); + Type tbArray = tb.MakeArrayType(2); + Type[] paramArray = { tbArray, typeof(int), typeof(int) }; + MethodBuilder getMethod = tb.DefineMethod("GetArray", MethodAttributes.Public | MethodAttributes.Static, tb, paramArray); + + MethodInfo arrayGetMethod = mb.GetArrayMethod(tbArray, "Get", CallingConventions.HasThis, tb, [typeof(int), typeof(int)]); + Assert.Equal(tbArray, arrayGetMethod.DeclaringType); + Assert.Equal("Get", arrayGetMethod.Name); + Assert.Equal(CallingConventions.HasThis, arrayGetMethod.CallingConvention); + Assert.Equal(tb, arrayGetMethod.ReturnType); + + ILGenerator getIL = getMethod.GetILGenerator(); + getIL.Emit(OpCodes.Ldarg_0); + getIL.Emit(OpCodes.Ldarg_1); + getIL.Emit(OpCodes.Ldarg_2); + getIL.Emit(OpCodes.Call, arrayGetMethod); + getIL.Emit(OpCodes.Ret); + + MethodInfo arraySetMethod = mb.GetArrayMethod(tbArray, "Set", CallingConventions.HasThis, typeof(void), [typeof(int), typeof(int), tb]); + MethodBuilder setMethod = tb.DefineMethod("SetArray", MethodAttributes.Public | MethodAttributes.Static, typeof(void), [tbArray, typeof(int), typeof(int), tb]); + ILGenerator setIL = setMethod.GetILGenerator(); + setIL.Emit(OpCodes.Ldarg_0); + setIL.Emit(OpCodes.Ldarg_1); + setIL.Emit(OpCodes.Ldarg_2); + setIL.Emit(OpCodes.Ldarg_3); + setIL.Emit(OpCodes.Call, arraySetMethod); + setIL.Emit(OpCodes.Ret); + + tb.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("TestClass"); + object instance = Activator.CreateInstance(typeFromDisk); + Array a = Array.CreateInstance(typeFromDisk, 2, 2); + MethodInfo setArray = typeFromDisk.GetMethod("SetArray"); + setArray.Invoke(null, [a, 0, 0, instance]); + MethodInfo getArray = typeFromDisk.GetMethod("GetArray"); + object obj = getArray.Invoke(null, [a, 0, 0]); + Assert.NotNull(obj); + Assert.Equal(instance, obj); + tlc.Unload(); + } + } + + [Fact] + public void GetArrayMethod_InvalidArgument_ThrowsArgumentException() + { + ModuleBuilder module = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")).DefineDynamicModule("MyModule"); + + AssertExtensions.Throws("arrayClass", () => module.GetArrayMethod(null, "TestMethod", CallingConventions.Standard, null, null)); + AssertExtensions.Throws("methodName", () => module.GetArrayMethod(typeof(string[]), null, CallingConventions.Standard, typeof(void), null)); + AssertExtensions.Throws("methodName", () => module.GetArrayMethod(typeof(string[]), "", CallingConventions.Standard, null, null)); + AssertExtensions.Throws("parameterTypes", () => module.GetArrayMethod(typeof(string[]), "TestMethod", CallingConventions.Standard, null, [null])); + AssertExtensions.Throws(null, () => module.GetArrayMethod(typeof(Array), "TestMethod", CallingConventions.Standard, null, null)); + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index e38e3e1d6582c..4b04b03acd2a5 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -246,6 +246,115 @@ public abstract class Impl : InterfaceWithMethod public int Method(string s, int i) => 2; } + [Fact] + public void DefineMethodOverride_InterfaceImplementationWithByRefArrayTypes() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + ModuleBuilder module = ab.GetDynamicModule("MyModule"); + + TypeBuilder interfaceType = module.DefineType("InterfaceType", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract); + Type ptrType = type.MakePointerType(); + Type byrefType = type.MakeByRefType(); + Type arrayType = type.MakeArrayType(2); + MethodBuilder methPointerArg = interfaceType.DefineMethod("M1", MethodAttributes.Public | MethodAttributes.Abstract, typeof(void), [ptrType]); + MethodBuilder methByRefArg = interfaceType.DefineMethod("M1", MethodAttributes.Public | MethodAttributes.Abstract, typeof(int), [byrefType, typeof(string)]); + MethodBuilder methArrArg = interfaceType.DefineMethod("M1", MethodAttributes.Public | MethodAttributes.Abstract, typeof(void), [arrayType]); + interfaceType.CreateType(); + + TypeBuilder implType = module.DefineType("ImplType", TypeAttributes.Public, parent: typeof(object), [interfaceType]); + MethodBuilder pointerArgImpl = implType.DefineMethod("InterfaceType.M1", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), [ptrType]); + MethodBuilder byrefArgImpl = implType.DefineMethod("InterfaceType.M1", MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), [byrefType, typeof(string)]); + MethodBuilder arrayArgImpl = implType.DefineMethod("InterfaceType.M1", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), [arrayType]); + pointerArgImpl.GetILGenerator().Emit(OpCodes.Ret); + arrayArgImpl.GetILGenerator().Emit(OpCodes.Ret); + byrefArgImpl.GetILGenerator().Emit(OpCodes.Ret); + + implType.DefineMethodOverride(pointerArgImpl, methPointerArg); + implType.DefineMethodOverride(byrefArgImpl, methByRefArg); + implType.DefineMethodOverride(arrayArgImpl, interfaceType.GetMethod("M1", [arrayType])); + + implType.CreateType(); // succeeds + } + + [Fact] + public void TypeBuilderImplementsGenericInterfaceWithTypeBuilderGenericConstraint() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + ModuleBuilder module = ab.GetDynamicModule("MyModule"); + TypeBuilder ifaceType = module.DefineType("InterfaceType", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract); + TypeBuilder implType = module.DefineType("ImplType", TypeAttributes.Public); + + GenericTypeParameterBuilder[] gParams = implType.DefineGenericParameters("T"); + gParams[0].SetInterfaceConstraints(ifaceType); + Type constructedGenericInterface = typeof(IComparable<>).MakeGenericType(gParams); + implType.AddInterfaceImplementation(constructedGenericInterface); + + MethodBuilder compareToImpl = implType.DefineMethod("CompareTo", MethodAttributes.Public, typeof(int), [gParams[0]]); + + ILGenerator ilGenerator = compareToImpl.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Ret); + + type.CreateType(); + implType.CreateType(); // succeeds + } + + [Fact] + public void TypeBuilderImplementsGenericInterfaceWithTypeBuilderArgument() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + ModuleBuilder module = ab.GetDynamicModule("MyModule"); + Type constructedGenericInterface = typeof(IComparable<>).MakeGenericType(type); + + TypeBuilder implType = module.DefineType("ImplType", TypeAttributes.Public, parent: typeof(object), [constructedGenericInterface]); + MethodBuilder compareToImpl = implType.DefineMethod("CompareTo", MethodAttributes.Public, typeof(int), [type]); + + ILGenerator ilGenerator = compareToImpl.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Ret); + + type.CreateType(); + implType.CreateType(); // succeeds + } + + [Fact] + public void TypeBuilderImplementsGenericInterface() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + ModuleBuilder module = ab.GetDynamicModule("MyModule"); + TypeBuilder implType = module.DefineType("ImplType", TypeAttributes.Public); + + GenericTypeParameterBuilder[] gParams = implType.DefineGenericParameters("T"); + Type constructedGenericInterface = typeof(IComparable<>).MakeGenericType(gParams); + implType.AddInterfaceImplementation(constructedGenericInterface); + + MethodBuilder compareToImpl = implType.DefineMethod("CompareTo", MethodAttributes.Public, typeof(int), [gParams[0]]); + + ILGenerator ilGenerator = compareToImpl.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Ret); + + type.CreateType(); + implType.CreateType(); // succeeds + } + + [Fact] + public void TypeBuilderImplementsConstructedGenericInterface() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + ModuleBuilder module = ab.GetDynamicModule("MyModule"); + + TypeBuilder implType = module.DefineType("ImplType", TypeAttributes.Public, parent: typeof(object), [typeof(IComparable)]); + MethodBuilder compareToImpl = implType.DefineMethod("CompareTo", MethodAttributes.Public, typeof(int), [typeof(string)]); + + ILGenerator ilGenerator = compareToImpl.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Ret); + + type.CreateType(); + implType.CreateType(); // succeeds + } + [Fact] public void GetInterfaceMap_WithImplicitOverride_DefineMethodOverride() { @@ -336,35 +445,6 @@ public interface IStaticAbstract static abstract void Method(); } - - [Fact] - public void CreateType_ValidateAllAbstractMethodsAreImplemented() - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeNotImplementedIfaceMethod); - typeNotImplementedIfaceMethod.AddInterfaceImplementation(typeof(DefineMethodOverrideInterface)); - ModuleBuilder module = ab.GetDynamicModule("MyModule"); - TypeBuilder partiallyImplementedType = module.DefineType("Type2", TypeAttributes.Public); - partiallyImplementedType.AddInterfaceImplementation(typeof(InterfaceDerivedFromOtherInterface)); - partiallyImplementedType.DefineMethod("M2", MethodAttributes.Public, typeof(string), [typeof(int)]).GetILGenerator().Emit(OpCodes.Ret); - TypeBuilder baseTypeImplementedTheInterfaceMethod = module.DefineType("Type3", TypeAttributes.Public, parent: typeof(DefineMethodOverrideClass)); - baseTypeImplementedTheInterfaceMethod.AddInterfaceImplementation(typeof(InterfaceDerivedFromOtherInterface)); - baseTypeImplementedTheInterfaceMethod.DefineMethod("M2", MethodAttributes.Public, typeof(string), [typeof(int)]).GetILGenerator().Emit(OpCodes.Ret); - TypeBuilder baseTypePartiallyImplemented = module.DefineType("Type4", TypeAttributes.Public, parent: typeof(PartialImplementation)); - baseTypePartiallyImplemented.AddInterfaceImplementation(typeof(InterfaceDerivedFromOtherInterface)); - TypeBuilder interfaceHasStaticAbstractMethod = module.DefineType("Type5", TypeAttributes.Public); - interfaceHasStaticAbstractMethod.AddInterfaceImplementation(typeof(IStaticAbstract)); - TypeBuilder interfaceMethodHasDefaultImplementation = module.DefineType("Type6", TypeAttributes.Public); - interfaceMethodHasDefaultImplementation.AddInterfaceImplementation(typeof(IDefaultImplementation)); - - Assert.Throws(() => typeNotImplementedIfaceMethod.CreateType()); - Assert.Throws(() => partiallyImplementedType.CreateType()); - baseTypeImplementedTheInterfaceMethod.CreateType(); // succeeds - interfaceMethodHasDefaultImplementation.CreateType(); //succeeds - Assert.Throws(() => baseTypePartiallyImplemented.CreateType()); - Assert.Throws(() => interfaceHasStaticAbstractMethod.CreateType()); - Assert.Throws(() => interfaceMethodHasDefaultImplementation.DefineTypeInitializer()); - } - [Fact] public void CreateType_ValidateMethods() { @@ -731,5 +811,86 @@ public static void DefineUninitializedDataTest() tlc.Unload(); } } + + public static List FieldTestData = new List() + { + new object[] { "TestName1", typeof(object), FieldAttributes.Public, FieldAttributes.Public }, + new object[] { "A!?123C", typeof(int), FieldAttributes.Assembly, FieldAttributes.Assembly }, + new object[] { "a\0b\0c", typeof(string), FieldAttributes.FamANDAssem | FieldAttributes.Static, FieldAttributes.FamANDAssem | FieldAttributes.Static }, + new object[] { "\uD800\uDC00", Helpers.DynamicType(TypeAttributes.Public).AsType(), FieldAttributes.Family, FieldAttributes.Family }, + new object[] { "\u043F\u0440\u0438\u0432\u0435\u0442", typeof(EmptyNonGenericInterface1), FieldAttributes.FamORAssem, FieldAttributes.FamORAssem }, + new object[] { "Test Name With Spaces", typeof(EmptyEnum), FieldAttributes.Public, FieldAttributes.Public }, + new object[] { "TestName2", typeof(EmptyNonGenericClass), FieldAttributes.HasDefault, FieldAttributes.PrivateScope }, + new object[] { "TestName3", typeof(EmptyNonGenericStruct), FieldAttributes.HasFieldMarshal, FieldAttributes.PrivateScope }, + new object[] { "TestName4", typeof(EmptyGenericClass), FieldAttributes.HasFieldRVA, FieldAttributes.PrivateScope }, + new object[] { "TestName5", typeof(EmptyGenericStruct), FieldAttributes.Literal | FieldAttributes.Static, FieldAttributes.Literal | FieldAttributes.Static }, + new object[] { "testname5", typeof(int), FieldAttributes.NotSerialized, FieldAttributes.NotSerialized }, + new object[] { "TestName7", typeof(int[]), FieldAttributes.PinvokeImpl, FieldAttributes.PinvokeImpl }, + new object[] { "TestName8", typeof(int).MakePointerType(), FieldAttributes.Private, FieldAttributes.Private }, + new object[] { "TestName9", typeof(EmptyGenericClass<>), FieldAttributes.PrivateScope, FieldAttributes.PrivateScope }, + new object[] { "TestName10", typeof(int), FieldAttributes.Public, FieldAttributes.Public }, + new object[] { "TestName11", typeof(int), FieldAttributes.RTSpecialName, FieldAttributes.PrivateScope }, + new object[] { "TestName1", typeof(int), FieldAttributes.SpecialName, FieldAttributes.SpecialName }, + new object[] { "TestName1", typeof(int), FieldAttributes.Public | FieldAttributes.Static, FieldAttributes.Public | FieldAttributes.Static } + }; + + [Fact] + public void GetFieldGetFieldsTest() + { + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + foreach(object[] fd in FieldTestData) + { + FieldBuilder field = type.DefineField((string)fd[0], (Type)fd[1], (FieldAttributes)fd[2]); + Assert.Equal(fd[0], field.Name); + Assert.Equal(fd[1], field.FieldType); + Assert.Equal(fd[3], field.Attributes); + Assert.Equal(type.AsType(), field.DeclaringType); + Assert.Equal(field.Module, field.Module); + } + + type.CreateType(); + FieldInfo[] allFields = type.GetFields(Helpers.AllFlags); + Assert.Equal(FieldTestData.Count, allFields.Length); + Assert.Equal(4, type.GetFields().Length); + Assert.Equal(3, type.GetFields(BindingFlags.Public | BindingFlags.Instance).Length); + Assert.Equal(1, type.GetFields(BindingFlags.Public | BindingFlags.Static).Length); + Assert.Equal(12, type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Length); + Assert.Equal(2, type.GetFields(BindingFlags.NonPublic | BindingFlags.Static).Length); + + Assert.Throws(() => type.GetField("TestName1", Helpers.AllFlags)); + Assert.Equal(allFields[0], type.GetField("TestName1", BindingFlags.Public | BindingFlags.Instance)); + Assert.Equal(allFields[allFields.Length-1], type.GetField("TestName1", BindingFlags.Public | BindingFlags.Static)); + Assert.Equal(allFields[10], type.GetField("testname5", Helpers.AllFlags)); + Assert.Equal(allFields[10], type.GetField("testname5", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase)); + Assert.Equal(allFields[9], type.GetField("testname5", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.IgnoreCase)); + } + + [Fact] + public void AbstractBaseMethodImplementationReturnsDifferentType() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + TypeBuilder baseType = ab.GetDynamicModule("MyModule").DefineType("Base", TypeAttributes.Public | TypeAttributes.Abstract); + MethodBuilder getBase = baseType.DefineMethod("Get", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, baseType, null); + type.SetParent(baseType); + MethodBuilder getDerived = type.DefineMethod("Get", MethodAttributes.Public | MethodAttributes.Virtual, type, null); + ILGenerator ilGenerator = getDerived.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Ret); + + type.DefineMethodOverride(getDerived, getBase); + baseType.CreateType(); + type.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo getFromDisk = typeFromDisk.GetMethod("Get"); + object instance = Activator.CreateInstance(typeFromDisk); + object obj = getFromDisk.Invoke(instance, null); + Assert.IsType(typeFromDisk, obj); + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index bd510e83c6598..6489fe2715faf 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -379,6 +379,36 @@ private void AssertPointerType(Type testType) Assert.Equal("TestInterface*", testType.Name); } + [Fact] + public void GenericTypeParameter_MakePointerType_MakeByRefType_MakeArrayType() + { + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters("T"); + Type pointerType = typeParams[0].MakePointerType(); + Type byrefType = typeParams[0].MakeByRefType(); + Type arrayType = typeParams[0].MakeArrayType(); + Type arrayType2 = typeParams[0].MakeArrayType(3); + FieldBuilder field = type.DefineField("Field", pointerType, FieldAttributes.Public); + FieldBuilder fieldArray = type.DefineField("FieldArray", arrayType2, FieldAttributes.Public); + MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Public); + method.SetSignature(arrayType, null, null, [byrefType], null, null); + method.GetILGenerator().Emit(OpCodes.Ret); + Type genericIntType = type.MakeGenericType(typeof(int)); + type.CreateType(); + + Assert.Equal(pointerType, type.GetField("Field").FieldType); + Assert.True(type.GetField("Field").FieldType.IsPointer); + Assert.Equal(arrayType2, type.GetField("FieldArray").FieldType); + Assert.True(type.GetField("FieldArray").FieldType.IsArray); + Assert.Equal(3, type.GetField("FieldArray").FieldType.GetArrayRank()); + MethodInfo testMethod = type.GetMethod("TestMethod"); + Assert.Equal(arrayType, testMethod.ReturnType); + Assert.True(testMethod.ReturnType.IsArray); + Assert.Equal(1, testMethod.GetParameters().Length); + Assert.Equal(byrefType, testMethod.GetParameters()[0].ParameterType); + Assert.True(testMethod.GetParameters()[0].ParameterType.IsByRef); + } + public static IEnumerable SaveGenericType_TestData() { yield return new object[] { new string[] { "U", "T" }, new Type[] { typeof(string), typeof(int) }, "TestInterface[System.String,System.Int32]" };