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 | |
16 | inline 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 | |
25 | inline 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 | |
35 | inline 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 | |
44 | inline 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 | |
76 | inline 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 | |
119 | FORCEINLINE 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 | |
152 | inline TypeHandle ArrayBase::GetTypeHandle() const |
153 | { |
154 | WRAPPER_NO_CONTRACT; |
155 | return GetTypeHandle(GetMethodTable()); |
156 | } |
157 | |
158 | inline /* 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 |
195 | inline CorElementType ArrayBase::GetArrayElementType() const |
196 | { |
197 | WRAPPER_NO_CONTRACT; |
198 | return GetMethodTable()->GetArrayElementType(); |
199 | } |
200 | |
201 | inline unsigned ArrayBase::GetRank() const |
202 | { |
203 | WRAPPER_NO_CONTRACT; |
204 | return GetMethodTable()->GetRank(); |
205 | } |
206 | |
207 | // Total element count for the array |
208 | inline DWORD ArrayBase::GetNumComponents() const |
209 | { |
210 | LIMITED_METHOD_CONTRACT; |
211 | SUPPORTS_DAC; |
212 | return m_NumComponents; |
213 | } |
214 | |
215 | #ifndef DACCESS_COMPILE |
216 | inline 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 | |
228 | inline 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 | |
241 | inline /* 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 | |
252 | inline /* 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 | } |
261 | inline /* 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 |
276 | inline 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 | |
314 | inline 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 | |
325 | inline 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 | |
345 | inline 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 | |
365 | template<class F> |
366 | inline 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 | |