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.CPP |
6 | // |
7 | // Definitions of a Com+ Object |
8 | // |
9 | |
10 | |
11 | |
12 | #include "common.h" |
13 | |
14 | #include "vars.hpp" |
15 | #include "class.h" |
16 | #include "object.h" |
17 | #include "threads.h" |
18 | #include "excep.h" |
19 | #include "eeconfig.h" |
20 | #include "gcheaputilities.h" |
21 | #include "field.h" |
22 | #include "argdestination.h" |
23 | |
24 | |
25 | SVAL_IMPL(INT32, ArrayBase, s_arrayBoundsZero); |
26 | |
27 | // follow the necessary rules to get a new valid hashcode for an object |
28 | DWORD Object::ComputeHashCode() |
29 | { |
30 | DWORD hashCode; |
31 | |
32 | // note that this algorithm now uses at most HASHCODE_BITS so that it will |
33 | // fit into the objheader if the hashcode has to be moved back into the objheader |
34 | // such as for an object that is being frozen |
35 | do |
36 | { |
37 | // we use the high order bits in this case because they're more random |
38 | hashCode = GetThread()->GetNewHashCode() >> (32-HASHCODE_BITS); |
39 | } |
40 | while (hashCode == 0); // need to enforce hashCode != 0 |
41 | |
42 | // verify that it really fits into HASHCODE_BITS |
43 | _ASSERTE((hashCode & ((1<<HASHCODE_BITS)-1)) == hashCode); |
44 | |
45 | return hashCode; |
46 | } |
47 | |
48 | #ifndef DACCESS_COMPILE |
49 | INT32 Object::GetHashCodeEx() |
50 | { |
51 | CONTRACTL |
52 | { |
53 | MODE_COOPERATIVE; |
54 | THROWS; |
55 | GC_NOTRIGGER; |
56 | SO_TOLERANT; |
57 | } |
58 | CONTRACTL_END |
59 | |
60 | // This loop exists because we're inspecting the header dword of the object |
61 | // and it may change under us because of races with other threads. |
62 | // On top of that, it may have the spin lock bit set, in which case we're |
63 | // not supposed to change it. |
64 | // In all of these case, we need to retry the operation. |
65 | DWORD iter = 0; |
66 | DWORD dwSwitchCount = 0; |
67 | while (true) |
68 | { |
69 | DWORD bits = GetHeader()->GetBits(); |
70 | |
71 | if (bits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) |
72 | { |
73 | if (bits & BIT_SBLK_IS_HASHCODE) |
74 | { |
75 | // Common case: the object already has a hash code |
76 | return bits & MASK_HASHCODE; |
77 | } |
78 | else |
79 | { |
80 | // We have a sync block index. This means if we already have a hash code, |
81 | // it is in the sync block, otherwise we generate a new one and store it there |
82 | SyncBlock *psb = GetSyncBlock(); |
83 | DWORD hashCode = psb->GetHashCode(); |
84 | if (hashCode != 0) |
85 | return hashCode; |
86 | |
87 | hashCode = ComputeHashCode(); |
88 | |
89 | return psb->SetHashCode(hashCode); |
90 | } |
91 | } |
92 | else |
93 | { |
94 | // If a thread is holding the thin lock or an appdomain index is set, we need a syncblock |
95 | if ((bits & (SBLK_MASK_LOCK_THREADID | (SBLK_MASK_APPDOMAININDEX << SBLK_APPDOMAIN_SHIFT))) != 0) |
96 | { |
97 | GetSyncBlock(); |
98 | // No need to replicate the above code dealing with sync blocks |
99 | // here - in the next iteration of the loop, we'll realize |
100 | // we have a syncblock, and we'll do the right thing. |
101 | } |
102 | else |
103 | { |
104 | // We want to change the header in this case, so we have to check the BIT_SBLK_SPIN_LOCK bit first |
105 | if (bits & BIT_SBLK_SPIN_LOCK) |
106 | { |
107 | iter++; |
108 | if ((iter % 1024) != 0 && g_SystemInfo.dwNumberOfProcessors > 1) |
109 | { |
110 | YieldProcessor(); // indicate to the processor that we are spining |
111 | } |
112 | else |
113 | { |
114 | __SwitchToThread(0, ++dwSwitchCount); |
115 | } |
116 | continue; |
117 | } |
118 | |
119 | DWORD hashCode = ComputeHashCode(); |
120 | |
121 | DWORD newBits = bits | BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE | hashCode; |
122 | |
123 | if (GetHeader()->SetBits(newBits, bits) == bits) |
124 | return hashCode; |
125 | // Header changed under us - let's restart this whole thing. |
126 | } |
127 | } |
128 | } |
129 | } |
130 | #endif // #ifndef DACCESS_COMPILE |
131 | |
132 | BOOL Object::ValidateObjectWithPossibleAV() |
133 | { |
134 | CANNOT_HAVE_CONTRACT; |
135 | SUPPORTS_DAC; |
136 | |
137 | return GetGCSafeMethodTable()->ValidateWithPossibleAV(); |
138 | } |
139 | |
140 | |
141 | #ifndef DACCESS_COMPILE |
142 | |
143 | TypeHandle Object::GetTrueTypeHandle() |
144 | { |
145 | CONTRACTL |
146 | { |
147 | NOTHROW; |
148 | GC_NOTRIGGER; |
149 | SO_TOLERANT; |
150 | MODE_COOPERATIVE; |
151 | } |
152 | CONTRACTL_END; |
153 | |
154 | if (m_pMethTab->IsArray()) |
155 | return ((ArrayBase*) this)->GetTypeHandle(); |
156 | else |
157 | return TypeHandle(GetMethodTable()); |
158 | } |
159 | |
160 | // There are cases where it is not possible to get a type handle during a GC. |
161 | // If we can get the type handle, this method will return it. |
162 | // Otherwise, the method will return NULL. |
163 | TypeHandle Object::GetGCSafeTypeHandleIfPossible() const |
164 | { |
165 | CONTRACTL |
166 | { |
167 | NOTHROW; |
168 | GC_NOTRIGGER; |
169 | if(!IsGCThread()) { MODE_COOPERATIVE; } |
170 | } |
171 | CONTRACTL_END; |
172 | |
173 | // Although getting the type handle is unsafe and could cause recursive type lookups |
174 | // in some cases, it's always safe and straightforward to get to the MethodTable. |
175 | MethodTable * pMT = GetGCSafeMethodTable(); |
176 | _ASSERTE(pMT != NULL); |
177 | |
178 | // Don't look at types that belong to an unloading AppDomain, or else |
179 | // pObj->GetGCSafeTypeHandle() can AV. For example, we encountered this AV when pObj |
180 | // was an array like this: |
181 | // |
182 | // MyValueType1<MyValueType2>[] myArray |
183 | // |
184 | // where MyValueType1<T> & MyValueType2 are defined in different assemblies. In such |
185 | // a case, looking up the type handle for myArray requires looking in |
186 | // MyValueType1<T>'s module's m_AssemblyRefByNameTable, which is garbage if its |
187 | // AppDomain is unloading. |
188 | // |
189 | // Another AV was encountered in a similar case, |
190 | // |
191 | // MyRefType1<MyRefType2>[] myArray |
192 | // |
193 | // where MyRefType2's module was unloaded by the time the GC occurred. In at least |
194 | // one case, the GC was caused by the AD unload itself (AppDomain::Unload -> |
195 | // AppDomain::Exit -> GCInterface::AddMemoryPressure -> WKS::GCHeapUtilities::GarbageCollect). |
196 | // |
197 | // To protect against all scenarios, verify that |
198 | // |
199 | // * The MT of the object is not getting unloaded, OR |
200 | // * In the case of arrays (potentially of arrays of arrays of arrays ...), the |
201 | // MT of the innermost element is not getting unloaded. This then ensures the |
202 | // MT of the original object (i.e., array) itself must not be getting |
203 | // unloaded either, since the MTs of arrays and of their elements are |
204 | // allocated on the same loader heap, except the case where the array is |
205 | // Object[], in which case its MT is in mscorlib and thus doesn't unload. |
206 | |
207 | MethodTable * pMTToCheck = pMT; |
208 | if (pMTToCheck->IsArray()) |
209 | { |
210 | TypeHandle thElem = static_cast<const ArrayBase * const>(this)->GetArrayElementTypeHandle(); |
211 | |
212 | // Ideally, we would just call thElem.GetLoaderModule() here. Unfortunately, the |
213 | // current TypeDesc::GetLoaderModule() implementation depends on data structures |
214 | // that might have been unloaded already. So we just simulate |
215 | // TypeDesc::GetLoaderModule() for the limited array case that we care about. In |
216 | // case we're dealing with an array of arrays of arrays etc. traverse until we |
217 | // find the deepest element, and that's the type we'll check |
218 | while (thElem.HasTypeParam()) |
219 | { |
220 | thElem = thElem.GetTypeParam(); |
221 | } |
222 | |
223 | pMTToCheck = thElem.GetMethodTable(); |
224 | } |
225 | |
226 | Module * pLoaderModule = pMTToCheck->GetLoaderModule(); |
227 | |
228 | BaseDomain * pBaseDomain = pLoaderModule->GetDomain(); |
229 | |
230 | // Don't look up types that are unloading due to Collectible Assemblies. Haven't been |
231 | // able to find a case where we actually encounter objects like this that can cause |
232 | // problems; however, it seems prudent to add this protection just in case. |
233 | LoaderAllocator * pLoaderAllocator = pLoaderModule->GetLoaderAllocator(); |
234 | _ASSERTE(pLoaderAllocator != NULL); |
235 | if ((pLoaderAllocator->IsCollectible()) && |
236 | (ObjectHandleIsNull(pLoaderAllocator->GetLoaderAllocatorObjectHandle()))) |
237 | { |
238 | return NULL; |
239 | } |
240 | |
241 | // Ok, it should now be safe to get the type handle |
242 | return GetGCSafeTypeHandle(); |
243 | } |
244 | |
245 | /* static */ BOOL Object::SupportsInterface(OBJECTREF pObj, MethodTable* pInterfaceMT) |
246 | { |
247 | CONTRACTL |
248 | { |
249 | THROWS; |
250 | GC_TRIGGERS; |
251 | INJECT_FAULT(COMPlusThrowOM()); |
252 | PRECONDITION(CheckPointer(pInterfaceMT)); |
253 | PRECONDITION(pObj->GetMethodTable()->IsRestored_NoLogging()); |
254 | PRECONDITION(pInterfaceMT->IsInterface()); |
255 | } |
256 | CONTRACTL_END |
257 | |
258 | BOOL bSupportsItf = FALSE; |
259 | |
260 | GCPROTECT_BEGIN(pObj) |
261 | { |
262 | // Make sure the interface method table has been restored. |
263 | pInterfaceMT->CheckRestore(); |
264 | |
265 | // Check to see if the static class definition indicates we implement the interface. |
266 | MethodTable * pMT = pObj->GetMethodTable(); |
267 | if (pMT->CanCastToInterface(pInterfaceMT)) |
268 | { |
269 | bSupportsItf = TRUE; |
270 | } |
271 | #ifdef FEATURE_COMINTEROP |
272 | else |
273 | if (pMT->IsComObjectType()) |
274 | { |
275 | // If this is a COM object, the static class definition might not be complete so we need |
276 | // to check if the COM object implements the interface. |
277 | bSupportsItf = ComObject::SupportsInterface(pObj, pInterfaceMT); |
278 | } |
279 | #endif // FEATURE_COMINTEROP |
280 | } |
281 | GCPROTECT_END(); |
282 | |
283 | return bSupportsItf; |
284 | } |
285 | |
286 | Assembly *AssemblyBaseObject::GetAssembly() |
287 | { |
288 | WRAPPER_NO_CONTRACT; |
289 | return m_pAssembly->GetAssembly(); |
290 | } |
291 | |
292 | #ifdef _DEBUG |
293 | // Object::DEBUG_SetAppDomain specified DEBUG_ONLY in the contract to disable SO-tolerance |
294 | // checking for paths that are DEBUG-only. |
295 | // |
296 | // NOTE: currently this is only used by WIN64 allocation helpers, but they really should |
297 | // be calling the JIT helper SetObjectAppDomain (which currently only exists for |
298 | // x86). |
299 | void Object::DEBUG_SetAppDomain(AppDomain *pDomain) |
300 | { |
301 | CONTRACTL |
302 | { |
303 | THROWS; |
304 | GC_NOTRIGGER; |
305 | DEBUG_ONLY; |
306 | INJECT_FAULT(COMPlusThrowOM();); |
307 | PRECONDITION(CheckPointer(pDomain)); |
308 | } |
309 | CONTRACTL_END; |
310 | |
311 | /*_ASSERTE(GetThread()->IsSOTolerant());*/ |
312 | SetAppDomain(pDomain); |
313 | } |
314 | #endif |
315 | |
316 | void Object::SetAppDomain(AppDomain *pDomain) |
317 | { |
318 | CONTRACTL |
319 | { |
320 | THROWS; |
321 | GC_NOTRIGGER; |
322 | SO_INTOLERANT; |
323 | INJECT_FAULT(COMPlusThrowOM();); |
324 | PRECONDITION(CheckPointer(pDomain)); |
325 | } |
326 | CONTRACTL_END; |
327 | |
328 | #ifndef _DEBUG |
329 | // |
330 | // If we have a per-app-domain method table, we can |
331 | // infer the app domain from the method table, so |
332 | // there is no reason to mark the object. |
333 | // |
334 | // But we don't do this in a debug build, because |
335 | // we want to be able to detect the case when the |
336 | // domain was unloaded from underneath an object (and |
337 | // the MethodTable will be toast in that case.) |
338 | // |
339 | _ASSERTE(pDomain == GetMethodTable()->GetDomain()); |
340 | #else |
341 | ADIndex index = pDomain->GetIndex(); |
342 | GetHeader()->SetAppDomainIndex(index); |
343 | #endif |
344 | |
345 | _ASSERTE(GetHeader()->GetAppDomainIndex().m_dwIndex != 0); |
346 | } |
347 | |
348 | BOOL Object::SetAppDomainNoThrow() |
349 | { |
350 | CONTRACTL |
351 | { |
352 | NOTHROW; |
353 | GC_NOTRIGGER; |
354 | SO_INTOLERANT; |
355 | } |
356 | CONTRACTL_END; |
357 | |
358 | BOOL success = FALSE; |
359 | |
360 | EX_TRY |
361 | { |
362 | SetAppDomain(); |
363 | success = TRUE; |
364 | } |
365 | EX_CATCH |
366 | { |
367 | _ASSERTE (!"Exception happened during Object::SetAppDomain" ); |
368 | } |
369 | EX_END_CATCH(RethrowTerminalExceptions) |
370 | |
371 | return success; |
372 | } |
373 | |
374 | AppDomain *Object::GetAppDomain() |
375 | { |
376 | CONTRACTL |
377 | { |
378 | NOTHROW; |
379 | GC_NOTRIGGER; |
380 | SO_TOLERANT; |
381 | MODE_COOPERATIVE; |
382 | } |
383 | CONTRACTL_END; |
384 | #ifndef _DEBUG |
385 | return (AppDomain*) GetMethodTable()->GetDomain(); |
386 | #endif |
387 | |
388 | ADIndex index = GetHeader()->GetAppDomainIndex(); |
389 | |
390 | if (index.m_dwIndex == 0) |
391 | return NULL; |
392 | |
393 | AppDomain *pDomain = SystemDomain::TestGetAppDomainAtIndex(index); |
394 | return pDomain; |
395 | } |
396 | |
397 | STRINGREF AllocateString(SString sstr) |
398 | { |
399 | CONTRACTL { |
400 | THROWS; |
401 | GC_TRIGGERS; |
402 | } CONTRACTL_END; |
403 | |
404 | COUNT_T length = sstr.GetCount(); // count of WCHARs excluding terminating NULL |
405 | STRINGREF strObj = AllocateString(length); |
406 | memcpyNoGCRefs(strObj->GetBuffer(), sstr.GetUnicode(), length*sizeof(WCHAR)); |
407 | |
408 | return strObj; |
409 | } |
410 | |
411 | CHARARRAYREF AllocateCharArray(DWORD dwArrayLength) |
412 | { |
413 | CONTRACTL |
414 | { |
415 | THROWS; |
416 | GC_TRIGGERS; |
417 | MODE_COOPERATIVE; |
418 | } |
419 | CONTRACTL_END; |
420 | return (CHARARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_CHAR, dwArrayLength); |
421 | } |
422 | |
423 | void Object::ValidatePromote(ScanContext *sc, DWORD flags) |
424 | { |
425 | STATIC_CONTRACT_NOTHROW; |
426 | STATIC_CONTRACT_GC_NOTRIGGER; |
427 | STATIC_CONTRACT_FORBID_FAULT; |
428 | |
429 | |
430 | #if defined (VERIFY_HEAP) |
431 | Validate(); |
432 | #endif |
433 | } |
434 | |
435 | void Object::ValidateHeap(Object *from, BOOL bDeep) |
436 | { |
437 | STATIC_CONTRACT_NOTHROW; |
438 | STATIC_CONTRACT_GC_NOTRIGGER; |
439 | STATIC_CONTRACT_FORBID_FAULT; |
440 | |
441 | #if defined (VERIFY_HEAP) |
442 | //no need to verify next object's header in this case |
443 | //since this is called in verify_heap, which will verfiy every object anyway |
444 | Validate(bDeep, FALSE); |
445 | #endif |
446 | } |
447 | |
448 | void Object::SetOffsetObjectRef(DWORD dwOffset, size_t dwValue) |
449 | { |
450 | STATIC_CONTRACT_NOTHROW; |
451 | STATIC_CONTRACT_GC_NOTRIGGER; |
452 | STATIC_CONTRACT_FORBID_FAULT; |
453 | STATIC_CONTRACT_MODE_COOPERATIVE; |
454 | STATIC_CONTRACT_SO_TOLERANT; |
455 | |
456 | OBJECTREF* location; |
457 | OBJECTREF o; |
458 | |
459 | location = (OBJECTREF *) &GetData()[dwOffset]; |
460 | o = ObjectToOBJECTREF(*(Object **) &dwValue); |
461 | |
462 | SetObjectReference( location, o, GetAppDomain() ); |
463 | } |
464 | |
465 | void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref) |
466 | { |
467 | STATIC_CONTRACT_NOTHROW; |
468 | STATIC_CONTRACT_GC_NOTRIGGER; |
469 | STATIC_CONTRACT_FORBID_FAULT; |
470 | STATIC_CONTRACT_MODE_COOPERATIVE; |
471 | STATIC_CONTRACT_CANNOT_TAKE_LOCK; |
472 | |
473 | // Assign value. We use casting to avoid going thru the overloaded |
474 | // OBJECTREF= operator which in this case would trigger a false |
475 | // write-barrier violation assert. |
476 | VolatileStore((Object**)dst, OBJECTREFToObject(ref)); |
477 | #ifdef _DEBUG |
478 | Thread::ObjectRefAssign(dst); |
479 | #endif |
480 | ErectWriteBarrier(dst, ref); |
481 | } |
482 | |
483 | void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT) |
484 | { |
485 | |
486 | STATIC_CONTRACT_NOTHROW; |
487 | STATIC_CONTRACT_GC_NOTRIGGER; |
488 | STATIC_CONTRACT_FORBID_FAULT; |
489 | STATIC_CONTRACT_MODE_COOPERATIVE; |
490 | |
491 | _ASSERTE(!pMT->IsArray()); // bunch of assumptions about arrays wrong. |
492 | |
493 | // <TODO> @todo Only call MemoryBarrier() if needed. |
494 | // Reflection is a known use case where this is required. |
495 | // Unboxing is a use case where this should not be required. |
496 | // </TODO> |
497 | MemoryBarrier(); |
498 | |
499 | // Copy the bulk of the data, and any non-GC refs. |
500 | switch (pMT->GetNumInstanceFieldBytes()) |
501 | { |
502 | case 1: |
503 | *(UINT8*)dest = *(UINT8*)src; |
504 | break; |
505 | #ifndef ALIGN_ACCESS |
506 | // we can hit an alignment fault if the value type has multiple |
507 | // smaller fields. Example: if there are two I4 fields, the |
508 | // value class can be aligned to 4-byte boundaries, yet the |
509 | // NumInstanceFieldBytes is 8 |
510 | case 2: |
511 | *(UINT16*)dest = *(UINT16*)src; |
512 | break; |
513 | case 4: |
514 | *(UINT32*)dest = *(UINT32*)src; |
515 | break; |
516 | case 8: |
517 | *(UINT64*)dest = *(UINT64*)src; |
518 | break; |
519 | #endif // !ALIGN_ACCESS |
520 | default: |
521 | memcpyNoGCRefs(dest, src, pMT->GetNumInstanceFieldBytes()); |
522 | break; |
523 | } |
524 | |
525 | // Tell the GC about any copies. |
526 | if (pMT->ContainsPointers()) |
527 | { |
528 | CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); |
529 | CGCDescSeries* cur = map->GetHighestSeries(); |
530 | CGCDescSeries* last = map->GetLowestSeries(); |
531 | DWORD size = pMT->GetBaseSize(); |
532 | _ASSERTE(cur >= last); |
533 | do |
534 | { |
535 | // offset to embedded references in this series must be |
536 | // adjusted by the VTable pointer, when in the unboxed state. |
537 | size_t offset = cur->GetSeriesOffset() - sizeof(void*); |
538 | OBJECTREF* srcPtr = (OBJECTREF*)(((BYTE*) src) + offset); |
539 | OBJECTREF* destPtr = (OBJECTREF*)(((BYTE*) dest) + offset); |
540 | OBJECTREF* srcPtrStop = (OBJECTREF*)((BYTE*) srcPtr + cur->GetSeriesSize() + size); |
541 | while (srcPtr < srcPtrStop) |
542 | { |
543 | SetObjectReferenceUnchecked(destPtr, ObjectToOBJECTREF(*(Object**)srcPtr)); |
544 | srcPtr++; |
545 | destPtr++; |
546 | } |
547 | cur--; |
548 | } while (cur >= last); |
549 | } |
550 | } |
551 | |
552 | // Copy value class into the argument specified by the argDest. |
553 | // The destOffset is nonzero when copying values into Nullable<T>, it is the offset |
554 | // of the T value inside of the Nullable<T> |
555 | void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset) |
556 | { |
557 | STATIC_CONTRACT_NOTHROW; |
558 | STATIC_CONTRACT_GC_NOTRIGGER; |
559 | STATIC_CONTRACT_FORBID_FAULT; |
560 | STATIC_CONTRACT_MODE_COOPERATIVE; |
561 | |
562 | #if defined(UNIX_AMD64_ABI) |
563 | |
564 | if (argDest->IsStructPassedInRegs()) |
565 | { |
566 | argDest->CopyStructToRegisters(src, pMT->GetNumInstanceFieldBytes(), destOffset); |
567 | return; |
568 | } |
569 | |
570 | #elif defined(_TARGET_ARM64_) |
571 | |
572 | if (argDest->IsHFA()) |
573 | { |
574 | argDest->CopyHFAStructToRegister(src, pMT->GetAlignedNumInstanceFieldBytes()); |
575 | return; |
576 | } |
577 | |
578 | #endif // UNIX_AMD64_ABI |
579 | // destOffset is only valid for Nullable<T> passed in registers |
580 | _ASSERTE(destOffset == 0); |
581 | |
582 | CopyValueClassUnchecked(argDest->GetDestinationAddress(), src, pMT); |
583 | } |
584 | |
585 | // Initialize the value class argument to zeros |
586 | void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT) |
587 | { |
588 | STATIC_CONTRACT_NOTHROW; |
589 | STATIC_CONTRACT_GC_NOTRIGGER; |
590 | STATIC_CONTRACT_FORBID_FAULT; |
591 | STATIC_CONTRACT_MODE_COOPERATIVE; |
592 | |
593 | #if defined(UNIX_AMD64_ABI) |
594 | |
595 | if (argDest->IsStructPassedInRegs()) |
596 | { |
597 | argDest->ZeroStructInRegisters(pMT->GetNumInstanceFieldBytes()); |
598 | return; |
599 | } |
600 | |
601 | #endif |
602 | InitValueClass(argDest->GetDestinationAddress(), pMT); |
603 | } |
604 | |
605 | #if defined (VERIFY_HEAP) |
606 | |
607 | #include "dbginterface.h" |
608 | |
609 | // make the checking code goes as fast as possible! |
610 | #if defined(_MSC_VER) |
611 | #pragma optimize("tgy", on) |
612 | #endif |
613 | |
614 | #define CREATE_CHECK_STRING(x) #x |
615 | #define CHECK_AND_TEAR_DOWN(x) \ |
616 | do{ \ |
617 | if (!(x)) \ |
618 | { \ |
619 | _ASSERTE(!CREATE_CHECK_STRING(x)); \ |
620 | EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); \ |
621 | } \ |
622 | } while (0) |
623 | |
624 | VOID Object::Validate(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock) |
625 | { |
626 | STATIC_CONTRACT_NOTHROW; |
627 | STATIC_CONTRACT_GC_NOTRIGGER; |
628 | STATIC_CONTRACT_FORBID_FAULT; |
629 | STATIC_CONTRACT_MODE_COOPERATIVE; |
630 | STATIC_CONTRACT_CANNOT_TAKE_LOCK; |
631 | |
632 | if (this == NULL) |
633 | { |
634 | return; // NULL is ok |
635 | } |
636 | |
637 | if (g_IBCLogger.InstrEnabled() && !GCStress<cfg_any>::IsEnabled()) |
638 | { |
639 | // If we are instrumenting for IBC (and GCStress is not enabled) |
640 | // then skip these Object::Validate() as they slow down the |
641 | // instrument phase by an order of magnitude |
642 | return; |
643 | } |
644 | |
645 | if (g_fEEShutDown & ShutDown_Phase2) |
646 | { |
647 | // During second phase of shutdown the code below is not guaranteed to work. |
648 | return; |
649 | } |
650 | |
651 | #ifdef _DEBUG |
652 | { |
653 | BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
654 | Thread *pThread = GetThread(); |
655 | |
656 | if (pThread != NULL && !(pThread->PreemptiveGCDisabled())) |
657 | { |
658 | // Debugger helper threads are special in that they take over for |
659 | // what would normally be a nonEE thread (the RCThread). If an |
660 | // EE thread is doing RCThread duty, then it should be treated |
661 | // as such. |
662 | // |
663 | // There are some GC threads in the same kind of category. Note that |
664 | // GetThread() sometimes returns them, if DLL_THREAD_ATTACH notifications |
665 | // have run some managed code. |
666 | if (!dbgOnly_IsSpecialEEThread() && !IsGCSpecialThread()) |
667 | _ASSERTE(!"OBJECTREF being accessed while thread is in preemptive GC mode." ); |
668 | } |
669 | END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
670 | } |
671 | #endif |
672 | |
673 | |
674 | { // ValidateInner can throw or fault on failure which violates contract. |
675 | CONTRACT_VIOLATION(ThrowsViolation | FaultViolation); |
676 | |
677 | // using inner helper because of TRY and stack objects with destructors. |
678 | ValidateInner(bDeep, bVerifyNextHeader, bVerifySyncBlock); |
679 | } |
680 | } |
681 | |
682 | VOID Object::ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock) |
683 | { |
684 | STATIC_CONTRACT_THROWS; // See CONTRACT_VIOLATION above |
685 | STATIC_CONTRACT_GC_NOTRIGGER; |
686 | STATIC_CONTRACT_FAULT; // See CONTRACT_VIOLATION above |
687 | STATIC_CONTRACT_MODE_COOPERATIVE; |
688 | STATIC_CONTRACT_CANNOT_TAKE_LOCK; |
689 | |
690 | int lastTest = 0; |
691 | |
692 | EX_TRY |
693 | { |
694 | // in order to avoid contract violations in the EH code we'll allow AVs here, |
695 | // they'll be handled in the catch block |
696 | AVInRuntimeImplOkayHolder avOk; |
697 | |
698 | MethodTable *pMT = GetGCSafeMethodTable(); |
699 | |
700 | lastTest = 1; |
701 | |
702 | CHECK_AND_TEAR_DOWN(pMT && pMT->Validate()); |
703 | lastTest = 2; |
704 | |
705 | bool noRangeChecks = |
706 | (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_NO_RANGE_CHECKS) == EEConfig::HEAPVERIFY_NO_RANGE_CHECKS; |
707 | |
708 | // noRangeChecks depends on initial values being FALSE |
709 | BOOL bSmallObjectHeapPtr = FALSE, bLargeObjectHeapPtr = FALSE; |
710 | if (!noRangeChecks) |
711 | { |
712 | bSmallObjectHeapPtr = GCHeapUtilities::GetGCHeap()->IsHeapPointer(this, true); |
713 | if (!bSmallObjectHeapPtr) |
714 | bLargeObjectHeapPtr = GCHeapUtilities::GetGCHeap()->IsHeapPointer(this); |
715 | |
716 | CHECK_AND_TEAR_DOWN(bSmallObjectHeapPtr || bLargeObjectHeapPtr); |
717 | } |
718 | |
719 | lastTest = 3; |
720 | |
721 | if (bDeep) |
722 | { |
723 | CHECK_AND_TEAR_DOWN(GetHeader()->Validate(bVerifySyncBlock)); |
724 | } |
725 | |
726 | lastTest = 4; |
727 | |
728 | if (bDeep && (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)) { |
729 | GCHeapUtilities::GetGCHeap()->ValidateObjectMember(this); |
730 | } |
731 | |
732 | lastTest = 5; |
733 | |
734 | // since bSmallObjectHeapPtr is initialized to FALSE |
735 | // we skip checking noRangeChecks since if skipping |
736 | // is enabled bSmallObjectHeapPtr will always be false. |
737 | if (bSmallObjectHeapPtr) { |
738 | CHECK_AND_TEAR_DOWN(!GCHeapUtilities::GetGCHeap()->IsObjectInFixedHeap(this)); |
739 | } |
740 | |
741 | lastTest = 6; |
742 | |
743 | lastTest = 7; |
744 | |
745 | _ASSERTE(GCHeapUtilities::IsGCHeapInitialized()); |
746 | // try to validate next object's header |
747 | if (bDeep |
748 | && bVerifyNextHeader |
749 | && GCHeapUtilities::GetGCHeap()->RuntimeStructuresValid() |
750 | //NextObj could be very slow if concurrent GC is going on |
751 | && !GCHeapUtilities::GetGCHeap ()->IsConcurrentGCInProgress ()) |
752 | { |
753 | Object * nextObj = GCHeapUtilities::GetGCHeap ()->NextObj (this); |
754 | if ((nextObj != NULL) && |
755 | (nextObj->GetGCSafeMethodTable() != g_pFreeObjectMethodTable)) |
756 | { |
757 | CHECK_AND_TEAR_DOWN(nextObj->GetHeader()->Validate(FALSE)); |
758 | } |
759 | } |
760 | |
761 | lastTest = 8; |
762 | |
763 | #ifdef FEATURE_64BIT_ALIGNMENT |
764 | if (pMT->RequiresAlign8()) |
765 | { |
766 | CHECK_AND_TEAR_DOWN((((size_t)this) & 0x7) == (pMT->IsValueType()? 4:0)); |
767 | } |
768 | lastTest = 9; |
769 | #endif // FEATURE_64BIT_ALIGNMENT |
770 | |
771 | } |
772 | EX_CATCH |
773 | { |
774 | STRESS_LOG3(LF_ASSERT, LL_ALWAYS, "Detected use of corrupted OBJECTREF: %p [MT=%p] (lastTest=%d)" , this, lastTest > 0 ? (*(size_t*)this) : 0, lastTest); |
775 | CHECK_AND_TEAR_DOWN(!"Detected use of a corrupted OBJECTREF. Possible GC hole." ); |
776 | } |
777 | EX_END_CATCH(SwallowAllExceptions); |
778 | } |
779 | |
780 | |
781 | #endif // VERIFY_HEAP |
782 | |
783 | #ifndef DACCESS_COMPILE |
784 | #ifdef _DEBUG |
785 | void ArrayBase::AssertArrayTypeDescLoaded() |
786 | { |
787 | _ASSERTE (m_pMethTab->IsArray()); |
788 | |
789 | // The type should already be loaded |
790 | // See also: MethodTable::DoFullyLoad |
791 | TypeHandle th = ClassLoader::LoadArrayTypeThrowing(m_pMethTab->GetApproxArrayElementTypeHandle(), |
792 | m_pMethTab->GetInternalCorElementType(), |
793 | m_pMethTab->GetRank(), |
794 | ClassLoader::DontLoadTypes); |
795 | |
796 | _ASSERTE(!th.IsNull()); |
797 | } |
798 | #endif // DEBUG |
799 | #endif // !DACCESS_COMPILE |
800 | |
801 | /*==================================NewString=================================== |
802 | **Action: Creates a System.String object. |
803 | **Returns: |
804 | **Arguments: |
805 | **Exceptions: |
806 | ==============================================================================*/ |
807 | STRINGREF StringObject::NewString(INT32 length) { |
808 | CONTRACTL { |
809 | GC_TRIGGERS; |
810 | MODE_COOPERATIVE; |
811 | PRECONDITION(length>=0); |
812 | } CONTRACTL_END; |
813 | |
814 | STRINGREF pString; |
815 | |
816 | if (length<0) { |
817 | return NULL; |
818 | } else if (length == 0) { |
819 | return GetEmptyString(); |
820 | } else { |
821 | pString = AllocateString(length); |
822 | _ASSERTE(pString->GetBuffer()[length] == 0); |
823 | |
824 | return pString; |
825 | } |
826 | } |
827 | |
828 | |
829 | /*==================================NewString=================================== |
830 | **Action: Many years ago, VB didn't have the concept of a byte array, so enterprising |
831 | ** users created one by allocating a BSTR with an odd length and using it to |
832 | ** store bytes. A generation later, we're still stuck supporting this behavior. |
833 | ** The way that we do this is to take advantage of the difference between the |
834 | ** array length and the string length. The string length will always be the |
835 | ** number of characters between the start of the string and the terminating 0. |
836 | ** If we need an odd number of bytes, we'll take one wchar after the terminating 0. |
837 | ** (e.g. at position StringLength+1). The high-order byte of this wchar is |
838 | ** reserved for flags and the low-order byte is our odd byte. This function is |
839 | ** used to allocate a string of that shape, but we don't actually mark the |
840 | ** trailing byte as being in use yet. |
841 | **Returns: A newly allocated string. Null if length is less than 0. |
842 | **Arguments: length -- the length of the string to allocate |
843 | ** bHasTrailByte -- whether the string also has a trailing byte. |
844 | **Exceptions: OutOfMemoryException if AllocateString fails. |
845 | ==============================================================================*/ |
846 | STRINGREF StringObject::NewString(INT32 length, BOOL bHasTrailByte) { |
847 | CONTRACTL { |
848 | GC_TRIGGERS; |
849 | MODE_COOPERATIVE; |
850 | PRECONDITION(length>=0 && length != INT32_MAX); |
851 | } CONTRACTL_END; |
852 | |
853 | STRINGREF pString; |
854 | if (length<0 || length == INT32_MAX) { |
855 | return NULL; |
856 | } else if (length == 0) { |
857 | return GetEmptyString(); |
858 | } else { |
859 | pString = AllocateString(length); |
860 | _ASSERTE(pString->GetBuffer()[length]==0); |
861 | if (bHasTrailByte) { |
862 | _ASSERTE(pString->GetBuffer()[length+1]==0); |
863 | } |
864 | } |
865 | |
866 | return pString; |
867 | } |
868 | |
869 | //======================================================================== |
870 | // Creates a System.String object and initializes from |
871 | // the supplied null-terminated C string. |
872 | // |
873 | // Maps NULL to null. This function does *not* return null to indicate |
874 | // error situations: it throws an exception instead. |
875 | //======================================================================== |
876 | STRINGREF StringObject::NewString(const WCHAR *pwsz) |
877 | { |
878 | CONTRACTL { |
879 | GC_TRIGGERS; |
880 | MODE_COOPERATIVE; |
881 | } CONTRACTL_END; |
882 | |
883 | if (!pwsz) |
884 | { |
885 | return NULL; |
886 | } |
887 | else |
888 | { |
889 | |
890 | DWORD nch = (DWORD)wcslen(pwsz); |
891 | if (nch==0) { |
892 | return GetEmptyString(); |
893 | } |
894 | |
895 | #if 0 |
896 | // |
897 | // This assert is disabled because it is valid for us to get a |
898 | // pointer from the gc heap here as long as it is pinned. This |
899 | // can happen when a string is marshalled to unmanaged by |
900 | // pinning and then later put into a struct and that struct is |
901 | // then marshalled to managed. |
902 | // |
903 | _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsHeapPointer((BYTE *) pwsz) || |
904 | !"pwsz can not point to GC Heap" ); |
905 | #endif // 0 |
906 | |
907 | STRINGREF pString = AllocateString( nch ); |
908 | |
909 | memcpyNoGCRefs(pString->GetBuffer(), pwsz, nch*sizeof(WCHAR)); |
910 | _ASSERTE(pString->GetBuffer()[nch] == 0); |
911 | return pString; |
912 | } |
913 | } |
914 | |
915 | #if defined(_MSC_VER) && defined(_TARGET_X86_) |
916 | #pragma optimize("y", on) // Small critical routines, don't put in EBP frame |
917 | #endif |
918 | |
919 | STRINGREF StringObject::NewString(const WCHAR *pwsz, int length) { |
920 | CONTRACTL { |
921 | THROWS; |
922 | GC_TRIGGERS; |
923 | MODE_COOPERATIVE; |
924 | PRECONDITION(length>=0); |
925 | } CONTRACTL_END; |
926 | |
927 | if (!pwsz) |
928 | { |
929 | return NULL; |
930 | } |
931 | else if (length <= 0) { |
932 | return GetEmptyString(); |
933 | } else { |
934 | #if 0 |
935 | // |
936 | // This assert is disabled because it is valid for us to get a |
937 | // pointer from the gc heap here as long as it is pinned. This |
938 | // can happen when a string is marshalled to unmanaged by |
939 | // pinning and then later put into a struct and that struct is |
940 | // then marshalled to managed. |
941 | // |
942 | _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsHeapPointer((BYTE *) pwsz) || |
943 | !"pwsz can not point to GC Heap" ); |
944 | #endif // 0 |
945 | STRINGREF pString = AllocateString(length); |
946 | |
947 | memcpyNoGCRefs(pString->GetBuffer(), pwsz, length*sizeof(WCHAR)); |
948 | _ASSERTE(pString->GetBuffer()[length] == 0); |
949 | return pString; |
950 | } |
951 | } |
952 | |
953 | #if defined(_MSC_VER) && defined(_TARGET_X86_) |
954 | #pragma optimize("", on) // Go back to command line default optimizations |
955 | #endif |
956 | |
957 | STRINGREF StringObject::NewString(LPCUTF8 psz) |
958 | { |
959 | CONTRACTL { |
960 | GC_TRIGGERS; |
961 | MODE_COOPERATIVE; |
962 | THROWS; |
963 | PRECONDITION(CheckPointer(psz)); |
964 | } CONTRACTL_END; |
965 | |
966 | int length = (int)strlen(psz); |
967 | if (length == 0) { |
968 | return GetEmptyString(); |
969 | } |
970 | CQuickBytes qb; |
971 | WCHAR* pwsz = (WCHAR*) qb.AllocThrows((length) * sizeof(WCHAR)); |
972 | length = WszMultiByteToWideChar(CP_UTF8, 0, psz, length, pwsz, length); |
973 | if (length == 0) { |
974 | COMPlusThrow(kArgumentException, W("Arg_InvalidUTF8String" )); |
975 | } |
976 | return NewString(pwsz, length); |
977 | } |
978 | |
979 | STRINGREF StringObject::NewString(LPCUTF8 psz, int cBytes) |
980 | { |
981 | CONTRACTL { |
982 | GC_TRIGGERS; |
983 | MODE_COOPERATIVE; |
984 | THROWS; |
985 | PRECONDITION(CheckPointer(psz, NULL_OK)); |
986 | } CONTRACTL_END; |
987 | |
988 | if (!psz) |
989 | return NULL; |
990 | |
991 | _ASSERTE(psz); |
992 | _ASSERTE(cBytes >= 0); |
993 | if (cBytes == 0) { |
994 | return GetEmptyString(); |
995 | } |
996 | int cWszBytes = 0; |
997 | if (!ClrSafeInt<int>::multiply(cBytes, sizeof(WCHAR), cWszBytes)) |
998 | COMPlusThrowOM(); |
999 | CQuickBytes qb; |
1000 | WCHAR* pwsz = (WCHAR*) qb.AllocThrows(cWszBytes); |
1001 | int length = WszMultiByteToWideChar(CP_UTF8, 0, psz, cBytes, pwsz, cBytes); |
1002 | if (length == 0) { |
1003 | COMPlusThrow(kArgumentException, W("Arg_InvalidUTF8String" )); |
1004 | } |
1005 | return NewString(pwsz, length); |
1006 | } |
1007 | |
1008 | // |
1009 | // |
1010 | // STATIC MEMBER VARIABLES |
1011 | // |
1012 | // |
1013 | STRINGREF* StringObject::EmptyStringRefPtr=NULL; |
1014 | |
1015 | //The special string helpers are used as flag bits for weird strings that have bytes |
1016 | //after the terminating 0. The only case where we use this right now is the VB BSTR as |
1017 | //byte array which is described in MakeStringAsByteArrayFromBytes. |
1018 | #define SPECIAL_STRING_VB_BYTE_ARRAY 0x100 |
1019 | |
1020 | FORCEINLINE BOOL MARKS_VB_BYTE_ARRAY(WCHAR x) |
1021 | { |
1022 | return static_cast<BOOL>(x & SPECIAL_STRING_VB_BYTE_ARRAY); |
1023 | } |
1024 | |
1025 | FORCEINLINE WCHAR MAKE_VB_TRAIL_BYTE(BYTE x) |
1026 | { |
1027 | return static_cast<WCHAR>(x) | SPECIAL_STRING_VB_BYTE_ARRAY; |
1028 | } |
1029 | |
1030 | FORCEINLINE BYTE GET_VB_TRAIL_BYTE(WCHAR x) |
1031 | { |
1032 | return static_cast<BYTE>(x & 0xFF); |
1033 | } |
1034 | |
1035 | |
1036 | /*==============================InitEmptyStringRefPtr============================ |
1037 | **Action: Gets an empty string refptr, cache the result. |
1038 | **Returns: The retrieved STRINGREF. |
1039 | ==============================================================================*/ |
1040 | STRINGREF* StringObject::InitEmptyStringRefPtr() { |
1041 | CONTRACTL { |
1042 | THROWS; |
1043 | MODE_ANY; |
1044 | GC_TRIGGERS; |
1045 | } CONTRACTL_END; |
1046 | |
1047 | GCX_COOP(); |
1048 | |
1049 | EEStringData data(0, W("" ), TRUE); |
1050 | EmptyStringRefPtr = SystemDomain::System()->DefaultDomain()->GetLoaderAllocator()->GetStringObjRefPtrFromUnicodeString(&data); |
1051 | return EmptyStringRefPtr; |
1052 | } |
1053 | |
1054 | // strAChars must be null-terminated, with an appropriate aLength |
1055 | // strBChars must be null-terminated, with an appropriate bLength OR bLength == -1 |
1056 | // If bLength == -1, we stop on the first null character in strBChars |
1057 | BOOL StringObject::CaseInsensitiveCompHelper(__in_ecount(aLength) WCHAR *strAChars, __in_z INT8 *strBChars, INT32 aLength, INT32 bLength, INT32 *result) { |
1058 | CONTRACTL { |
1059 | NOTHROW; |
1060 | GC_NOTRIGGER; |
1061 | MODE_ANY; |
1062 | PRECONDITION(CheckPointer(strAChars)); |
1063 | PRECONDITION(CheckPointer(strBChars)); |
1064 | PRECONDITION(CheckPointer(result)); |
1065 | SO_TOLERANT; |
1066 | } CONTRACTL_END; |
1067 | |
1068 | WCHAR *strAStart = strAChars; |
1069 | INT8 *strBStart = strBChars; |
1070 | unsigned charA; |
1071 | unsigned charB; |
1072 | |
1073 | for(;;) { |
1074 | charA = *strAChars; |
1075 | charB = (unsigned) *strBChars; |
1076 | |
1077 | //Case-insensitive comparison on chars greater than 0x7F |
1078 | //requires a locale-aware casing operation and we're not going there. |
1079 | if ((charA|charB)>0x7F) { |
1080 | *result = 0; |
1081 | return FALSE; |
1082 | } |
1083 | |
1084 | // uppercase both chars. |
1085 | if (charA>='a' && charA<='z') { |
1086 | charA ^= 0x20; |
1087 | } |
1088 | if (charB>='a' && charB<='z') { |
1089 | charB ^= 0x20; |
1090 | } |
1091 | |
1092 | //Return the (case-insensitive) difference between them. |
1093 | if (charA!=charB) { |
1094 | *result = (int)(charA-charB); |
1095 | return TRUE; |
1096 | } |
1097 | |
1098 | |
1099 | if (charA==0) // both strings have null character |
1100 | { |
1101 | if (bLength == -1) |
1102 | { |
1103 | *result = aLength - static_cast<INT32>(strAChars - strAStart); |
1104 | return TRUE; |
1105 | } |
1106 | if (strAChars==strAStart + aLength || strBChars==strBStart + bLength) |
1107 | { |
1108 | *result = aLength - bLength; |
1109 | return TRUE; |
1110 | } |
1111 | // else both embedded zeros |
1112 | } |
1113 | |
1114 | // Next char |
1115 | strAChars++; strBChars++; |
1116 | } |
1117 | |
1118 | } |
1119 | |
1120 | /*=============================InternalHasHighChars============================= |
1121 | **Action: Checks if the string can be sorted quickly. The requirements are that |
1122 | ** the string contain no character greater than 0x80 and that the string not |
1123 | ** contain an apostrophe or a hypen. Apostrophe and hyphen are excluded so that |
1124 | ** words like co-op and coop sort together. |
1125 | **Returns: Void. The side effect is to set a bit on the string indicating whether or not |
1126 | ** the string contains high chars. |
1127 | **Arguments: The String to be checked. |
1128 | **Exceptions: None |
1129 | ==============================================================================*/ |
1130 | DWORD StringObject::InternalCheckHighChars() { |
1131 | WRAPPER_NO_CONTRACT; |
1132 | |
1133 | WCHAR *chars; |
1134 | WCHAR c; |
1135 | INT32 length; |
1136 | |
1137 | RefInterpretGetStringValuesDangerousForGC((WCHAR **) &chars, &length); |
1138 | |
1139 | DWORD stringState = STRING_STATE_FAST_OPS; |
1140 | |
1141 | for (int i=0; i<length; i++) { |
1142 | c = chars[i]; |
1143 | if (c>=0x80) { |
1144 | SetHighCharState(STRING_STATE_HIGH_CHARS); |
1145 | return STRING_STATE_HIGH_CHARS; |
1146 | } else if (HighCharHelper::IsHighChar((int)c)) { |
1147 | //This means that we have a character which forces special sorting, |
1148 | //but doesn't necessarily force slower casing and indexing. We'll |
1149 | //set a value to remember this, but we need to check the rest of |
1150 | //the string because we may still find a charcter greater than 0x7f. |
1151 | stringState = STRING_STATE_SPECIAL_SORT; |
1152 | } |
1153 | } |
1154 | |
1155 | SetHighCharState(stringState); |
1156 | return stringState; |
1157 | } |
1158 | |
1159 | #ifdef VERIFY_HEAP |
1160 | /*=============================ValidateHighChars============================= |
1161 | **Action: Validate if the HighChars bits is set correctly, no side effect |
1162 | **Returns: BOOL for result of validation |
1163 | **Arguments: The String to be checked. |
1164 | **Exceptions: None |
1165 | ==============================================================================*/ |
1166 | BOOL StringObject::ValidateHighChars() |
1167 | { |
1168 | WRAPPER_NO_CONTRACT; |
1169 | DWORD curStringState = GetHighCharState (); |
1170 | // state could always be undetermined |
1171 | if (curStringState == STRING_STATE_UNDETERMINED) |
1172 | { |
1173 | return TRUE; |
1174 | } |
1175 | |
1176 | WCHAR *chars; |
1177 | INT32 length; |
1178 | RefInterpretGetStringValuesDangerousForGC((WCHAR **) &chars, &length); |
1179 | |
1180 | DWORD stringState = STRING_STATE_FAST_OPS; |
1181 | for (int i=0; i<length; i++) { |
1182 | WCHAR c = chars[i]; |
1183 | if (c>=0x80) |
1184 | { |
1185 | // if there is a high char in the string, the state has to be STRING_STATE_HIGH_CHARS |
1186 | return curStringState == STRING_STATE_HIGH_CHARS; |
1187 | } |
1188 | else if (HighCharHelper::IsHighChar((int)c)) { |
1189 | //This means that we have a character which forces special sorting, |
1190 | //but doesn't necessarily force slower casing and indexing. We'll |
1191 | //set a value to remember this, but we need to check the rest of |
1192 | //the string because we may still find a charcter greater than 0x7f. |
1193 | stringState = STRING_STATE_SPECIAL_SORT; |
1194 | } |
1195 | } |
1196 | |
1197 | return stringState == curStringState; |
1198 | } |
1199 | |
1200 | #endif //VERIFY_HEAP |
1201 | |
1202 | /*============================InternalTrailByteCheck============================ |
1203 | **Action: Many years ago, VB didn't have the concept of a byte array, so enterprising |
1204 | ** users created one by allocating a BSTR with an odd length and using it to |
1205 | ** store bytes. A generation later, we're still stuck supporting this behavior. |
1206 | ** The way that we do this is stick the trail byte in the sync block |
1207 | ** whenever we encounter such a situation. Since we expect this to be a very corner case |
1208 | ** accessing the sync block seems like a good enough solution |
1209 | ** |
1210 | **Returns: True if <CODE>str</CODE> contains a VB trail byte, false otherwise. |
1211 | **Arguments: str -- The string to be examined. |
1212 | **Exceptions: None |
1213 | ==============================================================================*/ |
1214 | BOOL StringObject::HasTrailByte() { |
1215 | WRAPPER_NO_CONTRACT; |
1216 | STATIC_CONTRACT_SO_TOLERANT; |
1217 | |
1218 | SyncBlock * pSyncBlock = PassiveGetSyncBlock(); |
1219 | if(pSyncBlock != NULL) |
1220 | { |
1221 | return pSyncBlock->HasCOMBstrTrailByte(); |
1222 | } |
1223 | |
1224 | return FALSE; |
1225 | } |
1226 | |
1227 | /*=================================GetTrailByte================================= |
1228 | **Action: If <CODE>str</CODE> contains a vb trail byte, returns a copy of it. |
1229 | **Returns: True if <CODE>str</CODE> contains a trail byte. *bTrailByte is set to |
1230 | ** the byte in question if <CODE>str</CODE> does have a trail byte, otherwise |
1231 | ** it's set to 0. |
1232 | **Arguments: str -- The string being examined. |
1233 | ** bTrailByte -- An out param to hold the value of the trail byte. |
1234 | **Exceptions: None. |
1235 | ==============================================================================*/ |
1236 | BOOL StringObject::GetTrailByte(BYTE *bTrailByte) { |
1237 | CONTRACTL |
1238 | { |
1239 | NOTHROW; |
1240 | GC_NOTRIGGER; |
1241 | SO_TOLERANT; |
1242 | MODE_ANY; |
1243 | } |
1244 | CONTRACTL_END; |
1245 | _ASSERTE(bTrailByte); |
1246 | *bTrailByte=0; |
1247 | |
1248 | BOOL retValue = HasTrailByte(); |
1249 | |
1250 | if(retValue) |
1251 | { |
1252 | *bTrailByte = GET_VB_TRAIL_BYTE(GetHeader()->PassiveGetSyncBlock()->GetCOMBstrTrailByte()); |
1253 | } |
1254 | |
1255 | return retValue; |
1256 | } |
1257 | |
1258 | /*=================================SetTrailByte================================= |
1259 | **Action: Sets the trail byte in the sync block |
1260 | **Returns: True. |
1261 | **Arguments: str -- The string into which to set the trail byte. |
1262 | ** bTrailByte -- The trail byte to be added to the string. |
1263 | **Exceptions: None. |
1264 | ==============================================================================*/ |
1265 | BOOL StringObject::SetTrailByte(BYTE bTrailByte) { |
1266 | WRAPPER_NO_CONTRACT; |
1267 | |
1268 | GetHeader()->GetSyncBlock()->SetCOMBstrTrailByte(MAKE_VB_TRAIL_BYTE(bTrailByte)); |
1269 | return TRUE; |
1270 | } |
1271 | |
1272 | #ifdef USE_CHECKED_OBJECTREFS |
1273 | |
1274 | //------------------------------------------------------------- |
1275 | // Default constructor, for non-initializing declarations: |
1276 | // |
1277 | // OBJECTREF or; |
1278 | //------------------------------------------------------------- |
1279 | OBJECTREF::OBJECTREF() |
1280 | { |
1281 | STATIC_CONTRACT_NOTHROW; |
1282 | STATIC_CONTRACT_GC_NOTRIGGER; |
1283 | STATIC_CONTRACT_FORBID_FAULT; |
1284 | |
1285 | STATIC_CONTRACT_VIOLATION(SOToleranceViolation); |
1286 | |
1287 | m_asObj = (Object*)POISONC; |
1288 | Thread::ObjectRefNew(this); |
1289 | } |
1290 | |
1291 | //------------------------------------------------------------- |
1292 | // Copy constructor, for passing OBJECTREF's as function arguments. |
1293 | //------------------------------------------------------------- |
1294 | OBJECTREF::OBJECTREF(const OBJECTREF & objref) |
1295 | { |
1296 | STATIC_CONTRACT_NOTHROW; |
1297 | STATIC_CONTRACT_GC_NOTRIGGER; |
1298 | STATIC_CONTRACT_MODE_COOPERATIVE; |
1299 | STATIC_CONTRACT_FORBID_FAULT; |
1300 | |
1301 | STATIC_CONTRACT_VIOLATION(SOToleranceViolation); |
1302 | |
1303 | VALIDATEOBJECT(objref.m_asObj); |
1304 | |
1305 | // !!! If this assert is fired, there are two possibilities: |
1306 | // !!! 1. You are doing a type cast, e.g. *(OBJECTREF*)pObj |
1307 | // !!! Instead, you should use ObjectToOBJECTREF(*(Object**)pObj), |
1308 | // !!! or ObjectToSTRINGREF(*(StringObject**)pObj) |
1309 | // !!! 2. There is a real GC hole here. |
1310 | // !!! Either way you need to fix the code. |
1311 | _ASSERTE(Thread::IsObjRefValid(&objref)); |
1312 | if ((objref.m_asObj != 0) && |
1313 | ((IGCHeap*)GCHeapUtilities::GetGCHeap())->IsHeapPointer( (BYTE*)this )) |
1314 | { |
1315 | _ASSERTE(!"Write Barrier violation. Must use SetObjectReference() to assign OBJECTREF's into the GC heap!" ); |
1316 | } |
1317 | m_asObj = objref.m_asObj; |
1318 | |
1319 | if (m_asObj != 0) { |
1320 | ENABLESTRESSHEAP(); |
1321 | } |
1322 | |
1323 | Thread::ObjectRefNew(this); |
1324 | } |
1325 | |
1326 | |
1327 | //------------------------------------------------------------- |
1328 | // To allow NULL to be used as an OBJECTREF. |
1329 | //------------------------------------------------------------- |
1330 | OBJECTREF::OBJECTREF(TADDR nul) |
1331 | { |
1332 | STATIC_CONTRACT_NOTHROW; |
1333 | STATIC_CONTRACT_GC_NOTRIGGER; |
1334 | STATIC_CONTRACT_FORBID_FAULT; |
1335 | |
1336 | STATIC_CONTRACT_VIOLATION(SOToleranceViolation); |
1337 | |
1338 | //_ASSERTE(nul == 0); |
1339 | m_asObj = (Object*)nul; |
1340 | if( m_asObj != NULL) |
1341 | { |
1342 | // REVISIT_TODO: fix this, why is this constructor being used for non-null object refs? |
1343 | STATIC_CONTRACT_VIOLATION(ModeViolation); |
1344 | |
1345 | VALIDATEOBJECT(m_asObj); |
1346 | ENABLESTRESSHEAP(); |
1347 | } |
1348 | Thread::ObjectRefNew(this); |
1349 | } |
1350 | |
1351 | //------------------------------------------------------------- |
1352 | // This is for the GC's use only. Non-GC code should never |
1353 | // use the "Object" class directly. The unused "int" argument |
1354 | // prevents C++ from using this to implicitly convert Object*'s |
1355 | // to OBJECTREF. |
1356 | //------------------------------------------------------------- |
1357 | OBJECTREF::OBJECTREF(Object *pObject) |
1358 | { |
1359 | STATIC_CONTRACT_NOTHROW; |
1360 | STATIC_CONTRACT_GC_NOTRIGGER; |
1361 | STATIC_CONTRACT_MODE_COOPERATIVE; |
1362 | STATIC_CONTRACT_FORBID_FAULT; |
1363 | |
1364 | DEBUG_ONLY_FUNCTION; |
1365 | |
1366 | if ((pObject != 0) && |
1367 | ((IGCHeap*)GCHeapUtilities::GetGCHeap())->IsHeapPointer( (BYTE*)this )) |
1368 | { |
1369 | _ASSERTE(!"Write Barrier violation. Must use SetObjectReference() to assign OBJECTREF's into the GC heap!" ); |
1370 | } |
1371 | m_asObj = pObject; |
1372 | VALIDATEOBJECT(m_asObj); |
1373 | if (m_asObj != 0) { |
1374 | ENABLESTRESSHEAP(); |
1375 | } |
1376 | Thread::ObjectRefNew(this); |
1377 | } |
1378 | |
1379 | void OBJECTREF::Validate(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock) |
1380 | { |
1381 | LIMITED_METHOD_CONTRACT; |
1382 | m_asObj->Validate(bDeep, bVerifyNextHeader, bVerifySyncBlock); |
1383 | } |
1384 | |
1385 | //------------------------------------------------------------- |
1386 | // Test against NULL. |
1387 | //------------------------------------------------------------- |
1388 | int OBJECTREF::operator!() const |
1389 | { |
1390 | STATIC_CONTRACT_NOTHROW; |
1391 | STATIC_CONTRACT_GC_NOTRIGGER; |
1392 | STATIC_CONTRACT_FORBID_FAULT; |
1393 | |
1394 | // We don't do any validation here, as we want to allow zero comparison in preemptive mode |
1395 | return !m_asObj; |
1396 | } |
1397 | |
1398 | //------------------------------------------------------------- |
1399 | // Compare two OBJECTREF's. |
1400 | //------------------------------------------------------------- |
1401 | int OBJECTREF::operator==(const OBJECTREF &objref) const |
1402 | { |
1403 | STATIC_CONTRACT_NOTHROW; |
1404 | STATIC_CONTRACT_GC_NOTRIGGER; |
1405 | STATIC_CONTRACT_FORBID_FAULT; |
1406 | |
1407 | if (objref.m_asObj != NULL) // Allow comparison to zero in preemptive mode |
1408 | { |
1409 | // REVISIT_TODO: Weakening the contract system a little bit here. We should really |
1410 | // add a special NULLOBJECTREF which can be used for these situations and have |
1411 | // a seperate code path for that with the correct contract protections. |
1412 | STATIC_CONTRACT_VIOLATION(ModeViolation); |
1413 | |
1414 | VALIDATEOBJECT(objref.m_asObj); |
1415 | |
1416 | // !!! If this assert is fired, there are two possibilities: |
1417 | // !!! 1. You are doing a type cast, e.g. *(OBJECTREF*)pObj |
1418 | // !!! Instead, you should use ObjectToOBJECTREF(*(Object**)pObj), |
1419 | // !!! or ObjectToSTRINGREF(*(StringObject**)pObj) |
1420 | // !!! 2. There is a real GC hole here. |
1421 | // !!! Either way you need to fix the code. |
1422 | _ASSERTE(Thread::IsObjRefValid(&objref)); |
1423 | VALIDATEOBJECT(m_asObj); |
1424 | // If this assert fires, you probably did not protect |
1425 | // your OBJECTREF and a GC might have occurred. To |
1426 | // where the possible GC was, set a breakpoint in Thread::TriggersGC |
1427 | _ASSERTE(Thread::IsObjRefValid(this)); |
1428 | |
1429 | if (m_asObj != 0 || objref.m_asObj != 0) { |
1430 | ENABLESTRESSHEAP(); |
1431 | } |
1432 | } |
1433 | return m_asObj == objref.m_asObj; |
1434 | } |
1435 | |
1436 | //------------------------------------------------------------- |
1437 | // Compare two OBJECTREF's. |
1438 | //------------------------------------------------------------- |
1439 | int OBJECTREF::operator!=(const OBJECTREF &objref) const |
1440 | { |
1441 | STATIC_CONTRACT_NOTHROW; |
1442 | STATIC_CONTRACT_GC_NOTRIGGER; |
1443 | STATIC_CONTRACT_FORBID_FAULT; |
1444 | |
1445 | if (objref.m_asObj != NULL) // Allow comparison to zero in preemptive mode |
1446 | { |
1447 | // REVISIT_TODO: Weakening the contract system a little bit here. We should really |
1448 | // add a special NULLOBJECTREF which can be used for these situations and have |
1449 | // a seperate code path for that with the correct contract protections. |
1450 | STATIC_CONTRACT_VIOLATION(ModeViolation); |
1451 | |
1452 | VALIDATEOBJECT(objref.m_asObj); |
1453 | |
1454 | // !!! If this assert is fired, there are two possibilities: |
1455 | // !!! 1. You are doing a type cast, e.g. *(OBJECTREF*)pObj |
1456 | // !!! Instead, you should use ObjectToOBJECTREF(*(Object**)pObj), |
1457 | // !!! or ObjectToSTRINGREF(*(StringObject**)pObj) |
1458 | // !!! 2. There is a real GC hole here. |
1459 | // !!! Either way you need to fix the code. |
1460 | _ASSERTE(Thread::IsObjRefValid(&objref)); |
1461 | VALIDATEOBJECT(m_asObj); |
1462 | // If this assert fires, you probably did not protect |
1463 | // your OBJECTREF and a GC might have occurred. To |
1464 | // where the possible GC was, set a breakpoint in Thread::TriggersGC |
1465 | _ASSERTE(Thread::IsObjRefValid(this)); |
1466 | |
1467 | if (m_asObj != 0 || objref.m_asObj != 0) { |
1468 | ENABLESTRESSHEAP(); |
1469 | } |
1470 | } |
1471 | |
1472 | return m_asObj != objref.m_asObj; |
1473 | } |
1474 | |
1475 | |
1476 | //------------------------------------------------------------- |
1477 | // Forward method calls. |
1478 | //------------------------------------------------------------- |
1479 | Object* OBJECTREF::operator->() |
1480 | { |
1481 | STATIC_CONTRACT_NOTHROW; |
1482 | STATIC_CONTRACT_GC_NOTRIGGER; |
1483 | STATIC_CONTRACT_FORBID_FAULT; |
1484 | |
1485 | VALIDATEOBJECT(m_asObj); |
1486 | // If this assert fires, you probably did not protect |
1487 | // your OBJECTREF and a GC might have occurred. To |
1488 | // where the possible GC was, set a breakpoint in Thread::TriggersGC |
1489 | _ASSERTE(Thread::IsObjRefValid(this)); |
1490 | |
1491 | if (m_asObj != 0) { |
1492 | ENABLESTRESSHEAP(); |
1493 | } |
1494 | |
1495 | // if you are using OBJECTREF directly, |
1496 | // you probably want an Object * |
1497 | return (Object *)m_asObj; |
1498 | } |
1499 | |
1500 | |
1501 | //------------------------------------------------------------- |
1502 | // Forward method calls. |
1503 | //------------------------------------------------------------- |
1504 | const Object* OBJECTREF::operator->() const |
1505 | { |
1506 | STATIC_CONTRACT_NOTHROW; |
1507 | STATIC_CONTRACT_GC_NOTRIGGER; |
1508 | STATIC_CONTRACT_FORBID_FAULT; |
1509 | |
1510 | VALIDATEOBJECT(m_asObj); |
1511 | // If this assert fires, you probably did not protect |
1512 | // your OBJECTREF and a GC might have occurred. To |
1513 | // where the possible GC was, set a breakpoint in Thread::TriggersGC |
1514 | _ASSERTE(Thread::IsObjRefValid(this)); |
1515 | |
1516 | if (m_asObj != 0) { |
1517 | ENABLESTRESSHEAP(); |
1518 | } |
1519 | |
1520 | // if you are using OBJECTREF directly, |
1521 | // you probably want an Object * |
1522 | return (Object *)m_asObj; |
1523 | } |
1524 | |
1525 | |
1526 | //------------------------------------------------------------- |
1527 | // Assignment. We don't validate the destination so as not |
1528 | // to break the sequence: |
1529 | // |
1530 | // OBJECTREF or; |
1531 | // or = ...; |
1532 | //------------------------------------------------------------- |
1533 | OBJECTREF& OBJECTREF::operator=(const OBJECTREF &objref) |
1534 | { |
1535 | STATIC_CONTRACT_NOTHROW; |
1536 | STATIC_CONTRACT_GC_NOTRIGGER; |
1537 | STATIC_CONTRACT_FORBID_FAULT; |
1538 | |
1539 | VALIDATEOBJECT(objref.m_asObj); |
1540 | |
1541 | // !!! If this assert is fired, there are two possibilities: |
1542 | // !!! 1. You are doing a type cast, e.g. *(OBJECTREF*)pObj |
1543 | // !!! Instead, you should use ObjectToOBJECTREF(*(Object**)pObj), |
1544 | // !!! or ObjectToSTRINGREF(*(StringObject**)pObj) |
1545 | // !!! 2. There is a real GC hole here. |
1546 | // !!! Either way you need to fix the code. |
1547 | _ASSERTE(Thread::IsObjRefValid(&objref)); |
1548 | |
1549 | if ((objref.m_asObj != 0) && |
1550 | ((IGCHeap*)GCHeapUtilities::GetGCHeap())->IsHeapPointer( (BYTE*)this )) |
1551 | { |
1552 | _ASSERTE(!"Write Barrier violation. Must use SetObjectReference() to assign OBJECTREF's into the GC heap!" ); |
1553 | } |
1554 | Thread::ObjectRefAssign(this); |
1555 | |
1556 | m_asObj = objref.m_asObj; |
1557 | if (m_asObj != 0) { |
1558 | ENABLESTRESSHEAP(); |
1559 | } |
1560 | return *this; |
1561 | } |
1562 | |
1563 | //------------------------------------------------------------- |
1564 | // Allows for the assignment of NULL to a OBJECTREF |
1565 | //------------------------------------------------------------- |
1566 | |
1567 | OBJECTREF& OBJECTREF::operator=(TADDR nul) |
1568 | { |
1569 | STATIC_CONTRACT_NOTHROW; |
1570 | STATIC_CONTRACT_GC_NOTRIGGER; |
1571 | STATIC_CONTRACT_FORBID_FAULT; |
1572 | |
1573 | _ASSERTE(nul == 0); |
1574 | Thread::ObjectRefAssign(this); |
1575 | m_asObj = (Object*)nul; |
1576 | if (m_asObj != 0) { |
1577 | ENABLESTRESSHEAP(); |
1578 | } |
1579 | return *this; |
1580 | } |
1581 | #endif // DEBUG |
1582 | |
1583 | #ifdef _DEBUG |
1584 | |
1585 | void* __cdecl GCSafeMemCpy(void * dest, const void * src, size_t len) |
1586 | { |
1587 | STATIC_CONTRACT_NOTHROW; |
1588 | STATIC_CONTRACT_GC_NOTRIGGER; |
1589 | STATIC_CONTRACT_FORBID_FAULT; |
1590 | STATIC_CONTRACT_SO_TOLERANT; |
1591 | |
1592 | if (!(((*(BYTE**)&dest) < g_lowest_address ) || |
1593 | ((*(BYTE**)&dest) >= g_highest_address))) |
1594 | { |
1595 | Thread* pThread = GetThread(); |
1596 | |
1597 | // GCHeapUtilities::IsHeapPointer has race when called in preemptive mode. It walks the list of segments |
1598 | // that can be modified by GC. Do the check below only if it is safe to do so. |
1599 | if (pThread != NULL && pThread->PreemptiveGCDisabled()) |
1600 | { |
1601 | // Note there is memcpyNoGCRefs which will allow you to do a memcpy into the GC |
1602 | // heap if you really know you don't need to call the write barrier |
1603 | |
1604 | _ASSERTE(!GCHeapUtilities::GetGCHeap()->IsHeapPointer((BYTE *) dest) || |
1605 | !"using memcpy to copy into the GC heap, use CopyValueClass" ); |
1606 | } |
1607 | } |
1608 | return memcpyNoGCRefs(dest, src, len); |
1609 | } |
1610 | |
1611 | #endif // _DEBUG |
1612 | |
1613 | // This function clears a piece of memory in a GC safe way. It makes the guarantee |
1614 | // that it will clear memory in at least pointer sized chunks whenever possible. |
1615 | // Unaligned memory at the beginning and remaining bytes at the end are written bytewise. |
1616 | // We must make this guarantee whenever we clear memory in the GC heap that could contain |
1617 | // object references. The GC or other user threads can read object references at any time, |
1618 | // clearing them bytewise can result in a read on another thread getting incorrect data. |
1619 | void __fastcall ZeroMemoryInGCHeap(void* mem, size_t size) |
1620 | { |
1621 | WRAPPER_NO_CONTRACT; |
1622 | BYTE* memBytes = (BYTE*) mem; |
1623 | BYTE* endBytes = &memBytes[size]; |
1624 | |
1625 | // handle unaligned bytes at the beginning |
1626 | while (!IS_ALIGNED(memBytes, sizeof(PTR_PTR_VOID)) && memBytes < endBytes) |
1627 | *memBytes++ = 0; |
1628 | |
1629 | // now write pointer sized pieces |
1630 | // volatile ensures that this doesn't get optimized back into a memset call |
1631 | size_t nPtrs = (endBytes - memBytes) / sizeof(PTR_PTR_VOID); |
1632 | PTR_VOID volatile * memPtr = (PTR_PTR_VOID) memBytes; |
1633 | for (size_t i = 0; i < nPtrs; i++) |
1634 | *memPtr++ = 0; |
1635 | |
1636 | // handle remaining bytes at the end |
1637 | memBytes = (BYTE*) memPtr; |
1638 | while (memBytes < endBytes) |
1639 | *memBytes++ = 0; |
1640 | } |
1641 | |
1642 | void StackTraceArray::Append(StackTraceElement const * begin, StackTraceElement const * end) |
1643 | { |
1644 | CONTRACTL |
1645 | { |
1646 | THROWS; |
1647 | GC_TRIGGERS; |
1648 | MODE_COOPERATIVE; |
1649 | PRECONDITION(IsProtectedByGCFrame((OBJECTREF*)this)); |
1650 | } |
1651 | CONTRACTL_END; |
1652 | |
1653 | // ensure that only one thread can write to the array |
1654 | EnsureThreadAffinity(); |
1655 | |
1656 | size_t newsize = Size() + (end - begin); |
1657 | Grow(newsize); |
1658 | memcpyNoGCRefs(GetData() + Size(), begin, (end - begin) * sizeof(StackTraceElement)); |
1659 | MemoryBarrier(); // prevent the newsize from being reordered with the array copy |
1660 | SetSize(newsize); |
1661 | |
1662 | #if defined(_DEBUG) |
1663 | CheckState(); |
1664 | #endif |
1665 | } |
1666 | |
1667 | void StackTraceArray::CheckState() const |
1668 | { |
1669 | CONTRACTL |
1670 | { |
1671 | NOTHROW; |
1672 | GC_NOTRIGGER; |
1673 | MODE_COOPERATIVE; |
1674 | } |
1675 | CONTRACTL_END; |
1676 | |
1677 | if (!m_array) |
1678 | return; |
1679 | |
1680 | assert(GetObjectThread() == GetThread()); |
1681 | |
1682 | size_t size = Size(); |
1683 | StackTraceElement const * p; |
1684 | p = GetData(); |
1685 | for (size_t i = 0; i < size; ++i) |
1686 | assert(p[i].pFunc != NULL); |
1687 | } |
1688 | |
1689 | void StackTraceArray::Grow(size_t grow_size) |
1690 | { |
1691 | CONTRACTL |
1692 | { |
1693 | THROWS; |
1694 | GC_TRIGGERS; |
1695 | MODE_COOPERATIVE; |
1696 | INJECT_FAULT(ThrowOutOfMemory();); |
1697 | PRECONDITION(IsProtectedByGCFrame((OBJECTREF*)this)); |
1698 | } |
1699 | CONTRACTL_END; |
1700 | |
1701 | size_t raw_size = grow_size * sizeof(StackTraceElement) + sizeof(ArrayHeader); |
1702 | |
1703 | if (!m_array) |
1704 | { |
1705 | SetArray(I1ARRAYREF(AllocatePrimitiveArray(ELEMENT_TYPE_I1, static_cast<DWORD>(raw_size)))); |
1706 | SetSize(0); |
1707 | SetObjectThread(); |
1708 | } |
1709 | else |
1710 | { |
1711 | if (Capacity() >= raw_size) |
1712 | return; |
1713 | |
1714 | // allocate a new array, copy the data |
1715 | size_t new_capacity = Max(Capacity() * 2, raw_size); |
1716 | |
1717 | _ASSERTE(new_capacity >= grow_size * sizeof(StackTraceElement) + sizeof(ArrayHeader)); |
1718 | |
1719 | I1ARRAYREF newarr = (I1ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_I1, static_cast<DWORD>(new_capacity)); |
1720 | memcpyNoGCRefs(newarr->GetDirectPointerToNonObjectElements(), |
1721 | GetRaw(), |
1722 | Size() * sizeof(StackTraceElement) + sizeof(ArrayHeader)); |
1723 | |
1724 | SetArray(newarr); |
1725 | } |
1726 | } |
1727 | |
1728 | void StackTraceArray::EnsureThreadAffinity() |
1729 | { |
1730 | WRAPPER_NO_CONTRACT; |
1731 | |
1732 | if (!m_array) |
1733 | return; |
1734 | |
1735 | if (GetObjectThread() != GetThread()) |
1736 | { |
1737 | // object is being changed by a thread different from the one which created it |
1738 | // make a copy of the array to prevent a race condition when two different threads try to change it |
1739 | StackTraceArray copy; |
1740 | GCPROTECT_BEGIN(copy); |
1741 | copy.CopyFrom(*this); |
1742 | this->Swap(copy); |
1743 | GCPROTECT_END(); |
1744 | } |
1745 | } |
1746 | |
1747 | #ifdef _MSC_VER |
1748 | #pragma warning(disable: 4267) |
1749 | #endif |
1750 | |
1751 | // Deep copies the stack trace array |
1752 | void StackTraceArray::CopyFrom(StackTraceArray const & src) |
1753 | { |
1754 | CONTRACTL |
1755 | { |
1756 | THROWS; |
1757 | GC_TRIGGERS; |
1758 | MODE_COOPERATIVE; |
1759 | INJECT_FAULT(ThrowOutOfMemory();); |
1760 | PRECONDITION(IsProtectedByGCFrame((OBJECTREF*)this)); |
1761 | PRECONDITION(IsProtectedByGCFrame((OBJECTREF*)&src)); |
1762 | } |
1763 | CONTRACTL_END; |
1764 | |
1765 | m_array = (I1ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_I1, static_cast<DWORD>(src.Capacity())); |
1766 | |
1767 | Volatile<size_t> size = src.Size(); |
1768 | memcpyNoGCRefs(GetRaw(), src.GetRaw(), size * sizeof(StackTraceElement) + sizeof(ArrayHeader)); |
1769 | |
1770 | SetSize(size); // set size to the exact value which was used when we copied the data |
1771 | // another thread might have changed it at the time of copying |
1772 | SetObjectThread(); // affinitize the newly created array with the current thread |
1773 | } |
1774 | |
1775 | #ifdef _MSC_VER |
1776 | #pragma warning(default: 4267) |
1777 | #endif |
1778 | |
1779 | |
1780 | #ifdef _DEBUG |
1781 | //=============================================================================== |
1782 | // Code that insures that our unmanaged version of Nullable is consistant with |
1783 | // the managed version Nullable<T> for all T. |
1784 | |
1785 | void Nullable::CheckFieldOffsets(TypeHandle nullableType) |
1786 | { |
1787 | LIMITED_METHOD_CONTRACT; |
1788 | |
1789 | /*** |
1790 | // The non-instantiated method tables like List<T> that are used |
1791 | // by reflection and verification do not have correct field offsets |
1792 | // but we never make instances of these anyway. |
1793 | if (nullableMT->ContainsGenericVariables()) |
1794 | return; |
1795 | ***/ |
1796 | |
1797 | MethodTable* nullableMT = nullableType.GetMethodTable(); |
1798 | |
1799 | // insure that the managed version of the table is the same as the |
1800 | // unmanaged. Note that we can't do this in mscorlib.h because this |
1801 | // class is generic and field layout depends on the instantiation. |
1802 | |
1803 | _ASSERTE(nullableMT->GetNumInstanceFields() == 2); |
1804 | FieldDesc* field = nullableMT->GetApproxFieldDescListRaw(); |
1805 | |
1806 | _ASSERTE(strcmp(field->GetDebugName(), "hasValue" ) == 0); |
1807 | // _ASSERTE(field->GetOffset() == offsetof(Nullable, hasValue)); |
1808 | field++; |
1809 | |
1810 | _ASSERTE(strcmp(field->GetDebugName(), "value" ) == 0); |
1811 | // _ASSERTE(field->GetOffset() == offsetof(Nullable, value)); |
1812 | } |
1813 | #endif |
1814 | |
1815 | //=============================================================================== |
1816 | // Returns true if nullableMT is Nullable<T> for T is equivalent to paramMT |
1817 | |
1818 | BOOL Nullable::IsNullableForTypeHelper(MethodTable* nullableMT, MethodTable* paramMT) |
1819 | { |
1820 | CONTRACTL |
1821 | { |
1822 | THROWS; |
1823 | GC_TRIGGERS; |
1824 | SO_TOLERANT; |
1825 | MODE_ANY; |
1826 | } |
1827 | CONTRACTL_END; |
1828 | if (!nullableMT->IsNullable()) |
1829 | return FALSE; |
1830 | |
1831 | // we require the parameter types to be equivalent |
1832 | return TypeHandle(paramMT).IsEquivalentTo(nullableMT->GetInstantiation()[0]); |
1833 | } |
1834 | |
1835 | //=============================================================================== |
1836 | // Returns true if nullableMT is Nullable<T> for T == paramMT |
1837 | |
1838 | BOOL Nullable::IsNullableForTypeHelperNoGC(MethodTable* nullableMT, MethodTable* paramMT) |
1839 | { |
1840 | LIMITED_METHOD_CONTRACT; |
1841 | if (!nullableMT->IsNullable()) |
1842 | return FALSE; |
1843 | |
1844 | // we require an exact match of the parameter types |
1845 | return TypeHandle(paramMT) == nullableMT->GetInstantiation()[0]; |
1846 | } |
1847 | |
1848 | //=============================================================================== |
1849 | CLR_BOOL* Nullable::HasValueAddr(MethodTable* nullableMT) { |
1850 | |
1851 | LIMITED_METHOD_CONTRACT; |
1852 | |
1853 | _ASSERTE(strcmp(nullableMT->GetApproxFieldDescListRaw()[0].GetDebugName(), "hasValue" ) == 0); |
1854 | _ASSERTE(nullableMT->GetApproxFieldDescListRaw()[0].GetOffset() == 0); |
1855 | return (CLR_BOOL*) this; |
1856 | } |
1857 | |
1858 | //=============================================================================== |
1859 | void* Nullable::ValueAddr(MethodTable* nullableMT) { |
1860 | |
1861 | LIMITED_METHOD_CONTRACT; |
1862 | |
1863 | _ASSERTE(strcmp(nullableMT->GetApproxFieldDescListRaw()[1].GetDebugName(), "value" ) == 0); |
1864 | return (((BYTE*) this) + nullableMT->GetApproxFieldDescListRaw()[1].GetOffset()); |
1865 | } |
1866 | |
1867 | //=============================================================================== |
1868 | // Special Logic to box a nullable<T> as a boxed<T> |
1869 | |
1870 | OBJECTREF Nullable::Box(void* srcPtr, MethodTable* nullableMT) |
1871 | { |
1872 | CONTRACTL |
1873 | { |
1874 | THROWS; |
1875 | GC_TRIGGERS; |
1876 | MODE_COOPERATIVE; |
1877 | } |
1878 | CONTRACTL_END; |
1879 | |
1880 | FAULT_NOT_FATAL(); // FIX_NOW: why do we need this? |
1881 | |
1882 | Nullable* src = (Nullable*) srcPtr; |
1883 | |
1884 | _ASSERTE(IsNullableType(nullableMT)); |
1885 | // We better have a concrete instantiation, or our field offset asserts are not useful |
1886 | _ASSERTE(!nullableMT->ContainsGenericVariables()); |
1887 | |
1888 | if (!*src->HasValueAddr(nullableMT)) |
1889 | return NULL; |
1890 | |
1891 | OBJECTREF obj = 0; |
1892 | GCPROTECT_BEGININTERIOR (src); |
1893 | MethodTable* argMT = nullableMT->GetInstantiation()[0].GetMethodTable(); |
1894 | obj = argMT->Allocate(); |
1895 | CopyValueClass(obj->UnBox(), src->ValueAddr(nullableMT), argMT, obj->GetAppDomain()); |
1896 | GCPROTECT_END (); |
1897 | |
1898 | return obj; |
1899 | } |
1900 | |
1901 | //=============================================================================== |
1902 | // Special Logic to unbox a boxed T as a nullable<T> |
1903 | |
1904 | BOOL Nullable::UnBox(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) |
1905 | { |
1906 | CONTRACTL |
1907 | { |
1908 | THROWS; |
1909 | GC_TRIGGERS; |
1910 | MODE_COOPERATIVE; |
1911 | SO_TOLERANT; |
1912 | } |
1913 | CONTRACTL_END; |
1914 | Nullable* dest = (Nullable*) destPtr; |
1915 | BOOL fRet = TRUE; |
1916 | |
1917 | // We should only get here if we are unboxing a T as a Nullable<T> |
1918 | _ASSERTE(IsNullableType(destMT)); |
1919 | |
1920 | // We better have a concrete instantiation, or our field offset asserts are not useful |
1921 | _ASSERTE(!destMT->ContainsGenericVariables()); |
1922 | |
1923 | if (boxedVal == NULL) |
1924 | { |
1925 | // Logically we are doing *dest->HasValueAddr(destMT) = false; |
1926 | // We zero out the whole structure becasue it may contain GC references |
1927 | // and these need to be initialized to zero. (could optimize in the non-GC case) |
1928 | InitValueClass(destPtr, destMT); |
1929 | fRet = TRUE; |
1930 | } |
1931 | else |
1932 | { |
1933 | GCPROTECT_BEGIN(boxedVal); |
1934 | if (!IsNullableForType(destMT, boxedVal->GetMethodTable())) |
1935 | { |
1936 | // For safety's sake, also allow true nullables to be unboxed normally. |
1937 | // This should not happen normally, but we want to be robust |
1938 | if (destMT->IsEquivalentTo(boxedVal->GetMethodTable())) |
1939 | { |
1940 | CopyValueClass(dest, boxedVal->GetData(), destMT, boxedVal->GetAppDomain()); |
1941 | fRet = TRUE; |
1942 | } |
1943 | else |
1944 | { |
1945 | fRet = FALSE; |
1946 | } |
1947 | } |
1948 | else |
1949 | { |
1950 | *dest->HasValueAddr(destMT) = true; |
1951 | CopyValueClass(dest->ValueAddr(destMT), boxedVal->UnBox(), boxedVal->GetMethodTable(), boxedVal->GetAppDomain()); |
1952 | fRet = TRUE; |
1953 | } |
1954 | GCPROTECT_END(); |
1955 | } |
1956 | return fRet; |
1957 | } |
1958 | |
1959 | //=============================================================================== |
1960 | // Special Logic to unbox a boxed T as a nullable<T> |
1961 | // Does not handle type equivalence (may conservatively return FALSE) |
1962 | BOOL Nullable::UnBoxNoGC(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) |
1963 | { |
1964 | CONTRACTL |
1965 | { |
1966 | NOTHROW; |
1967 | GC_NOTRIGGER; |
1968 | MODE_COOPERATIVE; |
1969 | SO_TOLERANT; |
1970 | } |
1971 | CONTRACTL_END; |
1972 | Nullable* dest = (Nullable*) destPtr; |
1973 | |
1974 | // We should only get here if we are unboxing a T as a Nullable<T> |
1975 | _ASSERTE(IsNullableType(destMT)); |
1976 | |
1977 | // We better have a concrete instantiation, or our field offset asserts are not useful |
1978 | _ASSERTE(!destMT->ContainsGenericVariables()); |
1979 | |
1980 | if (boxedVal == NULL) |
1981 | { |
1982 | // Logically we are doing *dest->HasValueAddr(destMT) = false; |
1983 | // We zero out the whole structure becasue it may contain GC references |
1984 | // and these need to be initialized to zero. (could optimize in the non-GC case) |
1985 | InitValueClass(destPtr, destMT); |
1986 | } |
1987 | else |
1988 | { |
1989 | if (!IsNullableForTypeNoGC(destMT, boxedVal->GetMethodTable())) |
1990 | { |
1991 | // For safety's sake, also allow true nullables to be unboxed normally. |
1992 | // This should not happen normally, but we want to be robust |
1993 | if (destMT == boxedVal->GetMethodTable()) |
1994 | { |
1995 | CopyValueClass(dest, boxedVal->GetData(), destMT, boxedVal->GetAppDomain()); |
1996 | return TRUE; |
1997 | } |
1998 | return FALSE; |
1999 | } |
2000 | |
2001 | *dest->HasValueAddr(destMT) = true; |
2002 | CopyValueClass(dest->ValueAddr(destMT), boxedVal->UnBox(), boxedVal->GetMethodTable(), boxedVal->GetAppDomain()); |
2003 | } |
2004 | return TRUE; |
2005 | } |
2006 | |
2007 | //=============================================================================== |
2008 | // Special Logic to unbox a boxed T as a nullable<T> into an argument |
2009 | // specified by the argDest. |
2010 | // Does not handle type equivalence (may conservatively return FALSE) |
2011 | BOOL Nullable::UnBoxIntoArgNoGC(ArgDestination *argDest, OBJECTREF boxedVal, MethodTable* destMT) |
2012 | { |
2013 | CONTRACTL |
2014 | { |
2015 | NOTHROW; |
2016 | GC_NOTRIGGER; |
2017 | MODE_COOPERATIVE; |
2018 | SO_TOLERANT; |
2019 | } |
2020 | CONTRACTL_END; |
2021 | |
2022 | #if defined(UNIX_AMD64_ABI) |
2023 | if (argDest->IsStructPassedInRegs()) |
2024 | { |
2025 | // We should only get here if we are unboxing a T as a Nullable<T> |
2026 | _ASSERTE(IsNullableType(destMT)); |
2027 | |
2028 | // We better have a concrete instantiation, or our field offset asserts are not useful |
2029 | _ASSERTE(!destMT->ContainsGenericVariables()); |
2030 | |
2031 | if (boxedVal == NULL) |
2032 | { |
2033 | // Logically we are doing *dest->HasValueAddr(destMT) = false; |
2034 | // We zero out the whole structure becasue it may contain GC references |
2035 | // and these need to be initialized to zero. (could optimize in the non-GC case) |
2036 | InitValueClassArg(argDest, destMT); |
2037 | } |
2038 | else |
2039 | { |
2040 | if (!IsNullableForTypeNoGC(destMT, boxedVal->GetMethodTable())) |
2041 | { |
2042 | // For safety's sake, also allow true nullables to be unboxed normally. |
2043 | // This should not happen normally, but we want to be robust |
2044 | if (destMT == boxedVal->GetMethodTable()) |
2045 | { |
2046 | CopyValueClassArg(argDest, boxedVal->GetData(), destMT, boxedVal->GetAppDomain(), 0); |
2047 | return TRUE; |
2048 | } |
2049 | return FALSE; |
2050 | } |
2051 | |
2052 | Nullable* dest = (Nullable*)argDest->GetStructGenRegDestinationAddress(); |
2053 | *dest->HasValueAddr(destMT) = true; |
2054 | int destOffset = (BYTE*)dest->ValueAddr(destMT) - (BYTE*)dest; |
2055 | CopyValueClassArg(argDest, boxedVal->UnBox(), boxedVal->GetMethodTable(), boxedVal->GetAppDomain(), destOffset); |
2056 | } |
2057 | return TRUE; |
2058 | } |
2059 | |
2060 | #endif // UNIX_AMD64_ABI |
2061 | |
2062 | return UnBoxNoGC(argDest->GetDestinationAddress(), boxedVal, destMT); |
2063 | } |
2064 | |
2065 | //=============================================================================== |
2066 | // Special Logic to unbox a boxed T as a nullable<T> |
2067 | // Does not do any type checks. |
2068 | void Nullable::UnBoxNoCheck(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) |
2069 | { |
2070 | CONTRACTL |
2071 | { |
2072 | NOTHROW; |
2073 | GC_NOTRIGGER; |
2074 | MODE_COOPERATIVE; |
2075 | SO_TOLERANT; |
2076 | } |
2077 | CONTRACTL_END; |
2078 | Nullable* dest = (Nullable*) destPtr; |
2079 | |
2080 | // We should only get here if we are unboxing a T as a Nullable<T> |
2081 | _ASSERTE(IsNullableType(destMT)); |
2082 | |
2083 | // We better have a concrete instantiation, or our field offset asserts are not useful |
2084 | _ASSERTE(!destMT->ContainsGenericVariables()); |
2085 | |
2086 | if (boxedVal == NULL) |
2087 | { |
2088 | // Logically we are doing *dest->HasValueAddr(destMT) = false; |
2089 | // We zero out the whole structure becasue it may contain GC references |
2090 | // and these need to be initialized to zero. (could optimize in the non-GC case) |
2091 | InitValueClass(destPtr, destMT); |
2092 | } |
2093 | else |
2094 | { |
2095 | if (IsNullableType(boxedVal->GetMethodTable())) |
2096 | { |
2097 | // For safety's sake, also allow true nullables to be unboxed normally. |
2098 | // This should not happen normally, but we want to be robust |
2099 | CopyValueClass(dest, boxedVal->GetData(), destMT, boxedVal->GetAppDomain()); |
2100 | } |
2101 | |
2102 | *dest->HasValueAddr(destMT) = true; |
2103 | CopyValueClass(dest->ValueAddr(destMT), boxedVal->UnBox(), boxedVal->GetMethodTable(), boxedVal->GetAppDomain()); |
2104 | } |
2105 | } |
2106 | |
2107 | //=============================================================================== |
2108 | // a boxed Nullable<T> should either be null or a boxed T, but sometimes it is |
2109 | // useful to have a 'true' boxed Nullable<T> (that is it has two fields). This |
2110 | // function returns a 'normalized' version of this pointer. |
2111 | |
2112 | OBJECTREF Nullable::NormalizeBox(OBJECTREF obj) { |
2113 | CONTRACTL |
2114 | { |
2115 | THROWS; |
2116 | GC_TRIGGERS; |
2117 | MODE_COOPERATIVE; |
2118 | } |
2119 | CONTRACTL_END; |
2120 | |
2121 | if (obj != NULL) { |
2122 | MethodTable* retMT = obj->GetMethodTable(); |
2123 | if (Nullable::IsNullableType(retMT)) |
2124 | obj = Nullable::Box(obj->GetData(), retMT); |
2125 | } |
2126 | return obj; |
2127 | } |
2128 | |
2129 | |
2130 | void ThreadBaseObject::SetInternal(Thread *it) |
2131 | { |
2132 | WRAPPER_NO_CONTRACT; |
2133 | |
2134 | // only allow a transition from NULL to non-NULL |
2135 | _ASSERTE((m_InternalThread == NULL) && (it != NULL)); |
2136 | m_InternalThread = it; |
2137 | |
2138 | // Now the native Thread will only be destroyed after the managed Thread is collected. |
2139 | // Tell the GC that the managed Thread actually represents much more memory. |
2140 | GCInterface::NewAddMemoryPressure(sizeof(Thread)); |
2141 | } |
2142 | |
2143 | void ThreadBaseObject::ClearInternal() |
2144 | { |
2145 | WRAPPER_NO_CONTRACT; |
2146 | |
2147 | _ASSERTE(m_InternalThread != NULL); |
2148 | m_InternalThread = NULL; |
2149 | GCInterface::NewRemoveMemoryPressure(sizeof(Thread)); |
2150 | } |
2151 | |
2152 | #endif // #ifndef DACCESS_COMPILE |
2153 | |
2154 | |
2155 | StackTraceElement const & StackTraceArray::operator[](size_t index) const |
2156 | { |
2157 | WRAPPER_NO_CONTRACT; |
2158 | return GetData()[index]; |
2159 | } |
2160 | |
2161 | StackTraceElement & StackTraceArray::operator[](size_t index) |
2162 | { |
2163 | WRAPPER_NO_CONTRACT; |
2164 | return GetData()[index]; |
2165 | } |
2166 | |
2167 | #if !defined(DACCESS_COMPILE) |
2168 | // Define the lock used to access stacktrace from an exception object |
2169 | SpinLock g_StackTraceArrayLock; |
2170 | |
2171 | void ExceptionObject::SetStackTrace(StackTraceArray const & stackTrace, PTRARRAYREF dynamicMethodArray) |
2172 | { |
2173 | CONTRACTL |
2174 | { |
2175 | GC_NOTRIGGER; |
2176 | NOTHROW; |
2177 | MODE_COOPERATIVE; |
2178 | SO_TOLERANT; |
2179 | } |
2180 | CONTRACTL_END; |
2181 | |
2182 | Thread *m_pThread = GetThread(); |
2183 | SpinLock::AcquireLock(&g_StackTraceArrayLock, SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); |
2184 | |
2185 | SetObjectReference((OBJECTREF*)&_stackTrace, (OBJECTREF)stackTrace.Get(), GetAppDomain()); |
2186 | SetObjectReference((OBJECTREF*)&_dynamicMethods, (OBJECTREF)dynamicMethodArray, GetAppDomain()); |
2187 | |
2188 | SpinLock::ReleaseLock(&g_StackTraceArrayLock, SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); |
2189 | |
2190 | } |
2191 | |
2192 | void ExceptionObject::SetNullStackTrace() |
2193 | { |
2194 | CONTRACTL |
2195 | { |
2196 | GC_NOTRIGGER; |
2197 | NOTHROW; |
2198 | MODE_COOPERATIVE; |
2199 | SO_TOLERANT; |
2200 | } |
2201 | CONTRACTL_END; |
2202 | |
2203 | Thread *m_pThread = GetThread(); |
2204 | SpinLock::AcquireLock(&g_StackTraceArrayLock, SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); |
2205 | |
2206 | I1ARRAYREF stackTraceArray = NULL; |
2207 | PTRARRAYREF dynamicMethodArray = NULL; |
2208 | |
2209 | SetObjectReference((OBJECTREF*)&_stackTrace, (OBJECTREF)stackTraceArray, GetAppDomain()); |
2210 | SetObjectReference((OBJECTREF*)&_dynamicMethods, (OBJECTREF)dynamicMethodArray, GetAppDomain()); |
2211 | |
2212 | SpinLock::ReleaseLock(&g_StackTraceArrayLock, SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); |
2213 | } |
2214 | |
2215 | #endif // !defined(DACCESS_COMPILE) |
2216 | |
2217 | void ExceptionObject::GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outDynamicMethodArray /*= NULL*/) const |
2218 | { |
2219 | CONTRACTL |
2220 | { |
2221 | GC_NOTRIGGER; |
2222 | NOTHROW; |
2223 | MODE_COOPERATIVE; |
2224 | SO_TOLERANT; |
2225 | } |
2226 | CONTRACTL_END; |
2227 | |
2228 | #if !defined(DACCESS_COMPILE) |
2229 | Thread *m_pThread = GetThread(); |
2230 | SpinLock::AcquireLock(&g_StackTraceArrayLock, SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); |
2231 | #endif // !defined(DACCESS_COMPILE) |
2232 | |
2233 | StackTraceArray temp(_stackTrace); |
2234 | stackTrace.Swap(temp); |
2235 | |
2236 | if (outDynamicMethodArray != NULL) |
2237 | { |
2238 | *outDynamicMethodArray = _dynamicMethods; |
2239 | } |
2240 | |
2241 | #if !defined(DACCESS_COMPILE) |
2242 | SpinLock::ReleaseLock(&g_StackTraceArrayLock, SPINLOCK_THREAD_PARAM_ONLY_IN_SOME_BUILDS); |
2243 | #endif // !defined(DACCESS_COMPILE) |
2244 | |
2245 | } |
2246 | |