1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
6 // COM+ Data Field Abstraction
15 // Temporary values stored in FieldDesc m_dwOffset during loading
16 // The high 5 bits must be zero (because in field.h we steal them for other uses), so we must choose values > 0
17 #define FIELD_OFFSET_MAX ((1<<27)-1)
18 #define FIELD_OFFSET_UNPLACED FIELD_OFFSET_MAX
19 #define FIELD_OFFSET_UNPLACED_GC_PTR (FIELD_OFFSET_MAX-1)
20 #define FIELD_OFFSET_VALUE_CLASS (FIELD_OFFSET_MAX-2)
21 #define FIELD_OFFSET_NOT_REAL_FIELD (FIELD_OFFSET_MAX-3)
23 // Offset to indicate an EnC added field. They don't have offsets as aren't placed in the object.
24 #define FIELD_OFFSET_NEW_ENC (FIELD_OFFSET_MAX-4)
25 #define FIELD_OFFSET_BIG_RVA (FIELD_OFFSET_MAX-5)
26 #define FIELD_OFFSET_LAST_REAL_OFFSET (FIELD_OFFSET_MAX-6) // real fields have to be smaller than this
30 // This describes a field - one of this is allocated for every field, so don't make this structure any larger.
33 // Field descriptors for fields in instantiated types may be shared between compatible instantiations
34 // Hence for reflection it's necessary to pair a field desc with the exact owning type handle
37 friend class MethodTableBuilder;
38 #ifdef DACCESS_COMPILE
39 friend class NativeImageDumper;
43 RelativePointer<PTR_MethodTable> m_pMTOfEnclosingClass; // This is used to hold the log2 of the field size temporarily during class loading. Yuck.
45 // See also: FieldDesc::InitializeFrom method
47 #if defined(DACCESS_COMPILE)
48 union { //create a union so I can get the correct offset for ClrDump.
52 // Note that we may store other information in the high bits if available --
53 // see enum_packedMBLayout and m_requiresFullMbValue for details.
57 unsigned m_isStatic : 1;
58 unsigned m_isThreadLocal : 1;
61 // Does this field's mb require all 24 bits
62 unsigned m_requiresFullMbValue : 1;
63 #if defined(DACCESS_COMPILE)
68 #if defined(DACCESS_COMPILE)
69 union { //create a union so I can get the correct offset for ClrDump
73 // Note: this has been as low as 22 bits in the past & seemed to be OK.
74 // we can steal some more bits here if we need them.
75 unsigned m_dwOffset : 27;
77 #if defined(DACCESS_COMPILE)
87 // Allocated by special heap means, don't construct me
90 #ifndef DACCESS_COMPILE
91 void InitializeFrom(const FieldDesc& sourceField, MethodTable *pMT)
93 m_pMTOfEnclosingClass.SetValue(pMT);
95 m_mb = sourceField.m_mb;
96 m_isStatic = sourceField.m_isStatic;
97 m_isThreadLocal = sourceField.m_isThreadLocal;
98 m_isRVA = sourceField.m_isRVA;
99 m_prot = sourceField.m_prot;
100 m_requiresFullMbValue = sourceField.m_requiresFullMbValue;
102 m_dwOffset = sourceField.m_dwOffset;
103 m_type = sourceField.m_type;
106 m_debugName = sourceField.m_debugName;
109 #endif // !DACCESS_COMPILE
112 inline LPUTF8 GetDebugName()
114 LIMITED_METHOD_CONTRACT;
119 #ifndef DACCESS_COMPILE
120 // This should be called. It was added so that Reflection
121 // can create FieldDesc's for the static primitive fields that aren't
122 // stored with the EEClass.
123 void SetMethodTable(MethodTable* mt)
125 LIMITED_METHOD_CONTRACT;
126 m_pMTOfEnclosingClass.SetValue(mt);
130 VOID Init(mdFieldDef mb,
131 CorElementType FieldType,
136 LPCSTR pszFieldName);
139 enum_packedMbLayout_MbMask = 0x01FFFF,
140 enum_packedMbLayout_NameHashMask = 0xFE0000
143 void SetMemberDef(mdFieldDef mb)
147 // Check if we have to avoid using the packed mb layout
148 if (RidFromToken(mb) > enum_packedMbLayout_MbMask)
150 m_requiresFullMbValue = 1;
153 // Set only the portion of m_mb we are using
154 if (!m_requiresFullMbValue)
156 m_mb &= ~enum_packedMbLayout_MbMask;
157 m_mb |= RidFromToken(mb);
161 m_mb = RidFromToken(mb);
165 mdFieldDef GetMemberDef() const
167 LIMITED_METHOD_DAC_CONTRACT;
169 // Check if this FieldDesc is using the packed mb layout
170 if (!m_requiresFullMbValue)
172 return TokenFromRid(m_mb & enum_packedMbLayout_MbMask, mdtFieldDef);
175 return TokenFromRid(m_mb, mdtFieldDef);
178 CorElementType GetFieldType()
180 LIMITED_METHOD_DAC_CONTRACT;
182 // Set in code:FieldDesc.Init which in turn is called from
183 // code:MethodTableBuilder.InitializeFieldDescs#InitCall which in turn calls
184 // code:MethodTableBuilder.InitializeFieldDescs#FieldDescTypeMorph
185 return (CorElementType) m_type;
188 DWORD GetFieldProtection()
190 LIMITED_METHOD_CONTRACT;
192 // Set in code:FieldDesc.Init which in turn is called from code:MethodTableBuilder::InitializeFieldDescs#InitCall
196 // Please only use this in a path that you have already guarenteed
197 // the assert is true
198 DWORD GetOffsetUnsafe()
200 LIMITED_METHOD_CONTRACT;
202 g_IBCLogger.LogFieldDescsAccess(this);
203 _ASSERTE(m_dwOffset <= FIELD_OFFSET_LAST_REAL_OFFSET);
209 LIMITED_METHOD_DAC_CONTRACT;
210 g_IBCLogger.LogFieldDescsAccess(this);
211 return GetOffset_NoLogging();
214 // During class load m_pMTOfEnclosingClass has the field size in it, so it has to use this version of
215 // GetOffset during that time
216 DWORD GetOffset_NoLogging()
218 LIMITED_METHOD_DAC_CONTRACT;
220 // Note FieldDescs are no longer on "hot" paths so the optimized code here
221 // does not look necessary.
223 if (m_dwOffset != FIELD_OFFSET_BIG_RVA) {
224 // Assert that the big RVA case handling doesn't get out of sync
225 // with the normal RVA case.
227 // The OutOfLine_BigRVAOffset() can't be correctly evaluated during the time
228 // that we repurposed m_pMTOfEnclosingClass for holding the field size
229 // I don't see any good way to determine when this is so hurray for
232 // As of 4/11/2012 I could repro this by turning on the COMPLUS log and
233 // the LOG() at line methodtablebuilder.cpp:7845
234 // MethodTableBuilder::PlaceRegularStaticFields() calls GetOffset_NoLogging()
235 if((DWORD)(DWORD_PTR&)m_pMTOfEnclosingClass > 16)
237 _ASSERTE(!this->IsRVA() || (m_dwOffset == OutOfLine_BigRVAOffset()));
243 return OutOfLine_BigRVAOffset();
246 DWORD OutOfLine_BigRVAOffset()
248 LIMITED_METHOD_DAC_CONTRACT;
252 // <NICE>I'm discarding a potential error here. According to the code in MDInternalRO.cpp,
253 // we won't get an error if we initially found the RVA. So I'm going to just
254 // assert it never happens.
256 // This is a small sin, but I don't see a good alternative. --cwb.</NICE>
258 hr = GetMDImport()->GetFieldRVA(GetMemberDef(), &rva);
259 _ASSERTE(SUCCEEDED(hr));
263 HRESULT SetOffset(DWORD dwOffset)
265 LIMITED_METHOD_CONTRACT;
268 // value class fields must be aligned to pointer-sized boundaries
271 // This is commented out because it isn't valid in all cases.
272 // This is still here because it is useful for finding alignment
275 //_ASSERTE((dwOffset > FIELD_OFFSET_LAST_REAL_OFFSET) ||
276 // (ELEMENT_TYPE_VALUETYPE != GetFieldType()) ||
277 // (IS_ALIGNED(dwOffset, sizeof(void*))));
279 m_dwOffset = dwOffset;
280 return((dwOffset > FIELD_OFFSET_LAST_REAL_OFFSET) ? COR_E_TYPELOAD : S_OK);
283 // Okay, we've stolen too many bits from FieldDescs. In the RVA case, there's no
284 // reason to believe they will be limited to 22 bits. So use a sentinel for the
285 // huge cases, and recover them from metadata on-demand.
286 void SetOffsetRVA(DWORD dwOffset)
288 LIMITED_METHOD_CONTRACT;
290 m_dwOffset = (dwOffset > FIELD_OFFSET_LAST_REAL_OFFSET)
291 ? FIELD_OFFSET_BIG_RVA
295 DWORD IsStatic() const
297 LIMITED_METHOD_DAC_CONTRACT;
302 BOOL IsSpecialStatic()
304 LIMITED_METHOD_CONTRACT;
306 return m_isStatic && (m_isRVA || m_isThreadLocal
310 BOOL IsRVA() const // Has an explicit RVA associated with it
312 LIMITED_METHOD_DAC_CONTRACT;
317 BOOL IsThreadStatic() const // Static relative to a thread
319 LIMITED_METHOD_DAC_CONTRACT;
321 return m_isThreadLocal;
324 // Indicate that this field was added by EnC
325 // Must only be called on instances of EnCFieldDesc
330 // EnC added fields don't live in the actual object, so don't have a real offset
331 SetOffset(FIELD_OFFSET_NEW_ENC);
334 // Was this field added by EnC?
335 // If this is true, then this object is an instance of EnCFieldDesc
338 LIMITED_METHOD_DAC_CONTRACT;
340 // EnC added fields don't have a real offset
341 return m_dwOffset == FIELD_OFFSET_NEW_ENC;
346 LIMITED_METHOD_DAC_CONTRACT;
348 return GetFieldType() == ELEMENT_TYPE_VALUETYPE;
353 LIMITED_METHOD_DAC_CONTRACT;
355 return (CorIsPrimitiveType(GetFieldType()) != FALSE);
360 #ifdef FEATURE_PREJIT
361 void SaveContents(DataImage *image);
362 void Fixup(DataImage *image);
363 #endif // FEATURE_PREJIT
367 // Return -1 if the type isn't loaded yet (i.e. if LookupFieldTypeHandle() would return null)
370 // These routines encapsulate the operation of getting and setting
372 void GetInstanceField(OBJECTREF o, VOID * pOutVal);
373 void SetInstanceField(OBJECTREF o, const VOID * pInVal);
375 void* GetInstanceAddress(OBJECTREF o);
377 // Get the address of a field within object 'o'
378 PTR_VOID GetAddress(PTR_VOID o);
380 PTR_VOID GetAddressNoThrowNoGC(PTR_VOID o);
381 void* GetAddressGuaranteedInHeap(void *o);
383 void* GetValuePtr(OBJECTREF o);
384 VOID SetValuePtr(OBJECTREF o, void* pValue);
385 DWORD GetValue32(OBJECTREF o);
386 VOID SetValue32(OBJECTREF o, DWORD dwValue);
387 OBJECTREF GetRefValue(OBJECTREF o);
388 VOID SetRefValue(OBJECTREF o, OBJECTREF orValue);
389 USHORT GetValue16(OBJECTREF o);
390 VOID SetValue16(OBJECTREF o, DWORD dwValue);
391 BYTE GetValue8(OBJECTREF o);
392 VOID SetValue8(OBJECTREF o, DWORD dwValue);
393 __int64 GetValue64(OBJECTREF o);
394 VOID SetValue64(OBJECTREF o, __int64 value);
396 PTR_MethodTable GetApproxEnclosingMethodTable_NoLogging()
398 LIMITED_METHOD_DAC_CONTRACT;
399 return m_pMTOfEnclosingClass.GetValue(PTR_HOST_MEMBER_TADDR(FieldDesc, this, m_pMTOfEnclosingClass));
402 PTR_MethodTable GetApproxEnclosingMethodTable()
404 LIMITED_METHOD_DAC_CONTRACT;
405 g_IBCLogger.LogFieldDescsAccess(this);
406 return GetApproxEnclosingMethodTable_NoLogging();
409 PTR_MethodTable GetEnclosingMethodTable()
411 LIMITED_METHOD_DAC_CONTRACT;
412 _ASSERTE(!IsSharedByGenericInstantiations());
413 return GetApproxEnclosingMethodTable();
416 // FieldDesc can be shared between generic instantiations. So List<String>._items
417 // is really the same as List<__Canon>._items. Hence, the FieldDesc itself
418 // cannot know the exact enclosing type. You need to provide the exact owner
419 // like List<String> or a subtype like MyInheritedList<String>.
420 MethodTable * GetExactDeclaringType(MethodTable * ownerOrSubType);
422 BOOL IsSharedByGenericInstantiations()
424 LIMITED_METHOD_DAC_CONTRACT;
425 return (!IsStatic()) && GetApproxEnclosingMethodTable()->IsSharedByGenericInstantiations();
428 BOOL IsFieldOfValueType()
431 return GetApproxEnclosingMethodTable()->IsValueType();
434 DWORD GetNumGenericClassArgs()
437 return GetApproxEnclosingMethodTable()->GetNumGenericArgs();
440 PTR_BYTE GetBaseInDomainLocalModule(DomainLocalModule * pLocalModule)
444 if (GetFieldType() == ELEMENT_TYPE_CLASS || GetFieldType() == ELEMENT_TYPE_VALUETYPE)
446 return pLocalModule->GetGCStaticsBasePointer(GetEnclosingMethodTable());
450 return pLocalModule->GetNonGCStaticsBasePointer(GetEnclosingMethodTable());
454 #ifndef DACCESS_COMPILE
461 INJECT_FAULT(COMPlusThrowOM());
465 MethodTable *pMT = GetEnclosingMethodTable();
467 return GetBaseInDomainLocalModule(pMT->GetDomainLocalModule());
470 #endif //!DACCESS_COMPILE
472 PTR_BYTE GetBaseInDomain(AppDomain * appDomain)
481 Module *pModule = GetEnclosingMethodTable()->GetModuleForStatics();
485 DomainLocalModule *pLocalModule = pModule->GetDomainLocalModule(appDomain);
486 if (pLocalModule == NULL)
489 return GetBaseInDomainLocalModule(pLocalModule);
492 // returns the address of the field
493 void* GetStaticAddress(void *base);
495 // In all cases except Value classes, the AddressHandle is
496 // simply the address of the static. For the case of value
497 // types, however, it is the address of OBJECTREF that holds
498 // the boxed value used to hold the value type. This is needed
499 // because the OBJECTREF moves, and the JIT needs to embed something
500 // in the code that does not move. Thus the jit has to
501 // dereference and unbox before the access.
502 PTR_VOID GetStaticAddressHandle(PTR_VOID base);
504 #ifndef DACCESS_COMPILE
505 OBJECTREF GetStaticOBJECTREF()
508 return *(OBJECTREF *)GetCurrentStaticAddress();
511 VOID SetStaticOBJECTREF(OBJECTREF objRef)
518 INJECT_FAULT(COMPlusThrowOM());
522 GCPROTECT_BEGIN(objRef);
523 OBJECTREF *pObjRef = (OBJECTREF *)GetCurrentStaticAddress();
524 SetObjectReference(pObjRef, objRef);
528 void* GetStaticValuePtr()
531 return *(void**)GetCurrentStaticAddress();
534 VOID SetStaticValuePtr(void *value)
537 *(void**)GetCurrentStaticAddress() = value;
540 DWORD GetStaticValue32()
543 return *(DWORD*)GetCurrentStaticAddress();
546 VOID SetStaticValue32(DWORD dwValue)
549 *(DWORD*)GetCurrentStaticAddress() = dwValue;
552 USHORT GetStaticValue16()
555 return *(USHORT*)GetCurrentStaticAddress();
558 VOID SetStaticValue16(DWORD dwValue)
561 *(USHORT*)GetCurrentStaticAddress() = (USHORT)dwValue;
564 BYTE GetStaticValue8()
567 return *(BYTE*)GetCurrentStaticAddress();
570 VOID SetStaticValue8(DWORD dwValue)
573 *(BYTE*)GetCurrentStaticAddress() = (BYTE)dwValue;
576 __int64 GetStaticValue64()
579 return *(__int64*)GetCurrentStaticAddress();
582 VOID SetStaticValue64(__int64 qwValue)
585 *(__int64*)GetCurrentStaticAddress() = qwValue;
588 void* GetCurrentStaticAddress()
595 INJECT_FAULT(COMPlusThrowOM());
599 _ASSERTE(IsStatic());
601 if (IsThreadStatic())
603 return Thread::GetStaticFieldAddress(this);
607 if (!IsRVA()) // for RVA the base is ignored
609 return GetStaticAddress((void *)dac_cast<TADDR>(base));
613 VOID CheckRunClassInitThrowing()
619 INJECT_FAULT(COMPlusThrowOM());
623 GetEnclosingMethodTable()->CheckRunClassInitThrowing();
625 #endif //DACCESS_COMPILE
629 LIMITED_METHOD_DAC_CONTRACT;
631 return GetApproxEnclosingMethodTable()->GetModule();
638 // Field Desc's are currently always saved into the same module as their
639 // corresponding method table.
640 return GetApproxEnclosingMethodTable()->IsZapped();
643 Module *GetLoaderModule()
647 // Field Desc's are currently always saved into the same module as their
648 // corresponding method table.
649 return GetApproxEnclosingMethodTable()->GetLoaderModule();
652 void GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig)
662 if (FAILED(GetMDImport()->GetSigOfFieldDef(GetMemberDef(), pcSig, ppSig)))
663 { // Class loader already asked for signature, so this should always succeed (unless there's a
664 // bug or a new code path)
665 _ASSERTE(!"If this ever fires, then this method should return HRESULT");
671 SigPointer GetSigPointer()
675 PCCOR_SIGNATURE pSig;
678 GetSig(&pSig, &cSig);
680 return SigPointer(pSig, cSig);
683 // This is slow (uses MetaData), don't use it!
695 IfFailThrow(GetMDImport()->GetNameOfFieldDef(GetMemberDef(), &szName));
696 _ASSERTE(szName != NULL);
699 // This is slow (uses MetaData), don't use it!
701 HRESULT GetName_NoThrow(LPCUTF8 *pszName)
711 return GetMDImport()->GetNameOfFieldDef(GetMemberDef(), pszName);
714 void PrecomputeNameHash();
715 BOOL MightHaveName(ULONG nameHashValue);
717 // <TODO>@TODO: </TODO>This is slow, don't use it!
718 DWORD GetAttributes()
729 if (FAILED(GetMDImport()->GetFieldDefProps(GetMemberDef(), &dwAttributes)))
730 { // Class loader already asked for attributes, so this should always succeed (unless there's a
731 // bug or a new code path)
732 _ASSERTE(!"If this ever fires, then this method should return HRESULT");
743 return IsFdPublic(GetFieldProtection());
749 return IsFdFamily(GetFieldProtection());
756 return IsFdPrivate(GetFieldProtection());
759 IMDInternalImport *GetMDImport()
761 LIMITED_METHOD_DAC_CONTRACT;
763 return GetModule()->GetMDImport();
766 #ifndef DACCESS_COMPILE
767 IMetaDataImport *GetRWImporter()
771 return GetModule()->GetRWImporter();
773 #endif // DACCESS_COMPILE
775 TypeHandle LookupFieldTypeHandle(ClassLoadLevel level = CLASS_LOADED, BOOL dropGenericArgumentLevel = FALSE);
777 TypeHandle LookupApproxFieldTypeHandle()
780 return LookupFieldTypeHandle(CLASS_LOAD_APPROXPARENTS, TRUE);
783 // Instance FieldDesc can be shared between generic instantiations. So List<String>._items
784 // is really the same as List<__Canon>._items. Hence, the FieldDesc itself
785 // cannot know the exact field type. This function returns the approximate field type.
786 // For eg. this will return "__Canon[]" for List<String>._items.
787 TypeHandle GetFieldTypeHandleThrowing(ClassLoadLevel level = CLASS_LOADED, BOOL dropGenericArgumentLevel = FALSE);
789 TypeHandle GetApproxFieldTypeHandleThrowing()
792 return GetFieldTypeHandleThrowing(CLASS_LOAD_APPROXPARENTS, TRUE);
795 // Given a type handle of an object and a method that comes from some
796 // superclass of the class of that object, find the instantiation of
797 // that superclass, i.e. the class instantiation which will be relevant
798 // to interpreting the signature of the method. The type handle of
799 // the object does not need to be given in all circumstances, in
800 // particular it is only needed for FieldDescs pFD that
801 // return true for pFD->GetApproxEnclosingMethodTable()->IsSharedByGenericInstantiations().
802 // In other cases it is allowed to be null and will be ignored.
804 // Will return NULL if the field is not in a generic class.
805 Instantiation GetExactClassInstantiation(TypeHandle possibleObjType);
807 // Instance FieldDesc can be shared between generic instantiations. So List<String>._items
808 // is really the same as List<__Canon>._items. Hence, the FieldDesc itself
809 // cannot know the exact field type. You need to specify the owner
810 // like List<String> in order to get the exact type which would be "String[]"
811 TypeHandle GetExactFieldType(TypeHandle owner);
813 #ifdef DACCESS_COMPILE
814 void EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
821 #ifndef DACCESS_COMPILE
822 REFLECTFIELDREF GetStubFieldInfo();