Switch over to managed Marvin implementation for string hashing (#17029)
[platform/upstream/coreclr.git] / src / vm / comutilnative.cpp
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
6 //
7
8 /*============================================================
9 **
10 ** File:  COMUtilNative
11 **
12 **  
13 **
14 ** Purpose: A dumping ground for classes which aren't large
15 ** enough to get their own file in the EE.
16 **
17 **
18 **
19 ===========================================================*/
20 #include "common.h"
21 #include "object.h"
22 #include "excep.h"
23 #include "vars.hpp"
24 #include "comutilnative.h"
25
26 #include "utilcode.h"
27 #include "frames.h"
28 #include "field.h"
29 #include "winwrap.h"
30 #include "gcheaputilities.h"
31 #include "fcall.h"
32 #include "invokeutil.h"
33 #include "eeconfig.h"
34 #include "typestring.h"
35 #include "sha1.h"
36 #include "finalizerthread.h"
37
38 #ifdef FEATURE_COMINTEROP
39     #include "comcallablewrapper.h"
40     #include "comcache.h"
41 #endif // FEATURE_COMINTEROP
42
43 #include "arraynative.inl"
44
45 #define STACK_OVERFLOW_MESSAGE   W("StackOverflowException")
46
47 /*===================================IsDigit====================================
48 **Returns a bool indicating whether the character passed in represents a   **
49 **digit.
50 ==============================================================================*/
51 bool IsDigit(WCHAR c, int radix, int *result)
52 {
53     CONTRACTL
54     {
55         NOTHROW;
56         GC_NOTRIGGER;
57         MODE_ANY;
58         PRECONDITION(CheckPointer(result));
59     }
60     CONTRACTL_END;
61
62     if (IS_DIGIT(c)) {
63         *result = DIGIT_TO_INT(c);
64     }
65     else if (c>='A' && c<='Z') {
66         //+10 is necessary because A is actually 10, etc.
67         *result = c-'A'+10;
68     }
69     else if (c>='a' && c<='z') {
70         //+10 is necessary because a is actually 10, etc.
71         *result = c-'a'+10;
72     }
73     else {
74         *result = -1;
75     }
76
77     if ((*result >=0) && (*result < radix))
78         return true;
79
80     return false;
81 }
82
83 INT32 wtoi(__in_ecount(length) WCHAR* wstr, DWORD length)
84 {  
85     CONTRACTL
86     {
87         NOTHROW;
88         GC_NOTRIGGER;
89         MODE_ANY;
90         PRECONDITION(CheckPointer(wstr));
91         PRECONDITION(length >= 0);
92     }
93     CONTRACTL_END;
94
95     DWORD i = 0;
96     int value;
97     INT32 result = 0;
98
99     while ( (i < length) && (IsDigit(wstr[i], 10 ,&value)) ) {
100         //Read all of the digits and convert to a number
101         result = result*10 + value;
102         i++;
103     }
104
105     return result;
106 }
107
108
109
110 //
111 //
112 // EXCEPTION NATIVE
113 //
114 //
115 FCIMPL1(FC_BOOL_RET, ExceptionNative::IsImmutableAgileException, Object* pExceptionUNSAFE)
116 {
117     FCALL_CONTRACT;
118
119     ASSERT(pExceptionUNSAFE != NULL);
120
121     OBJECTREF pException = (OBJECTREF) pExceptionUNSAFE;
122
123     // The preallocated exception objects may be used from multiple AppDomains
124     // and therefore must remain immutable from the application's perspective.
125     FC_RETURN_BOOL(CLRException::IsPreallocatedExceptionObject(pException));
126 }
127 FCIMPLEND
128
129 FCIMPL1(FC_BOOL_RET, ExceptionNative::IsTransient, INT32 hresult)
130 {
131     FCALL_CONTRACT;
132
133     FC_RETURN_BOOL(Exception::IsTransient(hresult));
134 }
135 FCIMPLEND
136
137
138 // This FCall sets a flag against the thread exception state to indicate to
139 // IL_Throw and the StackTraceInfo implementation to account for the fact
140 // that we have restored a foreign exception dispatch details.
141 //
142 // Refer to the respective methods for details on how they use this flag.
143 FCIMPL0(VOID, ExceptionNative::PrepareForForeignExceptionRaise)
144 {
145     FCALL_CONTRACT;
146
147     PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
148
149         // Set a flag against the TES to indicate this is a foreign exception raise.
150         pCurTES->SetRaisingForeignException();
151 }
152 FCIMPLEND
153
154 // Given an exception object, this method will extract the stacktrace and dynamic method array and set them up for return to the caller.
155 FCIMPL3(VOID, ExceptionNative::GetStackTracesDeepCopy, Object* pExceptionObjectUnsafe, Object **pStackTraceUnsafe, Object **pDynamicMethodsUnsafe);
156 {
157     CONTRACTL
158     {
159         FCALL_CHECK;
160     }
161     CONTRACTL_END;
162
163     ASSERT(pExceptionObjectUnsafe != NULL);
164     ASSERT(pStackTraceUnsafe != NULL);
165     ASSERT(pDynamicMethodsUnsafe != NULL);
166
167     struct _gc
168     {
169         StackTraceArray stackTrace;
170         StackTraceArray stackTraceCopy;
171         EXCEPTIONREF refException;
172         PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
173         PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
174     };
175     _gc gc;
176     ZeroMemory(&gc, sizeof(gc));
177
178     // GC protect the array reference
179     HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
180     
181     // Get the exception object reference
182     gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
183
184     // Fetch the stacktrace details from the exception under a lock
185     gc.refException->GetStackTrace(gc.stackTrace, &gc.dynamicMethodsArray);
186     
187     bool fHaveStackTrace = false;
188     bool fHaveDynamicMethodArray = false;
189
190     if ((unsigned)gc.stackTrace.Size() > 0)
191     {
192         // Deepcopy the array
193         gc.stackTraceCopy.CopyFrom(gc.stackTrace);
194         fHaveStackTrace = true;
195     }
196     
197     if (gc.dynamicMethodsArray != NULL)
198     {
199         // Get the number of elements in the dynamic methods array
200         unsigned   cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
201     
202         // ..and allocate a new array. This can trigger GC or throw under OOM.
203         gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
204     
205         // Deepcopy references to the new array we just allocated
206         memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
207                                                   cOrigDynamic * sizeof(Object *));
208
209         fHaveDynamicMethodArray = true;
210     }
211
212     // Prep to return
213     *pStackTraceUnsafe = fHaveStackTrace?OBJECTREFToObject(gc.stackTraceCopy.Get()):NULL;
214     *pDynamicMethodsUnsafe = fHaveDynamicMethodArray?OBJECTREFToObject(gc.dynamicMethodsArrayCopy):NULL;
215
216     HELPER_METHOD_FRAME_END();
217 }
218 FCIMPLEND
219
220 // Given an exception object and deep copied instances of a stacktrace and/or dynamic method array, this method will set the latter in the exception object instance.
221 FCIMPL3(VOID, ExceptionNative::SaveStackTracesFromDeepCopy, Object* pExceptionObjectUnsafe, Object *pStackTraceUnsafe, Object *pDynamicMethodsUnsafe);
222 {
223     CONTRACTL
224     {
225         FCALL_CHECK;
226     }
227     CONTRACTL_END;
228
229     ASSERT(pExceptionObjectUnsafe != NULL);
230
231     struct _gc
232     {
233         StackTraceArray stackTrace;
234         EXCEPTIONREF refException;
235         PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
236     };
237     _gc gc;
238     ZeroMemory(&gc, sizeof(gc));
239
240     // GC protect the array reference
241     HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
242     
243     // Get the exception object reference
244     gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
245
246     if (pStackTraceUnsafe != NULL)
247     {
248         // Copy the stacktrace
249         StackTraceArray stackTraceArray((I1ARRAYREF)ObjectToOBJECTREF(pStackTraceUnsafe));
250         gc.stackTrace.Swap(stackTraceArray);
251     }
252
253     gc.dynamicMethodsArray = NULL;
254     if (pDynamicMethodsUnsafe != NULL)
255     {
256         gc.dynamicMethodsArray = (PTRARRAYREF)ObjectToOBJECTREF(pDynamicMethodsUnsafe);
257     }
258
259     // If there is no stacktrace, then there cannot be any dynamic method array. Thus,
260     // save stacktrace only when we have it.
261     if (gc.stackTrace.Size() > 0)
262     {
263         // Save the stacktrace details in the exception under a lock
264         gc.refException->SetStackTrace(gc.stackTrace, gc.dynamicMethodsArray);
265     }
266     else
267     {
268         gc.refException->SetNullStackTrace();
269     }
270
271     HELPER_METHOD_FRAME_END();
272 }
273 FCIMPLEND
274
275 // This method performs a deep copy of the stack trace array.
276 FCIMPL1(Object*, ExceptionNative::CopyStackTrace, Object* pStackTraceUNSAFE)
277 {
278     FCALL_CONTRACT;
279
280     ASSERT(pStackTraceUNSAFE != NULL);
281
282     struct _gc
283     {
284         StackTraceArray stackTrace;
285         StackTraceArray stackTraceCopy;
286         _gc(I1ARRAYREF refStackTrace)
287             : stackTrace(refStackTrace)
288         {}
289     };
290     _gc gc((I1ARRAYREF)(ObjectToOBJECTREF(pStackTraceUNSAFE)));
291
292     // GC protect the array reference
293     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
294         
295     // Deepcopy the array
296     gc.stackTraceCopy.CopyFrom(gc.stackTrace);
297     
298     HELPER_METHOD_FRAME_END();
299
300     return OBJECTREFToObject(gc.stackTraceCopy.Get());
301 }
302 FCIMPLEND
303
304 // This method performs a deep copy of the dynamic method array.
305 FCIMPL1(Object*, ExceptionNative::CopyDynamicMethods, Object* pDynamicMethodsUNSAFE)
306 {
307     FCALL_CONTRACT;
308
309     ASSERT(pDynamicMethodsUNSAFE != NULL);
310
311     struct _gc
312     {
313         PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
314         PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
315         _gc()
316         {}
317     };
318     _gc gc;
319     ZeroMemory(&gc, sizeof(gc));
320     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
321     
322     gc.dynamicMethodsArray = (PTRARRAYREF)(ObjectToOBJECTREF(pDynamicMethodsUNSAFE));
323
324     // Get the number of elements in the array
325     unsigned   cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
326     // ..and allocate a new array. This can trigger GC or throw under OOM.
327     gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
328     
329     // Copy references to the new array we just allocated
330     memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
331                                               cOrigDynamic * sizeof(Object *));
332     HELPER_METHOD_FRAME_END();
333
334     return OBJECTREFToObject(gc.dynamicMethodsArrayCopy);
335 }
336 FCIMPLEND
337
338
339 BSTR BStrFromString(STRINGREF s)
340 {
341     CONTRACTL
342     {
343         THROWS;
344     }
345     CONTRACTL_END;
346
347     WCHAR *wz;
348     int cch;
349     BSTR bstr;
350
351     if (s == NULL)
352         return NULL;
353
354     s->RefInterpretGetStringValuesDangerousForGC(&wz, &cch);
355
356     bstr = SysAllocString(wz);
357     if (bstr == NULL)
358         COMPlusThrowOM();
359
360     return bstr;
361 }
362
363 static BSTR GetExceptionDescription(OBJECTREF objException)
364 {
365     CONTRACTL
366     {
367         THROWS;
368         GC_TRIGGERS;
369         MODE_COOPERATIVE;
370         PRECONDITION( IsException(objException->GetMethodTable()) );
371     }
372     CONTRACTL_END;
373
374     BSTR bstrDescription;
375
376     STRINGREF MessageString = NULL;
377     GCPROTECT_BEGIN(MessageString)
378     GCPROTECT_BEGIN(objException)
379     {
380 #ifdef FEATURE_APPX
381         if (AppX::IsAppXProcess())
382         {
383             // In AppX, call Exception.ToString(false, false) which returns a string containing the exception class
384             // name and callstack without file paths/names. This is used for unhandled exception bucketing/analysis.
385             MethodDescCallSite getMessage(METHOD__EXCEPTION__TO_STRING, &objException);
386
387             ARG_SLOT GetMessageArgs[] =
388             {
389                 ObjToArgSlot(objException),
390                 BoolToArgSlot(false),  // needFileLineInfo
391                 BoolToArgSlot(false)   // needMessage
392             };
393             MessageString = getMessage.Call_RetSTRINGREF(GetMessageArgs);
394         }
395         else
396 #endif // FEATURE_APPX
397         {
398             // read Exception.Message property
399             MethodDescCallSite getMessage(METHOD__EXCEPTION__GET_MESSAGE, &objException);
400
401             ARG_SLOT GetMessageArgs[] = { ObjToArgSlot(objException)};
402             MessageString = getMessage.Call_RetSTRINGREF(GetMessageArgs);
403
404             // if the message string is empty then use the exception classname.
405             if (MessageString == NULL || MessageString->GetStringLength() == 0) {
406                 // call GetClassName
407                 MethodDescCallSite getClassName(METHOD__EXCEPTION__GET_CLASS_NAME, &objException);
408                 ARG_SLOT GetClassNameArgs[] = { ObjToArgSlot(objException)};
409                 MessageString = getClassName.Call_RetSTRINGREF(GetClassNameArgs);
410                 _ASSERTE(MessageString != NULL && MessageString->GetStringLength() != 0);
411             }
412         }
413
414         // Allocate the description BSTR.
415         int DescriptionLen = MessageString->GetStringLength();
416         bstrDescription = SysAllocStringLen(MessageString->GetBuffer(), DescriptionLen);
417     }
418     GCPROTECT_END();
419     GCPROTECT_END();
420
421     return bstrDescription;
422 }
423
424 static BSTR GetExceptionSource(OBJECTREF objException)
425 {
426     CONTRACTL
427     {
428         THROWS;
429         GC_TRIGGERS;
430         MODE_COOPERATIVE;
431         PRECONDITION( IsException(objException->GetMethodTable()) );
432     }
433     CONTRACTL_END;
434
435     STRINGREF refRetVal;
436     GCPROTECT_BEGIN(objException)
437
438     // read Exception.Source property
439     MethodDescCallSite getSource(METHOD__EXCEPTION__GET_SOURCE, &objException);
440
441     ARG_SLOT GetSourceArgs[] = { ObjToArgSlot(objException)};
442
443     refRetVal = getSource.Call_RetSTRINGREF(GetSourceArgs);
444
445     GCPROTECT_END();
446     return BStrFromString(refRetVal);
447 }
448
449 static void GetExceptionHelp(OBJECTREF objException, BSTR *pbstrHelpFile, DWORD *pdwHelpContext)
450 {
451     CONTRACTL
452     {
453         THROWS;
454         GC_TRIGGERS;
455         MODE_COOPERATIVE;
456         INJECT_FAULT(COMPlusThrowOM());
457         PRECONDITION(IsException(objException->GetMethodTable()));
458         PRECONDITION(CheckPointer(pbstrHelpFile));
459         PRECONDITION(CheckPointer(pdwHelpContext));
460     }
461     CONTRACTL_END;
462
463     *pdwHelpContext = 0;
464
465     GCPROTECT_BEGIN(objException);
466
467     // read Exception.HelpLink property
468     MethodDescCallSite getHelpLink(METHOD__EXCEPTION__GET_HELP_LINK, &objException);
469
470     ARG_SLOT GetHelpLinkArgs[] = { ObjToArgSlot(objException)};
471     *pbstrHelpFile = BStrFromString(getHelpLink.Call_RetSTRINGREF(GetHelpLinkArgs));
472
473     GCPROTECT_END();
474
475     // parse the help file to check for the presence of helpcontext
476     int len = SysStringLen(*pbstrHelpFile);
477     int pos = len;
478     WCHAR *pwstr = *pbstrHelpFile;
479     if (pwstr) {
480         BOOL fFoundPound = FALSE;
481
482         for (pos = len - 1; pos >= 0; pos--) {
483             if (pwstr[pos] == W('#')) {
484                 fFoundPound = TRUE;
485                 break;
486             }
487         }
488
489         if (fFoundPound) {
490             int PoundPos = pos;
491             int NumberStartPos = -1;
492             BOOL bNumberStarted = FALSE;
493             BOOL bNumberFinished = FALSE;
494             BOOL bInvalidDigitsFound = FALSE;
495
496             _ASSERTE(pwstr[pos] == W('#'));
497
498             // Check to see if the string to the right of the pound a valid number.
499             for (pos++; pos < len; pos++) {
500                 if (bNumberFinished) {
501                     if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
502                         bInvalidDigitsFound = TRUE;
503                         break;
504                     }
505                 }
506                 else if (bNumberStarted) {
507                     if (COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
508                         bNumberFinished = TRUE;
509                     }
510                     else if (!COMCharacter::nativeIsDigit(pwstr[pos])) {
511                         bInvalidDigitsFound = TRUE;
512                         break;
513                     }
514                 }
515                 else {
516                     if (COMCharacter::nativeIsDigit(pwstr[pos])) {
517                         NumberStartPos = pos;
518                         bNumberStarted = TRUE;
519                     }
520                     else if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
521                         bInvalidDigitsFound = TRUE;
522                         break;
523                     }
524                 }
525             }
526
527             if (bNumberStarted && !bInvalidDigitsFound) {
528                 // Grab the help context and remove it from the help file.
529                 *pdwHelpContext = (DWORD)wtoi(&pwstr[NumberStartPos], len - NumberStartPos);
530
531                 // Allocate a new help file string of the right length.
532                 BSTR strOld = *pbstrHelpFile;
533                 *pbstrHelpFile = SysAllocStringLen(strOld, PoundPos);
534                 SysFreeString(strOld);
535                 if (!*pbstrHelpFile)
536                     COMPlusThrowOM();
537             }
538         }
539     }
540 }
541
542 // NOTE: caller cleans up any partially initialized BSTRs in pED
543 void ExceptionNative::GetExceptionData(OBJECTREF objException, ExceptionData *pED)
544 {
545     CONTRACTL
546     {
547         THROWS;
548         GC_TRIGGERS;
549         MODE_COOPERATIVE;
550         PRECONDITION(IsException(objException->GetMethodTable()));
551         PRECONDITION(CheckPointer(pED));
552     }
553     CONTRACTL_END;
554
555     ZeroMemory(pED, sizeof(ExceptionData));
556
557     if (objException->GetMethodTable() == g_pStackOverflowExceptionClass) {
558         // In a low stack situation, most everything else in here will fail.
559         // <TODO>@TODO: We're not turning the guard page back on here, yet.</TODO>
560         pED->hr = COR_E_STACKOVERFLOW;
561         pED->bstrDescription = SysAllocString(STACK_OVERFLOW_MESSAGE);
562         return;
563     }
564
565     GCPROTECT_BEGIN(objException);
566     pED->hr = GetExceptionHResult(objException);
567     pED->bstrDescription = GetExceptionDescription(objException);
568     pED->bstrSource = GetExceptionSource(objException);
569     GetExceptionHelp(objException, &pED->bstrHelpFile, &pED->dwHelpContext);
570     GCPROTECT_END();
571     return;
572 }
573
574 #ifdef FEATURE_COMINTEROP
575
576 HRESULT SimpleComCallWrapper::IErrorInfo_hr()
577 {
578     WRAPPER_NO_CONTRACT;
579     return GetExceptionHResult(this->GetObjectRef());
580 }
581
582 BSTR SimpleComCallWrapper::IErrorInfo_bstrDescription()
583 {
584     WRAPPER_NO_CONTRACT;
585     return GetExceptionDescription(this->GetObjectRef());
586 }
587
588 BSTR SimpleComCallWrapper::IErrorInfo_bstrSource()
589 {
590     WRAPPER_NO_CONTRACT;
591     return GetExceptionSource(this->GetObjectRef());
592 }
593
594 BSTR SimpleComCallWrapper::IErrorInfo_bstrHelpFile()
595 {
596     WRAPPER_NO_CONTRACT;
597     BSTR  bstrHelpFile;
598     DWORD dwHelpContext;
599     GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
600     return bstrHelpFile;
601 }
602
603 DWORD SimpleComCallWrapper::IErrorInfo_dwHelpContext()
604 {
605     WRAPPER_NO_CONTRACT;
606     BSTR  bstrHelpFile;
607     DWORD dwHelpContext;
608     GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
609     SysFreeString(bstrHelpFile);
610     return dwHelpContext;
611 }
612
613 GUID SimpleComCallWrapper::IErrorInfo_guid()
614 {
615     LIMITED_METHOD_CONTRACT;
616     return GUID_NULL;
617 }
618
619 #endif // FEATURE_COMINTEROP
620
621 FCIMPL0(EXCEPTION_POINTERS*, ExceptionNative::GetExceptionPointers)
622 {
623     FCALL_CONTRACT;
624
625     EXCEPTION_POINTERS* retVal = NULL;
626
627     Thread *pThread = GetThread();
628     _ASSERTE(pThread);
629
630     if (pThread->IsExceptionInProgress())
631     {
632         retVal = pThread->GetExceptionState()->GetExceptionPointers();
633     }
634
635     return retVal;
636 }
637 FCIMPLEND
638
639 FCIMPL0(INT32, ExceptionNative::GetExceptionCode)
640 {
641     FCALL_CONTRACT;
642
643     INT32 retVal = 0;
644
645     Thread *pThread = GetThread();
646     _ASSERTE(pThread);
647
648     if (pThread->IsExceptionInProgress())
649     {
650         retVal = pThread->GetExceptionState()->GetExceptionCode();
651     }
652
653     return retVal;
654 }
655 FCIMPLEND
656
657
658 //
659 // This must be implemented as an FCALL because managed code cannot
660 // swallow a thread abort exception without resetting the abort,
661 // which we don't want to do.  Additionally, we can run into deadlocks
662 // if we use the ResourceManager to do resource lookups - it requires
663 // taking managed locks when initializing Globalization & Security,
664 // but a thread abort on a separate thread initializing those same
665 // systems would also do a resource lookup via the ResourceManager.
666 // We've deadlocked in CompareInfo.GetCompareInfo &
667 // Environment.GetResourceString.  It's not practical to take all of
668 // our locks within CER's to avoid this problem - just use the CLR's
669 // unmanaged resources.
670 //
671 void QCALLTYPE ExceptionNative::GetMessageFromNativeResources(ExceptionMessageKind kind, QCall::StringHandleOnStack retMesg)
672 {
673     QCALL_CONTRACT;
674
675     BEGIN_QCALL;
676
677     SString buffer;
678     HRESULT hr = S_OK;
679     const WCHAR * wszFallbackString = NULL;
680
681     switch(kind) {
682     case ThreadAbort:
683         hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_ABORT);
684         if (FAILED(hr)) {
685             wszFallbackString = W("Thread was being aborted.");
686         }
687         break;
688
689     case ThreadInterrupted:
690         hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_INTERRUPTED);
691         if (FAILED(hr)) {
692             wszFallbackString = W("Thread was interrupted from a waiting state.");
693         }
694         break;
695
696     case OutOfMemory:
697         hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_OUT_OF_MEMORY);
698         if (FAILED(hr)) {
699             wszFallbackString = W("Insufficient memory to continue the execution of the program.");
700         }
701         break;
702
703     default:
704         _ASSERTE(!"Unknown ExceptionMessageKind value!");
705     }
706     if (FAILED(hr)) {       
707         STRESS_LOG1(LF_BCL, LL_ALWAYS, "LoadResource error: %x", hr);
708         _ASSERTE(wszFallbackString != NULL);
709         retMesg.Set(wszFallbackString);
710     }
711     else {
712         retMesg.Set(buffer);
713     }
714
715     END_QCALL;
716 }
717
718 // BlockCopy
719 // This method from one primitive array to another based
720 //  upon an offset into each an a byte count.
721 FCIMPL5(VOID, Buffer::BlockCopy, ArrayBase *src, int srcOffset, ArrayBase *dst, int dstOffset, int count)
722 {
723     FCALL_CONTRACT;
724
725     // Verify that both the src and dst are Arrays of primitive
726     //  types.
727     // <TODO>@TODO: We need to check for booleans</TODO>
728     if (src==NULL || dst==NULL)
729         FCThrowArgumentNullVoid((src==NULL) ? W("src") : W("dst"));
730
731     SIZE_T srcLen, dstLen;
732
733     //
734     // Use specialized fast path for byte arrays because of it is what Buffer::BlockCopy is 
735     // typically used for.
736     //
737
738     MethodTable * pByteArrayMT = g_pByteArrayMT;
739     _ASSERTE(pByteArrayMT != NULL);
740     
741     // Optimization: If src is a byte array, we can
742     // simply set srcLen to GetNumComponents, without having
743     // to call GetComponentSize or verifying GetArrayElementType
744     if (src->GetMethodTable() == pByteArrayMT)
745     {
746         srcLen = src->GetNumComponents();
747     }
748     else
749     {
750         srcLen = src->GetNumComponents() * src->GetComponentSize();
751
752         // We only want to allow arrays of primitives, no Objects.
753         const CorElementType srcET = src->GetArrayElementType();
754         if (!CorTypeInfo::IsPrimitiveType_NoThrow(srcET))
755             FCThrowArgumentVoid(W("src"), W("Arg_MustBePrimArray"));
756     }
757     
758     // Optimization: If copying to/from the same array, then
759     // we know that dstLen and srcLen must be the same.
760     if (src == dst)
761     {
762         dstLen = srcLen;
763     }
764     else if (dst->GetMethodTable() == pByteArrayMT)
765     {
766         dstLen = dst->GetNumComponents();
767     }
768     else
769     {
770         dstLen = dst->GetNumComponents() * dst->GetComponentSize();
771         if (dst->GetMethodTable() != src->GetMethodTable())
772         {
773             const CorElementType dstET = dst->GetArrayElementType();
774             if (!CorTypeInfo::IsPrimitiveType_NoThrow(dstET))
775                 FCThrowArgumentVoid(W("dest"), W("Arg_MustBePrimArray"));
776         }
777     }
778
779     if (srcOffset < 0 || dstOffset < 0 || count < 0) {
780         const wchar_t* str = W("srcOffset");
781         if (dstOffset < 0) str = W("dstOffset");
782         if (count < 0) str = W("count");
783         FCThrowArgumentOutOfRangeVoid(str, W("ArgumentOutOfRange_NeedNonNegNum"));
784     }
785
786     if (srcLen < (SIZE_T)srcOffset + (SIZE_T)count || dstLen < (SIZE_T)dstOffset + (SIZE_T)count) {
787         FCThrowArgumentVoid(NULL, W("Argument_InvalidOffLen"));
788     }
789     
790     PTR_BYTE srcPtr = src->GetDataPtr() + srcOffset;
791     PTR_BYTE dstPtr = dst->GetDataPtr() + dstOffset;
792
793     if ((srcPtr != dstPtr) && (count > 0)) {
794 #if defined(_AMD64_) && !defined(PLATFORM_UNIX)
795         JIT_MemCpy(dstPtr, srcPtr, count);
796 #else
797         memmove(dstPtr, srcPtr, count);
798 #endif
799     }
800
801     FC_GC_POLL();
802 }
803 FCIMPLEND
804
805
806 void QCALLTYPE MemoryNative::Clear(void *dst, size_t length)
807 {
808     QCALL_CONTRACT;
809
810     memset(dst, 0, length);
811 }
812
813 FCIMPL3(VOID, MemoryNative::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount)
814 {
815     FCALL_CONTRACT;
816
817     InlinedMemmoveGCRefsHelper(dst, src, byteCount);
818
819     FC_GC_POLL();
820 }
821 FCIMPLEND
822
823 void QCALLTYPE Buffer::MemMove(void *dst, void *src, size_t length)
824 {
825     QCALL_CONTRACT;
826
827 #if !defined(FEATURE_CORESYSTEM)
828     // Callers of memcpy do expect and handle access violations in some scenarios.
829     // Access violations in the runtime dll are turned into fail fast by the vector exception handler by default.
830     // We need to supress this behavior for CoreCLR using AVInRuntimeImplOkayHolder because of memcpy is statically linked in.
831     AVInRuntimeImplOkayHolder avOk;
832 #endif
833
834     memmove(dst, src, length);
835 }
836
837 // Returns a bool to indicate if the array is of primitive types or not.
838 FCIMPL1(FC_BOOL_RET, Buffer::IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE)
839 {
840     FCALL_CONTRACT;
841
842     _ASSERTE(arrayUNSAFE != NULL);
843
844     // Check the type from the contained element's handle
845     TypeHandle elementTH = arrayUNSAFE->GetArrayElementTypeHandle();
846     BOOL fIsPrimitiveTypeArray = CorTypeInfo::IsPrimitiveType_NoThrow(elementTH.GetVerifierCorElementType());
847
848     FC_RETURN_BOOL(fIsPrimitiveTypeArray);
849
850 }
851 FCIMPLEND
852
853 // Gets a particular byte out of the array.  The array can't be an array of Objects - it
854 // must be a primitive array.
855 FCIMPL2(FC_UINT8_RET, Buffer::GetByte, ArrayBase *arrayUNSAFE, INT32 index)
856 {
857     FCALL_CONTRACT;
858
859     _ASSERTE(arrayUNSAFE != NULL);
860     _ASSERTE(index >=0 && index < ((INT32)(arrayUNSAFE->GetComponentSize() * arrayUNSAFE->GetNumComponents())));
861
862     UINT8 bData = *((BYTE*)arrayUNSAFE->GetDataPtr() + index);
863     return bData;
864 }
865 FCIMPLEND
866
867 // Sets a particular byte in an array.  The array can't be an array of Objects - it
868 // must be a primitive array.
869 //
870 // Semantically the bData argment is of type BYTE but FCallCheckSignature expects the 
871 // type to be UINT8 and raises an error if this isn't this case when 
872 // COMPlus_ConsistencyCheck is set.
873 FCIMPL3(VOID, Buffer::SetByte, ArrayBase *arrayUNSAFE, INT32 index, UINT8 bData)
874 {
875     FCALL_CONTRACT;
876
877     _ASSERTE(arrayUNSAFE != NULL);
878     _ASSERTE(index >=0 && index < ((INT32)(arrayUNSAFE->GetComponentSize() * arrayUNSAFE->GetNumComponents())));
879     
880     *((BYTE*)arrayUNSAFE->GetDataPtr() + index) = (BYTE) bData;
881 }
882 FCIMPLEND
883
884 // Returns the length in bytes of an array containing
885 // primitive type elements
886 FCIMPL1(INT32, Buffer::ByteLength, ArrayBase* arrayUNSAFE)
887 {
888     FCALL_CONTRACT;
889
890     _ASSERTE(arrayUNSAFE != NULL);
891
892     SIZE_T iRetVal = arrayUNSAFE->GetNumComponents() * arrayUNSAFE->GetComponentSize();
893
894     // This API is explosed both as Buffer.ByteLength and also used indirectly in argument
895     // checks for Buffer.GetByte/SetByte.
896     //
897     // If somebody called Get/SetByte on 2GB+ arrays, there is a decent chance that 
898     // the computation of the index has overflowed. Thus we intentionally always 
899     // throw on 2GB+ arrays in Get/SetByte argument checks (even for indicies <2GB)
900     // to prevent people from running into a trap silently.
901     if (iRetVal > INT32_MAX)
902         FCThrow(kOverflowException);
903
904     return (INT32)iRetVal;
905 }
906 FCIMPLEND
907
908 //
909 // GCInterface
910 //
911 MethodDesc *GCInterface::m_pCacheMethod=NULL;
912
913 UINT64   GCInterface::m_ulMemPressure = 0;
914 UINT64   GCInterface::m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
915 INT32    GCInterface::m_gc_counts[3] = {0,0,0};
916 CrstStatic GCInterface::m_MemoryPressureLock;
917
918 UINT64   GCInterface::m_addPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0};   // history of memory pressure additions
919 UINT64   GCInterface::m_remPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0};   // history of memory pressure removals
920
921 // incremented after a gen2 GC has been detected,
922 // (m_iteration % NEW_PRESSURE_COUNT) is used as an index into m_addPressure and m_remPressure
923 UINT     GCInterface::m_iteration = 0;
924
925 FCIMPL0(int, GCInterface::GetGcLatencyMode)
926 {
927     FCALL_CONTRACT;
928
929     FC_GC_POLL_NOT_NEEDED();
930
931     int result = (INT32)GCHeapUtilities::GetGCHeap()->GetGcLatencyMode();
932     return result;
933 }
934 FCIMPLEND
935
936 FCIMPL1(int, GCInterface::SetGcLatencyMode, int newLatencyMode)
937 {
938     FCALL_CONTRACT;
939
940     FC_GC_POLL_NOT_NEEDED();
941     
942     return GCHeapUtilities::GetGCHeap()->SetGcLatencyMode(newLatencyMode);
943 }
944 FCIMPLEND
945
946 FCIMPL0(int, GCInterface::GetLOHCompactionMode)
947 {
948     FCALL_CONTRACT;
949
950     FC_GC_POLL_NOT_NEEDED();
951
952     int result = (INT32)GCHeapUtilities::GetGCHeap()->GetLOHCompactionMode();
953     return result;
954 }
955 FCIMPLEND
956
957 FCIMPL1(void, GCInterface::SetLOHCompactionMode, int newLOHCompactionyMode)
958 {
959     FCALL_CONTRACT;
960
961     FC_GC_POLL_NOT_NEEDED();
962     
963     GCHeapUtilities::GetGCHeap()->SetLOHCompactionMode(newLOHCompactionyMode);
964 }
965 FCIMPLEND
966
967
968 FCIMPL2(FC_BOOL_RET, GCInterface::RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage)
969 {
970     FCALL_CONTRACT;
971
972     FC_GC_POLL_NOT_NEEDED();
973
974     FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->RegisterForFullGCNotification(gen2Percentage, lohPercentage));
975 }
976 FCIMPLEND
977
978 FCIMPL0(FC_BOOL_RET, GCInterface::CancelFullGCNotification)
979 {
980     FCALL_CONTRACT;
981
982     FC_GC_POLL_NOT_NEEDED();
983     FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->CancelFullGCNotification());
984 }
985 FCIMPLEND
986
987 FCIMPL1(int, GCInterface::WaitForFullGCApproach, int millisecondsTimeout)
988 {
989     CONTRACTL
990     {
991         THROWS;
992         MODE_COOPERATIVE;
993         DISABLED(GC_TRIGGERS);  // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
994         SO_TOLERANT;
995     }
996     CONTRACTL_END;
997
998     int result = 0; 
999
1000     //We don't need to check the top end because the GC will take care of that.
1001     HELPER_METHOD_FRAME_BEGIN_RET_0();
1002
1003     DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
1004     result = GCHeapUtilities::GetGCHeap()->WaitForFullGCApproach(dwMilliseconds);
1005
1006     HELPER_METHOD_FRAME_END();
1007
1008     return result;
1009 }
1010 FCIMPLEND
1011
1012 FCIMPL1(int, GCInterface::WaitForFullGCComplete, int millisecondsTimeout)
1013 {
1014     CONTRACTL
1015     {
1016         THROWS;
1017         MODE_COOPERATIVE;
1018         DISABLED(GC_TRIGGERS);  // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
1019         SO_TOLERANT;
1020     }
1021     CONTRACTL_END;
1022
1023     int result = 0; 
1024
1025     //We don't need to check the top end because the GC will take care of that.
1026     HELPER_METHOD_FRAME_BEGIN_RET_0();
1027
1028     DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
1029     result = GCHeapUtilities::GetGCHeap()->WaitForFullGCComplete(dwMilliseconds);
1030
1031     HELPER_METHOD_FRAME_END();
1032
1033     return result;
1034 }
1035 FCIMPLEND
1036
1037 /*================================GetGeneration=================================
1038 **Action: Returns the generation in which args->obj is found.
1039 **Returns: The generation in which args->obj is found.
1040 **Arguments: args->obj -- The object to locate.
1041 **Exceptions: ArgumentException if args->obj is null.
1042 ==============================================================================*/
1043 FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE)
1044 {
1045     FCALL_CONTRACT;
1046
1047     if (objUNSAFE == NULL)
1048         FCThrowArgumentNull(W("obj"));
1049
1050     int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE);
1051     FC_GC_POLL_RET();
1052     return result;
1053 }
1054 FCIMPLEND
1055
1056 /*================================CollectionCount=================================
1057 **Action: Returns the number of collections for this generation since the begining of the life of the process
1058 **Returns: The collection count.
1059 **Arguments: args->generation -- The generation
1060 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1061 ==============================================================================*/
1062 FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount)
1063 {
1064     FCALL_CONTRACT;
1065
1066     //We've already checked this in GC.cs, so we'll just assert it here.
1067     _ASSERTE(generation >= 0);
1068
1069     //We don't need to check the top end because the GC will take care of that.
1070     int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount);
1071     FC_GC_POLL_RET();
1072     return result;
1073 }
1074 FCIMPLEND
1075
1076 int QCALLTYPE GCInterface::StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC)
1077 {
1078     QCALL_CONTRACT;
1079
1080     int retVal = 0;
1081
1082     BEGIN_QCALL;
1083
1084     GCX_COOP();
1085
1086     retVal = GCHeapUtilities::GetGCHeap()->StartNoGCRegion((ULONGLONG)totalSize, 
1087                                                   !!lohSizeKnown,
1088                                                   (ULONGLONG)lohSize,
1089                                                   !!disallowFullBlockingGC);
1090
1091     END_QCALL;
1092
1093     return retVal;
1094 }
1095
1096 int QCALLTYPE GCInterface::EndNoGCRegion()
1097 {
1098     QCALL_CONTRACT;
1099
1100     int retVal = FALSE;
1101
1102     BEGIN_QCALL;
1103
1104     retVal = GCHeapUtilities::GetGCHeap()->EndNoGCRegion();
1105
1106     END_QCALL;
1107
1108     return retVal;
1109 }
1110
1111 /*===============================GetGenerationWR================================
1112 **Action: Returns the generation in which the object pointed to by a WeakReference is found.
1113 **Returns:
1114 **Arguments: args->handle -- the OBJECTHANDLE to the object which we're locating.
1115 **Exceptions: ArgumentException if handle points to an object which is not accessible.
1116 ==============================================================================*/
1117 FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle)
1118 {
1119     FCALL_CONTRACT;
1120
1121     int iRetVal = 0;
1122
1123     HELPER_METHOD_FRAME_BEGIN_RET_0();
1124
1125     OBJECTREF temp;
1126     temp = ObjectFromHandle((OBJECTHANDLE) handle);
1127     if (temp == NULL)
1128         COMPlusThrowArgumentNull(W("weak handle"));
1129
1130     iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp));
1131
1132     HELPER_METHOD_FRAME_END();
1133
1134     return iRetVal;
1135 }
1136 FCIMPLEND
1137
1138 /*================================GetTotalMemory================================
1139 **Action: Returns the total number of bytes in use
1140 **Returns: The total number of bytes in use
1141 **Arguments: None
1142 **Exceptions: None
1143 ==============================================================================*/
1144 INT64 QCALLTYPE GCInterface::GetTotalMemory()
1145 {
1146     QCALL_CONTRACT;
1147
1148     INT64 iRetVal = 0;
1149
1150     BEGIN_QCALL;
1151
1152     GCX_COOP();
1153     iRetVal = (INT64) GCHeapUtilities::GetGCHeap()->GetTotalBytesInUse();
1154
1155     END_QCALL;
1156
1157     return iRetVal;
1158 }
1159
1160 /*==============================Collect=========================================
1161 **Action: Collects all generations <= args->generation
1162 **Returns: void
1163 **Arguments: args->generation:  The maximum generation to collect
1164 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1165 ==============================================================================*/
1166 void QCALLTYPE GCInterface::Collect(INT32 generation, INT32 mode)
1167 {
1168     QCALL_CONTRACT;
1169
1170     BEGIN_QCALL;
1171
1172     //We've already checked this in GC.cs, so we'll just assert it here.
1173     _ASSERTE(generation >= -1);
1174
1175     //We don't need to check the top end because the GC will take care of that.
1176
1177     GCX_COOP();
1178     GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, mode);
1179
1180     END_QCALL;
1181 }
1182
1183
1184 /*==========================WaitForPendingFinalizers============================
1185 **Action: Run all Finalizers that haven't been run.
1186 **Arguments: None
1187 **Exceptions: None
1188 ==============================================================================*/
1189 void QCALLTYPE GCInterface::WaitForPendingFinalizers()
1190 {
1191     QCALL_CONTRACT;
1192
1193     BEGIN_QCALL;
1194
1195     FinalizerThread::FinalizerThreadWait();
1196
1197     END_QCALL;
1198 }
1199
1200
1201 /*===============================GetMaxGeneration===============================
1202 **Action: Returns the largest GC generation
1203 **Returns: The largest GC Generation
1204 **Arguments: None
1205 **Exceptions: None
1206 ==============================================================================*/
1207 FCIMPL0(int, GCInterface::GetMaxGeneration)
1208 {
1209     FCALL_CONTRACT;
1210
1211     return(INT32)GCHeapUtilities::GetGCHeap()->GetMaxGeneration();
1212 }
1213 FCIMPLEND
1214
1215 /*===============================GetAllocatedBytesForCurrentThread===============================
1216 **Action: Computes the allocated bytes so far on the current thread
1217 **Returns: The allocated bytes so far on the current thread
1218 **Arguments: None
1219 **Exceptions: None
1220 ==============================================================================*/
1221 FCIMPL0(INT64, GCInterface::GetAllocatedBytesForCurrentThread)
1222 {
1223     FCALL_CONTRACT;
1224
1225     INT64 currentAllocated = 0;
1226     Thread *pThread = GetThread();
1227     gc_alloc_context* ac = pThread->GetAllocContext();
1228     currentAllocated = ac->alloc_bytes + ac->alloc_bytes_loh - (ac->alloc_limit - ac->alloc_ptr);
1229
1230     return currentAllocated;
1231 }
1232 FCIMPLEND
1233
1234 /*==============================SuppressFinalize================================
1235 **Action: Indicate that an object's finalizer should not be run by the system
1236 **Arguments: Object of interest
1237 **Exceptions: None
1238 ==============================================================================*/
1239 FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj)
1240 {
1241     FCALL_CONTRACT;
1242
1243     // Checked by the caller
1244     _ASSERTE(obj != NULL);
1245
1246     if (!obj->GetMethodTable ()->HasFinalizer())
1247         return;
1248
1249     GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj);
1250     FC_GC_POLL();
1251 }
1252 FCIMPLEND
1253
1254
1255 /*============================ReRegisterForFinalize==============================
1256 **Action: Indicate that an object's finalizer should be run by the system.
1257 **Arguments: Object of interest
1258 **Exceptions: None
1259 ==============================================================================*/
1260 FCIMPL1(void, GCInterface::ReRegisterForFinalize, Object *obj)
1261 {
1262     FCALL_CONTRACT;
1263
1264     // Checked by the caller
1265     _ASSERTE(obj != NULL);
1266
1267     if (obj->GetMethodTable()->HasFinalizer())
1268     {
1269         HELPER_METHOD_FRAME_BEGIN_1(obj);
1270         GCHeapUtilities::GetGCHeap()->RegisterForFinalization(-1, obj);
1271         HELPER_METHOD_FRAME_END();
1272     }
1273 }
1274 FCIMPLEND
1275
1276 FORCEINLINE UINT64 GCInterface::InterlockedAdd (UINT64 *pAugend, UINT64 addend) {
1277     WRAPPER_NO_CONTRACT;
1278     STATIC_CONTRACT_SO_TOLERANT;
1279
1280     UINT64 oldMemValue;
1281     UINT64 newMemValue;
1282
1283     do {
1284         oldMemValue = *pAugend;
1285         newMemValue = oldMemValue + addend;
1286
1287         // check for overflow
1288         if (newMemValue < oldMemValue)
1289         {
1290             newMemValue = UINT64_MAX;
1291         }
1292     } while (InterlockedCompareExchange64((LONGLONG*) pAugend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1293
1294     return newMemValue;
1295 }
1296
1297 FORCEINLINE UINT64 GCInterface::InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend) {
1298     WRAPPER_NO_CONTRACT;
1299     STATIC_CONTRACT_SO_TOLERANT;
1300
1301     UINT64 oldMemValue;
1302     UINT64 newMemValue;
1303
1304     do {
1305         oldMemValue = *pMinuend;
1306         newMemValue = oldMemValue - subtrahend;
1307
1308         // check for underflow
1309         if (newMemValue > oldMemValue)
1310             newMemValue = 0;
1311         
1312     } while (InterlockedCompareExchange64((LONGLONG*) pMinuend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1313
1314     return newMemValue;
1315 }
1316
1317 void QCALLTYPE GCInterface::_AddMemoryPressure(UINT64 bytesAllocated) 
1318 {
1319     QCALL_CONTRACT;
1320
1321     // AddMemoryPressure could cause a GC, so we need a frame 
1322     BEGIN_QCALL;
1323     AddMemoryPressure(bytesAllocated);
1324     END_QCALL;
1325 }
1326
1327 void GCInterface::AddMemoryPressure(UINT64 bytesAllocated)
1328 {
1329     CONTRACTL
1330     {
1331         THROWS;
1332         GC_TRIGGERS;
1333         MODE_ANY;
1334     }
1335     CONTRACTL_END;
1336
1337     SendEtwAddMemoryPressureEvent(bytesAllocated);
1338
1339     UINT64 newMemValue = InterlockedAdd(&m_ulMemPressure, bytesAllocated);
1340
1341     if (newMemValue > m_ulThreshold)
1342     {
1343         INT32 gen_collect = 0;
1344         {
1345             GCX_PREEMP();
1346             CrstHolder holder(&m_MemoryPressureLock);
1347
1348             // to avoid collecting too often, take the max threshold of the linear and geometric growth 
1349             // heuristics.          
1350             UINT64 addMethod;
1351             UINT64 multMethod;
1352             UINT64 bytesAllocatedMax = (UINT64_MAX - m_ulThreshold) / 8;
1353
1354             if (bytesAllocated >= bytesAllocatedMax) // overflow check
1355             {
1356                 addMethod = UINT64_MAX;
1357             }
1358             else
1359             {
1360                 addMethod = m_ulThreshold + bytesAllocated * 8;
1361             }
1362
1363             multMethod = newMemValue + newMemValue / 10;
1364             if (multMethod < newMemValue) // overflow check
1365             {
1366                 multMethod = UINT64_MAX;
1367             }
1368
1369             m_ulThreshold = (addMethod > multMethod) ? addMethod : multMethod;
1370             for (int i = 0; i <= 1; i++)
1371             {
1372                 if ((GCHeapUtilities::GetGCHeap()->CollectionCount(i) / RELATIVE_GC_RATIO) > GCHeapUtilities::GetGCHeap()->CollectionCount(i + 1))
1373                 {
1374                     gen_collect = i + 1;
1375                     break;
1376                 }
1377             }
1378         }
1379
1380         PREFIX_ASSUME(gen_collect <= 2);
1381
1382         if ((gen_collect == 0) || (m_gc_counts[gen_collect] == GCHeapUtilities::GetGCHeap()->CollectionCount(gen_collect)))
1383         {
1384             GarbageCollectModeAny(gen_collect);
1385         }
1386
1387         for (int i = 0; i < 3; i++) 
1388         {
1389             m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
1390         }
1391     }
1392 }
1393
1394 #ifdef _WIN64
1395 const unsigned MIN_MEMORYPRESSURE_BUDGET = 4 * 1024 * 1024;        // 4 MB
1396 #else // _WIN64
1397 const unsigned MIN_MEMORYPRESSURE_BUDGET = 3 * 1024 * 1024;        // 3 MB
1398 #endif // _WIN64
1399
1400 const unsigned MAX_MEMORYPRESSURE_RATIO = 10;                      // 40 MB or 30 MB
1401
1402
1403 // Resets pressure accounting after a gen2 GC has occurred.
1404 void GCInterface::CheckCollectionCount()
1405 {
1406     LIMITED_METHOD_CONTRACT;
1407
1408     IGCHeap * pHeap = GCHeapUtilities::GetGCHeap();
1409     
1410     if (m_gc_counts[2] != pHeap->CollectionCount(2))
1411     {
1412         for (int i = 0; i < 3; i++) 
1413         {
1414             m_gc_counts[i] = pHeap->CollectionCount(i);
1415         }
1416
1417         m_iteration++;
1418
1419         UINT p = m_iteration % NEW_PRESSURE_COUNT;
1420
1421         m_addPressure[p] = 0;   // new pressure will be accumulated here
1422         m_remPressure[p] = 0; 
1423     }
1424 }
1425
1426
1427 // New AddMemoryPressure implementation (used by RCW and the CLRServicesImpl class)
1428 //
1429 //   1. Less sensitive than the original implementation (start budget 3 MB)
1430 //   2. Focuses more on newly added memory pressure
1431 //   3. Budget adjusted by effectiveness of last 3 triggered GC (add / remove ratio, max 10x)
1432 //   4. Budget maxed with 30% of current managed GC size
1433 //   5. If Gen2 GC is happening naturally, ignore past pressure
1434 //
1435 // Here's a brief description of the ideal algorithm for Add/Remove memory pressure:
1436 // Do a GC when (HeapStart < X * MemPressureGrowth) where
1437 // - HeapStart is GC Heap size after doing the last GC
1438 // - MemPressureGrowth is the net of Add and Remove since the last GC
1439 // - X is proportional to our guess of the ummanaged memory death rate per GC interval,
1440 //   and would be calculated based on historic data using standard exponential approximation:
1441 //   Xnew = UMDeath/UMTotal * 0.5 + Xprev
1442 //
1443 void GCInterface::NewAddMemoryPressure(UINT64 bytesAllocated)
1444 {
1445     CONTRACTL
1446     {
1447         THROWS;
1448         GC_TRIGGERS;
1449         MODE_ANY;
1450     }
1451     CONTRACTL_END;
1452
1453     CheckCollectionCount();
1454
1455     UINT p = m_iteration % NEW_PRESSURE_COUNT;
1456
1457     UINT64 newMemValue = InterlockedAdd(&m_addPressure[p], bytesAllocated);
1458
1459     static_assert(NEW_PRESSURE_COUNT == 4, "NewAddMemoryPressure contains unrolled loops which depend on NEW_PRESSURE_COUNT");
1460
1461     UINT64 add = m_addPressure[0] + m_addPressure[1] + m_addPressure[2] + m_addPressure[3] - m_addPressure[p];
1462     UINT64 rem = m_remPressure[0] + m_remPressure[1] + m_remPressure[2] + m_remPressure[3] - m_remPressure[p];
1463
1464     STRESS_LOG4(LF_GCINFO, LL_INFO10000, "AMP Add: %I64u => added=%I64u total_added=%I64u total_removed=%I64u",
1465         bytesAllocated, newMemValue, add, rem);
1466
1467     SendEtwAddMemoryPressureEvent(bytesAllocated); 
1468
1469     if (newMemValue >= MIN_MEMORYPRESSURE_BUDGET)
1470     {
1471         UINT64 budget = MIN_MEMORYPRESSURE_BUDGET;
1472
1473         if (m_iteration >= NEW_PRESSURE_COUNT) // wait until we have enough data points
1474         {
1475             // Adjust according to effectiveness of GC
1476             // Scale budget according to past m_addPressure / m_remPressure ratio
1477             if (add >= rem * MAX_MEMORYPRESSURE_RATIO)
1478             {
1479                 budget = MIN_MEMORYPRESSURE_BUDGET * MAX_MEMORYPRESSURE_RATIO;
1480             }
1481             else if (add > rem)
1482             {
1483                 CONSISTENCY_CHECK(rem != 0);
1484
1485                 // Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024)
1486                 budget = (add * 1024 / rem) * budget / 1024;
1487             }
1488         }
1489
1490         // If still over budget, check current managed heap size
1491         if (newMemValue >= budget)
1492         {
1493             IGCHeap *pGCHeap = GCHeapUtilities::GetGCHeap();
1494             UINT64 heapOver3 = pGCHeap->GetCurrentObjSize() / 3;
1495
1496             if (budget < heapOver3) // Max
1497             {
1498                 budget = heapOver3;
1499             }
1500
1501             if (newMemValue >= budget)
1502             {
1503                 // last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time
1504                 if ((pGCHeap->GetNow() - pGCHeap->GetLastGCStartTime(2)) > (pGCHeap->GetLastGCDuration(2) * 5))
1505                 {
1506                     STRESS_LOG6(LF_GCINFO, LL_INFO10000, "AMP Budget: pressure=%I64u ? budget=%I64u (total_added=%I64u, total_removed=%I64u, mng_heap=%I64u) pos=%d",
1507                         newMemValue, budget, add, rem, heapOver3 * 3, m_iteration);
1508
1509                     GarbageCollectModeAny(2);
1510
1511                     CheckCollectionCount();
1512                 }
1513             }
1514         }
1515     }
1516 }
1517
1518 void QCALLTYPE GCInterface::_RemoveMemoryPressure(UINT64 bytesAllocated)
1519 {
1520     QCALL_CONTRACT;
1521
1522     BEGIN_QCALL;
1523     RemoveMemoryPressure(bytesAllocated);
1524     END_QCALL;
1525 }
1526
1527 void GCInterface::RemoveMemoryPressure(UINT64 bytesAllocated)
1528 {
1529     CONTRACTL
1530     {
1531         NOTHROW;
1532         GC_TRIGGERS;
1533         MODE_ANY;
1534     }
1535     CONTRACTL_END;
1536
1537     SendEtwRemoveMemoryPressureEvent(bytesAllocated);
1538
1539     UINT64 newMemValue = InterlockedSub(&m_ulMemPressure, bytesAllocated);
1540     UINT64 new_th;  
1541     UINT64 bytesAllocatedMax = (m_ulThreshold / 4);
1542     UINT64 addMethod;
1543     UINT64 multMethod = (m_ulThreshold - m_ulThreshold / 20); // can never underflow
1544     if (bytesAllocated >= bytesAllocatedMax) // protect against underflow
1545     {
1546         m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
1547         return;
1548     }
1549     else
1550     {
1551         addMethod = m_ulThreshold - bytesAllocated * 4;
1552     }
1553
1554     new_th = (addMethod < multMethod) ? addMethod : multMethod;
1555
1556     if (newMemValue <= new_th)
1557     {
1558         GCX_PREEMP();
1559         CrstHolder holder(&m_MemoryPressureLock);
1560         if (new_th > MIN_GC_MEMORYPRESSURE_THRESHOLD)
1561             m_ulThreshold = new_th;
1562         else
1563             m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
1564
1565         for (int i = 0; i < 3; i++) 
1566         {
1567             m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
1568         }
1569     }
1570 }
1571
1572 void GCInterface::NewRemoveMemoryPressure(UINT64 bytesAllocated)
1573 {
1574     CONTRACTL
1575     {
1576         NOTHROW;
1577         GC_TRIGGERS;
1578         MODE_ANY;
1579     }
1580     CONTRACTL_END;
1581
1582     CheckCollectionCount();
1583     
1584     UINT p = m_iteration % NEW_PRESSURE_COUNT;
1585
1586     SendEtwRemoveMemoryPressureEvent(bytesAllocated);
1587
1588     InterlockedAdd(&m_remPressure[p], bytesAllocated);
1589
1590     STRESS_LOG2(LF_GCINFO, LL_INFO10000, "AMP Remove: %I64u => removed=%I64u",
1591         bytesAllocated, m_remPressure[p]);
1592 }
1593
1594 inline void GCInterface::SendEtwAddMemoryPressureEvent(UINT64 bytesAllocated)
1595 {
1596     CONTRACTL
1597     {
1598         THROWS;
1599         GC_TRIGGERS;
1600         MODE_ANY;
1601     }
1602     CONTRACTL_END;
1603
1604     FireEtwIncreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
1605 }
1606
1607 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
1608 NOINLINE void GCInterface::SendEtwRemoveMemoryPressureEvent(UINT64 bytesAllocated)
1609 {
1610     CONTRACTL
1611     {
1612         NOTHROW;
1613         GC_TRIGGERS;
1614         MODE_ANY;
1615     }
1616     CONTRACTL_END;
1617
1618     EX_TRY
1619     {
1620         FireEtwDecreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
1621     }
1622     EX_CATCH
1623     {
1624         // Ignore failures
1625     }
1626     EX_END_CATCH(SwallowAllExceptions)
1627 }
1628
1629 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
1630 NOINLINE void GCInterface::GarbageCollectModeAny(int generation)
1631 {
1632     CONTRACTL
1633     {
1634         THROWS;
1635         GC_TRIGGERS;
1636         MODE_ANY;
1637     }
1638     CONTRACTL_END;
1639
1640     GCX_COOP();
1641     GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, collection_non_blocking);
1642 }
1643
1644 //
1645 // COMInterlocked
1646 //
1647
1648 #include <optsmallperfcritical.h>
1649
1650 FCIMPL2(INT32,COMInterlocked::Exchange, INT32 *location, INT32 value)
1651 {
1652     FCALL_CONTRACT;
1653
1654     if( NULL == location) {
1655         FCThrow(kNullReferenceException);
1656     }
1657
1658     return FastInterlockExchange((LONG *) location, value);
1659 }
1660 FCIMPLEND
1661
1662 FCIMPL2_IV(INT64,COMInterlocked::Exchange64, INT64 *location, INT64 value)
1663 {
1664     FCALL_CONTRACT;
1665
1666     if( NULL == location) {
1667         FCThrow(kNullReferenceException);
1668     }
1669
1670     return FastInterlockExchangeLong((INT64 *) location, value);
1671 }
1672 FCIMPLEND
1673
1674 FCIMPL2(LPVOID,COMInterlocked::ExchangePointer, LPVOID *location, LPVOID value)
1675 {
1676     FCALL_CONTRACT;
1677
1678     if( NULL == location) {
1679         FCThrow(kNullReferenceException);
1680     }
1681
1682     FCUnique(0x15);
1683     return FastInterlockExchangePointer(location, value);
1684 }
1685 FCIMPLEND
1686
1687 FCIMPL3(INT32, COMInterlocked::CompareExchange, INT32* location, INT32 value, INT32 comparand)
1688 {
1689     FCALL_CONTRACT;
1690
1691     if( NULL == location) {
1692         FCThrow(kNullReferenceException);
1693     }
1694
1695     return FastInterlockCompareExchange((LONG*)location, value, comparand);
1696 }
1697 FCIMPLEND
1698
1699 FCIMPL4(INT32, COMInterlocked::CompareExchangeReliableResult, INT32* location, INT32 value, INT32 comparand, CLR_BOOL* succeeded)
1700 {
1701     FCALL_CONTRACT;
1702
1703     if( NULL == location) {
1704         FCThrow(kNullReferenceException);
1705     }
1706
1707     INT32 result = FastInterlockCompareExchange((LONG*)location, value, comparand);
1708     if (result == comparand)
1709         *succeeded = true;
1710
1711     return result;
1712 }
1713 FCIMPLEND
1714
1715 FCIMPL3_IVV(INT64, COMInterlocked::CompareExchange64, INT64* location, INT64 value, INT64 comparand)
1716 {
1717     FCALL_CONTRACT;
1718
1719     if( NULL == location) {
1720         FCThrow(kNullReferenceException);
1721     }
1722
1723     return FastInterlockCompareExchangeLong((INT64*)location, value, comparand);
1724 }
1725 FCIMPLEND
1726
1727 FCIMPL3(LPVOID,COMInterlocked::CompareExchangePointer, LPVOID *location, LPVOID value, LPVOID comparand)
1728 {
1729     FCALL_CONTRACT;
1730
1731     if( NULL == location) {
1732         FCThrow(kNullReferenceException);
1733     }
1734
1735     FCUnique(0x59);
1736     return FastInterlockCompareExchangePointer(location, value, comparand);
1737 }
1738 FCIMPLEND
1739
1740 FCIMPL2_IV(float,COMInterlocked::ExchangeFloat, float *location, float value)
1741 {
1742     FCALL_CONTRACT;
1743
1744     if( NULL == location) {
1745         FCThrow(kNullReferenceException);
1746     }
1747
1748     LONG ret = FastInterlockExchange((LONG *) location, *(LONG*)&value);
1749     return *(float*)&ret;
1750 }
1751 FCIMPLEND
1752
1753 FCIMPL2_IV(double,COMInterlocked::ExchangeDouble, double *location, double value)
1754 {
1755     FCALL_CONTRACT;
1756
1757     if( NULL == location) {
1758         FCThrow(kNullReferenceException);
1759     }
1760
1761
1762     INT64 ret = FastInterlockExchangeLong((INT64 *) location, *(INT64*)&value);
1763     return *(double*)&ret;
1764 }
1765 FCIMPLEND
1766
1767 FCIMPL3_IVV(float,COMInterlocked::CompareExchangeFloat, float *location, float value, float comparand)
1768 {
1769     FCALL_CONTRACT;
1770
1771     if( NULL == location) {
1772         FCThrow(kNullReferenceException);
1773     }
1774
1775     LONG ret = (LONG)FastInterlockCompareExchange((LONG*) location, *(LONG*)&value, *(LONG*)&comparand);
1776     return *(float*)&ret;
1777 }
1778 FCIMPLEND
1779
1780 FCIMPL3_IVV(double,COMInterlocked::CompareExchangeDouble, double *location, double value, double comparand)
1781 {
1782     FCALL_CONTRACT;
1783
1784     if( NULL == location) {
1785         FCThrow(kNullReferenceException);
1786     }
1787
1788     INT64 ret = (INT64)FastInterlockCompareExchangeLong((INT64*) location, *(INT64*)&value, *(INT64*)&comparand);
1789     return *(double*)&ret;
1790 }
1791 FCIMPLEND
1792
1793 FCIMPL2(LPVOID,COMInterlocked::ExchangeObject, LPVOID*location, LPVOID value)
1794 {
1795     FCALL_CONTRACT;
1796
1797     if( NULL == location) {
1798         FCThrow(kNullReferenceException);
1799     }
1800
1801     LPVOID ret = FastInterlockExchangePointer(location, value);
1802 #ifdef _DEBUG
1803     Thread::ObjectRefAssign((OBJECTREF *)location);
1804 #endif
1805     ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
1806     return ret;
1807 }
1808 FCIMPLEND
1809
1810 FCIMPL3(LPVOID,COMInterlocked::CompareExchangeObject, LPVOID *location, LPVOID value, LPVOID comparand)
1811 {
1812     FCALL_CONTRACT;
1813
1814     if( NULL == location) {
1815         FCThrow(kNullReferenceException);
1816     }
1817
1818     // <TODO>@todo: only set ref if is updated</TODO>
1819     LPVOID ret = FastInterlockCompareExchangePointer(location, value, comparand);
1820     if (ret == comparand) {
1821 #ifdef _DEBUG
1822         Thread::ObjectRefAssign((OBJECTREF *)location);
1823 #endif
1824         ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
1825     }
1826     return ret;
1827 }
1828 FCIMPLEND
1829
1830 FCIMPL2(INT32,COMInterlocked::ExchangeAdd32, INT32 *location, INT32 value)
1831 {
1832     FCALL_CONTRACT;
1833
1834     if( NULL == location) {
1835         FCThrow(kNullReferenceException);
1836     }
1837
1838     return FastInterlockExchangeAdd((LONG *) location, value);
1839 }
1840 FCIMPLEND
1841
1842 FCIMPL2_IV(INT64,COMInterlocked::ExchangeAdd64, INT64 *location, INT64 value)
1843 {
1844     FCALL_CONTRACT;
1845
1846     if( NULL == location) {
1847         FCThrow(kNullReferenceException);
1848     }
1849
1850     return FastInterlockExchangeAddLong((INT64 *) location, value);
1851 }
1852 FCIMPLEND
1853
1854 FCIMPL0(void, COMInterlocked::FCMemoryBarrier)
1855 {
1856     FCALL_CONTRACT;
1857
1858     MemoryBarrier();
1859     FC_GC_POLL();
1860 }
1861 FCIMPLEND
1862
1863 #include <optdefault.h>
1864
1865 void QCALLTYPE COMInterlocked::MemoryBarrierProcessWide()
1866 {
1867     QCALL_CONTRACT;
1868
1869     FlushProcessWriteBuffers();
1870 }
1871
1872 static BOOL HasOverriddenMethod(MethodTable* mt, MethodTable* classMT, WORD methodSlot)
1873 {
1874     CONTRACTL{
1875         NOTHROW;
1876         GC_NOTRIGGER;
1877         MODE_ANY;
1878         SO_TOLERANT;
1879     } CONTRACTL_END;
1880
1881     _ASSERTE(mt != NULL);
1882     _ASSERTE(classMT != NULL);
1883     _ASSERTE(methodSlot != 0);
1884
1885     PCODE actual = mt->GetRestoredSlot(methodSlot);
1886     PCODE base = classMT->GetRestoredSlot(methodSlot);
1887
1888     if (actual == base)
1889     {
1890         return FALSE;
1891     }
1892
1893     if (!classMT->IsZapped())
1894     {
1895         // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs
1896         // to detect match reliably
1897         if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
1898         {
1899             return FALSE;
1900         }
1901     }
1902
1903     return TRUE;
1904 }
1905
1906 static BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
1907 {
1908     CONTRACTL
1909     {
1910         THROWS;
1911         GC_TRIGGERS;
1912         MODE_COOPERATIVE;
1913     } CONTRACTL_END;
1914
1915     _ASSERTE(mt != NULL);
1916
1917     if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
1918     {
1919         return mt->CanCompareBitsOrUseFastGetHashCode();
1920     }
1921
1922     if (mt->ContainsPointers()
1923         || mt->IsNotTightlyPacked())
1924     {
1925         mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
1926         return FALSE;
1927     }
1928
1929     MethodTable* valueTypeMT = MscorlibBinder::GetClass(CLASS__VALUE_TYPE);
1930     WORD slotEquals = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__EQUALS)->GetSlot();
1931     WORD slotGetHashCode = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__GET_HASH_CODE)->GetSlot();
1932
1933     // Check the input type.
1934     if (HasOverriddenMethod(mt, valueTypeMT, slotEquals)
1935         || HasOverriddenMethod(mt, valueTypeMT, slotGetHashCode))
1936     {
1937         mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
1938
1939         // If overridden Equals or GetHashCode found, stop searching further.
1940         return FALSE;
1941     }
1942
1943     BOOL canCompareBitsOrUseFastGetHashCode = TRUE;
1944
1945     // The type itself did not override Equals or GetHashCode, go for its fields.
1946     ApproxFieldDescIterator iter = ApproxFieldDescIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
1947     for (FieldDesc* pField = iter.Next(); pField != NULL; pField = iter.Next())
1948     {
1949         if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
1950         {
1951             // Check current field type.
1952             MethodTable* fieldMethodTable = pField->GetApproxFieldTypeHandleThrowing().GetMethodTable();
1953             if (!CanCompareBitsOrUseFastGetHashCode(fieldMethodTable))
1954             {
1955                 canCompareBitsOrUseFastGetHashCode = FALSE;
1956                 break;
1957             }
1958         }
1959         else if (pField->GetFieldType() == ELEMENT_TYPE_R8
1960                 || pField->GetFieldType() == ELEMENT_TYPE_R4)
1961         {
1962             // We have double/single field, cannot compare in fast path.
1963             canCompareBitsOrUseFastGetHashCode = FALSE;
1964             break;
1965         }
1966     }
1967
1968     // We've gone through all instance fields. It's time to cache the result.
1969     // Note SetCanCompareBitsOrUseFastGetHashCode(BOOL) ensures the checked flag
1970     // and canCompare flag being set atomically to avoid race.
1971     mt->SetCanCompareBitsOrUseFastGetHashCode(canCompareBitsOrUseFastGetHashCode);
1972
1973     return canCompareBitsOrUseFastGetHashCode;
1974 }
1975
1976 NOINLINE static FC_BOOL_RET CanCompareBitsHelper(MethodTable* mt, OBJECTREF objRef)
1977 {
1978     FC_INNER_PROLOG(ValueTypeHelper::CanCompareBits);
1979
1980     _ASSERTE(mt != NULL);
1981     _ASSERTE(objRef != NULL);
1982
1983     BOOL ret = FALSE;
1984
1985     HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
1986
1987     ret = CanCompareBitsOrUseFastGetHashCode(mt);
1988
1989     HELPER_METHOD_FRAME_END();
1990     FC_INNER_EPILOG();
1991
1992     FC_RETURN_BOOL(ret);
1993 }
1994
1995 // Return true if the valuetype does not contain pointer, is tightly packed, 
1996 // does not have floating point number field and does not override Equals method.
1997 FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
1998 {
1999     FCALL_CONTRACT;
2000
2001     _ASSERTE(obj != NULL);
2002     MethodTable* mt = obj->GetMethodTable();
2003
2004     if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2005     {
2006         FC_RETURN_BOOL(mt->CanCompareBitsOrUseFastGetHashCode());
2007     }
2008
2009     OBJECTREF objRef(obj);
2010
2011     FC_INNER_RETURN(FC_BOOL_RET, CanCompareBitsHelper(mt, objRef));
2012 }
2013 FCIMPLEND
2014
2015 FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1, Object* obj2)
2016 {
2017     FCALL_CONTRACT;
2018
2019     _ASSERTE(obj1 != NULL);
2020     _ASSERTE(obj2 != NULL);
2021     _ASSERTE(!obj1->GetMethodTable()->ContainsPointers());
2022     _ASSERTE(obj1->GetSize() == obj2->GetSize());
2023
2024     TypeHandle pTh = obj1->GetTypeHandle();
2025
2026     FC_RETURN_BOOL(memcmp(obj1->GetData(),obj2->GetData(),pTh.GetSize()) == 0);
2027 }
2028 FCIMPLEND
2029
2030 static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef)
2031 {
2032     CONTRACTL
2033     {
2034         NOTHROW;
2035         GC_NOTRIGGER;
2036         MODE_COOPERATIVE;
2037         SO_TOLERANT;
2038     } CONTRACTL_END;
2039
2040     INT32 hashCode = 0;
2041     INT32 *pObj = (INT32*)pObjRef;
2042             
2043     // this is a struct with no refs and no "strange" offsets, just go through the obj and xor the bits
2044     INT32 size = mt->GetNumInstanceFieldBytes();
2045     for (INT32 i = 0; i < (INT32)(size / sizeof(INT32)); i++)
2046         hashCode ^= *pObj++;
2047
2048     return hashCode;
2049 }
2050
2051 static INT32 RegularGetValueTypeHashCode(MethodTable *mt, void *pObjRef)
2052 {
2053     CONTRACTL
2054     {
2055         THROWS;
2056         GC_TRIGGERS;
2057         MODE_COOPERATIVE;
2058     } CONTRACTL_END;
2059
2060     INT32 hashCode = 0;
2061
2062     GCPROTECT_BEGININTERIOR(pObjRef);
2063
2064     BOOL canUseFastGetHashCodeHelper = FALSE;
2065     if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2066     {
2067         canUseFastGetHashCodeHelper = mt->CanCompareBitsOrUseFastGetHashCode();
2068     }
2069     else
2070     {
2071         canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(mt);
2072     }
2073
2074     // While we shouln't get here directly from ValueTypeHelper::GetHashCode, if we recurse we need to 
2075     // be able to handle getting the hashcode for an embedded structure whose hashcode is computed by the fast path.
2076     if (canUseFastGetHashCodeHelper)
2077     {
2078         hashCode = FastGetValueTypeHashCodeHelper(mt, pObjRef);
2079     }
2080     else
2081     {
2082         // it's looking ugly so we'll use the old behavior in managed code. Grab the first non-null
2083         // field and return its hash code or 'it' as hash code
2084         // <TODO> Note that the old behavior has already been broken for value types
2085         //              that is qualified for CanUseFastGetHashCodeHelper. So maybe we should
2086         //              change the implementation here to use all fields instead of just the 1st one.
2087         // </TODO>
2088         //
2089         // <TODO> check this approximation - we may be losing exact type information </TODO>
2090         ApproxFieldDescIterator fdIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
2091
2092         FieldDesc *field;
2093         while ((field = fdIterator.Next()) != NULL)
2094         {
2095             _ASSERTE(!field->IsRVA());
2096             if (field->IsObjRef())
2097             {
2098                 // if we get an object reference we get the hash code out of that
2099                 if (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()) != NULL)
2100                 {
2101                     PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__OBJECT__GET_HASH_CODE, (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe())));
2102                     DECLARE_ARGHOLDER_ARRAY(args, 1);
2103                     args[ARGNUM_0] = PTR_TO_ARGHOLDER(*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2104                     CALL_MANAGED_METHOD(hashCode, INT32, args);
2105                 }
2106                 else
2107                 {
2108                     // null object reference, try next
2109                     continue;
2110                 }
2111             }
2112             else
2113             {
2114                 CorElementType fieldType = field->GetFieldType();
2115                 if (fieldType == ELEMENT_TYPE_R8)
2116                 {
2117                     PREPARE_NONVIRTUAL_CALLSITE(METHOD__DOUBLE__GET_HASH_CODE);
2118                     DECLARE_ARGHOLDER_ARRAY(args, 1);
2119                     args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2120                     CALL_MANAGED_METHOD(hashCode, INT32, args);
2121                 }
2122                 else if (fieldType == ELEMENT_TYPE_R4)
2123                 {
2124                     PREPARE_NONVIRTUAL_CALLSITE(METHOD__SINGLE__GET_HASH_CODE);
2125                     DECLARE_ARGHOLDER_ARRAY(args, 1);
2126                     args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2127                     CALL_MANAGED_METHOD(hashCode, INT32, args);
2128                 }
2129                 else if (fieldType != ELEMENT_TYPE_VALUETYPE)
2130                 {
2131                     UINT fieldSize = field->LoadSize();
2132                     INT32 *pValue = (INT32*)((BYTE *)pObjRef + field->GetOffsetUnsafe());
2133                     for (INT32 j = 0; j < (INT32)(fieldSize / sizeof(INT32)); j++)
2134                         hashCode ^= *pValue++;
2135                 }
2136                 else
2137                 {
2138                     // got another value type. Get the type
2139                     TypeHandle fieldTH = field->GetFieldTypeHandleThrowing();
2140                     _ASSERTE(!fieldTH.IsNull());
2141                     hashCode = RegularGetValueTypeHashCode(fieldTH.GetMethodTable(), (BYTE *)pObjRef + field->GetOffsetUnsafe());
2142                 }
2143             }
2144             break;
2145         }
2146     }
2147
2148     GCPROTECT_END();
2149
2150     return hashCode;
2151 }
2152
2153 // The default implementation of GetHashCode() for all value types.
2154 // Note that this implementation reveals the value of the fields.
2155 // So if the value type contains any sensitive information it should
2156 // implement its own GetHashCode().
2157 FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE)
2158 {
2159     FCALL_CONTRACT;
2160
2161     if (objUNSAFE == NULL)
2162         FCThrow(kNullReferenceException);
2163
2164     OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE);
2165     VALIDATEOBJECTREF(obj);
2166
2167     INT32 hashCode = 0;
2168     MethodTable *pMT = objUNSAFE->GetMethodTable();
2169
2170     // We don't want to expose the method table pointer in the hash code
2171     // Let's use the typeID instead.
2172     UINT32 typeID = pMT->LookupTypeID();
2173     if (typeID == TypeIDProvider::INVALID_TYPE_ID)
2174     {
2175         // If the typeID has yet to be generated, fall back to GetTypeID
2176         // This only needs to be done once per MethodTable
2177         HELPER_METHOD_FRAME_BEGIN_RET_1(obj);        
2178         typeID = pMT->GetTypeID();
2179         HELPER_METHOD_FRAME_END();
2180     }
2181
2182     // To get less colliding and more evenly distributed hash codes,
2183     // we munge the class index with two big prime numbers
2184     hashCode = typeID * 711650207 + 2506965631U;
2185
2186     BOOL canUseFastGetHashCodeHelper = FALSE;
2187     if (pMT->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2188     {
2189         canUseFastGetHashCodeHelper = pMT->CanCompareBitsOrUseFastGetHashCode();
2190     }
2191     else
2192     {
2193         HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2194         canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(pMT);
2195         HELPER_METHOD_FRAME_END();
2196     }
2197
2198     if (canUseFastGetHashCodeHelper)
2199     {
2200         hashCode ^= FastGetValueTypeHashCodeHelper(pMT, obj->UnBox());
2201     }
2202     else
2203     {
2204         HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2205         hashCode ^= RegularGetValueTypeHashCode(pMT, obj->UnBox());
2206         HELPER_METHOD_FRAME_END();
2207     }
2208
2209     return hashCode;
2210 }
2211 FCIMPLEND
2212
2213 static LONG s_dwSeed;
2214
2215 FCIMPL1(INT32, ValueTypeHelper::GetHashCodeOfPtr, LPVOID ptr)
2216 {
2217     FCALL_CONTRACT;
2218
2219     INT32 hashCode = (INT32)((INT64)(ptr));
2220
2221     if (hashCode == 0)
2222     {
2223         return 0;
2224     }
2225
2226     DWORD dwSeed = s_dwSeed;
2227
2228     // Initialize s_dwSeed lazily
2229     if (dwSeed == 0)
2230     {
2231         // We use the first non-0 pointer as the seed, all hashcodes will be based off that.
2232         // This is to make sure that we only reveal relative memory addresses and never absolute ones.
2233         dwSeed = hashCode;
2234         InterlockedCompareExchange(&s_dwSeed, dwSeed, 0);
2235         dwSeed = s_dwSeed;
2236     }
2237     _ASSERTE(dwSeed != 0);
2238
2239     return hashCode - dwSeed;
2240 }
2241 FCIMPLEND
2242
2243 static MethodTable * g_pStreamMT;
2244 static WORD g_slotBeginRead, g_slotEndRead;
2245 static WORD g_slotBeginWrite, g_slotEndWrite;
2246
2247 static bool HasOverriddenStreamMethod(MethodTable * pMT, WORD slot)
2248 {
2249     CONTRACTL{
2250         NOTHROW;
2251         GC_NOTRIGGER;
2252         MODE_ANY;
2253         SO_TOLERANT;
2254     } CONTRACTL_END;
2255
2256     PCODE actual = pMT->GetRestoredSlot(slot);
2257     PCODE base = g_pStreamMT->GetRestoredSlot(slot);
2258     if (actual == base)
2259         return false;
2260
2261     if (!g_pStreamMT->IsZapped())
2262     {
2263         // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs 
2264         // to detect match reliably
2265         if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
2266             return false;
2267     }
2268
2269     return true;
2270 }
2271
2272 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndRead, Object *stream)
2273 {
2274     FCALL_CONTRACT;
2275
2276     if (stream == NULL)
2277         FC_RETURN_BOOL(TRUE);
2278
2279     if (g_pStreamMT == NULL || g_slotBeginRead == 0 || g_slotEndRead == 0)
2280     {
2281         HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
2282         g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
2283         g_slotBeginRead = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_READ)->GetSlot();
2284         g_slotEndRead = MscorlibBinder::GetMethod(METHOD__STREAM__END_READ)->GetSlot();
2285         HELPER_METHOD_FRAME_END();
2286     }
2287
2288     MethodTable * pMT = stream->GetMethodTable();
2289
2290     FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead));
2291 }
2292 FCIMPLEND
2293
2294 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndWrite, Object *stream)
2295 {
2296     FCALL_CONTRACT;
2297
2298     if (stream == NULL) 
2299         FC_RETURN_BOOL(TRUE);
2300
2301     if (g_pStreamMT == NULL || g_slotBeginWrite == 0 || g_slotEndWrite == 0)
2302     {
2303         HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
2304         g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
2305         g_slotBeginWrite = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_WRITE)->GetSlot();
2306         g_slotEndWrite = MscorlibBinder::GetMethod(METHOD__STREAM__END_WRITE)->GetSlot();
2307         HELPER_METHOD_FRAME_END();
2308     }
2309
2310     MethodTable * pMT = stream->GetMethodTable();
2311
2312     FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginWrite) || HasOverriddenStreamMethod(pMT, g_slotEndWrite));
2313 }
2314 FCIMPLEND