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.
4//
5// OBJECT.INL
6//
7// Definitions inline functions of a Com+ Object
8//
9
10
11#ifndef _OBJECT_INL_
12#define _OBJECT_INL_
13
14#include "object.h"
15
16inline PTR_VOID Object::UnBox() // if it is a value class, get the pointer to the first field
17{
18 LIMITED_METHOD_DAC_CONTRACT;
19 _ASSERTE(GetMethodTable()->IsValueType());
20 _ASSERTE(!Nullable::IsNullableType(TypeHandle(GetMethodTable())));
21
22 return dac_cast<PTR_BYTE>(this) + sizeof(*this);
23}
24
25inline ADIndex Object::GetAppDomainIndex()
26{
27 WRAPPER_NO_CONTRACT;
28#ifndef _DEBUG
29 // ok to cast to AppDomain because we know it's a real AppDomain if it's not shared
30 return (dac_cast<PTR_AppDomain>(GetGCSafeMethodTable()->GetDomain())->GetIndex());
31#endif
32 return GetHeader()->GetAppDomainIndex();
33}
34
35inline DWORD Object::GetNumComponents()
36{
37 LIMITED_METHOD_DAC_CONTRACT;
38 // Yes, we may not even be an array, which means we are reading some of the object's memory - however,
39 // ComponentSize will multiply out this value. Therefore, m_NumComponents must be the first field in
40 // ArrayBase.
41 return dac_cast<PTR_ArrayBase>(this)->m_NumComponents;
42}
43
44inline SIZE_T Object::GetSize()
45{
46 LIMITED_METHOD_DAC_CONTRACT;
47
48 // mask the alignment bits because this methos is called during GC
49 MethodTable *mT = GetGCSafeMethodTable();
50
51 // strings have component size2, all other non-arrays should have 0
52 _ASSERTE(( mT->GetComponentSize() <= 2) || mT->IsArray());
53
54 size_t s = mT->GetBaseSize();
55 if (mT->HasComponentSize())
56 s += (size_t)GetNumComponents() * mT->RawGetComponentSize();
57 return s;
58}
59
60__forceinline /*static*/ DWORD StringObject::GetBaseSize()
61{
62 LIMITED_METHOD_DAC_CONTRACT;
63
64 return OBJECT_BASESIZE + sizeof(DWORD) /* length */ + sizeof(WCHAR) /* null terminator */;
65}
66
67__forceinline /*static*/ SIZE_T StringObject::GetSize(DWORD strLen)
68{
69 LIMITED_METHOD_DAC_CONTRACT;
70
71 return GetBaseSize() + strLen * sizeof(WCHAR);
72}
73
74#ifdef DACCESS_COMPILE
75
76inline void Object::EnumMemoryRegions(void)
77{
78 SUPPORTS_DAC;
79
80 PTR_MethodTable methodTable = GetGCSafeMethodTable();
81
82 TADDR ptr = dac_cast<TADDR>(this) - sizeof(ObjHeader);
83 SIZE_T size = sizeof(ObjHeader) + sizeof(Object);
84
85 // If it is unsafe to touch the MethodTable so just enumerate
86 // the base object.
87 if (methodTable.IsValid())
88 {
89 size = sizeof(ObjHeader) + GetSize();
90 }
91
92#if defined (_DEBUG)
93 // Test hook: when testing on debug builds, we want an easy way to test that the following while
94 // correctly terminates in the face of ridiculous stuff from the target.
95 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget) == 1)
96 {
97 // Pretend all objects are incredibly large.
98 size |= 0xf0000000;
99 }
100#endif // defined (_DEBUG)
101
102 // Unfortunately, DacEnumMemoryRegion takes only ULONG32 as size argument
103 while (size > 0) {
104 // Use 0x10000000 instead of MAX_ULONG32 so that the chunks stays aligned
105 SIZE_T chunk = min(size, 0x10000000);
106 // If for any reason we can't enumerate the memory, stop. This would generally mean
107 // that we have target corruption, or that the target is executing, etc.
108 if (!DacEnumMemoryRegion(ptr, chunk))
109 break;
110 ptr += chunk; size -= chunk;
111 }
112
113 // As an Object is very low-level don't propagate
114 // the enumeration to the MethodTable.
115}
116
117#else // !DACCESS_COMPILE
118
119FORCEINLINE bool Object::TryEnterObjMonitorSpinHelper()
120{
121 CONTRACTL{
122 SO_TOLERANT;
123 NOTHROW;
124 GC_NOTRIGGER;
125 MODE_COOPERATIVE;
126 } CONTRACTL_END;
127
128 Thread *pCurThread = GetThread();
129 if (pCurThread->CatchAtSafePointOpportunistic())
130 {
131 return false;
132 }
133
134 AwareLock::EnterHelperResult result = EnterObjMonitorHelper(pCurThread);
135 if (result == AwareLock::EnterHelperResult_Entered)
136 {
137 return true;
138 }
139 if (result == AwareLock::EnterHelperResult_Contention)
140 {
141 result = EnterObjMonitorHelperSpin(pCurThread);
142 if (result == AwareLock::EnterHelperResult_Entered)
143 {
144 return true;
145 }
146 }
147 return false;
148}
149
150#endif // DACCESS_COMPILE
151
152inline TypeHandle ArrayBase::GetTypeHandle() const
153{
154 WRAPPER_NO_CONTRACT;
155 return GetTypeHandle(GetMethodTable());
156}
157
158inline /* static */ TypeHandle ArrayBase::GetTypeHandle(MethodTable * pMT)
159{
160 CONTRACTL
161 {
162 NOTHROW;
163 GC_NOTRIGGER;
164 FORBID_FAULT;
165 SO_TOLERANT;
166 SUPPORTS_DAC;
167 }
168 CONTRACTL_END
169
170 _ASSERTE(pMT != NULL);
171
172 // This ensures that we can always get the typehandle for an object in hand
173 // without triggering the noisy parts of the loader.
174 //
175 // The debugger can cause this routine to be called on an unmanaged thread
176 // so this really is important.
177 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
178
179 CorElementType kind = pMT->GetInternalCorElementType();
180 unsigned rank = pMT->GetRank();
181 // Note that this load should always succeed because there is an invariant that
182 // if we have allocated an array object of type T then the ArrayTypeDesc
183 // for T[] is available and restored
184
185 // @todo This should be turned into a probe with a hard SO when we have one
186 // See also: ArrayBase::SetArrayMethodTable, ArrayBase::SetArrayMethodTableForLargeObject and MethodTable::DoFullyLoad
187 CONTRACT_VIOLATION(SOToleranceViolation);
188 // == FailIfNotLoadedOrNotRestored
189 TypeHandle arrayType = ClassLoader::LoadArrayTypeThrowing(pMT->GetApproxArrayElementTypeHandle(), kind, rank, ClassLoader::DontLoadTypes);
190 CONSISTENCY_CHECK(!arrayType.IsNull());
191 return(arrayType);
192}
193
194 // Get the CorElementType for the elements in the array. Avoids creating a TypeHandle
195inline CorElementType ArrayBase::GetArrayElementType() const
196{
197 WRAPPER_NO_CONTRACT;
198 return GetMethodTable()->GetArrayElementType();
199}
200
201inline unsigned ArrayBase::GetRank() const
202{
203 WRAPPER_NO_CONTRACT;
204 return GetMethodTable()->GetRank();
205}
206
207// Total element count for the array
208inline DWORD ArrayBase::GetNumComponents() const
209{
210 LIMITED_METHOD_CONTRACT;
211 SUPPORTS_DAC;
212 return m_NumComponents;
213}
214
215#ifndef DACCESS_COMPILE
216inline void ArrayBase::SetArrayMethodTable(MethodTable *pArrayMT)
217{
218 LIMITED_METHOD_CONTRACT;
219
220 SetMethodTable(pArrayMT
221 DEBUG_ARG(TRUE));
222
223#ifdef _DEBUG
224 AssertArrayTypeDescLoaded();
225#endif // _DEBUG
226}
227
228inline void ArrayBase::SetArrayMethodTableForLargeObject(MethodTable *pArrayMT)
229{
230 LIMITED_METHOD_CONTRACT;
231
232 SetMethodTableForLargeObject(pArrayMT
233 DEBUG_ARG(TRUE));
234
235#ifdef _DEBUG
236 AssertArrayTypeDescLoaded();
237#endif // _DEBUG
238}
239#endif // !DACCESS_COMPILE
240
241inline /* static */ unsigned ArrayBase::GetDataPtrOffset(MethodTable* pMT)
242{
243 LIMITED_METHOD_CONTRACT;
244 SUPPORTS_DAC;
245#if !defined(DACCESS_COMPILE)
246 _ASSERTE(pMT->IsArray());
247#endif // DACCESS_COMPILE
248 // The -sizeof(ObjHeader) is because of the sync block, which is before "this"
249 return pMT->GetBaseSize() - OBJHEADER_SIZE;
250}
251
252inline /* static */ unsigned ArrayBase::GetBoundsOffset(MethodTable* pMT)
253{
254 WRAPPER_NO_CONTRACT;
255 _ASSERTE(pMT->IsArray());
256 if (!pMT->IsMultiDimArray())
257 return OBJECT_SIZE /* offset(ArrayBase, m_NumComponents */;
258 _ASSERTE(pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY);
259 return ARRAYBASE_SIZE;
260}
261inline /* static */ unsigned ArrayBase::GetLowerBoundsOffset(MethodTable* pMT)
262{
263 LIMITED_METHOD_CONTRACT;
264 _ASSERTE(pMT->IsArray());
265 // There is no good offset for this for a SZARRAY.
266 _ASSERTE(pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY);
267 // Lower bounds info is after total bounds info
268 // and total bounds info has rank elements
269 return GetBoundsOffset(pMT) +
270 dac_cast<PTR_ArrayClass>(pMT->GetClass())->GetRank() *
271 sizeof(INT32);
272}
273
274// Get the element type for the array, this works whether the the element
275// type is stored in the array or not
276inline TypeHandle ArrayBase::GetArrayElementTypeHandle() const
277{
278 STATIC_CONTRACT_SO_TOLERANT;
279 STATIC_CONTRACT_NOTHROW;
280 STATIC_CONTRACT_GC_NOTRIGGER;
281 STATIC_CONTRACT_FORBID_FAULT;
282 STATIC_CONTRACT_SUPPORTS_DAC;
283
284 return GetGCSafeMethodTable()->GetApproxArrayElementTypeHandle();
285}
286
287//===============================================================================
288// Returns true if this pMT is Nullable<T> for T is equivalent to paramMT
289
290__forceinline BOOL Nullable::IsNullableForType(TypeHandle type, MethodTable* paramMT)
291{
292 if (type.IsTypeDesc())
293 return FALSE;
294 if (!type.AsMethodTable()->HasInstantiation()) // shortcut, if it is not generic it can't be Nullable<T>
295 return FALSE;
296 return Nullable::IsNullableForTypeHelper(type.AsMethodTable(), paramMT);
297}
298
299//===============================================================================
300// Returns true if this pMT is Nullable<T> for T == paramMT
301
302__forceinline BOOL Nullable::IsNullableForTypeNoGC(TypeHandle type, MethodTable* paramMT)
303{
304 if (type.IsTypeDesc())
305 return FALSE;
306 if (!type.AsMethodTable()->HasInstantiation()) // shortcut, if it is not generic it can't be Nullable<T>
307 return FALSE;
308 return Nullable::IsNullableForTypeHelperNoGC(type.AsMethodTable(), paramMT);
309}
310
311//===============================================================================
312// Returns true if this type is Nullable<T> for some T.
313
314inline BOOL Nullable::IsNullableType(TypeHandle type)
315{
316 WRAPPER_NO_CONTRACT;
317 SUPPORTS_DAC;
318
319 if (type.IsTypeDesc())
320 return FALSE;
321
322 return type.AsMethodTable()->IsNullable();
323}
324
325inline TypeHandle Object::GetTypeHandle()
326{
327 CONTRACTL
328 {
329 NOTHROW;
330 GC_NOTRIGGER;
331 FORBID_FAULT;
332 SO_TOLERANT;
333 SUPPORTS_DAC;
334 }
335 CONTRACTL_END
336
337 _ASSERTE(m_pMethTab == GetGCSafeMethodTable());
338
339 if (m_pMethTab->IsArray())
340 return (dac_cast<PTR_ArrayBase>(this))->GetTypeHandle();
341 else
342 return TypeHandle(m_pMethTab);
343}
344
345inline TypeHandle Object::GetGCSafeTypeHandle() const
346{
347 CONTRACTL
348 {
349 NOTHROW;
350 GC_NOTRIGGER;
351 SO_TOLERANT;
352 MODE_ANY;
353 }
354 CONTRACTL_END;
355
356 MethodTable * pMT = GetGCSafeMethodTable();
357 _ASSERTE(pMT != NULL);
358
359 if (pMT->IsArray())
360 return ArrayBase::GetTypeHandle(pMT);
361 else
362 return TypeHandle(pMT);
363}
364
365template<class F>
366inline void FindByRefPointerOffsetsInByRefLikeObject(PTR_MethodTable pMT, SIZE_T baseOffset, const F processPointerOffset)
367{
368 WRAPPER_NO_CONTRACT;
369 _ASSERTE(pMT != nullptr);
370 _ASSERTE(pMT->IsByRefLike());
371
372 // TODO: TypedReference should ideally be implemented as a by-ref-like struct containing a ByReference<T> field,
373 // in which case the check for g_TypedReferenceMT below would not be necessary
374 if (pMT == g_TypedReferenceMT || pMT->HasSameTypeDefAs(g_pByReferenceClass))
375 {
376 processPointerOffset(baseOffset);
377 return;
378 }
379
380 ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS);
381 for (FieldDesc *pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next())
382 {
383 if (pFD->GetFieldType() != ELEMENT_TYPE_VALUETYPE)
384 {
385 continue;
386 }
387
388 // TODO: GetApproxFieldTypeHandleThrowing may throw. This is a potential stress problem for fragile NGen of non-CoreLib
389 // assemblies. It won't ever throw for CoreCLR with R2R. Figure out if anything needs to be done to deal with the
390 // exception.
391 PTR_MethodTable pFieldMT = pFD->GetApproxFieldTypeHandleThrowing().AsMethodTable();
392 if (!pFieldMT->IsByRefLike())
393 {
394 continue;
395 }
396
397 FindByRefPointerOffsetsInByRefLikeObject(pFieldMT, baseOffset + pFD->GetOffset(), processPointerOffset);
398 }
399}
400
401#endif // _OBJECT_INL_
402