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.
8 /*============================================================
10 ** File: COMUtilNative
14 ** Purpose: A dumping ground for classes which aren't large
15 ** enough to get their own file in the EE.
19 ===========================================================*/
24 #include "comutilnative.h"
30 #include "gcheaputilities.h"
32 #include "invokeutil.h"
34 #include "typestring.h"
36 #include "finalizerthread.h"
38 #ifdef FEATURE_COMINTEROP
39 #include "comcallablewrapper.h"
41 #endif // FEATURE_COMINTEROP
43 #define STACK_OVERFLOW_MESSAGE W("StackOverflowException")
45 //These are defined in System.ParseNumbers and should be kept in sync.
46 #define PARSE_TREATASUNSIGNED 0x200
47 #define PARSE_TREATASI1 0x400
48 #define PARSE_TREATASI2 0x800
49 #define PARSE_ISTIGHT 0x1000
50 #define PARSE_NOSPACE 0x2000
55 // PARSENUMBERS (and helper functions)
59 /*===================================IsDigit====================================
60 **Returns a bool indicating whether the character passed in represents a **
62 ==============================================================================*/
63 bool IsDigit(WCHAR c, int radix, int *result)
70 PRECONDITION(CheckPointer(result));
75 *result = DIGIT_TO_INT(c);
77 else if (c>='A' && c<='Z') {
78 //+10 is necessary because A is actually 10, etc.
81 else if (c>='a' && c<='z') {
82 //+10 is necessary because a is actually 10, etc.
89 if ((*result >=0) && (*result < radix))
95 INT32 wtoi(__in_ecount(length) WCHAR* wstr, DWORD length)
102 PRECONDITION(CheckPointer(wstr));
103 PRECONDITION(length >= 0);
111 while ( (i < length) && (IsDigit(wstr[i], 10 ,&value)) ) {
112 //Read all of the digits and convert to a number
113 result = result*10 + value;
120 INT32 ParseNumbers::GrabInts(const INT32 radix, __in_ecount(length) WCHAR *buffer, const int length, int *i, BOOL isUnsigned)
127 PRECONDITION(CheckPointer(buffer));
128 PRECONDITION(CheckPointer(i));
129 PRECONDITION(*i >= 0);
130 PRECONDITION(length >= 0);
131 PRECONDITION( radix==2 || radix==8 || radix==10 || radix==16 );
139 // Allow all non-decimal numbers to set the sign bit.
140 if (radix==10 && !isUnsigned) {
141 maxVal = (0x7FFFFFFF / 10);
143 //Read all of the digits and convert to a number
144 while (*i<length&&(IsDigit(buffer[*i],radix,&value))) {
145 // Check for overflows - this is sufficient & correct.
146 if (result > maxVal || ((INT32)result)<0)
147 COMPlusThrow(kOverflowException, W("Overflow_Int32"));
148 result = result*radix + value;
151 if ((INT32)result<0 && result!=0x80000000)
152 COMPlusThrow(kOverflowException, W("Overflow_Int32"));
156 maxVal = ((UINT32) -1) / radix;
158 //Read all of the digits and convert to a number
159 while (*i<length&&(IsDigit(buffer[*i],radix,&value))) {
160 // Check for overflows - this is sufficient & correct.
162 COMPlusThrow(kOverflowException, W("Overflow_UInt32"));
163 // the above check won't cover 4294967296 to 4294967299
164 UINT32 temp = result*radix + value;
165 if( temp < result) { // this means overflow as well
166 COMPlusThrow(kOverflowException, W("Overflow_UInt32"));
173 return(INT32) result;
176 INT64 ParseNumbers::GrabLongs(const INT32 radix, __in_ecount(length) WCHAR *buffer, const int length, int *i, BOOL isUnsigned)
183 PRECONDITION(CheckPointer(buffer));
184 PRECONDITION(CheckPointer(i));
185 PRECONDITION(*i >= 0);
186 PRECONDITION(length >= 0);
194 // Allow all non-decimal numbers to set the sign bit.
195 if (radix==10 && !isUnsigned) {
196 maxVal = (UI64(0x7FFFFFFFFFFFFFFF) / 10);
198 //Read all of the digits and convert to a number
199 while (*i<length&&(IsDigit(buffer[*i],radix,&value))) {
200 // Check for overflows - this is sufficient & correct.
201 if (result > maxVal || ((INT64)result)<0)
202 COMPlusThrow(kOverflowException, W("Overflow_Int64"));
203 result = result*radix + value;
206 if ((INT64)result<0 && result!=UI64(0x8000000000000000))
207 COMPlusThrow(kOverflowException, W("Overflow_Int64"));
211 maxVal = ((UINT64) -1L) / radix;
213 //Read all of the digits and convert to a number
214 while (*i<length&&(IsDigit(buffer[*i],radix,&value))) {
215 // Check for overflows - this is sufficient & correct.
217 COMPlusThrow(kOverflowException, W("Overflow_UInt64"));
219 UINT64 temp = result*radix + value;
220 if( temp < result) { // this means overflow as well
221 COMPlusThrow(kOverflowException, W("Overflow_UInt64"));
228 return(INT64) result;
231 void EatWhiteSpace(__in_ecount(length) WCHAR *buffer, int length, int *i)
238 PRECONDITION(CheckPointer(buffer));
239 PRECONDITION(CheckPointer(i));
240 PRECONDITION(length >= 0);
244 for (; *i<length && COMCharacter::nativeIsWhiteSpace(buffer[*i]); (*i)++);
247 FCIMPL5_VII(LPVOID, ParseNumbers::LongToString, INT64 n, INT32 radix, INT32 width, CLR_CHAR paddingChar, INT32 flags)
253 HELPER_METHOD_FRAME_BEGIN_RET_0();
255 bool isNegative = false;
261 WCHAR buffer[67];//Longest possible string length for an integer in binary notation with prefix
263 if (radix<MinRadix || radix>MaxRadix)
264 COMPlusThrowArgumentException(W("radix"), W("Arg_InvalidBase"));
266 //If the number is negative, make it positive and remember the sign.
270 // For base 10, write out -num, but other bases write out the
271 // 2's complement bit pattern
283 else if (flags&PrintAsI2)
285 else if (flags&PrintAsI4)
288 //Special case the 0.
294 //Pull apart the number and put the digits (in reverse order) into the buffer.
295 for (index=0; l>0; l=l/radix, index++) {
296 if ((charVal=(int)(l%radix))<10)
297 buffer[index] = (WCHAR)(charVal + '0');
299 buffer[index] = (WCHAR)(charVal + 'a' - 10);
303 //If they want the base, append that to the string (in reverse order)
304 if (radix!=10 && ((flags&PrintBase)!=0)) {
312 else if ((flags&PrintRadixBase)!=0) {
314 buffer[index++]=((radix%10)+'0');
315 buffer[index++]=((static_cast<char>(radix)/10)+'0');
320 //If it was negative, append the sign.
325 //else if they requested, add the '+';
326 else if ((flags&PrintSign)!=0) {
330 //If they requested a leading space, put it on.
331 else if ((flags&PrefixSpace)!=0) {
336 //Figure out the size of our string.
342 STRINGREF Local = StringObject::NewString(buffLength);
343 WCHAR *LocalBuffer = Local->GetBuffer();
345 //Put the characters into the String in reverse order
346 //Fill the remaining space -- if there is any --
347 //with the correct padding character.
348 if ((flags&LeftAlign)!=0) {
349 for (i=0; i<index; i++) {
350 LocalBuffer[i]=buffer[index-i-1];
352 for (;i<buffLength; i++) {
353 LocalBuffer[i]=paddingChar;
357 for (i=0; i<index; i++) {
358 LocalBuffer[buffLength-i-1]=buffer[i];
360 for (int j=buffLength-i-1; j>=0; j--) {
361 LocalBuffer[j]=paddingChar;
365 *((STRINGREF *)&rv)=Local;
367 HELPER_METHOD_FRAME_END();
374 FCIMPL5(LPVOID, ParseNumbers::IntToString, INT32 n, INT32 radix, INT32 width, CLR_CHAR paddingChar, INT32 flags)
380 HELPER_METHOD_FRAME_BEGIN_RET_0();
382 bool isNegative = false;
388 WCHAR buffer[66]; //Longest possible string length for an integer in binary notation with prefix
390 if (radix<MinRadix || radix>MaxRadix)
391 COMPlusThrowArgumentException(W("radix"), W("Arg_InvalidBase"));
393 //If the number is negative, make it positive and remember the sign.
394 //If the number is MIN_VALUE, this will still be negative, so we'll have to
395 //special case this later.
398 // For base 10, write out -num, but other bases write out the
399 // 2's complement bit pattern
409 //The conversion to a UINT will sign extend the number. In order to ensure
410 //that we only get as many bits as we expect, we chop the number.
411 if (flags&PrintAsI1) {
414 else if (flags&PrintAsI2) {
417 else if (flags&PrintAsI4) {
421 //Special case the 0.
431 buffer[index++] = (WCHAR)(charVal + '0');
434 buffer[index++] = (WCHAR)(charVal + 'a' - 10);
440 //If they want the base, append that to the string (in reverse order)
441 if (radix!=10 && ((flags&PrintBase)!=0)) {
452 //If it was negative, append the sign.
457 //else if they requested, add the '+';
458 else if ((flags&PrintSign)!=0) {
462 //If they requested a leading space, put it on.
463 else if ((flags&PrefixSpace)!=0) {
468 //Figure out the size of our string.
476 STRINGREF Local = StringObject::NewString(buffLength);
477 WCHAR *LocalBuffer = Local->GetBuffer();
479 //Put the characters into the String in reverse order
480 //Fill the remaining space -- if there is any --
481 //with the correct padding character.
482 if ((flags&LeftAlign)!=0) {
483 for (i=0; i<index; i++) {
484 LocalBuffer[i]=buffer[index-i-1];
486 for (;i<buffLength; i++) {
487 LocalBuffer[i]=paddingChar;
491 for (i=0; i<index; i++) {
492 LocalBuffer[buffLength-i-1]=buffer[i];
494 for (int j=buffLength-i-1; j>=0; j--) {
495 LocalBuffer[j]=paddingChar;
499 *((STRINGREF *)&rv)=Local;
501 HELPER_METHOD_FRAME_END();
508 /*===================================FixRadix===================================
509 **It's possible that we parsed the radix in a base other than 10 by accident.
510 **This method will take that number, verify that it only contained valid base 10
511 **digits, and then do the conversion to base 10. If it contained invalid digits,
512 **they tried to pass us a radix such as 1A, so we throw a FormatException.
514 **Args: oldVal: The value that we had actually parsed in some arbitrary base.
515 ** oldBase: The base in which we actually did the parsing.
517 **Returns: oldVal as if it had been parsed as a base-10 number.
518 **Exceptions: FormatException if either of the digits in the radix aren't
519 ** valid base-10 numbers.
520 ==============================================================================*/
521 int FixRadix(int oldVal, int oldBase)
531 int firstDigit = (oldVal/oldBase);
532 int secondDigit = (oldVal%oldBase);
534 if ((firstDigit>=10) || (secondDigit>=10))
535 COMPlusThrow(kFormatException, W("Format_BadBase"));
537 return(firstDigit*10)+secondDigit;
540 /*=================================StringToLong=================================
544 ==============================================================================*/
545 FCIMPL4(INT64, ParseNumbers::StringToLong, StringObject * s, INT32 radix, INT32 flags, INT32 *currPos)
551 HELPER_METHOD_FRAME_BEGIN_RET_1(s);
557 int grabNumbersStart=0;
560 _ASSERTE((flags & PARSE_TREATASI1) == 0 && (flags & PARSE_TREATASI2) == 0);
563 i = currPos ? *currPos : 0;
565 //Do some radix checking.
566 //A radix of -1 says to use whatever base is spec'd on the number.
567 //Parse in Base10 until we figure out what the base actually is.
568 r = (-1==radix)?10:radix;
570 if (r!=2 && r!=10 && r!=8 && r!=16)
571 COMPlusThrow(kArgumentException, W("Arg_InvalidBase"));
573 s->RefInterpretGetStringValuesDangerousForGC(&input, &length);
575 if (i<0 || i>=length)
576 COMPlusThrowArgumentOutOfRange(W("startIndex"), W("ArgumentOutOfRange_Index"));
578 //Get rid of the whitespace and then check that we've still got some digits to parse.
579 if (!(flags & PARSE_ISTIGHT) && !(flags & PARSE_NOSPACE)) {
580 EatWhiteSpace(input,length,&i);
582 COMPlusThrow(kFormatException, W("Format_EmptyInputString"));
588 COMPlusThrow(kArgumentException, W("Arg_CannotHaveNegativeValue"));
590 if (flags & PARSE_TREATASUNSIGNED)
591 COMPlusThrow(kOverflowException, W("Overflow_NegativeUnsigned"));
596 else if (input[i]=='+') {
600 if ((radix==-1 || radix==16) && (i+1<length) && input[i]=='0') {
601 if (input[i+1]=='x' || input [i+1]=='X') {
608 result = GrabLongs(r,input,length,&i, (flags & PARSE_TREATASUNSIGNED));
610 //Check if they passed us a string with no parsable digits.
611 if (i==grabNumbersStart)
612 COMPlusThrow(kFormatException, W("Format_NoParsibleDigits"));
614 if (flags & PARSE_ISTIGHT) {
615 //If we've got effluvia left at the end of the string, complain.
617 COMPlusThrow(kFormatException, W("Format_ExtraJunkAtEnd"));
620 //Put the current index back into the correct place.
621 if (currPos != NULL) *currPos = i;
623 //Return the value properly signed.
624 if ((UINT64) result==UI64(0x8000000000000000) && sign==1 && r==10 && !(flags & PARSE_TREATASUNSIGNED))
625 COMPlusThrow(kOverflowException, W("Overflow_Int64"));
634 HELPER_METHOD_FRAME_END();
640 FCIMPL4(INT32, ParseNumbers::StringToInt, StringObject * s, INT32 radix, INT32 flags, INT32* currPos)
646 HELPER_METHOD_FRAME_BEGIN_RET_1(s);
652 int grabNumbersStart=0;
655 // TreatAsI1 and TreatAsI2 are mutually exclusive.
656 _ASSERTE(!((flags & PARSE_TREATASI1) != 0 && (flags & PARSE_TREATASI2) != 0));
659 //They're requied to tell me where to start parsing.
660 i = currPos ? (*currPos) : 0;
662 //Do some radix checking.
663 //A radix of -1 says to use whatever base is spec'd on the number.
664 //Parse in Base10 until we figure out what the base actually is.
665 r = (-1==radix)?10:radix;
667 if (r!=2 && r!=10 && r!=8 && r!=16)
668 COMPlusThrow(kArgumentException, W("Arg_InvalidBase"));
670 s->RefInterpretGetStringValuesDangerousForGC(&input, &length);
672 if (i<0 || i>=length)
673 COMPlusThrowArgumentOutOfRange(W("startIndex"), W("ArgumentOutOfRange_Index"));
675 //Get rid of the whitespace and then check that we've still got some digits to parse.
676 if (!(flags & PARSE_ISTIGHT) && !(flags & PARSE_NOSPACE)) {
677 EatWhiteSpace(input,length,&i);
679 COMPlusThrow(kFormatException, W("Format_EmptyInputString"));
685 COMPlusThrow(kArgumentException, W("Arg_CannotHaveNegativeValue"));
687 if (flags & PARSE_TREATASUNSIGNED)
688 COMPlusThrow(kOverflowException, W("Overflow_NegativeUnsigned"));
693 else if (input[i]=='+') {
697 //Consume the 0x if we're in an unknown base or in base-16.
698 if ((radix==-1||radix==16) && (i+1<length) && input[i]=='0') {
699 if (input[i+1]=='x' || input [i+1]=='X') {
706 result = GrabInts(r,input,length,&i, (flags & PARSE_TREATASUNSIGNED));
708 //Check if they passed us a string with no parsable digits.
709 if (i==grabNumbersStart)
710 COMPlusThrow(kFormatException, W("Format_NoParsibleDigits"));
712 if (flags & PARSE_ISTIGHT) {
713 //If we've got effluvia left at the end of the string, complain.
715 COMPlusThrow(kFormatException, W("Format_ExtraJunkAtEnd"));
718 //Put the current index back into the correct place.
719 if (currPos != NULL) *currPos = i;
721 //Return the value properly signed.
722 if (flags & PARSE_TREATASI1) {
723 if ((UINT32)result > 0xFF)
724 COMPlusThrow(kOverflowException, W("Overflow_SByte"));
726 // result looks positive when parsed as an I4
727 _ASSERTE(sign==1 || r==10);
729 else if (flags & PARSE_TREATASI2) {
730 if ((UINT32)result > 0xFFFF)
731 COMPlusThrow(kOverflowException, W("Overflow_Int16"));
733 // result looks positive when parsed as an I4
734 _ASSERTE(sign==1 || r==10);
736 else if ((UINT32) result==0x80000000U && sign==1 && r==10 && !(flags & PARSE_TREATASUNSIGNED)) {
737 COMPlusThrow(kOverflowException, W("Overflow_Int32"));
747 HELPER_METHOD_FRAME_END();
758 FCIMPL1(FC_BOOL_RET, ExceptionNative::IsImmutableAgileException, Object* pExceptionUNSAFE)
762 ASSERT(pExceptionUNSAFE != NULL);
764 OBJECTREF pException = (OBJECTREF) pExceptionUNSAFE;
766 // The preallocated exception objects may be used from multiple AppDomains
767 // and therefore must remain immutable from the application's perspective.
768 FC_RETURN_BOOL(CLRException::IsPreallocatedExceptionObject(pException));
772 FCIMPL1(FC_BOOL_RET, ExceptionNative::IsTransient, INT32 hresult)
776 FC_RETURN_BOOL(Exception::IsTransient(hresult));
781 #if defined(FEATURE_EXCEPTIONDISPATCHINFO)
782 // This FCall sets a flag against the thread exception state to indicate to
783 // IL_Throw and the StackTraceInfo implementation to account for the fact
784 // that we have restored a foreign exception dispatch details.
786 // Refer to the respective methods for details on how they use this flag.
787 FCIMPL0(VOID, ExceptionNative::PrepareForForeignExceptionRaise)
791 PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
793 // Set a flag against the TES to indicate this is a foreign exception raise.
794 pCurTES->SetRaisingForeignException();
798 // Given an exception object, this method will extract the stacktrace and dynamic method array and set them up for return to the caller.
799 FCIMPL3(VOID, ExceptionNative::GetStackTracesDeepCopy, Object* pExceptionObjectUnsafe, Object **pStackTraceUnsafe, Object **pDynamicMethodsUnsafe);
807 ASSERT(pExceptionObjectUnsafe != NULL);
808 ASSERT(pStackTraceUnsafe != NULL);
809 ASSERT(pDynamicMethodsUnsafe != NULL);
813 StackTraceArray stackTrace;
814 StackTraceArray stackTraceCopy;
815 EXCEPTIONREF refException;
816 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
817 PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
820 ZeroMemory(&gc, sizeof(gc));
822 // GC protect the array reference
823 HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
825 // Get the exception object reference
826 gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
828 // Fetch the stacktrace details from the exception under a lock
829 gc.refException->GetStackTrace(gc.stackTrace, &gc.dynamicMethodsArray);
831 bool fHaveStackTrace = false;
832 bool fHaveDynamicMethodArray = false;
834 if ((unsigned)gc.stackTrace.Size() > 0)
836 // Deepcopy the array
837 gc.stackTraceCopy.CopyFrom(gc.stackTrace);
838 fHaveStackTrace = true;
841 if (gc.dynamicMethodsArray != NULL)
843 // Get the number of elements in the dynamic methods array
844 unsigned cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
846 // ..and allocate a new array. This can trigger GC or throw under OOM.
847 gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
849 // Deepcopy references to the new array we just allocated
850 memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
851 cOrigDynamic * sizeof(Object *));
853 fHaveDynamicMethodArray = true;
857 *pStackTraceUnsafe = fHaveStackTrace?OBJECTREFToObject(gc.stackTraceCopy.Get()):NULL;
858 *pDynamicMethodsUnsafe = fHaveDynamicMethodArray?OBJECTREFToObject(gc.dynamicMethodsArrayCopy):NULL;
860 HELPER_METHOD_FRAME_END();
864 // 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.
865 FCIMPL3(VOID, ExceptionNative::SaveStackTracesFromDeepCopy, Object* pExceptionObjectUnsafe, Object *pStackTraceUnsafe, Object *pDynamicMethodsUnsafe);
873 ASSERT(pExceptionObjectUnsafe != NULL);
877 StackTraceArray stackTrace;
878 EXCEPTIONREF refException;
879 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
882 ZeroMemory(&gc, sizeof(gc));
884 // GC protect the array reference
885 HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
887 // Get the exception object reference
888 gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
890 if (pStackTraceUnsafe != NULL)
892 // Copy the stacktrace
893 StackTraceArray stackTraceArray((I1ARRAYREF)ObjectToOBJECTREF(pStackTraceUnsafe));
894 gc.stackTrace.Swap(stackTraceArray);
897 gc.dynamicMethodsArray = NULL;
898 if (pDynamicMethodsUnsafe != NULL)
900 gc.dynamicMethodsArray = (PTRARRAYREF)ObjectToOBJECTREF(pDynamicMethodsUnsafe);
903 // If there is no stacktrace, then there cannot be any dynamic method array. Thus,
904 // save stacktrace only when we have it.
905 if (gc.stackTrace.Size() > 0)
907 // Save the stacktrace details in the exception under a lock
908 gc.refException->SetStackTrace(gc.stackTrace, gc.dynamicMethodsArray);
912 gc.refException->SetNullStackTrace();
915 HELPER_METHOD_FRAME_END();
919 // This method performs a deep copy of the stack trace array.
920 FCIMPL1(Object*, ExceptionNative::CopyStackTrace, Object* pStackTraceUNSAFE)
924 ASSERT(pStackTraceUNSAFE != NULL);
928 StackTraceArray stackTrace;
929 StackTraceArray stackTraceCopy;
930 _gc(I1ARRAYREF refStackTrace)
931 : stackTrace(refStackTrace)
934 _gc gc((I1ARRAYREF)(ObjectToOBJECTREF(pStackTraceUNSAFE)));
936 // GC protect the array reference
937 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
939 // Deepcopy the array
940 gc.stackTraceCopy.CopyFrom(gc.stackTrace);
942 HELPER_METHOD_FRAME_END();
944 return OBJECTREFToObject(gc.stackTraceCopy.Get());
948 // This method performs a deep copy of the dynamic method array.
949 FCIMPL1(Object*, ExceptionNative::CopyDynamicMethods, Object* pDynamicMethodsUNSAFE)
953 ASSERT(pDynamicMethodsUNSAFE != NULL);
957 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
958 PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
963 ZeroMemory(&gc, sizeof(gc));
964 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
966 gc.dynamicMethodsArray = (PTRARRAYREF)(ObjectToOBJECTREF(pDynamicMethodsUNSAFE));
968 // Get the number of elements in the array
969 unsigned cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
970 // ..and allocate a new array. This can trigger GC or throw under OOM.
971 gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
973 // Copy references to the new array we just allocated
974 memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
975 cOrigDynamic * sizeof(Object *));
976 HELPER_METHOD_FRAME_END();
978 return OBJECTREFToObject(gc.dynamicMethodsArrayCopy);
982 #endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
984 BSTR BStrFromString(STRINGREF s)
999 s->RefInterpretGetStringValuesDangerousForGC(&wz, &cch);
1001 bstr = SysAllocString(wz);
1008 static BSTR GetExceptionDescription(OBJECTREF objException)
1015 PRECONDITION( IsException(objException->GetMethodTable()) );
1019 BSTR bstrDescription;
1021 STRINGREF MessageString = NULL;
1022 GCPROTECT_BEGIN(MessageString)
1023 GCPROTECT_BEGIN(objException)
1026 if (AppX::IsAppXProcess())
1028 // In AppX, call Exception.ToString(false, false) which returns a string containing the exception class
1029 // name and callstack without file paths/names. This is used for unhandled exception bucketing/analysis.
1030 MethodDescCallSite getMessage(METHOD__EXCEPTION__TO_STRING, &objException);
1032 ARG_SLOT GetMessageArgs[] =
1034 ObjToArgSlot(objException),
1035 BoolToArgSlot(false), // needFileLineInfo
1036 BoolToArgSlot(false) // needMessage
1038 MessageString = getMessage.Call_RetSTRINGREF(GetMessageArgs);
1041 #endif // FEATURE_APPX
1043 // read Exception.Message property
1044 MethodDescCallSite getMessage(METHOD__EXCEPTION__GET_MESSAGE, &objException);
1046 ARG_SLOT GetMessageArgs[] = { ObjToArgSlot(objException)};
1047 MessageString = getMessage.Call_RetSTRINGREF(GetMessageArgs);
1049 // if the message string is empty then use the exception classname.
1050 if (MessageString == NULL || MessageString->GetStringLength() == 0) {
1051 // call GetClassName
1052 MethodDescCallSite getClassName(METHOD__EXCEPTION__GET_CLASS_NAME, &objException);
1053 ARG_SLOT GetClassNameArgs[] = { ObjToArgSlot(objException)};
1054 MessageString = getClassName.Call_RetSTRINGREF(GetClassNameArgs);
1055 _ASSERTE(MessageString != NULL && MessageString->GetStringLength() != 0);
1059 // Allocate the description BSTR.
1060 int DescriptionLen = MessageString->GetStringLength();
1061 bstrDescription = SysAllocStringLen(MessageString->GetBuffer(), DescriptionLen);
1066 return bstrDescription;
1069 static BSTR GetExceptionSource(OBJECTREF objException)
1076 PRECONDITION( IsException(objException->GetMethodTable()) );
1080 STRINGREF refRetVal;
1081 GCPROTECT_BEGIN(objException)
1083 // read Exception.Source property
1084 MethodDescCallSite getSource(METHOD__EXCEPTION__GET_SOURCE, &objException);
1086 ARG_SLOT GetSourceArgs[] = { ObjToArgSlot(objException)};
1088 refRetVal = getSource.Call_RetSTRINGREF(GetSourceArgs);
1091 return BStrFromString(refRetVal);
1094 static void GetExceptionHelp(OBJECTREF objException, BSTR *pbstrHelpFile, DWORD *pdwHelpContext)
1101 INJECT_FAULT(COMPlusThrowOM());
1102 PRECONDITION(IsException(objException->GetMethodTable()));
1103 PRECONDITION(CheckPointer(pbstrHelpFile));
1104 PRECONDITION(CheckPointer(pdwHelpContext));
1108 *pdwHelpContext = 0;
1110 GCPROTECT_BEGIN(objException);
1112 // read Exception.HelpLink property
1113 MethodDescCallSite getHelpLink(METHOD__EXCEPTION__GET_HELP_LINK, &objException);
1115 ARG_SLOT GetHelpLinkArgs[] = { ObjToArgSlot(objException)};
1116 *pbstrHelpFile = BStrFromString(getHelpLink.Call_RetSTRINGREF(GetHelpLinkArgs));
1120 // parse the help file to check for the presence of helpcontext
1121 int len = SysStringLen(*pbstrHelpFile);
1123 WCHAR *pwstr = *pbstrHelpFile;
1125 BOOL fFoundPound = FALSE;
1127 for (pos = len - 1; pos >= 0; pos--) {
1128 if (pwstr[pos] == W('#')) {
1136 int NumberStartPos = -1;
1137 BOOL bNumberStarted = FALSE;
1138 BOOL bNumberFinished = FALSE;
1139 BOOL bInvalidDigitsFound = FALSE;
1141 _ASSERTE(pwstr[pos] == W('#'));
1143 // Check to see if the string to the right of the pound a valid number.
1144 for (pos++; pos < len; pos++) {
1145 if (bNumberFinished) {
1146 if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
1147 bInvalidDigitsFound = TRUE;
1151 else if (bNumberStarted) {
1152 if (COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
1153 bNumberFinished = TRUE;
1155 else if (!COMCharacter::nativeIsDigit(pwstr[pos])) {
1156 bInvalidDigitsFound = TRUE;
1161 if (COMCharacter::nativeIsDigit(pwstr[pos])) {
1162 NumberStartPos = pos;
1163 bNumberStarted = TRUE;
1165 else if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
1166 bInvalidDigitsFound = TRUE;
1172 if (bNumberStarted && !bInvalidDigitsFound) {
1173 // Grab the help context and remove it from the help file.
1174 *pdwHelpContext = (DWORD)wtoi(&pwstr[NumberStartPos], len - NumberStartPos);
1176 // Allocate a new help file string of the right length.
1177 BSTR strOld = *pbstrHelpFile;
1178 *pbstrHelpFile = SysAllocStringLen(strOld, PoundPos);
1179 SysFreeString(strOld);
1180 if (!*pbstrHelpFile)
1187 // NOTE: caller cleans up any partially initialized BSTRs in pED
1188 void ExceptionNative::GetExceptionData(OBJECTREF objException, ExceptionData *pED)
1195 PRECONDITION(IsException(objException->GetMethodTable()));
1196 PRECONDITION(CheckPointer(pED));
1200 ZeroMemory(pED, sizeof(ExceptionData));
1202 if (objException->GetMethodTable() == g_pStackOverflowExceptionClass) {
1203 // In a low stack situation, most everything else in here will fail.
1204 // <TODO>@TODO: We're not turning the guard page back on here, yet.</TODO>
1205 pED->hr = COR_E_STACKOVERFLOW;
1206 pED->bstrDescription = SysAllocString(STACK_OVERFLOW_MESSAGE);
1210 GCPROTECT_BEGIN(objException);
1211 pED->hr = GetExceptionHResult(objException);
1212 pED->bstrDescription = GetExceptionDescription(objException);
1213 pED->bstrSource = GetExceptionSource(objException);
1214 GetExceptionHelp(objException, &pED->bstrHelpFile, &pED->dwHelpContext);
1219 #ifdef FEATURE_COMINTEROP
1221 HRESULT SimpleComCallWrapper::IErrorInfo_hr()
1223 WRAPPER_NO_CONTRACT;
1224 return GetExceptionHResult(this->GetObjectRef());
1227 BSTR SimpleComCallWrapper::IErrorInfo_bstrDescription()
1229 WRAPPER_NO_CONTRACT;
1230 return GetExceptionDescription(this->GetObjectRef());
1233 BSTR SimpleComCallWrapper::IErrorInfo_bstrSource()
1235 WRAPPER_NO_CONTRACT;
1236 return GetExceptionSource(this->GetObjectRef());
1239 BSTR SimpleComCallWrapper::IErrorInfo_bstrHelpFile()
1241 WRAPPER_NO_CONTRACT;
1243 DWORD dwHelpContext;
1244 GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
1245 return bstrHelpFile;
1248 DWORD SimpleComCallWrapper::IErrorInfo_dwHelpContext()
1250 WRAPPER_NO_CONTRACT;
1252 DWORD dwHelpContext;
1253 GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
1254 SysFreeString(bstrHelpFile);
1255 return dwHelpContext;
1258 GUID SimpleComCallWrapper::IErrorInfo_guid()
1260 LIMITED_METHOD_CONTRACT;
1264 #endif // FEATURE_COMINTEROP
1266 FCIMPL0(EXCEPTION_POINTERS*, ExceptionNative::GetExceptionPointers)
1270 EXCEPTION_POINTERS* retVal = NULL;
1272 Thread *pThread = GetThread();
1275 if (pThread->IsExceptionInProgress())
1277 retVal = pThread->GetExceptionState()->GetExceptionPointers();
1284 FCIMPL0(INT32, ExceptionNative::GetExceptionCode)
1290 Thread *pThread = GetThread();
1293 if (pThread->IsExceptionInProgress())
1295 retVal = pThread->GetExceptionState()->GetExceptionCode();
1304 // This must be implemented as an FCALL because managed code cannot
1305 // swallow a thread abort exception without resetting the abort,
1306 // which we don't want to do. Additionally, we can run into deadlocks
1307 // if we use the ResourceManager to do resource lookups - it requires
1308 // taking managed locks when initializing Globalization & Security,
1309 // but a thread abort on a separate thread initializing those same
1310 // systems would also do a resource lookup via the ResourceManager.
1311 // We've deadlocked in CompareInfo.GetCompareInfo &
1312 // Environment.GetResourceString. It's not practical to take all of
1313 // our locks within CER's to avoid this problem - just use the CLR's
1314 // unmanaged resources.
1316 void QCALLTYPE ExceptionNative::GetMessageFromNativeResources(ExceptionMessageKind kind, QCall::StringHandleOnStack retMesg)
1324 const WCHAR * wszFallbackString = NULL;
1328 hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_ABORT);
1330 wszFallbackString = W("Thread was being aborted.");
1334 case ThreadInterrupted:
1335 hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_INTERRUPTED);
1337 wszFallbackString = W("Thread was interrupted from a waiting state.");
1342 hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_OUT_OF_MEMORY);
1344 wszFallbackString = W("Insufficient memory to continue the execution of the program.");
1349 _ASSERTE(!"Unknown ExceptionMessageKind value!");
1352 STRESS_LOG1(LF_BCL, LL_ALWAYS, "LoadResource error: %x", hr);
1353 _ASSERTE(wszFallbackString != NULL);
1354 retMesg.Set(wszFallbackString);
1357 retMesg.Set(buffer);
1364 // This method from one primitive array to another based
1365 // upon an offset into each an a byte count.
1366 FCIMPL5(VOID, Buffer::BlockCopy, ArrayBase *src, int srcOffset, ArrayBase *dst, int dstOffset, int count)
1370 // Verify that both the src and dst are Arrays of primitive
1372 // <TODO>@TODO: We need to check for booleans</TODO>
1373 if (src==NULL || dst==NULL)
1374 FCThrowArgumentNullVoid((src==NULL) ? W("src") : W("dst"));
1376 SIZE_T srcLen, dstLen;
1379 // Use specialized fast path for byte arrays because of it is what Buffer::BlockCopy is
1380 // typically used for.
1383 MethodTable * pByteArrayMT = g_pByteArrayMT;
1384 _ASSERTE(pByteArrayMT != NULL);
1386 // Optimization: If src is a byte array, we can
1387 // simply set srcLen to GetNumComponents, without having
1388 // to call GetComponentSize or verifying GetArrayElementType
1389 if (src->GetMethodTable() == pByteArrayMT)
1391 srcLen = src->GetNumComponents();
1395 srcLen = src->GetNumComponents() * src->GetComponentSize();
1397 // We only want to allow arrays of primitives, no Objects.
1398 const CorElementType srcET = src->GetArrayElementType();
1399 if (!CorTypeInfo::IsPrimitiveType_NoThrow(srcET))
1400 FCThrowArgumentVoid(W("src"), W("Arg_MustBePrimArray"));
1403 // Optimization: If copying to/from the same array, then
1404 // we know that dstLen and srcLen must be the same.
1409 else if (dst->GetMethodTable() == pByteArrayMT)
1411 dstLen = dst->GetNumComponents();
1415 dstLen = dst->GetNumComponents() * dst->GetComponentSize();
1416 if (dst->GetMethodTable() != src->GetMethodTable())
1418 const CorElementType dstET = dst->GetArrayElementType();
1419 if (!CorTypeInfo::IsPrimitiveType_NoThrow(dstET))
1420 FCThrowArgumentVoid(W("dest"), W("Arg_MustBePrimArray"));
1424 if (srcOffset < 0 || dstOffset < 0 || count < 0) {
1425 const wchar_t* str = W("srcOffset");
1426 if (dstOffset < 0) str = W("dstOffset");
1427 if (count < 0) str = W("count");
1428 FCThrowArgumentOutOfRangeVoid(str, W("ArgumentOutOfRange_NeedNonNegNum"));
1431 if (srcLen < (SIZE_T)srcOffset + (SIZE_T)count || dstLen < (SIZE_T)dstOffset + (SIZE_T)count) {
1432 FCThrowArgumentVoid(NULL, W("Argument_InvalidOffLen"));
1435 PTR_BYTE srcPtr = src->GetDataPtr() + srcOffset;
1436 PTR_BYTE dstPtr = dst->GetDataPtr() + dstOffset;
1438 if ((srcPtr != dstPtr) && (count > 0)) {
1439 #if defined(_AMD64_) && !defined(PLATFORM_UNIX)
1440 JIT_MemCpy(dstPtr, srcPtr, count);
1442 memmove(dstPtr, srcPtr, count);
1451 // InternalBlockCopy
1452 // This method from one primitive array to another based
1453 // upon an offset into each an a byte count.
1454 FCIMPL5(VOID, Buffer::InternalBlockCopy, ArrayBase *src, int srcOffset, ArrayBase *dst, int dstOffset, int count)
1458 // @TODO: We should consider writing this in managed code. We probably
1459 // cannot easily do this though - how do we get at the array's data?
1461 // Unfortunately, we must do a check to make sure we're writing within
1462 // the bounds of the array. This will ensure that we don't overwrite
1463 // memory elsewhere in the system nor do we write out junk. This can
1464 // happen if multiple threads interact with our IO classes simultaneously
1465 // without being threadsafe. Throw here.
1466 // Unfortunately this even applies to setting our internal buffers to
1467 // null. We don't want to debug races between Close and Read or Write.
1468 if (src == NULL || dst == NULL)
1469 FCThrowResVoid(kIndexOutOfRangeException, W("IndexOutOfRange_IORaceCondition"));
1471 SIZE_T srcLen = src->GetNumComponents() * src->GetComponentSize();
1472 SIZE_T dstLen = srcLen;
1474 dstLen = dst->GetNumComponents() * dst->GetComponentSize();
1476 if (srcOffset < 0 || dstOffset < 0 || count < 0)
1477 FCThrowResVoid(kIndexOutOfRangeException, W("IndexOutOfRange_IORaceCondition"));
1479 if (srcLen < (SIZE_T)srcOffset + (SIZE_T)count || dstLen < (SIZE_T)dstOffset + (SIZE_T)count)
1480 FCThrowResVoid(kIndexOutOfRangeException, W("IndexOutOfRange_IORaceCondition"));
1482 _ASSERTE(srcOffset >= 0);
1483 _ASSERTE((src->GetNumComponents() * src->GetComponentSize()) - (unsigned) srcOffset >= (unsigned) count);
1484 _ASSERTE((dst->GetNumComponents() * dst->GetComponentSize()) - (unsigned) dstOffset >= (unsigned) count);
1485 _ASSERTE(dstOffset >= 0);
1486 _ASSERTE(count >= 0);
1489 #if defined(_AMD64_) && !defined(PLATFORM_UNIX)
1490 JIT_MemCpy(dst->GetDataPtr() + dstOffset, src->GetDataPtr() + srcOffset, count);
1492 memmove(dst->GetDataPtr() + dstOffset, src->GetDataPtr() + srcOffset, count);
1499 void QCALLTYPE Buffer::MemMove(void *dst, void *src, size_t length)
1504 memmove(dst, src, length);
1507 // Returns a bool to indicate if the array is of primitive types or not.
1508 FCIMPL1(FC_BOOL_RET, Buffer::IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE)
1512 _ASSERTE(arrayUNSAFE != NULL);
1514 // Check the type from the contained element's handle
1515 TypeHandle elementTH = arrayUNSAFE->GetArrayElementTypeHandle();
1516 BOOL fIsPrimitiveTypeArray = CorTypeInfo::IsPrimitiveType_NoThrow(elementTH.GetVerifierCorElementType());
1518 FC_RETURN_BOOL(fIsPrimitiveTypeArray);
1523 // Gets a particular byte out of the array. The array can't be an array of Objects - it
1524 // must be a primitive array.
1525 FCIMPL2(FC_UINT8_RET, Buffer::GetByte, ArrayBase *arrayUNSAFE, INT32 index)
1529 _ASSERTE(arrayUNSAFE != NULL);
1530 _ASSERTE(index >=0 && index < ((INT32)(arrayUNSAFE->GetComponentSize() * arrayUNSAFE->GetNumComponents())));
1532 UINT8 bData = *((BYTE*)arrayUNSAFE->GetDataPtr() + index);
1537 // Sets a particular byte in an array. The array can't be an array of Objects - it
1538 // must be a primitive array.
1540 // Semantically the bData argment is of type BYTE but FCallCheckSignature expects the
1541 // type to be UINT8 and raises an error if this isn't this case when
1542 // COMPlus_ConsistencyCheck is set.
1543 FCIMPL3(VOID, Buffer::SetByte, ArrayBase *arrayUNSAFE, INT32 index, UINT8 bData)
1547 _ASSERTE(arrayUNSAFE != NULL);
1548 _ASSERTE(index >=0 && index < ((INT32)(arrayUNSAFE->GetComponentSize() * arrayUNSAFE->GetNumComponents())));
1550 *((BYTE*)arrayUNSAFE->GetDataPtr() + index) = (BYTE) bData;
1554 // Returns the length in bytes of an array containing
1555 // primitive type elements
1556 FCIMPL1(INT32, Buffer::ByteLength, ArrayBase* arrayUNSAFE)
1560 _ASSERTE(arrayUNSAFE != NULL);
1562 SIZE_T iRetVal = arrayUNSAFE->GetNumComponents() * arrayUNSAFE->GetComponentSize();
1564 // This API is explosed both as Buffer.ByteLength and also used indirectly in argument
1565 // checks for Buffer.GetByte/SetByte.
1567 // If somebody called Get/SetByte on 2GB+ arrays, there is a decent chance that
1568 // the computation of the index has overflowed. Thus we intentionally always
1569 // throw on 2GB+ arrays in Get/SetByte argument checks (even for indicies <2GB)
1570 // to prevent people from running into a trap silently.
1571 if (iRetVal > INT32_MAX)
1572 FCThrow(kOverflowException);
1574 return (INT32)iRetVal;
1581 MethodDesc *GCInterface::m_pCacheMethod=NULL;
1583 UINT64 GCInterface::m_ulMemPressure = 0;
1584 UINT64 GCInterface::m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
1585 INT32 GCInterface::m_gc_counts[3] = {0,0,0};
1586 CrstStatic GCInterface::m_MemoryPressureLock;
1588 UINT64 GCInterface::m_addPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0}; // history of memory pressure additions
1589 UINT64 GCInterface::m_remPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0}; // history of memory pressure removals
1591 // incremented after a gen2 GC has been detected,
1592 // (m_iteration % NEW_PRESSURE_COUNT) is used as an index into m_addPressure and m_remPressure
1593 UINT GCInterface::m_iteration = 0;
1595 FCIMPL0(int, GCInterface::GetGcLatencyMode)
1599 FC_GC_POLL_NOT_NEEDED();
1601 int result = (INT32)GCHeapUtilities::GetGCHeap()->GetGcLatencyMode();
1606 FCIMPL1(int, GCInterface::SetGcLatencyMode, int newLatencyMode)
1610 FC_GC_POLL_NOT_NEEDED();
1612 return GCHeapUtilities::GetGCHeap()->SetGcLatencyMode(newLatencyMode);
1616 FCIMPL0(int, GCInterface::GetLOHCompactionMode)
1620 FC_GC_POLL_NOT_NEEDED();
1622 int result = (INT32)GCHeapUtilities::GetGCHeap()->GetLOHCompactionMode();
1627 FCIMPL1(void, GCInterface::SetLOHCompactionMode, int newLOHCompactionyMode)
1631 FC_GC_POLL_NOT_NEEDED();
1633 GCHeapUtilities::GetGCHeap()->SetLOHCompactionMode(newLOHCompactionyMode);
1638 FCIMPL2(FC_BOOL_RET, GCInterface::RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage)
1642 FC_GC_POLL_NOT_NEEDED();
1644 FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->RegisterForFullGCNotification(gen2Percentage, lohPercentage));
1648 FCIMPL0(FC_BOOL_RET, GCInterface::CancelFullGCNotification)
1652 FC_GC_POLL_NOT_NEEDED();
1653 FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->CancelFullGCNotification());
1657 FCIMPL1(int, GCInterface::WaitForFullGCApproach, int millisecondsTimeout)
1663 DISABLED(GC_TRIGGERS); // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
1670 //We don't need to check the top end because the GC will take care of that.
1671 HELPER_METHOD_FRAME_BEGIN_RET_0();
1673 DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
1674 result = GCHeapUtilities::GetGCHeap()->WaitForFullGCApproach(dwMilliseconds);
1676 HELPER_METHOD_FRAME_END();
1682 FCIMPL1(int, GCInterface::WaitForFullGCComplete, int millisecondsTimeout)
1688 DISABLED(GC_TRIGGERS); // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
1695 //We don't need to check the top end because the GC will take care of that.
1696 HELPER_METHOD_FRAME_BEGIN_RET_0();
1698 DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
1699 result = GCHeapUtilities::GetGCHeap()->WaitForFullGCComplete(dwMilliseconds);
1701 HELPER_METHOD_FRAME_END();
1707 /*================================GetGeneration=================================
1708 **Action: Returns the generation in which args->obj is found.
1709 **Returns: The generation in which args->obj is found.
1710 **Arguments: args->obj -- The object to locate.
1711 **Exceptions: ArgumentException if args->obj is null.
1712 ==============================================================================*/
1713 FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE)
1717 if (objUNSAFE == NULL)
1718 FCThrowArgumentNull(W("obj"));
1720 int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE);
1726 /*================================CollectionCount=================================
1727 **Action: Returns the number of collections for this generation since the begining of the life of the process
1728 **Returns: The collection count.
1729 **Arguments: args->generation -- The generation
1730 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1731 ==============================================================================*/
1732 FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount)
1736 //We've already checked this in GC.cs, so we'll just assert it here.
1737 _ASSERTE(generation >= 0);
1739 //We don't need to check the top end because the GC will take care of that.
1740 int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount);
1746 int QCALLTYPE GCInterface::StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC)
1756 retVal = GCHeapUtilities::GetGCHeap()->StartNoGCRegion((ULONGLONG)totalSize,
1759 disallowFullBlockingGC);
1766 int QCALLTYPE GCInterface::EndNoGCRegion()
1774 retVal = GCHeapUtilities::GetGCHeap()->EndNoGCRegion();
1781 /*===============================GetGenerationWR================================
1782 **Action: Returns the generation in which the object pointed to by a WeakReference is found.
1784 **Arguments: args->handle -- the OBJECTHANDLE to the object which we're locating.
1785 **Exceptions: ArgumentException if handle points to an object which is not accessible.
1786 ==============================================================================*/
1787 FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle)
1793 HELPER_METHOD_FRAME_BEGIN_RET_0();
1796 temp = ObjectFromHandle((OBJECTHANDLE) handle);
1798 COMPlusThrowArgumentNull(W("weak handle"));
1800 iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp));
1802 HELPER_METHOD_FRAME_END();
1808 /*================================GetTotalMemory================================
1809 **Action: Returns the total number of bytes in use
1810 **Returns: The total number of bytes in use
1813 ==============================================================================*/
1814 INT64 QCALLTYPE GCInterface::GetTotalMemory()
1823 iRetVal = (INT64) GCHeapUtilities::GetGCHeap()->GetTotalBytesInUse();
1830 /*==============================Collect=========================================
1831 **Action: Collects all generations <= args->generation
1833 **Arguments: args->generation: The maximum generation to collect
1834 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1835 ==============================================================================*/
1836 void QCALLTYPE GCInterface::Collect(INT32 generation, INT32 mode)
1842 //We've already checked this in GC.cs, so we'll just assert it here.
1843 _ASSERTE(generation >= -1);
1845 //We don't need to check the top end because the GC will take care of that.
1848 GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, FALSE, mode);
1854 /*==========================WaitForPendingFinalizers============================
1855 **Action: Run all Finalizers that haven't been run.
1858 ==============================================================================*/
1859 void QCALLTYPE GCInterface::WaitForPendingFinalizers()
1865 FinalizerThread::FinalizerThreadWait();
1871 /*===============================GetMaxGeneration===============================
1872 **Action: Returns the largest GC generation
1873 **Returns: The largest GC Generation
1876 ==============================================================================*/
1877 FCIMPL0(int, GCInterface::GetMaxGeneration)
1881 return(INT32)GCHeapUtilities::GetGCHeap()->GetMaxGeneration();
1885 /*===============================GetAllocatedBytesForCurrentThread===============================
1886 **Action: Computes the allocated bytes so far on the current thread
1887 **Returns: The allocated bytes so far on the current thread
1890 ==============================================================================*/
1891 FCIMPL0(INT64, GCInterface::GetAllocatedBytesForCurrentThread)
1895 INT64 currentAllocated = 0;
1896 Thread *pThread = GetThread();
1897 gc_alloc_context* ac = pThread->GetAllocContext();
1898 currentAllocated = ac->alloc_bytes + ac->alloc_bytes_loh - (ac->alloc_limit - ac->alloc_ptr);
1900 return currentAllocated;
1904 /*==============================SuppressFinalize================================
1905 **Action: Indicate that an object's finalizer should not be run by the system
1906 **Arguments: Object of interest
1908 ==============================================================================*/
1909 FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj)
1913 // Checked by the caller
1914 _ASSERTE(obj != NULL);
1916 if (!obj->GetMethodTable ()->HasFinalizer())
1919 GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj);
1925 /*============================ReRegisterForFinalize==============================
1926 **Action: Indicate that an object's finalizer should be run by the system.
1927 **Arguments: Object of interest
1929 ==============================================================================*/
1930 FCIMPL1(void, GCInterface::ReRegisterForFinalize, Object *obj)
1934 // Checked by the caller
1935 _ASSERTE(obj != NULL);
1937 if (obj->GetMethodTable()->HasFinalizer())
1939 HELPER_METHOD_FRAME_BEGIN_1(obj);
1940 GCHeapUtilities::GetGCHeap()->RegisterForFinalization(-1, obj);
1941 HELPER_METHOD_FRAME_END();
1946 FORCEINLINE UINT64 GCInterface::InterlockedAdd (UINT64 *pAugend, UINT64 addend) {
1947 WRAPPER_NO_CONTRACT;
1948 STATIC_CONTRACT_SO_TOLERANT;
1954 oldMemValue = *pAugend;
1955 newMemValue = oldMemValue + addend;
1957 // check for overflow
1958 if (newMemValue < oldMemValue)
1960 newMemValue = UINT64_MAX;
1962 } while (InterlockedCompareExchange64((LONGLONG*) pAugend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1967 FORCEINLINE UINT64 GCInterface::InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend) {
1968 WRAPPER_NO_CONTRACT;
1969 STATIC_CONTRACT_SO_TOLERANT;
1975 oldMemValue = *pMinuend;
1976 newMemValue = oldMemValue - subtrahend;
1978 // check for underflow
1979 if (newMemValue > oldMemValue)
1982 } while (InterlockedCompareExchange64((LONGLONG*) pMinuend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1987 void QCALLTYPE GCInterface::_AddMemoryPressure(UINT64 bytesAllocated)
1991 // AddMemoryPressure could cause a GC, so we need a frame
1993 AddMemoryPressure(bytesAllocated);
1997 void GCInterface::AddMemoryPressure(UINT64 bytesAllocated)
2007 SendEtwAddMemoryPressureEvent(bytesAllocated);
2009 UINT64 newMemValue = InterlockedAdd(&m_ulMemPressure, bytesAllocated);
2011 if (newMemValue > m_ulThreshold)
2013 INT32 gen_collect = 0;
2016 CrstHolder holder(&m_MemoryPressureLock);
2018 // to avoid collecting too often, take the max threshold of the linear and geometric growth
2022 UINT64 bytesAllocatedMax = (UINT64_MAX - m_ulThreshold) / 8;
2024 if (bytesAllocated >= bytesAllocatedMax) // overflow check
2026 addMethod = UINT64_MAX;
2030 addMethod = m_ulThreshold + bytesAllocated * 8;
2033 multMethod = newMemValue + newMemValue / 10;
2034 if (multMethod < newMemValue) // overflow check
2036 multMethod = UINT64_MAX;
2039 m_ulThreshold = (addMethod > multMethod) ? addMethod : multMethod;
2040 for (int i = 0; i <= 1; i++)
2042 if ((GCHeapUtilities::GetGCHeap()->CollectionCount(i) / RELATIVE_GC_RATIO) > GCHeapUtilities::GetGCHeap()->CollectionCount(i + 1))
2044 gen_collect = i + 1;
2050 PREFIX_ASSUME(gen_collect <= 2);
2052 if ((gen_collect == 0) || (m_gc_counts[gen_collect] == GCHeapUtilities::GetGCHeap()->CollectionCount(gen_collect)))
2054 GarbageCollectModeAny(gen_collect);
2057 for (int i = 0; i < 3; i++)
2059 m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
2065 const unsigned MIN_MEMORYPRESSURE_BUDGET = 4 * 1024 * 1024; // 4 MB
2067 const unsigned MIN_MEMORYPRESSURE_BUDGET = 3 * 1024 * 1024; // 3 MB
2070 const unsigned MAX_MEMORYPRESSURE_RATIO = 10; // 40 MB or 30 MB
2073 // Resets pressure accounting after a gen2 GC has occurred.
2074 void GCInterface::CheckCollectionCount()
2076 LIMITED_METHOD_CONTRACT;
2078 IGCHeap * pHeap = GCHeapUtilities::GetGCHeap();
2080 if (m_gc_counts[2] != pHeap->CollectionCount(2))
2082 for (int i = 0; i < 3; i++)
2084 m_gc_counts[i] = pHeap->CollectionCount(i);
2089 UINT p = m_iteration % NEW_PRESSURE_COUNT;
2091 m_addPressure[p] = 0; // new pressure will be accumulated here
2092 m_remPressure[p] = 0;
2097 // New AddMemoryPressure implementation (used by RCW and the CLRServicesImpl class)
2099 // 1. Less sensitive than the original implementation (start budget 3 MB)
2100 // 2. Focuses more on newly added memory pressure
2101 // 3. Budget adjusted by effectiveness of last 3 triggered GC (add / remove ratio, max 10x)
2102 // 4. Budget maxed with 30% of current managed GC size
2103 // 5. If Gen2 GC is happening naturally, ignore past pressure
2105 // Here's a brief description of the ideal algorithm for Add/Remove memory pressure:
2106 // Do a GC when (HeapStart < X * MemPressureGrowth) where
2107 // - HeapStart is GC Heap size after doing the last GC
2108 // - MemPressureGrowth is the net of Add and Remove since the last GC
2109 // - X is proportional to our guess of the ummanaged memory death rate per GC interval,
2110 // and would be calculated based on historic data using standard exponential approximation:
2111 // Xnew = UMDeath/UMTotal * 0.5 + Xprev
2113 void GCInterface::NewAddMemoryPressure(UINT64 bytesAllocated)
2123 CheckCollectionCount();
2125 UINT p = m_iteration % NEW_PRESSURE_COUNT;
2127 UINT64 newMemValue = InterlockedAdd(&m_addPressure[p], bytesAllocated);
2129 static_assert(NEW_PRESSURE_COUNT == 4, "NewAddMemoryPressure contains unrolled loops which depend on NEW_PRESSURE_COUNT");
2131 UINT64 add = m_addPressure[0] + m_addPressure[1] + m_addPressure[2] + m_addPressure[3] - m_addPressure[p];
2132 UINT64 rem = m_remPressure[0] + m_remPressure[1] + m_remPressure[2] + m_remPressure[3] - m_remPressure[p];
2134 STRESS_LOG4(LF_GCINFO, LL_INFO10000, "AMP Add: %I64u => added=%I64u total_added=%I64u total_removed=%I64u",
2135 bytesAllocated, newMemValue, add, rem);
2137 SendEtwAddMemoryPressureEvent(bytesAllocated);
2139 if (newMemValue >= MIN_MEMORYPRESSURE_BUDGET)
2141 UINT64 budget = MIN_MEMORYPRESSURE_BUDGET;
2143 if (m_iteration >= NEW_PRESSURE_COUNT) // wait until we have enough data points
2145 // Adjust according to effectiveness of GC
2146 // Scale budget according to past m_addPressure / m_remPressure ratio
2147 if (add >= rem * MAX_MEMORYPRESSURE_RATIO)
2149 budget = MIN_MEMORYPRESSURE_BUDGET * MAX_MEMORYPRESSURE_RATIO;
2153 CONSISTENCY_CHECK(rem != 0);
2155 // Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024)
2156 budget = (add * 1024 / rem) * budget / 1024;
2160 // If still over budget, check current managed heap size
2161 if (newMemValue >= budget)
2163 IGCHeap *pGCHeap = GCHeapUtilities::GetGCHeap();
2164 UINT64 heapOver3 = pGCHeap->GetCurrentObjSize() / 3;
2166 if (budget < heapOver3) // Max
2171 if (newMemValue >= budget)
2173 // last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time
2174 if ((pGCHeap->GetNow() - pGCHeap->GetLastGCStartTime(2)) > (pGCHeap->GetLastGCDuration(2) * 5))
2176 STRESS_LOG6(LF_GCINFO, LL_INFO10000, "AMP Budget: pressure=%I64u ? budget=%I64u (total_added=%I64u, total_removed=%I64u, mng_heap=%I64u) pos=%d",
2177 newMemValue, budget, add, rem, heapOver3 * 3, m_iteration);
2179 GarbageCollectModeAny(2);
2181 CheckCollectionCount();
2188 void QCALLTYPE GCInterface::_RemoveMemoryPressure(UINT64 bytesAllocated)
2193 RemoveMemoryPressure(bytesAllocated);
2197 void GCInterface::RemoveMemoryPressure(UINT64 bytesAllocated)
2207 SendEtwRemoveMemoryPressureEvent(bytesAllocated);
2209 UINT64 newMemValue = InterlockedSub(&m_ulMemPressure, bytesAllocated);
2211 UINT64 bytesAllocatedMax = (m_ulThreshold / 4);
2213 UINT64 multMethod = (m_ulThreshold - m_ulThreshold / 20); // can never underflow
2214 if (bytesAllocated >= bytesAllocatedMax) // protect against underflow
2216 m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
2221 addMethod = m_ulThreshold - bytesAllocated * 4;
2224 new_th = (addMethod < multMethod) ? addMethod : multMethod;
2226 if (newMemValue <= new_th)
2229 CrstHolder holder(&m_MemoryPressureLock);
2230 if (new_th > MIN_GC_MEMORYPRESSURE_THRESHOLD)
2231 m_ulThreshold = new_th;
2233 m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
2235 for (int i = 0; i < 3; i++)
2237 m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
2242 void GCInterface::NewRemoveMemoryPressure(UINT64 bytesAllocated)
2252 CheckCollectionCount();
2254 UINT p = m_iteration % NEW_PRESSURE_COUNT;
2256 SendEtwRemoveMemoryPressureEvent(bytesAllocated);
2258 InterlockedAdd(&m_remPressure[p], bytesAllocated);
2260 STRESS_LOG2(LF_GCINFO, LL_INFO10000, "AMP Remove: %I64u => removed=%I64u",
2261 bytesAllocated, m_remPressure[p]);
2264 inline void GCInterface::SendEtwAddMemoryPressureEvent(UINT64 bytesAllocated)
2274 FireEtwIncreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
2277 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
2278 NOINLINE void GCInterface::SendEtwRemoveMemoryPressureEvent(UINT64 bytesAllocated)
2290 FireEtwDecreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
2296 EX_END_CATCH(SwallowAllExceptions)
2299 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
2300 NOINLINE void GCInterface::GarbageCollectModeAny(int generation)
2311 GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, FALSE, collection_non_blocking);
2318 #include <optsmallperfcritical.h>
2320 FCIMPL2(INT32,COMInterlocked::Exchange, INT32 *location, INT32 value)
2324 if( NULL == location) {
2325 FCThrow(kNullReferenceException);
2328 return FastInterlockExchange((LONG *) location, value);
2332 FCIMPL2_IV(INT64,COMInterlocked::Exchange64, INT64 *location, INT64 value)
2336 if( NULL == location) {
2337 FCThrow(kNullReferenceException);
2340 return FastInterlockExchangeLong((INT64 *) location, value);
2344 FCIMPL2(LPVOID,COMInterlocked::ExchangePointer, LPVOID *location, LPVOID value)
2348 if( NULL == location) {
2349 FCThrow(kNullReferenceException);
2353 return FastInterlockExchangePointer(location, value);
2357 FCIMPL3(INT32, COMInterlocked::CompareExchange, INT32* location, INT32 value, INT32 comparand)
2361 if( NULL == location) {
2362 FCThrow(kNullReferenceException);
2365 return FastInterlockCompareExchange((LONG*)location, value, comparand);
2369 FCIMPL4(INT32, COMInterlocked::CompareExchangeReliableResult, INT32* location, INT32 value, INT32 comparand, CLR_BOOL* succeeded)
2373 if( NULL == location) {
2374 FCThrow(kNullReferenceException);
2377 INT32 result = FastInterlockCompareExchange((LONG*)location, value, comparand);
2378 if (result == comparand)
2385 FCIMPL3_IVV(INT64, COMInterlocked::CompareExchange64, INT64* location, INT64 value, INT64 comparand)
2389 if( NULL == location) {
2390 FCThrow(kNullReferenceException);
2393 return FastInterlockCompareExchangeLong((INT64*)location, value, comparand);
2397 FCIMPL3(LPVOID,COMInterlocked::CompareExchangePointer, LPVOID *location, LPVOID value, LPVOID comparand)
2401 if( NULL == location) {
2402 FCThrow(kNullReferenceException);
2406 return FastInterlockCompareExchangePointer(location, value, comparand);
2410 FCIMPL2_IV(float,COMInterlocked::ExchangeFloat, float *location, float value)
2414 if( NULL == location) {
2415 FCThrow(kNullReferenceException);
2418 LONG ret = FastInterlockExchange((LONG *) location, *(LONG*)&value);
2419 return *(float*)&ret;
2423 FCIMPL2_IV(double,COMInterlocked::ExchangeDouble, double *location, double value)
2427 if( NULL == location) {
2428 FCThrow(kNullReferenceException);
2432 INT64 ret = FastInterlockExchangeLong((INT64 *) location, *(INT64*)&value);
2433 return *(double*)&ret;
2437 FCIMPL3_IVV(float,COMInterlocked::CompareExchangeFloat, float *location, float value, float comparand)
2441 if( NULL == location) {
2442 FCThrow(kNullReferenceException);
2445 LONG ret = (LONG)FastInterlockCompareExchange((LONG*) location, *(LONG*)&value, *(LONG*)&comparand);
2446 return *(float*)&ret;
2450 FCIMPL3_IVV(double,COMInterlocked::CompareExchangeDouble, double *location, double value, double comparand)
2454 if( NULL == location) {
2455 FCThrow(kNullReferenceException);
2458 INT64 ret = (INT64)FastInterlockCompareExchangeLong((INT64*) location, *(INT64*)&value, *(INT64*)&comparand);
2459 return *(double*)&ret;
2463 FCIMPL2(LPVOID,COMInterlocked::ExchangeObject, LPVOID*location, LPVOID value)
2467 if( NULL == location) {
2468 FCThrow(kNullReferenceException);
2471 LPVOID ret = FastInterlockExchangePointer(location, value);
2473 Thread::ObjectRefAssign((OBJECTREF *)location);
2475 ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
2480 FCIMPL2_VV(void,COMInterlocked::ExchangeGeneric, FC_TypedByRef location, FC_TypedByRef value)
2484 LPVOID* loc = (LPVOID*)location.data;
2486 FCThrowVoid(kNullReferenceException);
2489 LPVOID val = *(LPVOID*)value.data;
2490 *(LPVOID*)value.data = FastInterlockExchangePointer(loc, val);
2492 Thread::ObjectRefAssign((OBJECTREF *)loc);
2494 ErectWriteBarrier((OBJECTREF*) loc, ObjectToOBJECTREF((Object*) val));
2498 FCIMPL3_VVI(void,COMInterlocked::CompareExchangeGeneric, FC_TypedByRef location, FC_TypedByRef value, LPVOID comparand)
2502 LPVOID* loc = (LPVOID*)location.data;
2503 LPVOID val = *(LPVOID*)value.data;
2505 FCThrowVoid(kNullReferenceException);
2508 LPVOID ret = FastInterlockCompareExchangePointer(loc, val, comparand);
2509 *(LPVOID*)value.data = ret;
2510 if(ret == comparand)
2513 Thread::ObjectRefAssign((OBJECTREF *)loc);
2515 ErectWriteBarrier((OBJECTREF*) loc, ObjectToOBJECTREF((Object*) val));
2520 FCIMPL3(LPVOID,COMInterlocked::CompareExchangeObject, LPVOID *location, LPVOID value, LPVOID comparand)
2524 if( NULL == location) {
2525 FCThrow(kNullReferenceException);
2528 // <TODO>@todo: only set ref if is updated</TODO>
2529 LPVOID ret = FastInterlockCompareExchangePointer(location, value, comparand);
2530 if (ret == comparand) {
2532 Thread::ObjectRefAssign((OBJECTREF *)location);
2534 ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
2540 FCIMPL2(INT32,COMInterlocked::ExchangeAdd32, INT32 *location, INT32 value)
2544 if( NULL == location) {
2545 FCThrow(kNullReferenceException);
2548 return FastInterlockExchangeAdd((LONG *) location, value);
2552 FCIMPL2_IV(INT64,COMInterlocked::ExchangeAdd64, INT64 *location, INT64 value)
2556 if( NULL == location) {
2557 FCThrow(kNullReferenceException);
2560 return FastInterlockExchangeAddLong((INT64 *) location, value);
2564 #include <optdefault.h>
2568 FCIMPL6(INT32, ManagedLoggingHelper::GetRegistryLoggingValues, CLR_BOOL* bLoggingEnabled, CLR_BOOL* bLogToConsole, INT32 *iLogLevel, CLR_BOOL* bPerfWarnings, CLR_BOOL* bCorrectnessWarnings, CLR_BOOL* bSafeHandleStackTraces)
2572 INT32 logFacility = 0;
2574 HELPER_METHOD_FRAME_BEGIN_RET_0();
2576 *bLoggingEnabled = (bool)(g_pConfig->GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogEnable, 0)!=0);
2577 *bLogToConsole = (bool)(g_pConfig->GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogToConsole, 0)!=0);
2578 *iLogLevel = (INT32)(g_pConfig->GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, 0));
2579 logFacility = (INT32)(g_pConfig->GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ManagedLogFacility, 0));
2580 *bPerfWarnings = (bool)(g_pConfig->GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_BCLPerfWarnings, 0)!=0);
2581 *bCorrectnessWarnings = (bool)(g_pConfig->GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_BCLCorrectnessWarnings, 0)!=0);
2582 *bSafeHandleStackTraces = (bool)(g_pConfig->GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SafeHandleStackTraces, 0)!=0);
2584 HELPER_METHOD_FRAME_END(); \
2590 // Return true if the valuetype does not contain pointer and is tightly packed
2591 FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
2595 _ASSERTE(obj != NULL);
2596 MethodTable* mt = obj->GetMethodTable();
2597 FC_RETURN_BOOL(!mt->ContainsPointers() && !mt->IsNotTightlyPacked());
2601 FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1, Object* obj2)
2605 _ASSERTE(obj1 != NULL);
2606 _ASSERTE(obj2 != NULL);
2607 _ASSERTE(!obj1->GetMethodTable()->ContainsPointers());
2608 _ASSERTE(obj1->GetSize() == obj2->GetSize());
2610 TypeHandle pTh = obj1->GetTypeHandle();
2612 FC_RETURN_BOOL(memcmp(obj1->GetData(),obj2->GetData(),pTh.GetSize()) == 0);
2616 static BOOL CanUseFastGetHashCodeHelper(MethodTable *mt)
2618 LIMITED_METHOD_CONTRACT;
2619 return !mt->ContainsPointers() && !mt->IsNotTightlyPacked();
2622 static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef)
2630 PRECONDITION(CanUseFastGetHashCodeHelper(mt));
2634 INT32 *pObj = (INT32*)pObjRef;
2636 // this is a struct with no refs and no "strange" offsets, just go through the obj and xor the bits
2637 INT32 size = mt->GetNumInstanceFieldBytes();
2638 for (INT32 i = 0; i < (INT32)(size / sizeof(INT32)); i++)
2639 hashCode ^= *pObj++;
2644 static INT32 RegularGetValueTypeHashCode(MethodTable *mt, void *pObjRef)
2654 INT32 *pObj = (INT32*)pObjRef;
2656 // While we shouln't get here directly from ValueTypeHelper::GetHashCode, if we recurse we need to
2657 // be able to handle getting the hashcode for an embedded structure whose hashcode is computed by the fast path.
2658 if (CanUseFastGetHashCodeHelper(mt))
2660 return FastGetValueTypeHashCodeHelper(mt, pObjRef);
2664 // it's looking ugly so we'll use the old behavior in managed code. Grab the first non-null
2665 // field and return its hash code or 'it' as hash code
2666 // <TODO> Note that the old behavior has already been broken for value types
2667 // that is qualified for CanUseFastGetHashCodeHelper. So maybe we should
2668 // change the implementation here to use all fields instead of just the 1st one.
2671 // <TODO> check this approximation - we may be losing exact type information </TODO>
2672 ApproxFieldDescIterator fdIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
2673 INT32 count = (INT32)fdIterator.Count();
2677 for (INT32 i = 0; i < count; i++)
2679 FieldDesc *field = fdIterator.Next();
2680 _ASSERTE(!field->IsRVA());
2681 void *pFieldValue = (BYTE *)pObj + field->GetOffsetUnsafe();
2682 if (field->IsObjRef())
2684 // if we get an object reference we get the hash code out of that
2685 if (*(Object**)pFieldValue != NULL)
2688 OBJECTREF fieldObjRef = ObjectToOBJECTREF(*(Object **) pFieldValue);
2689 GCPROTECT_BEGIN(fieldObjRef);
2691 MethodDescCallSite getHashCode(METHOD__OBJECT__GET_HASH_CODE, &fieldObjRef);
2694 ARG_SLOT arg[1] = {ObjToArgSlot(fieldObjRef)};
2695 hashCode = getHashCode.Call_RetI4(arg);
2701 // null object reference, try next
2707 UINT fieldSize = field->LoadSize();
2708 INT32 *pValue = (INT32*)pFieldValue;
2709 CorElementType fieldType = field->GetFieldType();
2710 if (fieldType != ELEMENT_TYPE_VALUETYPE)
2712 for (INT32 j = 0; j < (INT32)(fieldSize / sizeof(INT32)); j++)
2713 hashCode ^= *pValue++;
2717 // got another value type. Get the type
2718 TypeHandle fieldTH = field->LookupFieldTypeHandle(); // the type was loaded already
2719 _ASSERTE(!fieldTH.IsNull());
2720 hashCode = RegularGetValueTypeHashCode(fieldTH.GetMethodTable(), pValue);
2730 // The default implementation of GetHashCode() for all value types.
2731 // Note that this implementation reveals the value of the fields.
2732 // So if the value type contains any sensitive information it should
2733 // implement its own GetHashCode().
2734 FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE)
2738 if (objUNSAFE == NULL)
2739 FCThrow(kNullReferenceException);
2741 OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE);
2742 VALIDATEOBJECTREF(obj);
2745 MethodTable *pMT = objUNSAFE->GetMethodTable();
2747 // We don't want to expose the method table pointer in the hash code
2748 // Let's use the typeID instead.
2749 UINT32 typeID = pMT->LookupTypeID();
2750 if (typeID == TypeIDProvider::INVALID_TYPE_ID)
2752 // If the typeID has yet to be generated, fall back to GetTypeID
2753 // This only needs to be done once per MethodTable
2754 HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2755 typeID = pMT->GetTypeID();
2756 HELPER_METHOD_FRAME_END();
2759 // To get less colliding and more evenly distributed hash codes,
2760 // we munge the class index with two big prime numbers
2761 hashCode = typeID * 711650207 + 2506965631U;
2763 if (CanUseFastGetHashCodeHelper(pMT))
2765 hashCode ^= FastGetValueTypeHashCodeHelper(pMT, obj->UnBox());
2769 HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2770 hashCode ^= RegularGetValueTypeHashCode(pMT, obj->UnBox());
2771 HELPER_METHOD_FRAME_END();
2778 static LONG s_dwSeed;
2780 FCIMPL1(INT32, ValueTypeHelper::GetHashCodeOfPtr, LPVOID ptr)
2784 INT32 hashCode = (INT32)((INT64)(ptr));
2791 DWORD dwSeed = s_dwSeed;
2793 // Initialize s_dwSeed lazily
2796 // We use the first non-0 pointer as the seed, all hashcodes will be based off that.
2797 // This is to make sure that we only reveal relative memory addresses and never absolute ones.
2799 InterlockedCompareExchange(&s_dwSeed, dwSeed, 0);
2802 _ASSERTE(dwSeed != 0);
2804 return hashCode - dwSeed;
2809 COMNlsHashProvider COMNlsHashProvider::s_NlsHashProvider;
2812 COMNlsHashProvider::COMNlsHashProvider()
2814 LIMITED_METHOD_CONTRACT;
2816 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
2817 bUseRandomHashing = FALSE;
2819 pDefaultSeed = NULL;
2820 #endif // FEATURE_RANDOMIZED_STRING_HASHING
2823 INT32 COMNlsHashProvider::HashString(LPCWSTR szStr, SIZE_T strLen, BOOL forceRandomHashing, INT64 additionalEntropy)
2832 #ifndef FEATURE_RANDOMIZED_STRING_HASHING
2833 _ASSERTE(forceRandomHashing == false);
2834 _ASSERTE(additionalEntropy == 0);
2837 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
2838 if(bUseRandomHashing || forceRandomHashing)
2840 int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
2842 if(additionalEntropy == 0)
2844 SymCryptMarvin32(GetDefaultSeed(), (PCBYTE) szStr, strLen * sizeof(WCHAR), (PBYTE) &marvinResult);
2848 SYMCRYPT_MARVIN32_EXPANDED_SEED seed;
2849 CreateMarvin32Seed(additionalEntropy, &seed);
2850 SymCryptMarvin32(&seed, (PCBYTE) szStr, strLen * sizeof(WCHAR), (PBYTE) &marvinResult);
2853 return marvinResult[0] ^ marvinResult[1];
2857 #endif // FEATURE_RANDOMIZED_STRING_HASHING
2858 return ::HashString(szStr);
2859 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
2861 #endif // FEATURE_RANDOMIZED_STRING_HASHING
2865 INT32 COMNlsHashProvider::HashSortKey(PCBYTE pSrc, SIZE_T cbSrc, BOOL forceRandomHashing, INT64 additionalEntropy)
2874 #ifndef FEATURE_RANDOMIZED_STRING_HASHING
2875 _ASSERTE(forceRandomHashing == false);
2876 _ASSERTE(additionalEntropy == 0);
2879 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
2880 if(bUseRandomHashing || forceRandomHashing)
2882 int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
2884 // Sort Keys are terminated with a null byte which we didn't hash using the old algorithm,
2885 // so we don't have it with Marvin32 either.
2886 if(additionalEntropy == 0)
2888 SymCryptMarvin32(GetDefaultSeed(), pSrc, cbSrc - 1, (PBYTE) &marvinResult);
2892 SYMCRYPT_MARVIN32_EXPANDED_SEED seed;
2893 CreateMarvin32Seed(additionalEntropy, &seed);
2894 SymCryptMarvin32(&seed, pSrc, cbSrc - 1, (PBYTE) &marvinResult);
2897 return marvinResult[0] ^ marvinResult[1];
2901 #endif // FEATURE_RANDOMIZED_STRING_HASHING
2902 // Ok, lets build the hashcode -- mostly lifted from GetHashCode() in String.cs, for strings.
2905 const BYTE *pB = pSrc;
2908 while (pB != 0 && *pB != 0) {
2909 hash1 = ((hash1 << 5) + hash1) ^ *pB;
2913 // FUTURE: Update NewAPis::LCMapStringEx to perhaps use a different, bug free, Win32 API on Win2k3 to workaround the issue discussed below.
2915 // On Win2k3 Server, LCMapStringEx(LCMAP_SORTKEY) output does not correspond to CompareString in all cases, breaking the .NET GetHashCode<->Equality Contract
2916 // Due to a fluke in our GetHashCode method, we avoided this issue due to the break out of the loop on the binary-zero byte.
2921 hash2 = ((hash2 << 5) + hash2) ^ c;
2925 return hash1 + (hash2 * 1566083941);
2927 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
2929 #endif // FEATURE_RANDOMIZED_STRING_HASHING
2933 INT32 COMNlsHashProvider::HashiStringKnownLower80(LPCWSTR szStr, INT32 strLen, BOOL forceRandomHashing, INT64 additionalEntropy)
2942 #ifndef FEATURE_RANDOMIZED_STRING_HASHING
2943 _ASSERTE(forceRandomHashing == false);
2944 _ASSERTE(additionalEntropy == 0);
2947 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
2948 if(bUseRandomHashing || forceRandomHashing)
2950 WCHAR buf[SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE * 8];
2951 SYMCRYPT_MARVIN32_STATE marvinState;
2952 SYMCRYPT_MARVIN32_EXPANDED_SEED seed;
2954 if(additionalEntropy == 0)
2956 SymCryptMarvin32Init(&marvinState, GetDefaultSeed());
2960 CreateMarvin32Seed(additionalEntropy, &seed);
2961 SymCryptMarvin32Init(&marvinState, &seed);
2964 LPCWSTR szEnd = szStr + strLen;
2966 const UINT A_TO_Z_RANGE = (UINT)('z' - 'a');
2968 while (szStr != szEnd)
2970 size_t count = (sizeof(buf) / sizeof(buf[0]));
2972 if ((size_t)(szEnd - szStr) < count)
2973 count = (size_t)(szEnd - szStr);
2975 for (size_t i = 0; i<count; i++)
2979 if ((UINT)(c - 'a') <= A_TO_Z_RANGE) // if (c >='a' && c <= 'z')
2981 //If we have a lowercase character, ANDing off 0x20
2982 // will make it an uppercase character.
2991 SymCryptMarvin32Append(&marvinState, (PCBYTE) &buf, sizeof(WCHAR) * count);
2994 int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
2995 SymCryptMarvin32Result(&marvinState, (PBYTE) &marvinResult);
2996 return marvinResult[0] ^ marvinResult[1];
3000 #endif // FEATURE_RANDOMIZED_STRING_HASHING
3001 return ::HashiStringKnownLower80(szStr);
3002 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
3004 #endif // FEATURE_RANDOMIZED_STRING_HASHING
3008 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
3009 void COMNlsHashProvider::InitializeDefaultSeed()
3018 PCBYTE pEntropy = GetEntropy();
3019 AllocMemHolder<SYMCRYPT_MARVIN32_EXPANDED_SEED> pSeed(GetAppDomain()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(SYMCRYPT_MARVIN32_EXPANDED_SEED))));
3020 SymCryptMarvin32ExpandSeed(pSeed, pEntropy, SYMCRYPT_MARVIN32_SEED_SIZE);
3022 if(InterlockedCompareExchangeT(&pDefaultSeed, (PCSYMCRYPT_MARVIN32_EXPANDED_SEED) pSeed, NULL) == NULL)
3024 pSeed.SuppressRelease();
3028 PCSYMCRYPT_MARVIN32_EXPANDED_SEED COMNlsHashProvider::GetDefaultSeed()
3037 if(pDefaultSeed == NULL)
3039 InitializeDefaultSeed();
3042 return pDefaultSeed;
3045 PCBYTE COMNlsHashProvider::GetEntropy()
3054 if(pEntropy == NULL)
3056 AllocMemHolder<BYTE> pNewEntropy(GetAppDomain()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(SYMCRYPT_MARVIN32_SEED_SIZE))));
3059 PAL_Random(TRUE, pNewEntropy, SYMCRYPT_MARVIN32_SEED_SIZE);
3061 HCRYPTPROV hCryptProv;
3062 WszCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
3063 CryptGenRandom(hCryptProv, SYMCRYPT_MARVIN32_SEED_SIZE, pNewEntropy);
3064 CryptReleaseContext(hCryptProv, 0);
3067 if(InterlockedCompareExchangeT(&pEntropy, (PBYTE) pNewEntropy, NULL) == NULL)
3069 pNewEntropy.SuppressRelease();
3073 return (PCBYTE) pEntropy;
3077 void COMNlsHashProvider::CreateMarvin32Seed(INT64 additionalEntropy, PSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedMarvinSeed)
3086 INT64 *pEntropy = (INT64*) GetEntropy();
3089 entropy = *pEntropy ^ additionalEntropy;
3091 SymCryptMarvin32ExpandSeed(pExpandedMarvinSeed, (PCBYTE) &entropy, SYMCRYPT_MARVIN32_SEED_SIZE);
3093 #endif // FEATURE_RANDOMIZED_STRING_HASHING
3095 #ifdef FEATURE_COREFX_GLOBALIZATION
3096 INT32 QCALLTYPE CoreFxGlobalization::HashSortKey(PCBYTE pSortKey, INT32 cbSortKey, BOOL forceRandomizedHashing, INT64 additionalEntropy)
3104 retVal = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pSortKey, cbSortKey, forceRandomizedHashing, additionalEntropy);
3110 #endif //FEATURE_COREFX_GLOBALIZATION
3112 static MethodTable * g_pStreamMT;
3113 static WORD g_slotBeginRead, g_slotEndRead;
3114 static WORD g_slotBeginWrite, g_slotEndWrite;
3116 static bool HasOverriddenStreamMethod(MethodTable * pMT, WORD slot)
3125 PCODE actual = pMT->GetRestoredSlot(slot);
3126 PCODE base = g_pStreamMT->GetRestoredSlot(slot);
3130 if (!g_pStreamMT->IsZapped())
3132 // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs
3133 // to detect match reliably
3134 if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
3141 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndRead, Object *stream)
3146 FC_RETURN_BOOL(TRUE);
3148 if (g_pStreamMT == NULL || g_slotBeginRead == 0 || g_slotEndRead == 0)
3150 HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
3151 g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
3152 g_slotBeginRead = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_READ)->GetSlot();
3153 g_slotEndRead = MscorlibBinder::GetMethod(METHOD__STREAM__END_READ)->GetSlot();
3154 HELPER_METHOD_FRAME_END();
3157 MethodTable * pMT = stream->GetMethodTable();
3159 FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead));
3163 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndWrite, Object *stream)
3168 FC_RETURN_BOOL(TRUE);
3170 if (g_pStreamMT == NULL || g_slotBeginWrite == 0 || g_slotEndWrite == 0)
3172 HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
3173 g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
3174 g_slotBeginWrite = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_WRITE)->GetSlot();
3175 g_slotEndWrite = MscorlibBinder::GetMethod(METHOD__STREAM__END_WRITE)->GetSlot();
3176 HELPER_METHOD_FRAME_END();
3179 MethodTable * pMT = stream->GetMethodTable();
3181 FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginWrite) || HasOverriddenStreamMethod(pMT, g_slotEndWrite));