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 | FCIMPL5(void, GCInterface::GetMemoryInfo, UINT32* highMemLoadThreshold, UINT64* totalPhysicalMem, UINT32* lastRecordedMemLoad, size_t* lastRecordedHeapSize, size_t* lastRecordedFragmentation) |
926 | { |
927 | FCALL_CONTRACT; |
928 | |
929 | FC_GC_POLL_NOT_NEEDED(); |
930 | |
931 | return GCHeapUtilities::GetGCHeap()->GetMemoryInfo(highMemLoadThreshold, totalPhysicalMem, |
932 | lastRecordedMemLoad, |
933 | lastRecordedHeapSize, lastRecordedFragmentation); |
934 | } |
935 | FCIMPLEND |
936 | |
937 | FCIMPL0(int, GCInterface::GetGcLatencyMode) |
938 | { |
939 | FCALL_CONTRACT; |
940 | |
941 | FC_GC_POLL_NOT_NEEDED(); |
942 | |
943 | int result = (INT32)GCHeapUtilities::GetGCHeap()->GetGcLatencyMode(); |
944 | return result; |
945 | } |
946 | FCIMPLEND |
947 | |
948 | FCIMPL1(int, GCInterface::SetGcLatencyMode, int newLatencyMode) |
949 | { |
950 | FCALL_CONTRACT; |
951 | |
952 | FC_GC_POLL_NOT_NEEDED(); |
953 | |
954 | return GCHeapUtilities::GetGCHeap()->SetGcLatencyMode(newLatencyMode); |
955 | } |
956 | FCIMPLEND |
957 | |
958 | FCIMPL0(int, GCInterface::GetLOHCompactionMode) |
959 | { |
960 | FCALL_CONTRACT; |
961 | |
962 | FC_GC_POLL_NOT_NEEDED(); |
963 | |
964 | int result = (INT32)GCHeapUtilities::GetGCHeap()->GetLOHCompactionMode(); |
965 | return result; |
966 | } |
967 | FCIMPLEND |
968 | |
969 | FCIMPL1(void, GCInterface::SetLOHCompactionMode, int newLOHCompactionyMode) |
970 | { |
971 | FCALL_CONTRACT; |
972 | |
973 | FC_GC_POLL_NOT_NEEDED(); |
974 | |
975 | GCHeapUtilities::GetGCHeap()->SetLOHCompactionMode(newLOHCompactionyMode); |
976 | } |
977 | FCIMPLEND |
978 | |
979 | |
980 | FCIMPL2(FC_BOOL_RET, GCInterface::RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage) |
981 | { |
982 | FCALL_CONTRACT; |
983 | |
984 | FC_GC_POLL_NOT_NEEDED(); |
985 | |
986 | FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->RegisterForFullGCNotification(gen2Percentage, lohPercentage)); |
987 | } |
988 | FCIMPLEND |
989 | |
990 | FCIMPL0(FC_BOOL_RET, GCInterface::CancelFullGCNotification) |
991 | { |
992 | FCALL_CONTRACT; |
993 | |
994 | FC_GC_POLL_NOT_NEEDED(); |
995 | FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->CancelFullGCNotification()); |
996 | } |
997 | FCIMPLEND |
998 | |
999 | FCIMPL1(int, GCInterface::WaitForFullGCApproach, int millisecondsTimeout) |
1000 | { |
1001 | CONTRACTL |
1002 | { |
1003 | THROWS; |
1004 | MODE_COOPERATIVE; |
1005 | DISABLED(GC_TRIGGERS); // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F. |
1006 | SO_TOLERANT; |
1007 | } |
1008 | CONTRACTL_END; |
1009 | |
1010 | int result = 0; |
1011 | |
1012 | //We don't need to check the top end because the GC will take care of that. |
1013 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
1014 | |
1015 | DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout); |
1016 | result = GCHeapUtilities::GetGCHeap()->WaitForFullGCApproach(dwMilliseconds); |
1017 | |
1018 | HELPER_METHOD_FRAME_END(); |
1019 | |
1020 | return result; |
1021 | } |
1022 | FCIMPLEND |
1023 | |
1024 | FCIMPL1(int, GCInterface::WaitForFullGCComplete, int millisecondsTimeout) |
1025 | { |
1026 | CONTRACTL |
1027 | { |
1028 | THROWS; |
1029 | MODE_COOPERATIVE; |
1030 | DISABLED(GC_TRIGGERS); // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F. |
1031 | SO_TOLERANT; |
1032 | } |
1033 | CONTRACTL_END; |
1034 | |
1035 | int result = 0; |
1036 | |
1037 | //We don't need to check the top end because the GC will take care of that. |
1038 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
1039 | |
1040 | DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout); |
1041 | result = GCHeapUtilities::GetGCHeap()->WaitForFullGCComplete(dwMilliseconds); |
1042 | |
1043 | HELPER_METHOD_FRAME_END(); |
1044 | |
1045 | return result; |
1046 | } |
1047 | FCIMPLEND |
1048 | |
1049 | /*================================GetGeneration================================= |
1050 | **Action: Returns the generation in which args->obj is found. |
1051 | **Returns: The generation in which args->obj is found. |
1052 | **Arguments: args->obj -- The object to locate. |
1053 | **Exceptions: ArgumentException if args->obj is null. |
1054 | ==============================================================================*/ |
1055 | FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE) |
1056 | { |
1057 | FCALL_CONTRACT; |
1058 | |
1059 | if (objUNSAFE == NULL) |
1060 | FCThrowArgumentNull(W("obj" )); |
1061 | |
1062 | int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); |
1063 | FC_GC_POLL_RET(); |
1064 | return result; |
1065 | } |
1066 | FCIMPLEND |
1067 | |
1068 | /*================================CollectionCount================================= |
1069 | **Action: Returns the number of collections for this generation since the begining of the life of the process |
1070 | **Returns: The collection count. |
1071 | **Arguments: args->generation -- The generation |
1072 | **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration(); |
1073 | ==============================================================================*/ |
1074 | FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount) |
1075 | { |
1076 | FCALL_CONTRACT; |
1077 | |
1078 | //We've already checked this in GC.cs, so we'll just assert it here. |
1079 | _ASSERTE(generation >= 0); |
1080 | |
1081 | //We don't need to check the top end because the GC will take care of that. |
1082 | int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount); |
1083 | FC_GC_POLL_RET(); |
1084 | return result; |
1085 | } |
1086 | FCIMPLEND |
1087 | |
1088 | int QCALLTYPE GCInterface::StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC) |
1089 | { |
1090 | QCALL_CONTRACT; |
1091 | |
1092 | int retVal = 0; |
1093 | |
1094 | BEGIN_QCALL; |
1095 | |
1096 | GCX_COOP(); |
1097 | |
1098 | retVal = GCHeapUtilities::GetGCHeap()->StartNoGCRegion((ULONGLONG)totalSize, |
1099 | !!lohSizeKnown, |
1100 | (ULONGLONG)lohSize, |
1101 | !!disallowFullBlockingGC); |
1102 | |
1103 | END_QCALL; |
1104 | |
1105 | return retVal; |
1106 | } |
1107 | |
1108 | int QCALLTYPE GCInterface::EndNoGCRegion() |
1109 | { |
1110 | QCALL_CONTRACT; |
1111 | |
1112 | int retVal = FALSE; |
1113 | |
1114 | BEGIN_QCALL; |
1115 | |
1116 | retVal = GCHeapUtilities::GetGCHeap()->EndNoGCRegion(); |
1117 | |
1118 | END_QCALL; |
1119 | |
1120 | return retVal; |
1121 | } |
1122 | |
1123 | /*===============================GetGenerationWR================================ |
1124 | **Action: Returns the generation in which the object pointed to by a WeakReference is found. |
1125 | **Returns: |
1126 | **Arguments: args->handle -- the OBJECTHANDLE to the object which we're locating. |
1127 | **Exceptions: ArgumentException if handle points to an object which is not accessible. |
1128 | ==============================================================================*/ |
1129 | FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle) |
1130 | { |
1131 | FCALL_CONTRACT; |
1132 | |
1133 | int iRetVal = 0; |
1134 | |
1135 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
1136 | |
1137 | OBJECTREF temp; |
1138 | temp = ObjectFromHandle((OBJECTHANDLE) handle); |
1139 | if (temp == NULL) |
1140 | COMPlusThrowArgumentNull(W("weak handle" )); |
1141 | |
1142 | iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp)); |
1143 | |
1144 | HELPER_METHOD_FRAME_END(); |
1145 | |
1146 | return iRetVal; |
1147 | } |
1148 | FCIMPLEND |
1149 | |
1150 | /*================================GetTotalMemory================================ |
1151 | **Action: Returns the total number of bytes in use |
1152 | **Returns: The total number of bytes in use |
1153 | **Arguments: None |
1154 | **Exceptions: None |
1155 | ==============================================================================*/ |
1156 | INT64 QCALLTYPE GCInterface::GetTotalMemory() |
1157 | { |
1158 | QCALL_CONTRACT; |
1159 | |
1160 | INT64 iRetVal = 0; |
1161 | |
1162 | BEGIN_QCALL; |
1163 | |
1164 | GCX_COOP(); |
1165 | iRetVal = (INT64) GCHeapUtilities::GetGCHeap()->GetTotalBytesInUse(); |
1166 | |
1167 | END_QCALL; |
1168 | |
1169 | return iRetVal; |
1170 | } |
1171 | |
1172 | /*==============================Collect========================================= |
1173 | **Action: Collects all generations <= args->generation |
1174 | **Returns: void |
1175 | **Arguments: args->generation: The maximum generation to collect |
1176 | **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration(); |
1177 | ==============================================================================*/ |
1178 | void QCALLTYPE GCInterface::Collect(INT32 generation, INT32 mode) |
1179 | { |
1180 | QCALL_CONTRACT; |
1181 | |
1182 | BEGIN_QCALL; |
1183 | |
1184 | //We've already checked this in GC.cs, so we'll just assert it here. |
1185 | _ASSERTE(generation >= -1); |
1186 | |
1187 | //We don't need to check the top end because the GC will take care of that. |
1188 | |
1189 | GCX_COOP(); |
1190 | GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, mode); |
1191 | |
1192 | END_QCALL; |
1193 | } |
1194 | |
1195 | |
1196 | /*==========================WaitForPendingFinalizers============================ |
1197 | **Action: Run all Finalizers that haven't been run. |
1198 | **Arguments: None |
1199 | **Exceptions: None |
1200 | ==============================================================================*/ |
1201 | void QCALLTYPE GCInterface::WaitForPendingFinalizers() |
1202 | { |
1203 | QCALL_CONTRACT; |
1204 | |
1205 | BEGIN_QCALL; |
1206 | |
1207 | FinalizerThread::FinalizerThreadWait(); |
1208 | |
1209 | END_QCALL; |
1210 | } |
1211 | |
1212 | |
1213 | /*===============================GetMaxGeneration=============================== |
1214 | **Action: Returns the largest GC generation |
1215 | **Returns: The largest GC Generation |
1216 | **Arguments: None |
1217 | **Exceptions: None |
1218 | ==============================================================================*/ |
1219 | FCIMPL0(int, GCInterface::GetMaxGeneration) |
1220 | { |
1221 | FCALL_CONTRACT; |
1222 | |
1223 | return(INT32)GCHeapUtilities::GetGCHeap()->GetMaxGeneration(); |
1224 | } |
1225 | FCIMPLEND |
1226 | |
1227 | /*===============================GetAllocatedBytesForCurrentThread=============================== |
1228 | **Action: Computes the allocated bytes so far on the current thread |
1229 | **Returns: The allocated bytes so far on the current thread |
1230 | **Arguments: None |
1231 | **Exceptions: None |
1232 | ==============================================================================*/ |
1233 | FCIMPL0(INT64, GCInterface::GetAllocatedBytesForCurrentThread) |
1234 | { |
1235 | FCALL_CONTRACT; |
1236 | |
1237 | INT64 currentAllocated = 0; |
1238 | Thread *pThread = GetThread(); |
1239 | gc_alloc_context* ac = pThread->GetAllocContext(); |
1240 | currentAllocated = ac->alloc_bytes + ac->alloc_bytes_loh - (ac->alloc_limit - ac->alloc_ptr); |
1241 | |
1242 | return currentAllocated; |
1243 | } |
1244 | FCIMPLEND |
1245 | |
1246 | /*==============================SuppressFinalize================================ |
1247 | **Action: Indicate that an object's finalizer should not be run by the system |
1248 | **Arguments: Object of interest |
1249 | **Exceptions: None |
1250 | ==============================================================================*/ |
1251 | FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj) |
1252 | { |
1253 | FCALL_CONTRACT; |
1254 | |
1255 | // Checked by the caller |
1256 | _ASSERTE(obj != NULL); |
1257 | |
1258 | if (!obj->GetMethodTable ()->HasFinalizer()) |
1259 | return; |
1260 | |
1261 | GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj); |
1262 | FC_GC_POLL(); |
1263 | } |
1264 | FCIMPLEND |
1265 | |
1266 | |
1267 | /*============================ReRegisterForFinalize============================== |
1268 | **Action: Indicate that an object's finalizer should be run by the system. |
1269 | **Arguments: Object of interest |
1270 | **Exceptions: None |
1271 | ==============================================================================*/ |
1272 | FCIMPL1(void, GCInterface::ReRegisterForFinalize, Object *obj) |
1273 | { |
1274 | FCALL_CONTRACT; |
1275 | |
1276 | // Checked by the caller |
1277 | _ASSERTE(obj != NULL); |
1278 | |
1279 | if (obj->GetMethodTable()->HasFinalizer()) |
1280 | { |
1281 | HELPER_METHOD_FRAME_BEGIN_1(obj); |
1282 | if (!GCHeapUtilities::GetGCHeap()->RegisterForFinalization(-1, obj)) |
1283 | { |
1284 | ThrowOutOfMemory(); |
1285 | } |
1286 | HELPER_METHOD_FRAME_END(); |
1287 | } |
1288 | } |
1289 | FCIMPLEND |
1290 | |
1291 | FORCEINLINE UINT64 GCInterface::InterlockedAdd (UINT64 *pAugend, UINT64 addend) { |
1292 | WRAPPER_NO_CONTRACT; |
1293 | STATIC_CONTRACT_SO_TOLERANT; |
1294 | |
1295 | UINT64 oldMemValue; |
1296 | UINT64 newMemValue; |
1297 | |
1298 | do { |
1299 | oldMemValue = *pAugend; |
1300 | newMemValue = oldMemValue + addend; |
1301 | |
1302 | // check for overflow |
1303 | if (newMemValue < oldMemValue) |
1304 | { |
1305 | newMemValue = UINT64_MAX; |
1306 | } |
1307 | } while (InterlockedCompareExchange64((LONGLONG*) pAugend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue); |
1308 | |
1309 | return newMemValue; |
1310 | } |
1311 | |
1312 | FORCEINLINE UINT64 GCInterface::InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend) { |
1313 | WRAPPER_NO_CONTRACT; |
1314 | STATIC_CONTRACT_SO_TOLERANT; |
1315 | |
1316 | UINT64 oldMemValue; |
1317 | UINT64 newMemValue; |
1318 | |
1319 | do { |
1320 | oldMemValue = *pMinuend; |
1321 | newMemValue = oldMemValue - subtrahend; |
1322 | |
1323 | // check for underflow |
1324 | if (newMemValue > oldMemValue) |
1325 | newMemValue = 0; |
1326 | |
1327 | } while (InterlockedCompareExchange64((LONGLONG*) pMinuend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue); |
1328 | |
1329 | return newMemValue; |
1330 | } |
1331 | |
1332 | void QCALLTYPE GCInterface::_AddMemoryPressure(UINT64 bytesAllocated) |
1333 | { |
1334 | QCALL_CONTRACT; |
1335 | |
1336 | // AddMemoryPressure could cause a GC, so we need a frame |
1337 | BEGIN_QCALL; |
1338 | AddMemoryPressure(bytesAllocated); |
1339 | END_QCALL; |
1340 | } |
1341 | |
1342 | void GCInterface::AddMemoryPressure(UINT64 bytesAllocated) |
1343 | { |
1344 | CONTRACTL |
1345 | { |
1346 | THROWS; |
1347 | GC_TRIGGERS; |
1348 | MODE_ANY; |
1349 | } |
1350 | CONTRACTL_END; |
1351 | |
1352 | SendEtwAddMemoryPressureEvent(bytesAllocated); |
1353 | |
1354 | UINT64 newMemValue = InterlockedAdd(&m_ulMemPressure, bytesAllocated); |
1355 | |
1356 | if (newMemValue > m_ulThreshold) |
1357 | { |
1358 | INT32 gen_collect = 0; |
1359 | { |
1360 | GCX_PREEMP(); |
1361 | CrstHolder holder(&m_MemoryPressureLock); |
1362 | |
1363 | // to avoid collecting too often, take the max threshold of the linear and geometric growth |
1364 | // heuristics. |
1365 | UINT64 addMethod; |
1366 | UINT64 multMethod; |
1367 | UINT64 bytesAllocatedMax = (UINT64_MAX - m_ulThreshold) / 8; |
1368 | |
1369 | if (bytesAllocated >= bytesAllocatedMax) // overflow check |
1370 | { |
1371 | addMethod = UINT64_MAX; |
1372 | } |
1373 | else |
1374 | { |
1375 | addMethod = m_ulThreshold + bytesAllocated * 8; |
1376 | } |
1377 | |
1378 | multMethod = newMemValue + newMemValue / 10; |
1379 | if (multMethod < newMemValue) // overflow check |
1380 | { |
1381 | multMethod = UINT64_MAX; |
1382 | } |
1383 | |
1384 | m_ulThreshold = (addMethod > multMethod) ? addMethod : multMethod; |
1385 | for (int i = 0; i <= 1; i++) |
1386 | { |
1387 | if ((GCHeapUtilities::GetGCHeap()->CollectionCount(i) / RELATIVE_GC_RATIO) > GCHeapUtilities::GetGCHeap()->CollectionCount(i + 1)) |
1388 | { |
1389 | gen_collect = i + 1; |
1390 | break; |
1391 | } |
1392 | } |
1393 | } |
1394 | |
1395 | PREFIX_ASSUME(gen_collect <= 2); |
1396 | |
1397 | if ((gen_collect == 0) || (m_gc_counts[gen_collect] == GCHeapUtilities::GetGCHeap()->CollectionCount(gen_collect))) |
1398 | { |
1399 | GarbageCollectModeAny(gen_collect); |
1400 | } |
1401 | |
1402 | for (int i = 0; i < 3; i++) |
1403 | { |
1404 | m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i); |
1405 | } |
1406 | } |
1407 | } |
1408 | |
1409 | #ifdef _WIN64 |
1410 | const unsigned MIN_MEMORYPRESSURE_BUDGET = 4 * 1024 * 1024; // 4 MB |
1411 | #else // _WIN64 |
1412 | const unsigned MIN_MEMORYPRESSURE_BUDGET = 3 * 1024 * 1024; // 3 MB |
1413 | #endif // _WIN64 |
1414 | |
1415 | const unsigned MAX_MEMORYPRESSURE_RATIO = 10; // 40 MB or 30 MB |
1416 | |
1417 | |
1418 | // Resets pressure accounting after a gen2 GC has occurred. |
1419 | void GCInterface::CheckCollectionCount() |
1420 | { |
1421 | LIMITED_METHOD_CONTRACT; |
1422 | |
1423 | IGCHeap * pHeap = GCHeapUtilities::GetGCHeap(); |
1424 | |
1425 | if (m_gc_counts[2] != pHeap->CollectionCount(2)) |
1426 | { |
1427 | for (int i = 0; i < 3; i++) |
1428 | { |
1429 | m_gc_counts[i] = pHeap->CollectionCount(i); |
1430 | } |
1431 | |
1432 | m_iteration++; |
1433 | |
1434 | UINT p = m_iteration % NEW_PRESSURE_COUNT; |
1435 | |
1436 | m_addPressure[p] = 0; // new pressure will be accumulated here |
1437 | m_remPressure[p] = 0; |
1438 | } |
1439 | } |
1440 | |
1441 | |
1442 | // New AddMemoryPressure implementation (used by RCW and the CLRServicesImpl class) |
1443 | // |
1444 | // 1. Less sensitive than the original implementation (start budget 3 MB) |
1445 | // 2. Focuses more on newly added memory pressure |
1446 | // 3. Budget adjusted by effectiveness of last 3 triggered GC (add / remove ratio, max 10x) |
1447 | // 4. Budget maxed with 30% of current managed GC size |
1448 | // 5. If Gen2 GC is happening naturally, ignore past pressure |
1449 | // |
1450 | // Here's a brief description of the ideal algorithm for Add/Remove memory pressure: |
1451 | // Do a GC when (HeapStart < X * MemPressureGrowth) where |
1452 | // - HeapStart is GC Heap size after doing the last GC |
1453 | // - MemPressureGrowth is the net of Add and Remove since the last GC |
1454 | // - X is proportional to our guess of the ummanaged memory death rate per GC interval, |
1455 | // and would be calculated based on historic data using standard exponential approximation: |
1456 | // Xnew = UMDeath/UMTotal * 0.5 + Xprev |
1457 | // |
1458 | void GCInterface::NewAddMemoryPressure(UINT64 bytesAllocated) |
1459 | { |
1460 | CONTRACTL |
1461 | { |
1462 | THROWS; |
1463 | GC_TRIGGERS; |
1464 | MODE_ANY; |
1465 | } |
1466 | CONTRACTL_END; |
1467 | |
1468 | CheckCollectionCount(); |
1469 | |
1470 | UINT p = m_iteration % NEW_PRESSURE_COUNT; |
1471 | |
1472 | UINT64 newMemValue = InterlockedAdd(&m_addPressure[p], bytesAllocated); |
1473 | |
1474 | static_assert(NEW_PRESSURE_COUNT == 4, "NewAddMemoryPressure contains unrolled loops which depend on NEW_PRESSURE_COUNT" ); |
1475 | |
1476 | UINT64 add = m_addPressure[0] + m_addPressure[1] + m_addPressure[2] + m_addPressure[3] - m_addPressure[p]; |
1477 | UINT64 rem = m_remPressure[0] + m_remPressure[1] + m_remPressure[2] + m_remPressure[3] - m_remPressure[p]; |
1478 | |
1479 | STRESS_LOG4(LF_GCINFO, LL_INFO10000, "AMP Add: %I64u => added=%I64u total_added=%I64u total_removed=%I64u" , |
1480 | bytesAllocated, newMemValue, add, rem); |
1481 | |
1482 | SendEtwAddMemoryPressureEvent(bytesAllocated); |
1483 | |
1484 | if (newMemValue >= MIN_MEMORYPRESSURE_BUDGET) |
1485 | { |
1486 | UINT64 budget = MIN_MEMORYPRESSURE_BUDGET; |
1487 | |
1488 | if (m_iteration >= NEW_PRESSURE_COUNT) // wait until we have enough data points |
1489 | { |
1490 | // Adjust according to effectiveness of GC |
1491 | // Scale budget according to past m_addPressure / m_remPressure ratio |
1492 | if (add >= rem * MAX_MEMORYPRESSURE_RATIO) |
1493 | { |
1494 | budget = MIN_MEMORYPRESSURE_BUDGET * MAX_MEMORYPRESSURE_RATIO; |
1495 | } |
1496 | else if (add > rem) |
1497 | { |
1498 | CONSISTENCY_CHECK(rem != 0); |
1499 | |
1500 | // Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024) |
1501 | budget = (add * 1024 / rem) * budget / 1024; |
1502 | } |
1503 | } |
1504 | |
1505 | // If still over budget, check current managed heap size |
1506 | if (newMemValue >= budget) |
1507 | { |
1508 | IGCHeap *pGCHeap = GCHeapUtilities::GetGCHeap(); |
1509 | UINT64 heapOver3 = pGCHeap->GetCurrentObjSize() / 3; |
1510 | |
1511 | if (budget < heapOver3) // Max |
1512 | { |
1513 | budget = heapOver3; |
1514 | } |
1515 | |
1516 | if (newMemValue >= budget) |
1517 | { |
1518 | // last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time |
1519 | if ((pGCHeap->GetNow() - pGCHeap->GetLastGCStartTime(2)) > (pGCHeap->GetLastGCDuration(2) * 5)) |
1520 | { |
1521 | STRESS_LOG6(LF_GCINFO, LL_INFO10000, "AMP Budget: pressure=%I64u ? budget=%I64u (total_added=%I64u, total_removed=%I64u, mng_heap=%I64u) pos=%d" , |
1522 | newMemValue, budget, add, rem, heapOver3 * 3, m_iteration); |
1523 | |
1524 | GarbageCollectModeAny(2); |
1525 | |
1526 | CheckCollectionCount(); |
1527 | } |
1528 | } |
1529 | } |
1530 | } |
1531 | } |
1532 | |
1533 | void QCALLTYPE GCInterface::_RemoveMemoryPressure(UINT64 bytesAllocated) |
1534 | { |
1535 | QCALL_CONTRACT; |
1536 | |
1537 | BEGIN_QCALL; |
1538 | RemoveMemoryPressure(bytesAllocated); |
1539 | END_QCALL; |
1540 | } |
1541 | |
1542 | void GCInterface::RemoveMemoryPressure(UINT64 bytesAllocated) |
1543 | { |
1544 | CONTRACTL |
1545 | { |
1546 | NOTHROW; |
1547 | GC_TRIGGERS; |
1548 | MODE_ANY; |
1549 | } |
1550 | CONTRACTL_END; |
1551 | |
1552 | SendEtwRemoveMemoryPressureEvent(bytesAllocated); |
1553 | |
1554 | UINT64 newMemValue = InterlockedSub(&m_ulMemPressure, bytesAllocated); |
1555 | UINT64 new_th; |
1556 | UINT64 bytesAllocatedMax = (m_ulThreshold / 4); |
1557 | UINT64 addMethod; |
1558 | UINT64 multMethod = (m_ulThreshold - m_ulThreshold / 20); // can never underflow |
1559 | if (bytesAllocated >= bytesAllocatedMax) // protect against underflow |
1560 | { |
1561 | m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD; |
1562 | return; |
1563 | } |
1564 | else |
1565 | { |
1566 | addMethod = m_ulThreshold - bytesAllocated * 4; |
1567 | } |
1568 | |
1569 | new_th = (addMethod < multMethod) ? addMethod : multMethod; |
1570 | |
1571 | if (newMemValue <= new_th) |
1572 | { |
1573 | GCX_PREEMP(); |
1574 | CrstHolder holder(&m_MemoryPressureLock); |
1575 | if (new_th > MIN_GC_MEMORYPRESSURE_THRESHOLD) |
1576 | m_ulThreshold = new_th; |
1577 | else |
1578 | m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD; |
1579 | |
1580 | for (int i = 0; i < 3; i++) |
1581 | { |
1582 | m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i); |
1583 | } |
1584 | } |
1585 | } |
1586 | |
1587 | void GCInterface::NewRemoveMemoryPressure(UINT64 bytesAllocated) |
1588 | { |
1589 | CONTRACTL |
1590 | { |
1591 | NOTHROW; |
1592 | GC_TRIGGERS; |
1593 | MODE_ANY; |
1594 | } |
1595 | CONTRACTL_END; |
1596 | |
1597 | CheckCollectionCount(); |
1598 | |
1599 | UINT p = m_iteration % NEW_PRESSURE_COUNT; |
1600 | |
1601 | SendEtwRemoveMemoryPressureEvent(bytesAllocated); |
1602 | |
1603 | InterlockedAdd(&m_remPressure[p], bytesAllocated); |
1604 | |
1605 | STRESS_LOG2(LF_GCINFO, LL_INFO10000, "AMP Remove: %I64u => removed=%I64u" , |
1606 | bytesAllocated, m_remPressure[p]); |
1607 | } |
1608 | |
1609 | inline void GCInterface::SendEtwAddMemoryPressureEvent(UINT64 bytesAllocated) |
1610 | { |
1611 | CONTRACTL |
1612 | { |
1613 | THROWS; |
1614 | GC_TRIGGERS; |
1615 | MODE_ANY; |
1616 | } |
1617 | CONTRACTL_END; |
1618 | |
1619 | FireEtwIncreaseMemoryPressure(bytesAllocated, GetClrInstanceId()); |
1620 | } |
1621 | |
1622 | // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw. |
1623 | NOINLINE void GCInterface::SendEtwRemoveMemoryPressureEvent(UINT64 bytesAllocated) |
1624 | { |
1625 | CONTRACTL |
1626 | { |
1627 | NOTHROW; |
1628 | GC_TRIGGERS; |
1629 | MODE_ANY; |
1630 | } |
1631 | CONTRACTL_END; |
1632 | |
1633 | EX_TRY |
1634 | { |
1635 | FireEtwDecreaseMemoryPressure(bytesAllocated, GetClrInstanceId()); |
1636 | } |
1637 | EX_CATCH |
1638 | { |
1639 | // Ignore failures |
1640 | } |
1641 | EX_END_CATCH(SwallowAllExceptions) |
1642 | } |
1643 | |
1644 | // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw. |
1645 | NOINLINE void GCInterface::GarbageCollectModeAny(int generation) |
1646 | { |
1647 | CONTRACTL |
1648 | { |
1649 | THROWS; |
1650 | GC_TRIGGERS; |
1651 | MODE_ANY; |
1652 | } |
1653 | CONTRACTL_END; |
1654 | |
1655 | GCX_COOP(); |
1656 | GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, collection_non_blocking); |
1657 | } |
1658 | |
1659 | // |
1660 | // COMInterlocked |
1661 | // |
1662 | |
1663 | #include <optsmallperfcritical.h> |
1664 | |
1665 | FCIMPL2(INT32,COMInterlocked::Exchange, INT32 *location, INT32 value) |
1666 | { |
1667 | FCALL_CONTRACT; |
1668 | |
1669 | if( NULL == location) { |
1670 | FCThrow(kNullReferenceException); |
1671 | } |
1672 | |
1673 | return FastInterlockExchange((LONG *) location, value); |
1674 | } |
1675 | FCIMPLEND |
1676 | |
1677 | FCIMPL2_IV(INT64,COMInterlocked::Exchange64, INT64 *location, INT64 value) |
1678 | { |
1679 | FCALL_CONTRACT; |
1680 | |
1681 | if( NULL == location) { |
1682 | FCThrow(kNullReferenceException); |
1683 | } |
1684 | |
1685 | return FastInterlockExchangeLong((INT64 *) location, value); |
1686 | } |
1687 | FCIMPLEND |
1688 | |
1689 | FCIMPL2(LPVOID,COMInterlocked::ExchangePointer, LPVOID *location, LPVOID value) |
1690 | { |
1691 | FCALL_CONTRACT; |
1692 | |
1693 | if( NULL == location) { |
1694 | FCThrow(kNullReferenceException); |
1695 | } |
1696 | |
1697 | FCUnique(0x15); |
1698 | return FastInterlockExchangePointer(location, value); |
1699 | } |
1700 | FCIMPLEND |
1701 | |
1702 | FCIMPL3(INT32, COMInterlocked::CompareExchange, INT32* location, INT32 value, INT32 comparand) |
1703 | { |
1704 | FCALL_CONTRACT; |
1705 | |
1706 | if( NULL == location) { |
1707 | FCThrow(kNullReferenceException); |
1708 | } |
1709 | |
1710 | return FastInterlockCompareExchange((LONG*)location, value, comparand); |
1711 | } |
1712 | FCIMPLEND |
1713 | |
1714 | FCIMPL4(INT32, COMInterlocked::CompareExchangeReliableResult, INT32* location, INT32 value, INT32 comparand, CLR_BOOL* succeeded) |
1715 | { |
1716 | FCALL_CONTRACT; |
1717 | |
1718 | if( NULL == location) { |
1719 | FCThrow(kNullReferenceException); |
1720 | } |
1721 | |
1722 | INT32 result = FastInterlockCompareExchange((LONG*)location, value, comparand); |
1723 | if (result == comparand) |
1724 | *succeeded = true; |
1725 | |
1726 | return result; |
1727 | } |
1728 | FCIMPLEND |
1729 | |
1730 | FCIMPL3_IVV(INT64, COMInterlocked::CompareExchange64, INT64* location, INT64 value, INT64 comparand) |
1731 | { |
1732 | FCALL_CONTRACT; |
1733 | |
1734 | if( NULL == location) { |
1735 | FCThrow(kNullReferenceException); |
1736 | } |
1737 | |
1738 | return FastInterlockCompareExchangeLong((INT64*)location, value, comparand); |
1739 | } |
1740 | FCIMPLEND |
1741 | |
1742 | FCIMPL3(LPVOID,COMInterlocked::CompareExchangePointer, LPVOID *location, LPVOID value, LPVOID comparand) |
1743 | { |
1744 | FCALL_CONTRACT; |
1745 | |
1746 | if( NULL == location) { |
1747 | FCThrow(kNullReferenceException); |
1748 | } |
1749 | |
1750 | FCUnique(0x59); |
1751 | return FastInterlockCompareExchangePointer(location, value, comparand); |
1752 | } |
1753 | FCIMPLEND |
1754 | |
1755 | FCIMPL2_IV(float,COMInterlocked::ExchangeFloat, float *location, float value) |
1756 | { |
1757 | FCALL_CONTRACT; |
1758 | |
1759 | if( NULL == location) { |
1760 | FCThrow(kNullReferenceException); |
1761 | } |
1762 | |
1763 | LONG ret = FastInterlockExchange((LONG *) location, *(LONG*)&value); |
1764 | return *(float*)&ret; |
1765 | } |
1766 | FCIMPLEND |
1767 | |
1768 | FCIMPL2_IV(double,COMInterlocked::ExchangeDouble, double *location, double value) |
1769 | { |
1770 | FCALL_CONTRACT; |
1771 | |
1772 | if( NULL == location) { |
1773 | FCThrow(kNullReferenceException); |
1774 | } |
1775 | |
1776 | |
1777 | INT64 ret = FastInterlockExchangeLong((INT64 *) location, *(INT64*)&value); |
1778 | return *(double*)&ret; |
1779 | } |
1780 | FCIMPLEND |
1781 | |
1782 | FCIMPL3_IVV(float,COMInterlocked::CompareExchangeFloat, float *location, float value, float comparand) |
1783 | { |
1784 | FCALL_CONTRACT; |
1785 | |
1786 | if( NULL == location) { |
1787 | FCThrow(kNullReferenceException); |
1788 | } |
1789 | |
1790 | LONG ret = (LONG)FastInterlockCompareExchange((LONG*) location, *(LONG*)&value, *(LONG*)&comparand); |
1791 | return *(float*)&ret; |
1792 | } |
1793 | FCIMPLEND |
1794 | |
1795 | FCIMPL3_IVV(double,COMInterlocked::CompareExchangeDouble, double *location, double value, double comparand) |
1796 | { |
1797 | FCALL_CONTRACT; |
1798 | |
1799 | if( NULL == location) { |
1800 | FCThrow(kNullReferenceException); |
1801 | } |
1802 | |
1803 | INT64 ret = (INT64)FastInterlockCompareExchangeLong((INT64*) location, *(INT64*)&value, *(INT64*)&comparand); |
1804 | return *(double*)&ret; |
1805 | } |
1806 | FCIMPLEND |
1807 | |
1808 | FCIMPL2(LPVOID,COMInterlocked::ExchangeObject, LPVOID*location, LPVOID value) |
1809 | { |
1810 | FCALL_CONTRACT; |
1811 | |
1812 | if( NULL == location) { |
1813 | FCThrow(kNullReferenceException); |
1814 | } |
1815 | |
1816 | LPVOID ret = FastInterlockExchangePointer(location, value); |
1817 | #ifdef _DEBUG |
1818 | Thread::ObjectRefAssign((OBJECTREF *)location); |
1819 | #endif |
1820 | ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value)); |
1821 | return ret; |
1822 | } |
1823 | FCIMPLEND |
1824 | |
1825 | FCIMPL3(LPVOID,COMInterlocked::CompareExchangeObject, LPVOID *location, LPVOID value, LPVOID comparand) |
1826 | { |
1827 | FCALL_CONTRACT; |
1828 | |
1829 | if( NULL == location) { |
1830 | FCThrow(kNullReferenceException); |
1831 | } |
1832 | |
1833 | // <TODO>@todo: only set ref if is updated</TODO> |
1834 | LPVOID ret = FastInterlockCompareExchangePointer(location, value, comparand); |
1835 | if (ret == comparand) { |
1836 | #ifdef _DEBUG |
1837 | Thread::ObjectRefAssign((OBJECTREF *)location); |
1838 | #endif |
1839 | ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value)); |
1840 | } |
1841 | return ret; |
1842 | } |
1843 | FCIMPLEND |
1844 | |
1845 | FCIMPL2(INT32,COMInterlocked::ExchangeAdd32, INT32 *location, INT32 value) |
1846 | { |
1847 | FCALL_CONTRACT; |
1848 | |
1849 | if( NULL == location) { |
1850 | FCThrow(kNullReferenceException); |
1851 | } |
1852 | |
1853 | return FastInterlockExchangeAdd((LONG *) location, value); |
1854 | } |
1855 | FCIMPLEND |
1856 | |
1857 | FCIMPL2_IV(INT64,COMInterlocked::ExchangeAdd64, INT64 *location, INT64 value) |
1858 | { |
1859 | FCALL_CONTRACT; |
1860 | |
1861 | if( NULL == location) { |
1862 | FCThrow(kNullReferenceException); |
1863 | } |
1864 | |
1865 | return FastInterlockExchangeAddLong((INT64 *) location, value); |
1866 | } |
1867 | FCIMPLEND |
1868 | |
1869 | FCIMPL0(void, COMInterlocked::FCMemoryBarrier) |
1870 | { |
1871 | FCALL_CONTRACT; |
1872 | |
1873 | MemoryBarrier(); |
1874 | FC_GC_POLL(); |
1875 | } |
1876 | FCIMPLEND |
1877 | |
1878 | #include <optdefault.h> |
1879 | |
1880 | void QCALLTYPE COMInterlocked::MemoryBarrierProcessWide() |
1881 | { |
1882 | QCALL_CONTRACT; |
1883 | |
1884 | FlushProcessWriteBuffers(); |
1885 | } |
1886 | |
1887 | static BOOL HasOverriddenMethod(MethodTable* mt, MethodTable* classMT, WORD methodSlot) |
1888 | { |
1889 | CONTRACTL{ |
1890 | NOTHROW; |
1891 | GC_NOTRIGGER; |
1892 | MODE_ANY; |
1893 | SO_TOLERANT; |
1894 | } CONTRACTL_END; |
1895 | |
1896 | _ASSERTE(mt != NULL); |
1897 | _ASSERTE(classMT != NULL); |
1898 | _ASSERTE(methodSlot != 0); |
1899 | |
1900 | PCODE actual = mt->GetRestoredSlot(methodSlot); |
1901 | PCODE base = classMT->GetRestoredSlot(methodSlot); |
1902 | |
1903 | if (actual == base) |
1904 | { |
1905 | return FALSE; |
1906 | } |
1907 | |
1908 | if (!classMT->IsZapped()) |
1909 | { |
1910 | // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs |
1911 | // to detect match reliably |
1912 | if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base)) |
1913 | { |
1914 | return FALSE; |
1915 | } |
1916 | } |
1917 | |
1918 | return TRUE; |
1919 | } |
1920 | |
1921 | static BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt) |
1922 | { |
1923 | CONTRACTL |
1924 | { |
1925 | THROWS; |
1926 | GC_TRIGGERS; |
1927 | MODE_COOPERATIVE; |
1928 | } CONTRACTL_END; |
1929 | |
1930 | _ASSERTE(mt != NULL); |
1931 | |
1932 | if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode()) |
1933 | { |
1934 | return mt->CanCompareBitsOrUseFastGetHashCode(); |
1935 | } |
1936 | |
1937 | if (mt->ContainsPointers() |
1938 | || mt->IsNotTightlyPacked()) |
1939 | { |
1940 | mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode(); |
1941 | return FALSE; |
1942 | } |
1943 | |
1944 | MethodTable* valueTypeMT = MscorlibBinder::GetClass(CLASS__VALUE_TYPE); |
1945 | WORD slotEquals = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__EQUALS)->GetSlot(); |
1946 | WORD slotGetHashCode = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__GET_HASH_CODE)->GetSlot(); |
1947 | |
1948 | // Check the input type. |
1949 | if (HasOverriddenMethod(mt, valueTypeMT, slotEquals) |
1950 | || HasOverriddenMethod(mt, valueTypeMT, slotGetHashCode)) |
1951 | { |
1952 | mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode(); |
1953 | |
1954 | // If overridden Equals or GetHashCode found, stop searching further. |
1955 | return FALSE; |
1956 | } |
1957 | |
1958 | BOOL canCompareBitsOrUseFastGetHashCode = TRUE; |
1959 | |
1960 | // The type itself did not override Equals or GetHashCode, go for its fields. |
1961 | ApproxFieldDescIterator iter = ApproxFieldDescIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS); |
1962 | for (FieldDesc* pField = iter.Next(); pField != NULL; pField = iter.Next()) |
1963 | { |
1964 | if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE) |
1965 | { |
1966 | // Check current field type. |
1967 | MethodTable* fieldMethodTable = pField->GetApproxFieldTypeHandleThrowing().GetMethodTable(); |
1968 | if (!CanCompareBitsOrUseFastGetHashCode(fieldMethodTable)) |
1969 | { |
1970 | canCompareBitsOrUseFastGetHashCode = FALSE; |
1971 | break; |
1972 | } |
1973 | } |
1974 | else if (pField->GetFieldType() == ELEMENT_TYPE_R8 |
1975 | || pField->GetFieldType() == ELEMENT_TYPE_R4) |
1976 | { |
1977 | // We have double/single field, cannot compare in fast path. |
1978 | canCompareBitsOrUseFastGetHashCode = FALSE; |
1979 | break; |
1980 | } |
1981 | } |
1982 | |
1983 | // We've gone through all instance fields. It's time to cache the result. |
1984 | // Note SetCanCompareBitsOrUseFastGetHashCode(BOOL) ensures the checked flag |
1985 | // and canCompare flag being set atomically to avoid race. |
1986 | mt->SetCanCompareBitsOrUseFastGetHashCode(canCompareBitsOrUseFastGetHashCode); |
1987 | |
1988 | return canCompareBitsOrUseFastGetHashCode; |
1989 | } |
1990 | |
1991 | NOINLINE static FC_BOOL_RET CanCompareBitsHelper(MethodTable* mt, OBJECTREF objRef) |
1992 | { |
1993 | FC_INNER_PROLOG(ValueTypeHelper::CanCompareBits); |
1994 | |
1995 | _ASSERTE(mt != NULL); |
1996 | _ASSERTE(objRef != NULL); |
1997 | |
1998 | BOOL ret = FALSE; |
1999 | |
2000 | HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef); |
2001 | |
2002 | ret = CanCompareBitsOrUseFastGetHashCode(mt); |
2003 | |
2004 | HELPER_METHOD_FRAME_END(); |
2005 | FC_INNER_EPILOG(); |
2006 | |
2007 | FC_RETURN_BOOL(ret); |
2008 | } |
2009 | |
2010 | // Return true if the valuetype does not contain pointer, is tightly packed, |
2011 | // does not have floating point number field and does not override Equals method. |
2012 | FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj) |
2013 | { |
2014 | FCALL_CONTRACT; |
2015 | |
2016 | _ASSERTE(obj != NULL); |
2017 | MethodTable* mt = obj->GetMethodTable(); |
2018 | |
2019 | if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode()) |
2020 | { |
2021 | FC_RETURN_BOOL(mt->CanCompareBitsOrUseFastGetHashCode()); |
2022 | } |
2023 | |
2024 | OBJECTREF objRef(obj); |
2025 | |
2026 | FC_INNER_RETURN(FC_BOOL_RET, CanCompareBitsHelper(mt, objRef)); |
2027 | } |
2028 | FCIMPLEND |
2029 | |
2030 | FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1, Object* obj2) |
2031 | { |
2032 | FCALL_CONTRACT; |
2033 | |
2034 | _ASSERTE(obj1 != NULL); |
2035 | _ASSERTE(obj2 != NULL); |
2036 | _ASSERTE(!obj1->GetMethodTable()->ContainsPointers()); |
2037 | _ASSERTE(obj1->GetSize() == obj2->GetSize()); |
2038 | |
2039 | TypeHandle pTh = obj1->GetTypeHandle(); |
2040 | |
2041 | FC_RETURN_BOOL(memcmp(obj1->GetData(),obj2->GetData(),pTh.GetSize()) == 0); |
2042 | } |
2043 | FCIMPLEND |
2044 | |
2045 | static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef) |
2046 | { |
2047 | CONTRACTL |
2048 | { |
2049 | NOTHROW; |
2050 | GC_NOTRIGGER; |
2051 | MODE_COOPERATIVE; |
2052 | SO_TOLERANT; |
2053 | } CONTRACTL_END; |
2054 | |
2055 | INT32 hashCode = 0; |
2056 | INT32 *pObj = (INT32*)pObjRef; |
2057 | |
2058 | // this is a struct with no refs and no "strange" offsets, just go through the obj and xor the bits |
2059 | INT32 size = mt->GetNumInstanceFieldBytes(); |
2060 | for (INT32 i = 0; i < (INT32)(size / sizeof(INT32)); i++) |
2061 | hashCode ^= *pObj++; |
2062 | |
2063 | return hashCode; |
2064 | } |
2065 | |
2066 | static INT32 RegularGetValueTypeHashCode(MethodTable *mt, void *pObjRef) |
2067 | { |
2068 | CONTRACTL |
2069 | { |
2070 | THROWS; |
2071 | GC_TRIGGERS; |
2072 | MODE_COOPERATIVE; |
2073 | } CONTRACTL_END; |
2074 | |
2075 | INT32 hashCode = 0; |
2076 | |
2077 | GCPROTECT_BEGININTERIOR(pObjRef); |
2078 | |
2079 | BOOL canUseFastGetHashCodeHelper = FALSE; |
2080 | if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode()) |
2081 | { |
2082 | canUseFastGetHashCodeHelper = mt->CanCompareBitsOrUseFastGetHashCode(); |
2083 | } |
2084 | else |
2085 | { |
2086 | canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(mt); |
2087 | } |
2088 | |
2089 | // While we shouln't get here directly from ValueTypeHelper::GetHashCode, if we recurse we need to |
2090 | // be able to handle getting the hashcode for an embedded structure whose hashcode is computed by the fast path. |
2091 | if (canUseFastGetHashCodeHelper) |
2092 | { |
2093 | hashCode = FastGetValueTypeHashCodeHelper(mt, pObjRef); |
2094 | } |
2095 | else |
2096 | { |
2097 | // it's looking ugly so we'll use the old behavior in managed code. Grab the first non-null |
2098 | // field and return its hash code or 'it' as hash code |
2099 | // <TODO> Note that the old behavior has already been broken for value types |
2100 | // that is qualified for CanUseFastGetHashCodeHelper. So maybe we should |
2101 | // change the implementation here to use all fields instead of just the 1st one. |
2102 | // </TODO> |
2103 | // |
2104 | // <TODO> check this approximation - we may be losing exact type information </TODO> |
2105 | ApproxFieldDescIterator fdIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS); |
2106 | |
2107 | FieldDesc *field; |
2108 | while ((field = fdIterator.Next()) != NULL) |
2109 | { |
2110 | _ASSERTE(!field->IsRVA()); |
2111 | if (field->IsObjRef()) |
2112 | { |
2113 | // if we get an object reference we get the hash code out of that |
2114 | if (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()) != NULL) |
2115 | { |
2116 | PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__OBJECT__GET_HASH_CODE, (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()))); |
2117 | DECLARE_ARGHOLDER_ARRAY(args, 1); |
2118 | args[ARGNUM_0] = PTR_TO_ARGHOLDER(*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe())); |
2119 | CALL_MANAGED_METHOD(hashCode, INT32, args); |
2120 | } |
2121 | else |
2122 | { |
2123 | // null object reference, try next |
2124 | continue; |
2125 | } |
2126 | } |
2127 | else |
2128 | { |
2129 | CorElementType fieldType = field->GetFieldType(); |
2130 | if (fieldType == ELEMENT_TYPE_R8) |
2131 | { |
2132 | PREPARE_NONVIRTUAL_CALLSITE(METHOD__DOUBLE__GET_HASH_CODE); |
2133 | DECLARE_ARGHOLDER_ARRAY(args, 1); |
2134 | args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe())); |
2135 | CALL_MANAGED_METHOD(hashCode, INT32, args); |
2136 | } |
2137 | else if (fieldType == ELEMENT_TYPE_R4) |
2138 | { |
2139 | PREPARE_NONVIRTUAL_CALLSITE(METHOD__SINGLE__GET_HASH_CODE); |
2140 | DECLARE_ARGHOLDER_ARRAY(args, 1); |
2141 | args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe())); |
2142 | CALL_MANAGED_METHOD(hashCode, INT32, args); |
2143 | } |
2144 | else if (fieldType != ELEMENT_TYPE_VALUETYPE) |
2145 | { |
2146 | UINT fieldSize = field->LoadSize(); |
2147 | INT32 *pValue = (INT32*)((BYTE *)pObjRef + field->GetOffsetUnsafe()); |
2148 | for (INT32 j = 0; j < (INT32)(fieldSize / sizeof(INT32)); j++) |
2149 | hashCode ^= *pValue++; |
2150 | } |
2151 | else |
2152 | { |
2153 | // got another value type. Get the type |
2154 | TypeHandle fieldTH = field->GetFieldTypeHandleThrowing(); |
2155 | _ASSERTE(!fieldTH.IsNull()); |
2156 | hashCode = RegularGetValueTypeHashCode(fieldTH.GetMethodTable(), (BYTE *)pObjRef + field->GetOffsetUnsafe()); |
2157 | } |
2158 | } |
2159 | break; |
2160 | } |
2161 | } |
2162 | |
2163 | GCPROTECT_END(); |
2164 | |
2165 | return hashCode; |
2166 | } |
2167 | |
2168 | // The default implementation of GetHashCode() for all value types. |
2169 | // Note that this implementation reveals the value of the fields. |
2170 | // So if the value type contains any sensitive information it should |
2171 | // implement its own GetHashCode(). |
2172 | FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE) |
2173 | { |
2174 | FCALL_CONTRACT; |
2175 | |
2176 | if (objUNSAFE == NULL) |
2177 | FCThrow(kNullReferenceException); |
2178 | |
2179 | OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE); |
2180 | VALIDATEOBJECTREF(obj); |
2181 | |
2182 | INT32 hashCode = 0; |
2183 | MethodTable *pMT = objUNSAFE->GetMethodTable(); |
2184 | |
2185 | // We don't want to expose the method table pointer in the hash code |
2186 | // Let's use the typeID instead. |
2187 | UINT32 typeID = pMT->LookupTypeID(); |
2188 | if (typeID == TypeIDProvider::INVALID_TYPE_ID) |
2189 | { |
2190 | // If the typeID has yet to be generated, fall back to GetTypeID |
2191 | // This only needs to be done once per MethodTable |
2192 | HELPER_METHOD_FRAME_BEGIN_RET_1(obj); |
2193 | typeID = pMT->GetTypeID(); |
2194 | HELPER_METHOD_FRAME_END(); |
2195 | } |
2196 | |
2197 | // To get less colliding and more evenly distributed hash codes, |
2198 | // we munge the class index with two big prime numbers |
2199 | hashCode = typeID * 711650207 + 2506965631U; |
2200 | |
2201 | BOOL canUseFastGetHashCodeHelper = FALSE; |
2202 | if (pMT->HasCheckedCanCompareBitsOrUseFastGetHashCode()) |
2203 | { |
2204 | canUseFastGetHashCodeHelper = pMT->CanCompareBitsOrUseFastGetHashCode(); |
2205 | } |
2206 | else |
2207 | { |
2208 | HELPER_METHOD_FRAME_BEGIN_RET_1(obj); |
2209 | canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(pMT); |
2210 | HELPER_METHOD_FRAME_END(); |
2211 | } |
2212 | |
2213 | if (canUseFastGetHashCodeHelper) |
2214 | { |
2215 | hashCode ^= FastGetValueTypeHashCodeHelper(pMT, obj->UnBox()); |
2216 | } |
2217 | else |
2218 | { |
2219 | HELPER_METHOD_FRAME_BEGIN_RET_1(obj); |
2220 | hashCode ^= RegularGetValueTypeHashCode(pMT, obj->UnBox()); |
2221 | HELPER_METHOD_FRAME_END(); |
2222 | } |
2223 | |
2224 | return hashCode; |
2225 | } |
2226 | FCIMPLEND |
2227 | |
2228 | static LONG s_dwSeed; |
2229 | |
2230 | FCIMPL1(INT32, ValueTypeHelper::GetHashCodeOfPtr, LPVOID ptr) |
2231 | { |
2232 | FCALL_CONTRACT; |
2233 | |
2234 | INT32 hashCode = (INT32)((INT64)(ptr)); |
2235 | |
2236 | if (hashCode == 0) |
2237 | { |
2238 | return 0; |
2239 | } |
2240 | |
2241 | DWORD dwSeed = s_dwSeed; |
2242 | |
2243 | // Initialize s_dwSeed lazily |
2244 | if (dwSeed == 0) |
2245 | { |
2246 | // We use the first non-0 pointer as the seed, all hashcodes will be based off that. |
2247 | // This is to make sure that we only reveal relative memory addresses and never absolute ones. |
2248 | dwSeed = hashCode; |
2249 | InterlockedCompareExchange(&s_dwSeed, dwSeed, 0); |
2250 | dwSeed = s_dwSeed; |
2251 | } |
2252 | _ASSERTE(dwSeed != 0); |
2253 | |
2254 | return hashCode - dwSeed; |
2255 | } |
2256 | FCIMPLEND |
2257 | |
2258 | static MethodTable * g_pStreamMT; |
2259 | static WORD g_slotBeginRead, g_slotEndRead; |
2260 | static WORD g_slotBeginWrite, g_slotEndWrite; |
2261 | |
2262 | static bool HasOverriddenStreamMethod(MethodTable * pMT, WORD slot) |
2263 | { |
2264 | CONTRACTL{ |
2265 | NOTHROW; |
2266 | GC_NOTRIGGER; |
2267 | MODE_ANY; |
2268 | SO_TOLERANT; |
2269 | } CONTRACTL_END; |
2270 | |
2271 | PCODE actual = pMT->GetRestoredSlot(slot); |
2272 | PCODE base = g_pStreamMT->GetRestoredSlot(slot); |
2273 | if (actual == base) |
2274 | return false; |
2275 | |
2276 | if (!g_pStreamMT->IsZapped()) |
2277 | { |
2278 | // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs |
2279 | // to detect match reliably |
2280 | if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base)) |
2281 | return false; |
2282 | } |
2283 | |
2284 | return true; |
2285 | } |
2286 | |
2287 | FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndRead, Object *stream) |
2288 | { |
2289 | FCALL_CONTRACT; |
2290 | |
2291 | if (stream == NULL) |
2292 | FC_RETURN_BOOL(TRUE); |
2293 | |
2294 | if (g_pStreamMT == NULL || g_slotBeginRead == 0 || g_slotEndRead == 0) |
2295 | { |
2296 | HELPER_METHOD_FRAME_BEGIN_RET_1(stream); |
2297 | g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM); |
2298 | g_slotBeginRead = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_READ)->GetSlot(); |
2299 | g_slotEndRead = MscorlibBinder::GetMethod(METHOD__STREAM__END_READ)->GetSlot(); |
2300 | HELPER_METHOD_FRAME_END(); |
2301 | } |
2302 | |
2303 | MethodTable * pMT = stream->GetMethodTable(); |
2304 | |
2305 | FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead)); |
2306 | } |
2307 | FCIMPLEND |
2308 | |
2309 | FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndWrite, Object *stream) |
2310 | { |
2311 | FCALL_CONTRACT; |
2312 | |
2313 | if (stream == NULL) |
2314 | FC_RETURN_BOOL(TRUE); |
2315 | |
2316 | if (g_pStreamMT == NULL || g_slotBeginWrite == 0 || g_slotEndWrite == 0) |
2317 | { |
2318 | HELPER_METHOD_FRAME_BEGIN_RET_1(stream); |
2319 | g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM); |
2320 | g_slotBeginWrite = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_WRITE)->GetSlot(); |
2321 | g_slotEndWrite = MscorlibBinder::GetMethod(METHOD__STREAM__END_WRITE)->GetSlot(); |
2322 | HELPER_METHOD_FRAME_END(); |
2323 | } |
2324 | |
2325 | MethodTable * pMT = stream->GetMethodTable(); |
2326 | |
2327 | FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginWrite) || HasOverriddenStreamMethod(pMT, g_slotEndWrite)); |
2328 | } |
2329 | FCIMPLEND |
2330 | |