diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs index fe5da8367cdd4..f8b8c1efd35d0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs @@ -233,7 +233,7 @@ private int GetTypeRefNested(Type type, Module? refedModule, string? strRefedMod typeName = UnmangleTypeName(typeName); } - Debug.Assert(!type.IsByRef, "Must not be ByRef."); + Debug.Assert(!type.IsByRef, "Must not be ByRef. Get token from TypeSpec."); Debug.Assert(!type.IsGenericType || type.IsGenericTypeDefinition, "Must not have generic arguments."); ModuleBuilder thisModule = this; @@ -1081,19 +1081,16 @@ private int GetTypeTokenWorkerNoLock(Type type, bool getGenericDefinition) // instructions. Tokens are always relative to the Module. For example, // the token value for System.String is likely to be different from // Module to Module. Calling GetTypeToken will cause a reference to be - // added to the Module. This reference becomes a perminate part of the Module, - // multiple calles to this method with the same class have no additional side affects. - // This function is optimized to use the TypeDef token if Type is within the same module. - // We should also be aware of multiple dynamic modules and multiple implementation of Type!!! - if (type.IsByRef) - { - throw new ArgumentException(SR.Argument_CannotGetTypeTokenForByRef); - } - - if ((type.IsGenericType && (!type.IsGenericTypeDefinition || !getGenericDefinition)) || - type.IsGenericParameter || - type.IsArray || - type.IsPointer) + // added to the Module. This reference becomes a permanent part of the Module, + // multiple calls to this method with the same class have no additional side-effects. + // This function is optimized to use the TypeDef token if the Type is within the + // same module. We should also be aware of multiple dynamic modules and multiple + // implementations of a Type. + if ((type.IsGenericType && (!type.IsGenericTypeDefinition || !getGenericDefinition)) + || type.IsGenericParameter + || type.IsArray + || type.IsPointer + || type.IsByRef) { byte[] sig = SignatureHelper.GetTypeSigToken(this, type).InternalGetSignature(out int length); return GetTokenFromTypeSpec(sig, length); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs index 6d9b8121fa6b4..a242ec948896b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs @@ -1922,7 +1922,9 @@ private EventBuilder DefineEventNoLock(string name, EventAttributes attributes, int tkParent = 0; if (m_typeParent != null) + { tkParent = m_module.GetTypeTokenInternal(m_typeParent); + } ModuleBuilder module = m_module; diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 44b5250547689..bf31761902dce 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -1806,9 +1806,7 @@ HRESULT validateOneArg( // Validate the referenced type. if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, FALSE))) IfFailGo(hr); break; - case ELEMENT_TYPE_BYREF: //fallthru - if(TypeFromToken(tk)==mdtFieldDef) IfFailGo(VLDTR_E_SIG_BYREFINFIELD); - FALLTHROUGH; + case ELEMENT_TYPE_BYREF: case ELEMENT_TYPE_PINNED: case ELEMENT_TYPE_SZARRAY: // Validate the referenced type. diff --git a/src/coreclr/vm/field.cpp b/src/coreclr/vm/field.cpp index a0e1260976dee..3855f4a22c0df 100644 --- a/src/coreclr/vm/field.cpp +++ b/src/coreclr/vm/field.cpp @@ -61,6 +61,8 @@ VOID FieldDesc::Init(mdFieldDef mb, CorElementType FieldType, DWORD dwMemberAttr FieldType == ELEMENT_TYPE_R8 || FieldType == ELEMENT_TYPE_CLASS || FieldType == ELEMENT_TYPE_VALUETYPE || + FieldType == ELEMENT_TYPE_BYREF || + FieldType == ELEMENT_TYPE_TYPEDBYREF || FieldType == ELEMENT_TYPE_PTR || FieldType == ELEMENT_TYPE_FNPTR ); @@ -70,7 +72,8 @@ VOID FieldDesc::Init(mdFieldDef mb, CorElementType FieldType, DWORD dwMemberAttr m_requiresFullMbValue = 0; SetMemberDef(mb); - m_type = FieldType; + // A TypedByRef should be treated like a regular value type. + m_type = FieldType != ELEMENT_TYPE_TYPEDBYREF ? FieldType : ELEMENT_TYPE_VALUETYPE; m_prot = fdFieldAccessMask & dwMemberAttrs; m_isStatic = fIsStatic != 0; m_isRVA = fIsRVA != 0; @@ -81,7 +84,7 @@ VOID FieldDesc::Init(mdFieldDef mb, CorElementType FieldType, DWORD dwMemberAttr #endif _ASSERTE(GetMemberDef() == mb); // no truncation - _ASSERTE(GetFieldType() == FieldType); + _ASSERTE(GetFieldType() == FieldType || (FieldType == ELEMENT_TYPE_TYPEDBYREF && m_type == ELEMENT_TYPE_VALUETYPE)); _ASSERTE(GetFieldProtection() == (fdFieldAccessMask & dwMemberAttrs)); _ASSERTE((BOOL) IsStatic() == (fIsStatic != 0)); } @@ -152,6 +155,7 @@ TypeHandle FieldDesc::LookupFieldTypeHandle(ClassLoadLevel level, BOOL dropGener _ASSERTE(type == ELEMENT_TYPE_CLASS || type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_STRING || + type == ELEMENT_TYPE_TYPEDBYREF || type == ELEMENT_TYPE_SZARRAY || type == ELEMENT_TYPE_VAR ); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index e28434acabe56..f02693ae03421 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9002,9 +9002,6 @@ CorInfoType CEEInfo::getFieldTypeInternal (CORINFO_FIELD_HANDLE fieldHnd, FieldDesc* field = (FieldDesc*) fieldHnd; CorElementType type = field->GetFieldType(); - // TODO should not burn the time to do this for anything but Value Classes - _ASSERTE(type != ELEMENT_TYPE_BYREF); - if (type == ELEMENT_TYPE_I) { PTR_MethodTable enclosingMethodTable = field->GetApproxEnclosingMethodTable(); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 68d11affa4351..59566e2e71717 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -3935,6 +3935,27 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, break; } + case ELEMENT_TYPE_BYREF: + { + dwLog2FieldSize = LOG2_PTRSIZE; + if (fIsStatic) + { + // Byref-like types cannot be used for static fields + BuildMethodTableThrowException(IDS_CLASSLOAD_BYREFLIKE_STATICFIELD); + } + if (!bmtFP->fIsByRefLikeType) + { + // Non-byref-like types cannot contain byref-like instance fields + BuildMethodTableThrowException(IDS_CLASSLOAD_BYREFLIKE_INSTANCEFIELD); + } + break; + } + + case ELEMENT_TYPE_TYPEDBYREF: + { + goto IS_VALUETYPE; + } + // Class type variable (method type variables aren't allowed in fields) // These only occur in open types used for verification/reflection. case ELEMENT_TYPE_VAR: @@ -4042,7 +4063,10 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, pByValueClass = (MethodTable *)-1; } } // If 'this' is a value class - + } + // TypedReference shares the rest of the code here +IS_VALUETYPE: + { // It's not self-referential so try to load it if (pByValueClass == NULL) { diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index f98789424343b..e9d5e447ee90f 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -903,9 +903,6 @@ Cannot use function evaluation to create a TypedReference object. - - Cannot get TypeToken for a ByRef type. - Cannot set parent to an interface. diff --git a/src/libraries/System.Reflection.Emit/tests/MethodBuilder/MethodBuilderByRefs.cs b/src/libraries/System.Reflection.Emit/tests/MethodBuilder/MethodBuilderByRefs.cs new file mode 100644 index 0000000000000..01baa3bc010d9 --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/MethodBuilder/MethodBuilderByRefs.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + public class MethodBuilderByRefs + { + [Fact] + public void ByRef_Ldtoken() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Public, typeof(Type), Type.EmptyTypes); + ILGenerator ilg = method.GetILGenerator(); + ilg.Emit(OpCodes.Ldtoken, typeof(int).MakeByRefType()); + ilg.Emit(OpCodes.Ret); + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderDefineEnum.cs b/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderDefineEnum.cs index 34433ae11f391..e1d927cfa25b0 100644 --- a/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderDefineEnum.cs +++ b/src/libraries/System.Reflection.Emit/tests/ModuleBuilder/ModuleBuilderDefineEnum.cs @@ -160,11 +160,11 @@ public void DefineEnum_VoidUnderlyingType_ThrowsArgumentException() } [Fact] - public void DefineEnum_ByRefUnderlyingType_ThrowsCOMExceptionOnCreation() + public void DefineEnum_ByRefUnderlyingType_ThrowsTypeLoadExceptionOnCreation() { ModuleBuilder module = Helpers.DynamicModule(); EnumBuilder enumBuilder = module.DefineEnum("Name", TypeAttributes.Public, typeof(int).MakeByRefType()); - Assert.Throws(() => enumBuilder.CreateTypeInfo()); + Assert.Throws(() => enumBuilder.CreateTypeInfo()); } [Theory] diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index e5d46e991da82..d47bdd3466c79 100644 --- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -33,6 +33,7 @@ + diff --git a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderAddInterfaceImplementaion.cs b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderAddInterfaceImplementaion.cs index 8d9adab4662e0..da3e695dcd389 100644 --- a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderAddInterfaceImplementaion.cs +++ b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderAddInterfaceImplementaion.cs @@ -61,13 +61,6 @@ public void AddInterfaceImplementation_NullInterfaceType_ThrowsArgumentNullExcep AssertExtensions.Throws("interfaceType", () => type.AddInterfaceImplementation(null)); } - [Fact] - public void AddInterfaceImplementation_ByRefInterfaceType_ThrowsArgumentExceptioN() - { - TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); - AssertExtensions.Throws(null, () => type.AddInterfaceImplementation(typeof(int).MakeByRefType())); - } - [Fact] public void AddInterfaceImplementation_TypeAlreadyCreated_ThrowsInvalidOperationException() { diff --git a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineEvent.cs b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineEvent.cs index 2c7452339c9e8..59ebb7fc77bb4 100644 --- a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineEvent.cs +++ b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineEvent.cs @@ -106,11 +106,11 @@ public void DefineEvent_Invalid(string name, Type eventType, Type exceptionType) } [Fact] - public void DefineEvent_ByRefEventType_ThrowsArgumentException() + public void DefineEvent_ByRefEventType() { TypeBuilder type = Helpers.DynamicType(TypeAttributes.Class | TypeAttributes.Public); - - AssertExtensions.Throws(null, () => type.DefineEvent("Name", EventAttributes.None, typeof(int).MakeByRefType())); + type.DefineEvent("Name", EventAttributes.None, typeof(int).MakeByRefType()); + type.CreateTypeInfo().AsType(); } [Fact] diff --git a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineField.cs b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineField.cs index e765a08dcc403..a09e69f6738ad 100644 --- a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineField.cs +++ b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineField.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Xunit; @@ -112,15 +113,6 @@ public void DefineField_VoidFieldType_ThrowsArgumentException() AssertExtensions.Throws(null, () => type.DefineField("Name", typeof(void), FieldAttributes.Public)); } - [Fact] - public void DefineField_ByRefFieldType_ThrowsCOMExceptionOnCreation() - { - TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); - type.DefineField("Name", typeof(int).MakeByRefType(), FieldAttributes.Public); - - Assert.Throws(() => type.CreateTypeInfo()); - } - [Theory] [ActiveIssue("https://github.com/dotnet/runtime/issues/2389", TestRuntimes.Mono)] [InlineData((FieldAttributes)(-1), (FieldAttributes)(-38145))] @@ -152,6 +144,69 @@ public void DefineField_DynamicFieldTypeNotCreated_ThrowsTypeLoadException() Assert.Equal(createdFieldType, field.FieldType); } + [Fact] + public void DefineByRefField_Class_ThrowsTypeLoadExceptionOnCreation() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + type.DefineField("Name", typeof(int).MakeByRefType(), FieldAttributes.Public); + + Assert.Throws(() => type.CreateTypeInfo()); + } + + [Fact] + public void DefineByRefField_ValueType_NonByRefLike_ThrowsTypeLoadExceptionOnCreation() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public, baseType: typeof(ValueType)); + type.DefineField("Name", typeof(int).MakeByRefType(), FieldAttributes.Public); + + Assert.Throws(() => type.CreateTypeInfo()); + } + + [Fact] + public void DefineByRefField_ValueType_ByRefLike() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public, baseType: typeof(ValueType)); + + // Define type to be ByRefLike + CustomAttributeBuilder ca = new(typeof(IsByRefLikeAttribute).GetConstructors()[0], new object[] { }); + type.SetCustomAttribute(ca); + + type.DefineField("Name", typeof(int).MakeByRefType(), FieldAttributes.Public); + + Type createdType = type.CreateTypeInfo().AsType(); + FieldInfo[] fields = createdType.GetFields(); + Assert.Equal(1, fields.Length); + Assert.True(fields[0].FieldType.IsByRef); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/45152")] + public void Instantiate_ValueType_With_ByRefField() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public, baseType: typeof(ValueType)); + + // Define type to be ByRefLike + CustomAttributeBuilder ca = new(typeof(IsByRefLikeAttribute).GetConstructors()[0], new object[] { }); + type.SetCustomAttribute(ca); + + var field = type.DefineField("Name", typeof(int).MakeByRefType(), FieldAttributes.Public); + + var ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeof(string) }); + { + ILGenerator il = ctor.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarga_S, 1); + il.Emit(OpCodes.Stfld, field); + il.Emit(OpCodes.Ret); + } + + Type createdType = type.CreateTypeInfo().AsType(); + + var ctorToCall = createdType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, new[] { typeof(string) }); + var str = "12345"; + ctorToCall.Invoke(new[] { str }); + } + [Fact] public void GetField_TypeNotCreated_ThrowsNotSupportedException() { diff --git a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineNestedType.cs b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineNestedType.cs index cf24e8d87adad..d71506544525d 100644 --- a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineNestedType.cs +++ b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderDefineNestedType.cs @@ -210,15 +210,9 @@ public void DefineNestedType_NullInterface_ThrowsArgumentNullException() AssertExtensions.Throws("interfaces", () => type.DefineNestedType("Name", TypeAttributes.NestedPublic, typeof(object), new Type[] { null })); } - [Fact] - public void DefineNestedType_ByRefInterfaceType_ThrowsArgumentException() - { - TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); - AssertExtensions.Throws(null, () => type.DefineNestedType("Name", TypeAttributes.NestedPublic, typeof(object), new Type[] { typeof(int).MakeByRefType() })); - } - public static IEnumerable InvalidInterfaceType_TestData() { + yield return new object[] { typeof(int).MakeByRefType() }; yield return new object[] { typeof(EmptyNonGenericClass) }; yield return new object[] { typeof(EmptyNonGenericStruct) }; yield return new object[] { typeof(EmptyGenericClass) }; diff --git a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderSetParent.cs b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderSetParent.cs index 8a4e5ace9ec8d..5c985d52a4676 100644 --- a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderSetParent.cs +++ b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderSetParent.cs @@ -56,14 +56,14 @@ public void SetParent_InterfaceType_ThrowsArgumentException(TypeAttributes attri } [Fact] - public void SetParent_ByRefType_ThrowsArgumentExceptionOnCreation() + public void SetParent_ByRefType_ThrowsNotSupportedExceptionOnCreation() { TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); type.SetParent(typeof(int).MakeByRefType()); Assert.Equal(typeof(int).MakeByRefType(), type.BaseType); - AssertExtensions.Throws(null, () => type.CreateTypeInfo()); + Assert.Throws(() => type.CreateTypeInfo()); } [Fact] diff --git a/src/libraries/System.Reflection.Emit/tests/Utilities.cs b/src/libraries/System.Reflection.Emit/tests/Utilities.cs index 8e1c965e19a27..1e951be849a86 100644 --- a/src/libraries/System.Reflection.Emit/tests/Utilities.cs +++ b/src/libraries/System.Reflection.Emit/tests/Utilities.cs @@ -54,9 +54,16 @@ public static ModuleBuilder DynamicModule(string assemblyName = "TestAssembly", return DynamicAssembly(assemblyName).DefineDynamicModule(moduleName); } - public static TypeBuilder DynamicType(TypeAttributes attributes, string assemblyName = "TestAssembly", string moduleName = "TestModule", string typeName = "TestType") + public static TypeBuilder DynamicType(TypeAttributes attributes, string assemblyName = "TestAssembly", string moduleName = "TestModule", string typeName = "TestType", Type? baseType = null) { - return DynamicModule(assemblyName, moduleName).DefineType(typeName, attributes); + if (baseType is null) + { + return DynamicModule(assemblyName, moduleName).DefineType(typeName, attributes); + } + else + { + return DynamicModule(assemblyName, moduleName).DefineType(typeName, attributes, baseType); + } } public static EnumBuilder DynamicEnum(TypeAttributes visibility, Type underlyingType, string enumName = "TestEnum", string assemblyName = "TestAssembly", string moduleName = "TestModule") diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.Mono.cs index 51edf3f644875..64585857fffc0 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.Mono.cs @@ -819,9 +819,6 @@ public virtual void Emit(OpCode opcode, string str) public virtual void Emit(OpCode opcode, Type cls) { - if (cls != null && cls.IsByRef) - throw new ArgumentException("Cannot get TypeToken for a ByRef type."); - make_room(6); ll_emit(opcode); int token = token_gen.GetToken(cls!, opcode != OpCodes.Ldtoken); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.Mono.cs index 1e73f98f1505f..1209f517736a4 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.Mono.cs @@ -74,6 +74,7 @@ public sealed partial class TypeBuilder : TypeInfo [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private TypeInfo? created; + private bool is_byreflike_set; private int state; #endregion @@ -273,8 +274,7 @@ public void AddInterfaceImplementation([DynamicallyAccessedMembers(DynamicallyAc { if (interfaceType == null) throw new ArgumentNullException(nameof(interfaceType)); - if (interfaceType.IsByRef) - throw new ArgumentException(SR.Argument_CannotGetTypeTokenForByRef); + check_not_created(); if (interfaces != null) @@ -838,7 +838,7 @@ private bool has_ctor_method() if (parent != null) { if (parent.IsByRef) - throw new ArgumentException(); + throw new NotSupportedException(); if (IsInterface) throw new TypeLoadException(); } @@ -877,17 +877,6 @@ private bool has_ctor_method() } } - if (fields != null) - { - foreach (FieldBuilder fb in fields) - { - if (fb == null) - continue; - if (fb.FieldType.IsByRef) - throw new COMException(); - } - } - if (methods != null) { bool is_concrete = !IsAbstract; @@ -1544,6 +1533,10 @@ public void SetCustomAttribute(CustomAttributeBuilder customBuilder) { attrs |= TypeAttributes.HasSecurity; } + else if (attrname == "System.Runtime.CompilerServices.IsByRefLikeAttribute") + { + is_byreflike_set = true; + } if (cattrs != null) { @@ -1571,8 +1564,7 @@ public EventBuilder DefineEvent(string name, EventAttributes attributes, Type ev if (eventtype == null) throw new ArgumentNullException(nameof(eventtype)); check_not_created(); - if (eventtype.IsByRef) - throw new ArgumentException(SR.Argument_CannotGetTypeTokenForByRef); + EventBuilder res = new EventBuilder(this, name, attributes, eventtype); if (events != null) { diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index 2ae4f39dab855..0d8a74442b4d2 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -364,6 +364,14 @@ mono_class_setup_fields (MonoClass *klass) g_free (type_name); break; } + if (m_type_is_byref (field->type)) { + if (!m_class_is_byreflike (klass)) { + char *class_name = mono_type_get_full_name (klass); + mono_class_set_type_load_failure (klass, "Type %s is not a ByRefLike type so ref field, '%s', is invalid", class_name, field->name); + g_free (class_name); + break; + } + } /* The def_value of fields is compute lazily during vtable creation */ } diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 895a654cbfeaa..eceb20cf4d0f6 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -4626,13 +4626,20 @@ mono_ldtoken_checked (MonoImage *image, guint32 token, MonoClass **handle_class, case MONO_TOKEN_TYPE_REF: case MONO_TOKEN_TYPE_SPEC: { MonoType *type; + MonoClass *klass; if (handle_class) *handle_class = mono_defaults.typehandle_class; type = mono_type_get_checked (image, token, context, error); if (!type) return NULL; - mono_class_init_internal (mono_class_from_mono_type_internal (type)); + klass = mono_class_from_mono_type_internal (type); + mono_class_init_internal (klass); + if (mono_class_has_failure (klass)) { + mono_error_set_for_class_failure (error, klass); + return NULL; + } + /* We return a MonoType* as handle */ return type; } diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 2be830e375b15..efa4b0624548d 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -1222,6 +1222,7 @@ struct _MonoReflectionTypeBuilder { MonoGenericContainer *generic_container; MonoArray *generic_params; MonoReflectionType *created; + gboolean is_byreflike_set; gint32 state; }; diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index d87fade6e6122..5863a967d544f 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -887,6 +887,7 @@ compute_class_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int } else { /* fall through */ } + case MONO_TYPE_TYPEDBYREF: case MONO_TYPE_VALUETYPE: { MonoClass *fclass = mono_class_from_mono_type_internal (field->type); if (m_class_has_references (fclass)) { diff --git a/src/mono/mono/metadata/sre.c b/src/mono/mono/metadata/sre.c index 5a9e16eb81b8f..62254b3fa2c3e 100644 --- a/src/mono/mono/metadata/sre.c +++ b/src/mono/mono/metadata/sre.c @@ -3556,6 +3556,11 @@ typebuilder_setup_one_field (MonoDynamicImage *dynamic_image, MonoClass *klass, } } + if (m_type_is_byref (field->type) && !m_class_is_byreflike (klass)) { + mono_error_set_type_load_name (error, NULL, NULL, "Field '%s' is a byref in a non-byref-like type", field->name); + goto leave; + } + if ((fb->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) && (rva_data = fb->rva_data)) { char *base = mono_array_addr_internal (rva_data, char, 0); size_t size = mono_array_length_internal (rva_data); @@ -3857,6 +3862,12 @@ ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilderHandle ref_ mono_class_setup_supertypes (klass); mono_class_setup_mono_type (klass); + /* Check if the type is marked as byreflike. + * The IsByRefLike attribute only applies to value types and enums. This matches CoreCLR behavior. + */ + if (klass->enumtype || klass->valuetype) + klass->is_byreflike = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionTypeBuilder, ref_tb), is_byreflike_set); + /* enums are done right away */ if (!klass->enumtype) if (!ensure_runtime_vtable (klass, error)) diff --git a/src/tests/Loader/classloader/RefFields/InvalidCSharp.il b/src/tests/Loader/classloader/RefFields/InvalidCSharp.il new file mode 100644 index 0000000000000..b976227547def --- /dev/null +++ b/src/tests/Loader/classloader/RefFields/InvalidCSharp.il @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) } + +.assembly InvalidCSharp { } + +.class public auto ansi sealed beforefieldinit InvalidCSharp.InvalidStructWithRefField + extends [System.Runtime]System.ValueType +{ + // Type requires IsByRefLikeAttribute to be valid. + .field public string& invalid +} + +// This is invalid metadata and is unable to be loaded. +// - [field sig] (0x80131815 (VER_E_FIELD_SIG)) +// +// .class public auto ansi sealed beforefieldinit InvalidCSharp.InvalidStructWithStaticRefField +// extends [System.Runtime]System.ValueType +// { +// .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( +// 01 00 00 00 +// ) +// .field public static string& invalid +// } + +.class public auto ansi sealed beforefieldinit InvalidCSharp.WithRefField + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .field public string& Str + + .method public hidebysig specialname rtspecialname + instance void .ctor ( + string& + ) cil managed + { + ldarg.0 + ldarg.1 + stfld string& InvalidCSharp.WithRefField::Str + ret + } + + .method public hidebysig + instance bool ConfirmFieldInstance ( + string + ) cil managed + { + ldarg.0 + ldfld string& InvalidCSharp.WithRefField::Str + ldind.ref + ldarg.1 + ceq + ret + } +} + +.class public auto ansi sealed beforefieldinit InvalidCSharp.WithRefStructField + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .field public valuetype InvalidCSharp.WithRefField& Field + + .method public hidebysig specialname rtspecialname + instance void .ctor ( + valuetype InvalidCSharp.WithRefField& + ) cil managed + { + ldarg.0 + ldarg.1 + stfld valuetype InvalidCSharp.WithRefField& InvalidCSharp.WithRefStructField::Field + ret + } + + .method public hidebysig + instance bool ConfirmFieldInstance ( + valuetype InvalidCSharp.WithRefField& + ) cil managed + { + ldarg.0 + ldfld valuetype InvalidCSharp.WithRefField& InvalidCSharp.WithRefStructField::Field + ldind.ref + ldarg.1 + ldind.ref + ceq + ret + } +} + +.class public auto ansi sealed beforefieldinit InvalidCSharp.WithTypedReferenceField`1 + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .field public typedref Field + + .method public hidebysig specialname rtspecialname + instance void .ctor ( + !T + ) cil managed + { + ldarg.0 + ldarga.s 1 + mkrefany !T + stfld typedref InvalidCSharp.WithTypedReferenceField`1::Field + ret + } + + .method public hidebysig + instance class [System.Runtime]System.Type GetFieldType () cil managed + { + ldarg.0 + ldfld typedref InvalidCSharp.WithTypedReferenceField`1::Field + refanytype + call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle ) + ret + } +} \ No newline at end of file diff --git a/src/tests/Loader/classloader/RefFields/InvalidCSharp.ilproj b/src/tests/Loader/classloader/RefFields/InvalidCSharp.ilproj new file mode 100644 index 0000000000000..d577c8f9c7a1a --- /dev/null +++ b/src/tests/Loader/classloader/RefFields/InvalidCSharp.ilproj @@ -0,0 +1,8 @@ + + + Library + + + + + diff --git a/src/tests/Loader/classloader/RefFields/Validate.cs b/src/tests/Loader/classloader/RefFields/Validate.cs new file mode 100644 index 0000000000000..bffcd53de2f00 --- /dev/null +++ b/src/tests/Loader/classloader/RefFields/Validate.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using InvalidCSharp; + +using Xunit; + +class Validate +{ + [Fact] + public static void Validate_Invalid_RefField_Fails() + { + Console.WriteLine($"{nameof(Validate_Invalid_RefField_Fails)}..."); + Assert.Throws(() => { var t = typeof(InvalidStructWithRefField); }); + } + + [Fact] + public static void Validate_RefStructWithRefField_Load() + { + Console.WriteLine($"{nameof(Validate_RefStructWithRefField_Load)}..."); + var t = typeof(WithRefField); + } + + [Fact] + public static void Validate_Create_RefField() + { + var str = nameof(Validate_Create_RefField); + Console.WriteLine($"{str}..."); + + WithRefField s = new(ref str); + Assert.True(s.ConfirmFieldInstance(str)); + + string newStr = new(str); + Assert.False(s.ConfirmFieldInstance(newStr)); + } + + [Fact] + public static void Validate_Create_RefStructField() + { + var str = nameof(Validate_Create_RefStructField); + Console.WriteLine($"{str}..."); + + WithRefField s = new(ref str); + WithRefStructField t = new(ref s); + Assert.True(t.ConfirmFieldInstance(ref s)); + } + + [Fact] + public static void Validate_Create_TypedReferenceRefField() + { + Console.WriteLine($"{nameof(Validate_Create_TypedReferenceRefField)}..."); + + Validate v = new(); + WithTypedReferenceField s = new(v); + Assert.Equal(typeof(Validate), s.GetFieldType()); + } +} \ No newline at end of file diff --git a/src/tests/Loader/classloader/RefFields/Validate.csproj b/src/tests/Loader/classloader/RefFields/Validate.csproj new file mode 100644 index 0000000000000..96fedddd4bdda --- /dev/null +++ b/src/tests/Loader/classloader/RefFields/Validate.csproj @@ -0,0 +1,12 @@ + + + true + Exe + + + + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 7b718aa15617d..edf95e8c106f6 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1329,7 +1329,7 @@ - + @@ -2589,6 +2589,9 @@ expected failure: overlapped structs fail at AOT compile time, not runtime + + expected failure: unsupported type with ref field fails at AOT compile time, not runtime + https://github.com/dotnet/runtime/issues/57361