diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 5a3576b4bf4498..a451c19fce64c1 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -1134,6 +1134,22 @@ void ClassLoader::LoadExactParents(MethodTable* pMT) EnsureLoaded(TypeHandle(pMT->GetCanonicalMethodTable()), CLASS_LOAD_EXACTPARENTS); } + if (pMT->GetClass()->HasRVAStaticFields()) + { + ApproxFieldDescIterator fdIterator(pMT, ApproxFieldDescIterator::STATIC_FIELDS); + FieldDesc* pFD = NULL; + while ((pFD = fdIterator.Next()) != NULL) + { + if (pFD->IsByValue() && pFD->IsRVA()) + { + if (pFD->GetApproxFieldTypeHandleThrowing().GetMethodTable()->GetClass()->HasFieldsWhichMustBeInited()) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + } + } + } + LoadExactParentAndInterfacesTransitively(pMT); if (pMT->GetClass()->HasVTableMethodImpl()) diff --git a/src/coreclr/vm/class.h b/src/coreclr/vm/class.h index d815934d0ea390..73f6c1a48520c7 100644 --- a/src/coreclr/vm/class.h +++ b/src/coreclr/vm/class.h @@ -1317,15 +1317,15 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW! LIMITED_METHOD_CONTRACT; m_VMFlags |= (DWORD)VMFLAG_INLINE_ARRAY; } - DWORD HasNonPublicFields() + DWORD HasRVAStaticFields() { LIMITED_METHOD_CONTRACT; - return (m_VMFlags & VMFLAG_HASNONPUBLICFIELDS); + return (m_VMFlags & VMFLAG_HASRVASTATICFIELDS); } - void SetHasNonPublicFields() + void SetHasRVAStaticFields() { LIMITED_METHOD_CONTRACT; - m_VMFlags |= (DWORD)VMFLAG_HASNONPUBLICFIELDS; + m_VMFlags |= (DWORD)VMFLAG_HASRVASTATICFIELDS; } DWORD IsNotTightlyPacked() { @@ -1647,7 +1647,7 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW! VMFLAG_INLINE_ARRAY = 0x00010000, VMFLAG_NO_GUID = 0x00020000, - VMFLAG_HASNONPUBLICFIELDS = 0x00040000, + VMFLAG_HASRVASTATICFIELDS = 0x00040000, VMFLAG_HAS_CUSTOM_FIELD_ALIGNMENT = 0x00080000, VMFLAG_CONTAINS_STACK_PTR = 0x00100000, VMFLAG_PREFER_ALIGN8 = 0x00200000, // Would like to have 8-byte alignment diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 7d581ac8b79f90..1e8b5c87893c2c 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1812,12 +1812,6 @@ MethodTableBuilder::BuildMethodTableThrowing( BuildMethodTableThrowException(IDS_CLASSLOAD_ZEROSIZE); } - if (bmtFP->fHasSelfReferencingStaticValueTypeField_WithRVA) - { // Verify self-referencing statics with RVA (now when the ValueType size is known) - VerifySelfReferencingStaticValueTypeFields_WithRVA(pByValueClassCache); - } - - // Now setup the method table SetupMethodTable2(pLoaderModule); @@ -3827,62 +3821,175 @@ VOID MethodTableBuilder::AllocateFieldDescs() } } -//******************************************************************************* -BOOL MethodTableBuilder::IsSelfReferencingStaticValueTypeField(mdToken dwByValueClassToken, - bmtInternalInfo* bmtInternal, - const bmtGenericsInfo *bmtGenerics, - PCCOR_SIGNATURE pMemberSignature, - DWORD cMemberSignature) +CorElementType MethodTableBuilder::GetCorElementTypeOfTypeDefOrRefForStaticField(Module* module, mdToken typeDefOrRef) { STANDARD_VM_CONTRACT; - if (dwByValueClassToken != this->GetCl()) + MethodTable *pMTFound = NULL; + + if (TypeFromToken(typeDefOrRef) == mdtTypeDef) { - return FALSE; + pMTFound = module->LookupTypeDef(typeDefOrRef).AsMethodTable(); + } + else if (TypeFromToken(typeDefOrRef) == mdtTypeRef) + { + pMTFound = module->LookupTypeRef(typeDefOrRef).AsMethodTable(); } - if (!bmtGenerics->HasInstantiation()) + // The checking here for typeDefOrRef which matches GetCl is only intended to reduce the number + // of cases where we throw exceptions. It is not actually a correctness check, so that obviously + // self-referential loads don't trigger exceptions. + if ((pMTFound == NULL) && bmtInternal->pType != NULL && (typeDefOrRef != GetCl())) { - return TRUE; + EX_TRY + { + pMTFound = ClassLoader::LoadTypeDefOrRefThrowing(module, typeDefOrRef, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_APPROXPARENTS).AsMethodTable(); + } + EX_CATCH + { + // If this failed, we can fall back to directly working with the type definition, so catch all the exceptions + } + EX_END_CATCH } - // The value class is generic. Check that the signature of the field - // is _exactly_ equivalent to VC. Do this by consing up a fake - // signature. - DWORD nGenericArgs = bmtGenerics->GetNumGenericArgs(); - CONSISTENCY_CHECK(nGenericArgs != 0); + if (pMTFound != NULL) + { + if (pMTFound->IsByRefLike()) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BYREF_OR_BYREFLIKE_STATICFIELD); + } - SigBuilder sigBuilder; + if (pMTFound->IsEnum()) + { + return pMTFound->GetApproxFieldDescListRaw()[0].GetFieldType(); + } + else + { + _ASSERTE(pMTFound->IsValueType()); + return ELEMENT_TYPE_VALUETYPE; + } + } + + Module *pModuleOfTypeDef; + mdTypeDef tkTypeDef; - sigBuilder.AppendElementType(ELEMENT_TYPE_GENERICINST); - sigBuilder.AppendElementType(ELEMENT_TYPE_VALUETYPE); - sigBuilder.AppendToken(dwByValueClassToken); - sigBuilder.AppendData(nGenericArgs); - for (unsigned int typearg = 0; typearg < nGenericArgs; typearg++) + ClassLoader::ResolveTokenToTypeDefThrowing(module, typeDefOrRef, &pModuleOfTypeDef, &tkTypeDef); + + // First check to see if the type is byref-like + if (pModuleOfTypeDef->GetCustomAttribute(tkTypeDef, + WellKnownAttribute::IsByRefLike, + NULL, NULL) == S_OK) { - sigBuilder.AppendElementType(ELEMENT_TYPE_VAR); - sigBuilder.AppendData(typearg); + BuildMethodTableThrowException(IDS_CLASSLOAD_BYREF_OR_BYREFLIKE_STATICFIELD); } - DWORD cFakeSig; - PCCOR_SIGNATURE pFakeSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cFakeSig); + mdToken tkTypeDefExtends; + IfFailThrow(pModuleOfTypeDef->GetMDImport()->GetTypeDefProps(tkTypeDef, NULL, &tkTypeDefExtends)); + bool thisIsAnEnum = false; - PCCOR_SIGNATURE pFieldSig = pMemberSignature + 1; // skip the CALLCONV_FIELD + if (TypeFromToken(tkTypeDefExtends) == mdtTypeDef) + { + if (pModuleOfTypeDef->IsSystem()) + { + if (tkTypeDefExtends == CoreLibBinder::GetClassIfExist(CLASS__ENUM)->GetCl()) + { + thisIsAnEnum = true; + } + } + } + else + { + _ASSERTE(TypeFromToken(tkTypeDefExtends) == mdtTypeRef); + LPCSTR pszNameSpace; + LPCSTR pszClassName; + pModuleOfTypeDef->GetMDImport()->GetNameOfTypeRef(tkTypeDefExtends, &pszNameSpace, &pszClassName); + if (pszNameSpace != NULL && pszClassName != NULL && strcmp(pszNameSpace, "System") == 0 && strcmp(pszClassName, "Enum") == 0) + { + Module *pModuleOfSystemEnumType; + mdTypeDef tkTypeDefOfSystemEnumType; - return MetaSig::CompareElementType(pFakeSig, pFieldSig, - pFakeSig + cFakeSig, pMemberSignature + cMemberSignature, - GetModule(), GetModule(), - NULL, NULL); + ClassLoader::ResolveTokenToTypeDefThrowing(pModuleOfTypeDef, tkTypeDefExtends, &pModuleOfSystemEnumType, &tkTypeDefOfSystemEnumType); -} + if (pModuleOfSystemEnumType != NULL && pModuleOfSystemEnumType->IsSystem()) + { + thisIsAnEnum = true; + } + } + } -//******************************************************************************* -// -// Used pByValueClass cache to mark self-references -// -static BOOL IsSelfRef(MethodTable * pMT) -{ - return pMT == (MethodTable *)-1; + if (thisIsAnEnum) + { + // If this is an enum, we can return the type of the instance field + IMDInternalImport *pEnumModuleImport = pModuleOfTypeDef->GetMDImport(); + HENUMInternalHolder hEnumField(pEnumModuleImport); + IfFailThrow(hEnumField.EnumInitNoThrow(mdtFieldDef, tkTypeDef)); + mdFieldDef tkFieldDef; + while (hEnumField.EnumNext(&tkFieldDef)) + { + ULONG cMemberSignature; + PCCOR_SIGNATURE pMemberSignature; + DWORD flags; + + IfFailThrow(pEnumModuleImport->GetFieldDefProps(tkFieldDef, &flags)); + + if (IsFdStatic(flags) || IsFdLiteral(flags)) + { + continue; // Skip static and literal fields + } + IfFailThrow(pEnumModuleImport->GetSigOfFieldDef(tkFieldDef, &cMemberSignature, &pMemberSignature)); + + SigParser sig(pMemberSignature, cMemberSignature); + uint32_t ulCallConv; + IfFailThrow(sig.GetCallingConvInfo(&ulCallConv)); + if (ulCallConv != IMAGE_CEE_CS_CALLCONV_FIELD) + { + ThrowHR(COR_E_TYPELOAD); + } + + CorElementType elemType; + IfFailThrow(sig.GetElemType(&elemType)); + + switch (elemType) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I: + return elemType; + default: + break; + } + + // Invalid enum base type (probably) fall back to using the loaded type. + break; + } + + // Something went wrong looking for the instance field. Fall back to using the loaded type. + _ASSERTE(!"Could not find instance field on enum, fallback to using loaded type"); + MethodTable *pMTEnum = ClassLoader::LoadTypeDefThrowing(pModuleOfTypeDef, tkTypeDef, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef).AsMethodTable(); + + if (!pMTEnum->IsEnum()) + { + ThrowHR(COR_E_TYPELOAD); + } + + return pMTEnum->GetApproxFieldDescListRaw()[0].GetFieldType(); + } + else + { + // If this is not an enum, we can return ELEMENT_TYPE_VALUETYPE + return ELEMENT_TYPE_VALUETYPE; + } } //******************************************************************************* @@ -3950,9 +4057,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, if (IsFdLiteral(dwMemberAttrs)) continue; - if (!IsFdPublic(dwMemberAttrs)) - SetHasNonPublicFields(); - IfFailThrow(pInternalImport->GetSigOfFieldDef(bmtMetaData->pFields[i], &cMemberSignature, &pMemberSignature)); // Signature validation IfFailThrow(validateTokenSig(bmtMetaData->pFields[i],pMemberSignature,cMemberSignature,dwMemberAttrs,pInternalImport)); @@ -4104,6 +4208,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, case ELEMENT_TYPE_TYPEDBYREF: { + ElementType = ELEMENT_TYPE_VALUETYPE; goto IS_VALUETYPE; } @@ -4144,73 +4249,11 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, // By-value class BAD_FORMAT_NOTHROW_ASSERT(dwByValueClassToken != 0); - if (this->IsValueClass() && (pTokenModule == GetModule())) + if (fIsStatic) { - if (TypeFromToken(dwByValueClassToken) == mdtTypeRef) - { - // It's a typeref - check if it's a class that has a static field of itself - LPCUTF8 pszNameSpace; - LPCUTF8 pszClassName; - if (FAILED(pInternalImport->GetNameOfTypeRef(dwByValueClassToken, &pszNameSpace, &pszClassName))) - { - BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); - } - - if (IsStrLongerThan((char *)pszClassName, MAX_CLASS_NAME) - || IsStrLongerThan((char *)pszNameSpace, MAX_CLASS_NAME) - || (strlen(pszClassName) + strlen(pszNameSpace) + 1 >= MAX_CLASS_NAME)) - { - BuildMethodTableThrowException(BFA_TYPEREG_NAME_TOO_LONG, mdMethodDefNil); - } - - mdToken tkRes; - if (FAILED(pInternalImport->GetResolutionScopeOfTypeRef(dwByValueClassToken, &tkRes))) - { - BuildMethodTableThrowException(BFA_BAD_TYPEREF_TOKEN, dwByValueClassToken); - } - - if (TypeFromToken(tkRes) == mdtTypeRef) - { - if (!pInternalImport->IsValidToken(tkRes)) - { - BuildMethodTableThrowException(BFA_BAD_TYPEREF_TOKEN, mdMethodDefNil); - } - } - else - { - tkRes = mdTokenNil; - } - - if (FAILED(pInternalImport->FindTypeDef(pszNameSpace, - pszClassName, - tkRes, - &dwByValueClassToken))) - { - dwByValueClassToken = mdTokenNil; - } - } // If field is static typeref - - BOOL selfref = IsSelfReferencingStaticValueTypeField(dwByValueClassToken, - bmtInternal, - bmtGenerics, - pMemberSignature, - cMemberSignature); - - if (selfref) - { // immediately self-referential fields must be static. - if (!fIsStatic) - { - BuildMethodTableThrowException(IDS_CLASSLOAD_VALUEINSTANCEFIELD, mdMethodDefNil); - } - - if (!IsValueClass()) - { - BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_MUST_BE_BYVAL, mdTokenNil); - } - - pByValueClass = (MethodTable *)-1; - } - } // If 'this' is a value class + ElementType = GetCorElementTypeOfTypeDefOrRefForStaticField(pTokenModule, dwByValueClassToken); + pByValueClass = (MethodTable *)-1; + } } // TypedReference shares the rest of the code here IS_VALUETYPE: @@ -4222,7 +4265,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, { // Loading a non-self-ref valuetype field. OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOAD_APPROXPARENTS); - if (isEnCField || fIsStatic) + if (isEnCField) { // EnCFieldDescs are not created at normal MethodTableBuilder time, and don't need to avoid recursive generic instantiation pByValueClass = fsig.GetArgProps().GetTypeHandleThrowing(GetModule(), @@ -4231,8 +4274,23 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, CLASS_LOAD_APPROXPARENTS, TRUE ).GetMethodTable(); + + if (fIsStatic) + { + if (pByValueClass->IsEnum()) + { + BAD_FORMAT_NOTHROW_ASSERT(pByValueClass->GetNumInstanceFields() == 1); // enums must have exactly one field + FieldDesc * enumField = pByValueClass->GetApproxFieldDescListRaw(); + BAD_FORMAT_NOTHROW_ASSERT(!enumField->IsStatic()); // no real static fields on enums + ElementType = enumField->GetFieldType(); + } + else + { + ElementType = ELEMENT_TYPE_VALUETYPE; + } + } } - else + else if (!fIsStatic) { // We load the approximate type of the field to avoid recursion problems. // MethodTable::DoFullyLoad() will later load it fully @@ -4248,26 +4306,17 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, } } + if (fIsStatic && ElementType != ELEMENT_TYPE_VALUETYPE) + { + fIsByValue = FALSE; // we're going to treat it as the underlying type now + goto GOT_ELEMENT_TYPE; + } + // #FieldDescTypeMorph IF it is an enum, strip it down to its underlying type - if (IsSelfRef(pByValueClass) ? IsEnum() : pByValueClass->IsEnum()) - { - if (IsSelfRef(pByValueClass)) - { // It is self-referencing enum (ValueType) static field - it is forbidden in the ECMA spec, but supported by CLR since v1 - // Note: literal static fields are skipped early in this loop - if (bmtMFDescs->ppFieldDescList[0] == NULL) - { // The field is defined before (the only) instance field - // AppCompat with 3.5 SP1 and 4.0 RTM behavior - BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil); - } - // We will treat the field type as if it was its underlying type (we know its size and will check correctly RVA with the size - // later in this method) - // Therefore we do not have to run code:VerifySelfReferencingStaticValueTypeFields_WithRVA or code:#SelfReferencingStaticValueTypeField_Checks - } - BAD_FORMAT_NOTHROW_ASSERT((IsSelfRef(pByValueClass) ? - bmtEnumFields->dwNumInstanceFields : pByValueClass->GetNumInstanceFields()) - == 1); // enums must have exactly one field - FieldDesc * enumField = IsSelfRef(pByValueClass) ? - bmtMFDescs->ppFieldDescList[0] : pByValueClass->GetApproxFieldDescListRaw(); + if (!fIsStatic && pByValueClass->IsEnum()) + { + BAD_FORMAT_NOTHROW_ASSERT(pByValueClass->GetNumInstanceFields() == 1); // enums must have exactly one field + FieldDesc * enumField = pByValueClass->GetApproxFieldDescListRaw(); BAD_FORMAT_NOTHROW_ASSERT(!enumField->IsStatic()); // no real static fields on enums ElementType = enumField->GetFieldType(); BAD_FORMAT_NOTHROW_ASSERT(ElementType != ELEMENT_TYPE_VALUETYPE); @@ -4276,13 +4325,8 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, } // Check ByRefLike fields - if (!IsSelfRef(pByValueClass) && pByValueClass->IsByRefLike()) + if (!fIsStatic && pByValueClass->IsByRefLike()) { - if (fIsStatic) - { - // Byref-like types cannot be used for static fields - BuildMethodTableThrowException(IDS_CLASSLOAD_BYREF_OR_BYREFLIKE_STATICFIELD); - } if (!bmtFP->fIsByRefLikeType) { // Non-byref-like types cannot contain byref-like instance fields @@ -4290,12 +4334,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, } } - if (!IsSelfRef(pByValueClass) && pByValueClass->GetClass()->HasNonPublicFields()) - { // If a class has a field of type ValueType with non-public fields in it, - // the class must "inherit" this characteristic - SetHasNonPublicFields(); - } - if (!fHasRVA) { if (!fIsStatic) @@ -4303,8 +4341,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, // Inherit instance attributes EEClass * pFieldClass = pByValueClass->GetClass(); - if (pFieldClass->HasNonPublicFields()) - SetHasNonPublicFields(); if (pFieldClass->HasFieldsWhichMustBeInited()) SetHasFieldsWhichMustBeInited(); @@ -4334,7 +4370,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, // Thread static fields come after instance fields and regular static fields in this list if (fIsThreadStatic) { - (*pByValueClassCache)[bmtEnumFields->dwNumInstanceFields + bmtEnumFields->dwNumStaticFields - bmtEnumFields->dwNumThreadStaticFields + dwCurrentThreadStaticField] = pByValueClass; + (*pByValueClassCache)[bmtEnumFields->dwNumInstanceFields + bmtEnumFields->dwNumStaticFields - bmtEnumFields->dwNumThreadStaticFields + dwCurrentThreadStaticField] = NULL; // make sure to record the correct size for static field // layout dwLog2FieldSize = LOG2_PTRSIZE; // handle @@ -4342,7 +4378,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, // Regular static fields come after instance fields in this list else if (fIsStatic) { - (*pByValueClassCache)[bmtEnumFields->dwNumInstanceFields + dwCurrentStaticField] = pByValueClass; + (*pByValueClassCache)[bmtEnumFields->dwNumInstanceFields + dwCurrentStaticField] = NULL; // make sure to record the correct size for static field // layout dwLog2FieldSize = LOG2_PTRSIZE; // handle @@ -4459,21 +4495,8 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, BAD_FORMAT_NOTHROW_ASSERT(!"ObjectRef in an RVA field"); BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil); } - if (FieldDescElementType == ELEMENT_TYPE_VALUETYPE) - { - if (IsSelfRef(pByValueClass)) - { // We will verify self-referencing statics after the loop through all fields - see code:#SelfReferencingStaticValueTypeField_Checks - bmtFP->fHasSelfReferencingStaticValueTypeField_WithRVA = TRUE; - } - else - { - if (pByValueClass->GetClass()->HasFieldsWhichMustBeInited()) - { // RVA fields are not allowed to have GC pointers. - BAD_FORMAT_NOTHROW_ASSERT(!"ObjectRef in an RVA field"); - BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil); - } - } - } + + SetHasRVAStaticFields(); // The PE should be loaded by now. _ASSERT(GetModule()->GetPEAssembly()->IsLoaded()); @@ -4519,20 +4542,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, } // We processed all fields - //#SelfReferencingStaticValueTypeField_Checks - if (bmtFP->fHasSelfReferencingStaticValueTypeField_WithRVA) - { // The type has self-referencing static ValueType field with RVA, do more checks now that depend on all fields being processed - - // For enums we already checked its underlying type, we should not get here - _ASSERTE(!IsEnum()); - - if (HasFieldsWhichMustBeInited()) - { // RVA fields are not allowed to have GC pointers. - BAD_FORMAT_NOTHROW_ASSERT(!"ObjectRef in an RVA self-referencing static field"); - BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil); - } - } - DWORD dwNumInstanceFields = dwCurrentDeclaredField + (HasParent() ? GetParentMethodTable()->GetNumInstanceFields() : 0); DWORD dwNumStaticFields = bmtEnumFields->dwNumStaticFields; DWORD dwNumThreadStaticFields = bmtEnumFields->dwNumThreadStaticFields; @@ -4566,40 +4575,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, return; } // MethodTableBuilder::InitializeFieldDescs -//******************************************************************************* -// Verify self-referencing static ValueType fields with RVA (when the size of the ValueType is known). -void -MethodTableBuilder::VerifySelfReferencingStaticValueTypeFields_WithRVA( - MethodTable ** pByValueClassCache) -{ - STANDARD_VM_CONTRACT; - - _ASSERTE(bmtFP->fHasSelfReferencingStaticValueTypeField_WithRVA); - // Enum's static self-referencing fields have been verified as the underlying type of the enum, we should not get here for them - _ASSERTE(!IsEnum()); - // The size of the ValueType should be known at this point (the caller throws if it is 0) - _ASSERTE(bmtFP->NumInstanceFieldBytes != 0); - - FieldDesc * pFieldDescList = GetApproxFieldDescListRaw(); - DWORD nFirstThreadStaticFieldIndex = bmtEnumFields->dwNumInstanceFields + bmtEnumFields->dwNumStaticFields - bmtEnumFields->dwNumThreadStaticFields; - for (DWORD i = bmtEnumFields->dwNumInstanceFields; i < nFirstThreadStaticFieldIndex; i++) - { - FieldDesc * pFD = &pFieldDescList[i]; - _ASSERTE(pFD->IsStatic()); - - if (pFD->IsRVA() && pFD->IsByValue()) - { - _ASSERTE(pByValueClassCache[i] != NULL); - - if (IsSelfRef(pByValueClassCache[i])) - { - DWORD rva; - IfFailThrow(GetMDImport()->GetFieldRVA(pFD->GetMemberDef(), &rva)); - } - } - } -} // MethodTableBuilder::VerifySelfReferencingStaticValueTypeFields_WithRVA - //******************************************************************************* // Returns true if hEnclosingTypeCandidate encloses, at any arbitrary depth, // hNestedTypeCandidate; returns false otherwise. diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index 825b9ac9ee5f64..ccbca89b16c304 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -189,7 +189,6 @@ class MethodTableBuilder void SetIsComClassInterface() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetIsComClassInterface(); } #endif // FEATURE_COMINTEROP BOOL IsEnum() { WRAPPER_NO_CONTRACT; return bmtProp->fIsEnum; } - BOOL HasNonPublicFields() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->HasNonPublicFields(); } BOOL IsValueClass() { WRAPPER_NO_CONTRACT; return bmtProp->fIsValueClass; } BOOL IsUnsafeValueClass() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->IsUnsafeValueClass(); } BOOL IsAbstract() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->IsAbstract(); } @@ -222,7 +221,7 @@ class MethodTableBuilder // we create that object. void SetUnsafeValueClass() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetUnsafeValueClass(); } void SetHasFieldsWhichMustBeInited() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetHasFieldsWhichMustBeInited(); } - void SetHasNonPublicFields() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetHasNonPublicFields(); } + void SetHasRVAStaticFields() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetHasRVAStaticFields(); } void SetNumHandleRegularStatics(WORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNumHandleRegularStatics(x); } void SetNumHandleThreadStatics(WORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNumHandleThreadStatics(x); } void SetAlign8Candidate() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetAlign8Candidate(); } @@ -2091,7 +2090,6 @@ class MethodTableBuilder bool fIsAllGCPointers; bool fIsByRefLikeType; bool fHasFixedAddressValueTypes; - bool fHasSelfReferencingStaticValueTypeField_WithRVA; // These data members are specific to regular statics DWORD RegularStaticFieldStart[MAX_LOG2_PRIMITIVE_FIELD_SIZE+1]; // Byte offset where to start placing fields of this size @@ -2671,10 +2669,9 @@ class MethodTableBuilder unsigned * totalDeclaredSize); // -------------------------------------------------------------------------------------------- - // Verify self-referencing static ValueType fields with RVA (when the size of the ValueType is known). - void - VerifySelfReferencingStaticValueTypeFields_WithRVA( - MethodTable ** pByValueClassCache); + // Returns the CorElementType for the type referenced by typeDefOrRef, which must not be + // byreflike, and must be a valuetype of some form (enum or valuetype) + CorElementType GetCorElementTypeOfTypeDefOrRefForStaticField(Module* module, mdToken typeDefOrRef); // -------------------------------------------------------------------------------------------- // Returns TRUE if dwByValueClassToken refers to the type being built; otherwise returns FALSE. diff --git a/src/tests/Loader/classloader/Statics/Regressions/SelfReferentialStatics/SelfReferentialStatics.cs b/src/tests/Loader/classloader/Statics/Regressions/SelfReferentialStatics/SelfReferentialStatics.cs new file mode 100644 index 00000000000000..75ad64151e46fd --- /dev/null +++ b/src/tests/Loader/classloader/Statics/Regressions/SelfReferentialStatics/SelfReferentialStatics.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Globalization; +using Xunit; + +public class SelfReferentialStatics +{ + public interface IExample + { + public static Example DefaultExample { get; } = new(); + } + public struct Example : IExample { } + + public struct MyStruct + { + static ImmutableArray One; + } + + public struct Foo + { + public static readonly Foo Empty = default; + } + + public readonly struct StructureWithComplexStaticField + { + private readonly int _value; + + private StructureWithComplexStaticField(int value) + { + _value = value; + } + + public static readonly ReadOnlyMemory StaticField = + new StructureWithComplexStaticField[] { new(0), new(1), new(2), new(3) }; + + public override string ToString() => _value.ToString(CultureInfo.InvariantCulture); + } + + + public struct Bar + { + public static readonly Foo Baz; + public Bar(string message) => System.Console.WriteLine(message); + } + + [Fact] + [SkipOnMono("https://github.com/dotnet/runtime/issues/118472")] + public static void TestEntryPoint() + { + var example = IExample.DefaultExample; + + new Bar("Should print"); + Console.WriteLine(typeof(StructureWithComplexStaticField).FullName); + Console.WriteLine(new Foo()); + Console.WriteLine(example.GetType()); + Console.WriteLine(new MyStruct()); + Console.WriteLine("Worked"); + } +} diff --git a/src/tests/Loader/classloader/Statics/Regressions/SelfReferentialStatics/SelfReferentialStatics.csproj b/src/tests/Loader/classloader/Statics/Regressions/SelfReferentialStatics/SelfReferentialStatics.csproj new file mode 100644 index 00000000000000..efc38a9429de55 --- /dev/null +++ b/src/tests/Loader/classloader/Statics/Regressions/SelfReferentialStatics/SelfReferentialStatics.csproj @@ -0,0 +1,8 @@ + + + true + + + + +