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 | #include "common.h" |
10 | #include "dynamicmethod.h" |
11 | #include "object.h" |
12 | #include "method.hpp" |
13 | #include "comdelegate.h" |
14 | #include "field.h" |
15 | #include "contractimpl.h" |
16 | #include "nibblemapmacros.h" |
17 | #include "stringliteralmap.h" |
18 | #include "virtualcallstub.h" |
19 | |
20 | |
21 | #ifndef DACCESS_COMPILE |
22 | |
23 | // get the method table for dynamic methods |
24 | DynamicMethodTable* DomainFile::GetDynamicMethodTable() |
25 | { |
26 | CONTRACT (DynamicMethodTable*) |
27 | { |
28 | INSTANCE_CHECK; |
29 | THROWS; |
30 | GC_TRIGGERS; |
31 | MODE_ANY; |
32 | INJECT_FAULT(COMPlusThrowOM()); |
33 | POSTCONDITION(CheckPointer(m_pDynamicMethodTable)); |
34 | } |
35 | CONTRACT_END; |
36 | |
37 | if (!m_pDynamicMethodTable) |
38 | DynamicMethodTable::CreateDynamicMethodTable(&m_pDynamicMethodTable, GetModule(), GetAppDomain()); |
39 | |
40 | |
41 | RETURN m_pDynamicMethodTable; |
42 | } |
43 | |
44 | void ReleaseDynamicMethodTable(DynamicMethodTable *pDynMT) |
45 | { |
46 | WRAPPER_NO_CONTRACT; |
47 | if (pDynMT) |
48 | { |
49 | pDynMT->Destroy(); |
50 | } |
51 | } |
52 | |
53 | void DynamicMethodTable::CreateDynamicMethodTable(DynamicMethodTable **ppLocation, Module *pModule, AppDomain *pDomain) |
54 | { |
55 | CONTRACT_VOID |
56 | { |
57 | THROWS; |
58 | GC_TRIGGERS; |
59 | MODE_ANY; |
60 | INJECT_FAULT(COMPlusThrowOM()); |
61 | PRECONDITION(CheckPointer(ppLocation)); |
62 | PRECONDITION(CheckPointer(pModule)); |
63 | POSTCONDITION(CheckPointer(*ppLocation)); |
64 | } |
65 | CONTRACT_END; |
66 | |
67 | AllocMemTracker amt; |
68 | |
69 | LoaderHeap* pHeap = pDomain->GetHighFrequencyHeap(); |
70 | _ASSERTE(pHeap); |
71 | |
72 | if (*ppLocation) RETURN; |
73 | |
74 | DynamicMethodTable* pDynMT = (DynamicMethodTable*) |
75 | amt.Track(pHeap->AllocMem(S_SIZE_T(sizeof(DynamicMethodTable)))); |
76 | |
77 | // Note: Memory allocated on loader heap is zero filled |
78 | // memset((void*)pDynMT, 0, sizeof(DynamicMethodTable)); |
79 | |
80 | if (*ppLocation) RETURN; |
81 | |
82 | LOG((LF_BCL, LL_INFO100, "Level2 - Creating DynamicMethodTable {0x%p}...\n" , pDynMT)); |
83 | |
84 | Holder<DynamicMethodTable*, DoNothing, ReleaseDynamicMethodTable> dynMTHolder(pDynMT); |
85 | pDynMT->m_Crst.Init(CrstDynamicMT); |
86 | pDynMT->m_Module = pModule; |
87 | pDynMT->m_pDomain = pDomain; |
88 | pDynMT->MakeMethodTable(&amt); |
89 | |
90 | if (*ppLocation) RETURN; |
91 | |
92 | if (FastInterlockCompareExchangePointer(ppLocation, pDynMT, NULL) != NULL) |
93 | { |
94 | LOG((LF_BCL, LL_INFO100, "Level2 - Another thread got here first - deleting DynamicMethodTable {0x%p}...\n" , pDynMT)); |
95 | RETURN; |
96 | } |
97 | |
98 | dynMTHolder.SuppressRelease(); |
99 | |
100 | amt.SuppressRelease(); |
101 | LOG((LF_BCL, LL_INFO10, "Level1 - DynamicMethodTable created {0x%p}...\n" , pDynMT)); |
102 | RETURN; |
103 | } |
104 | |
105 | void DynamicMethodTable::MakeMethodTable(AllocMemTracker *pamTracker) |
106 | { |
107 | CONTRACTL |
108 | { |
109 | THROWS; |
110 | GC_TRIGGERS; |
111 | MODE_ANY; |
112 | INJECT_FAULT(COMPlusThrowOM()); |
113 | } |
114 | CONTRACTL_END; |
115 | |
116 | m_pMethodTable = CreateMinimalMethodTable(m_Module, m_pDomain->GetHighFrequencyHeap(), pamTracker); |
117 | } |
118 | |
119 | void DynamicMethodTable::Destroy() |
120 | { |
121 | CONTRACTL |
122 | { |
123 | NOTHROW; |
124 | GC_TRIGGERS; |
125 | MODE_ANY; |
126 | } |
127 | CONTRACTL_END; |
128 | |
129 | // Go over all DynamicMethodDescs and make sure that they are destroyed |
130 | |
131 | if (m_pMethodTable != NULL) |
132 | { |
133 | MethodTable::IntroducedMethodIterator it(m_pMethodTable); |
134 | for (; it.IsValid(); it.Next()) |
135 | { |
136 | DynamicMethodDesc *pMD = (DynamicMethodDesc*)it.GetMethodDesc(); |
137 | pMD->Destroy(TRUE /* fDomainUnload */); |
138 | } |
139 | } |
140 | |
141 | m_Crst.Destroy(); |
142 | LOG((LF_BCL, LL_INFO10, "Level1 - DynamicMethodTable destroyed {0x%p}\n" , this)); |
143 | } |
144 | |
145 | void DynamicMethodTable::AddMethodsToList() |
146 | { |
147 | CONTRACT_VOID |
148 | { |
149 | THROWS; |
150 | GC_TRIGGERS; |
151 | MODE_ANY; |
152 | INJECT_FAULT(COMPlusThrowOM()); |
153 | } |
154 | CONTRACT_END; |
155 | |
156 | AllocMemTracker amt; |
157 | |
158 | LoaderHeap* pHeap = m_pDomain->GetHighFrequencyHeap(); |
159 | _ASSERTE(pHeap); |
160 | |
161 | // |
162 | // allocate as many chunks as needed to hold the methods |
163 | // |
164 | MethodDescChunk* pChunk = MethodDescChunk::CreateChunk(pHeap, 0 /* one chunk of maximum size */, |
165 | mcDynamic, TRUE /* fNonVtableSlot */, TRUE /* fNativeCodeSlot */, FALSE /* fComPlusCallInfo */, m_pMethodTable, &amt); |
166 | if (m_DynamicMethodList) RETURN; |
167 | |
168 | int methodCount = pChunk->GetCount(); |
169 | |
170 | BYTE* pResolvers = (BYTE*)amt.Track(pHeap->AllocMem(S_SIZE_T(sizeof(LCGMethodResolver)) * S_SIZE_T(methodCount))); |
171 | if (m_DynamicMethodList) RETURN; |
172 | |
173 | DynamicMethodDesc *pNewMD = (DynamicMethodDesc *)pChunk->GetFirstMethodDesc(); |
174 | DynamicMethodDesc *pPrevMD = NULL; |
175 | // now go through all the methods in the chunk and link them |
176 | for(int i = 0; i < methodCount; i++) |
177 | { |
178 | _ASSERTE(pNewMD->GetClassification() == mcDynamic); |
179 | |
180 | pNewMD->SetMemberDef(0); |
181 | pNewMD->SetSlot(MethodTable::NO_SLOT); // we can't ever use the slot for dynamic methods |
182 | pNewMD->SetStatic(); |
183 | |
184 | pNewMD->m_dwExtendedFlags = mdPublic | mdStatic | DynamicMethodDesc::nomdLCGMethod; |
185 | |
186 | LCGMethodResolver* pResolver = new (pResolvers) LCGMethodResolver(); |
187 | pResolver->m_pDynamicMethod = pNewMD; |
188 | pResolver->m_DynamicMethodTable = this; |
189 | pNewMD->m_pResolver = pResolver; |
190 | |
191 | pNewMD->SetTemporaryEntryPoint(m_pDomain->GetLoaderAllocator(), &amt); |
192 | |
193 | #ifdef _DEBUG |
194 | pNewMD->m_pDebugMethodTable.SetValue(m_pMethodTable); |
195 | #endif |
196 | |
197 | if (pPrevMD) |
198 | { |
199 | pPrevMD->GetLCGMethodResolver()->m_next = pNewMD; |
200 | } |
201 | pPrevMD = pNewMD; |
202 | pNewMD = (DynamicMethodDesc *)(dac_cast<TADDR>(pNewMD) + pNewMD->SizeOf()); |
203 | |
204 | pResolvers += sizeof(LCGMethodResolver); |
205 | } |
206 | |
207 | if (m_DynamicMethodList) RETURN; |
208 | |
209 | { |
210 | // publish method list and method table |
211 | LockHolder lh(this); |
212 | if (m_DynamicMethodList) RETURN; |
213 | |
214 | // publish the new method descs on the method table |
215 | m_pMethodTable->GetClass()->AddChunk(pChunk); |
216 | m_DynamicMethodList = (DynamicMethodDesc*)pChunk->GetFirstMethodDesc(); |
217 | } |
218 | |
219 | amt.SuppressRelease(); |
220 | } |
221 | |
222 | DynamicMethodDesc* DynamicMethodTable::GetDynamicMethod(BYTE *psig, DWORD sigSize, PTR_CUTF8 name) |
223 | { |
224 | CONTRACT (DynamicMethodDesc*) |
225 | { |
226 | INSTANCE_CHECK; |
227 | THROWS; |
228 | GC_TRIGGERS; |
229 | MODE_ANY; |
230 | INJECT_FAULT(COMPlusThrowOM()); |
231 | PRECONDITION(CheckPointer(psig)); |
232 | PRECONDITION(sigSize > 0); |
233 | POSTCONDITION(CheckPointer(RETVAL)); |
234 | } |
235 | CONTRACT_END; |
236 | |
237 | LOG((LF_BCL, LL_INFO10000, "Level4 - Getting DynamicMethod\n" )); |
238 | |
239 | DynamicMethodDesc *pNewMD = NULL; |
240 | |
241 | for (;;) |
242 | { |
243 | { |
244 | LockHolder lh(this); |
245 | pNewMD = m_DynamicMethodList; |
246 | if (pNewMD) |
247 | { |
248 | m_DynamicMethodList = pNewMD->GetLCGMethodResolver()->m_next; |
249 | #ifdef _DEBUG |
250 | m_Used++; |
251 | #endif |
252 | break; |
253 | } |
254 | } |
255 | |
256 | LOG((LF_BCL, LL_INFO1000, "Level4 - DynamicMethod unavailable\n" )); |
257 | |
258 | // need to create more methoddescs |
259 | AddMethodsToList(); |
260 | } |
261 | _ASSERTE(pNewMD != NULL); |
262 | |
263 | // Reset the method desc into pristine state |
264 | |
265 | // Note: Reset has THROWS contract since it may allocate jump stub. It will never throw here |
266 | // since it will always reuse the existing jump stub. |
267 | pNewMD->Reset(); |
268 | |
269 | LOG((LF_BCL, LL_INFO1000, "Level3 - DynamicMethod obtained {0x%p} (used %d)\n" , pNewMD, m_Used)); |
270 | |
271 | // the store sig part of the method desc |
272 | pNewMD->SetStoredMethodSig((PCCOR_SIGNATURE)psig, sigSize); |
273 | // the dynamic part of the method desc |
274 | pNewMD->m_pszMethodName.SetValueMaybeNull(name); |
275 | |
276 | pNewMD->m_dwExtendedFlags = mdPublic | mdStatic | DynamicMethodDesc::nomdLCGMethod; |
277 | |
278 | #ifdef _DEBUG |
279 | pNewMD->m_pszDebugMethodName = name; |
280 | pNewMD->m_pszDebugClassName = (LPUTF8)"dynamicclass" ; |
281 | pNewMD->m_pszDebugMethodSignature = "DynamicMethod Signature not available" ; |
282 | #endif // _DEBUG |
283 | |
284 | #ifdef HAVE_GCCOVER |
285 | pNewMD->m_GcCover = NULL; |
286 | #endif |
287 | |
288 | pNewMD->SetNotInline(TRUE); |
289 | pNewMD->GetLCGMethodResolver()->Reset(); |
290 | |
291 | RETURN pNewMD; |
292 | } |
293 | |
294 | void DynamicMethodTable::LinkMethod(DynamicMethodDesc *pMethod) |
295 | { |
296 | CONTRACT_VOID |
297 | { |
298 | NOTHROW; |
299 | GC_TRIGGERS; |
300 | MODE_ANY; |
301 | PRECONDITION(CheckPointer(pMethod)); |
302 | } |
303 | CONTRACT_END; |
304 | |
305 | LOG((LF_BCL, LL_INFO10000, "Level4 - Returning DynamicMethod to free list {0x%p} (used %d)\n" , pMethod, m_Used)); |
306 | { |
307 | LockHolder lh(this); |
308 | pMethod->GetLCGMethodResolver()->m_next = m_DynamicMethodList; |
309 | m_DynamicMethodList = pMethod; |
310 | #ifdef _DEBUG |
311 | m_Used--; |
312 | #endif |
313 | } |
314 | |
315 | RETURN; |
316 | } |
317 | |
318 | |
319 | // |
320 | // CodeHeap implementation |
321 | // |
322 | HeapList* HostCodeHeap::CreateCodeHeap(CodeHeapRequestInfo *pInfo, EEJitManager *pJitManager) |
323 | { |
324 | CONTRACT (HeapList*) |
325 | { |
326 | THROWS; |
327 | GC_NOTRIGGER; |
328 | MODE_ANY; |
329 | INJECT_FAULT(COMPlusThrowOM()); |
330 | POSTCONDITION((RETVAL != NULL) || !pInfo->getThrowOnOutOfMemoryWithinRange()); |
331 | } |
332 | CONTRACT_END; |
333 | |
334 | NewHolder<HostCodeHeap> pCodeHeap(new HostCodeHeap(pJitManager)); |
335 | |
336 | HeapList *pHp = pCodeHeap->InitializeHeapList(pInfo); |
337 | if (pHp == NULL) |
338 | { |
339 | _ASSERTE(!pInfo->getThrowOnOutOfMemoryWithinRange()); |
340 | RETURN NULL; |
341 | } |
342 | |
343 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap creation {0x%p} - base addr 0x%p, size available 0x%p, nibble map ptr 0x%p\n" , |
344 | (HostCodeHeap*)pCodeHeap, pCodeHeap->m_pBaseAddr, pCodeHeap->m_TotalBytesAvailable, pCodeHeap->m_pHeapList->pHdrMap)); |
345 | |
346 | pCodeHeap.SuppressRelease(); |
347 | |
348 | LOG((LF_BCL, LL_INFO10, "Level1 - CodeHeap created {0x%p}\n" , (HostCodeHeap*)pCodeHeap)); |
349 | RETURN pHp; |
350 | } |
351 | |
352 | HostCodeHeap::HostCodeHeap(EEJitManager *pJitManager) |
353 | { |
354 | CONTRACTL |
355 | { |
356 | THROWS; |
357 | GC_NOTRIGGER; |
358 | MODE_ANY; |
359 | INJECT_FAULT(COMPlusThrowOM()); |
360 | } |
361 | CONTRACTL_END; |
362 | |
363 | m_pBaseAddr = NULL; |
364 | m_pLastAvailableCommittedAddr = NULL; |
365 | m_TotalBytesAvailable = 0; |
366 | m_ApproximateLargestBlock = 0; |
367 | m_AllocationCount = 0; |
368 | m_pHeapList = NULL; |
369 | m_pJitManager = (PTR_EEJitManager)pJitManager; |
370 | m_pFreeList = NULL; |
371 | m_pAllocator = NULL; |
372 | m_pNextHeapToRelease = NULL; |
373 | } |
374 | |
375 | HostCodeHeap::~HostCodeHeap() |
376 | { |
377 | LIMITED_METHOD_CONTRACT; |
378 | |
379 | if (m_pHeapList != NULL && m_pHeapList->pHdrMap != NULL) |
380 | delete[] m_pHeapList->pHdrMap; |
381 | |
382 | if (m_pBaseAddr) |
383 | ClrVirtualFree(m_pBaseAddr, 0, MEM_RELEASE); |
384 | LOG((LF_BCL, LL_INFO10, "Level1 - CodeHeap destroyed {0x%p}\n" , this)); |
385 | } |
386 | |
387 | HeapList* HostCodeHeap::InitializeHeapList(CodeHeapRequestInfo *pInfo) |
388 | { |
389 | CONTRACTL |
390 | { |
391 | THROWS; |
392 | GC_NOTRIGGER; |
393 | MODE_ANY; |
394 | } |
395 | CONTRACTL_END; |
396 | |
397 | size_t ReserveBlockSize = pInfo->getRequestSize(); |
398 | |
399 | // Add TrackAllocation, HeapList and very conservative padding to make sure we have enough for the allocation |
400 | ReserveBlockSize += sizeof(TrackAllocation) + sizeof(HeapList) + HOST_CODEHEAP_SIZE_ALIGN + 0x100; |
401 | |
402 | // reserve ReserveBlockSize rounded-up to VIRTUAL_ALLOC_RESERVE_GRANULARITY of memory |
403 | ReserveBlockSize = ALIGN_UP(ReserveBlockSize, VIRTUAL_ALLOC_RESERVE_GRANULARITY); |
404 | |
405 | if (pInfo->m_loAddr != NULL || pInfo->m_hiAddr != NULL) |
406 | { |
407 | m_pBaseAddr = ClrVirtualAllocWithinRange(pInfo->m_loAddr, pInfo->m_hiAddr, |
408 | ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS); |
409 | if (!m_pBaseAddr) |
410 | { |
411 | if (pInfo->getThrowOnOutOfMemoryWithinRange()) |
412 | ThrowOutOfMemoryWithinRange(); |
413 | return NULL; |
414 | } |
415 | } |
416 | else |
417 | { |
418 | // top up the ReserveBlockSize to suggested minimum |
419 | ReserveBlockSize = max(ReserveBlockSize, pInfo->getReserveSize()); |
420 | |
421 | m_pBaseAddr = ClrVirtualAllocExecutable(ReserveBlockSize, MEM_RESERVE, PAGE_NOACCESS); |
422 | if (!m_pBaseAddr) |
423 | ThrowOutOfMemory(); |
424 | } |
425 | |
426 | m_pLastAvailableCommittedAddr = m_pBaseAddr; |
427 | m_TotalBytesAvailable = ReserveBlockSize; |
428 | m_ApproximateLargestBlock = ReserveBlockSize; |
429 | m_pAllocator = pInfo->m_pAllocator; |
430 | |
431 | TrackAllocation *pTracker = AllocMemory_NoThrow(0, sizeof(HeapList), sizeof(void*), 0); |
432 | if (pTracker == NULL) |
433 | { |
434 | // This should only ever happen with fault injection |
435 | _ASSERTE(g_pConfig->ShouldInjectFault(INJECTFAULT_DYNAMICCODEHEAP)); |
436 | ThrowOutOfMemory(); |
437 | } |
438 | |
439 | HeapList* pHp = (HeapList *)(pTracker + 1); |
440 | |
441 | pHp->hpNext = NULL; |
442 | pHp->pHeap = (PTR_CodeHeap)this; |
443 | // wire it back |
444 | m_pHeapList = (PTR_HeapList)pHp; |
445 | |
446 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap creation {0x%p} - size available 0x%p, private data ptr [0x%p, 0x%p]\n" , |
447 | (HostCodeHeap*)this, m_TotalBytesAvailable, pTracker, pTracker->size)); |
448 | |
449 | // It is imporant to exclude the CLRPersonalityRoutine from the tracked range |
450 | pHp->startAddress = dac_cast<TADDR>(m_pBaseAddr) + pTracker->size; |
451 | pHp->mapBase = ROUND_DOWN_TO_PAGE(pHp->startAddress); // round down to next lower page align |
452 | pHp->pHdrMap = NULL; |
453 | pHp->endAddress = pHp->startAddress; |
454 | |
455 | pHp->maxCodeHeapSize = m_TotalBytesAvailable - pTracker->size; |
456 | pHp->reserveForJumpStubs = 0; |
457 | |
458 | #ifdef _WIN64 |
459 | emitJump((LPBYTE)pHp->CLRPersonalityRoutine, (void *)ProcessCLRException); |
460 | #endif |
461 | |
462 | size_t nibbleMapSize = HEAP2MAPSIZE(ROUND_UP_TO_PAGE(pHp->maxCodeHeapSize)); |
463 | pHp->pHdrMap = new DWORD[nibbleMapSize / sizeof(DWORD)]; |
464 | ZeroMemory(pHp->pHdrMap, nibbleMapSize); |
465 | |
466 | return pHp; |
467 | } |
468 | |
469 | HostCodeHeap::TrackAllocation* HostCodeHeap::AllocFromFreeList(size_t header, size_t size, DWORD alignment, size_t reserveForJumpStubs) |
470 | { |
471 | CONTRACTL |
472 | { |
473 | NOTHROW; |
474 | GC_NOTRIGGER; |
475 | MODE_ANY; |
476 | } |
477 | CONTRACTL_END; |
478 | |
479 | if (m_pFreeList) |
480 | { |
481 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Alloc size corrected 0x%X for free list\n" , this, size)); |
482 | // walk the list looking for a block with enough capacity |
483 | TrackAllocation *pCurrent = m_pFreeList; |
484 | TrackAllocation *pPrevious = NULL; |
485 | while (pCurrent) |
486 | { |
487 | BYTE* pPointer = ALIGN_UP((BYTE*)(pCurrent + 1) + header, alignment); |
488 | size_t realSize = ALIGN_UP(pPointer + size, sizeof(void*)) - (BYTE*)pCurrent; |
489 | if (pCurrent->size >= realSize + reserveForJumpStubs) |
490 | { |
491 | // found a block |
492 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Block found, size 0x%X\n" , this, pCurrent->size)); |
493 | |
494 | // The space left is not big enough for a new block, let's just |
495 | // update the TrackAllocation record for the current block |
496 | if (pCurrent->size - realSize < max(HOST_CODEHEAP_SIZE_ALIGN, sizeof(TrackAllocation))) |
497 | { |
498 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Item removed %p, size 0x%X\n" , this, pCurrent, pCurrent->size)); |
499 | // remove current |
500 | if (pPrevious) |
501 | { |
502 | pPrevious->pNext = pCurrent->pNext; |
503 | } |
504 | else |
505 | { |
506 | m_pFreeList = pCurrent->pNext; |
507 | } |
508 | } |
509 | else |
510 | { |
511 | // create a new TrackAllocation after the memory we just allocated and insert it into the free list |
512 | TrackAllocation *pNewCurrent = (TrackAllocation*)((BYTE*)pCurrent + realSize); |
513 | pNewCurrent->pNext = pCurrent->pNext; |
514 | pNewCurrent->size = pCurrent->size - realSize; |
515 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Item changed %p, new size 0x%X\n" , this, pNewCurrent, pNewCurrent->size)); |
516 | if (pPrevious) |
517 | { |
518 | pPrevious->pNext = pNewCurrent; |
519 | } |
520 | else |
521 | { |
522 | m_pFreeList = pNewCurrent; |
523 | } |
524 | |
525 | // We only need to update the size of the current block if we are creating a new block |
526 | pCurrent->size = realSize; |
527 | } |
528 | |
529 | pCurrent->pHeap = this; |
530 | |
531 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Allocation returned %p, size 0x%X - data -> %p\n" , this, pCurrent, pCurrent->size, pPointer)); |
532 | return pCurrent; |
533 | } |
534 | pPrevious = pCurrent; |
535 | pCurrent = pCurrent->pNext; |
536 | } |
537 | } |
538 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - No block in free list for size 0x%X\n" , this, size)); |
539 | return NULL; |
540 | } |
541 | |
542 | void HostCodeHeap::AddToFreeList(TrackAllocation *pBlockToInsert) |
543 | { |
544 | CONTRACTL |
545 | { |
546 | NOTHROW; |
547 | GC_NOTRIGGER; |
548 | MODE_ANY; |
549 | } |
550 | CONTRACTL_END; |
551 | |
552 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Add to FreeList [%p, 0x%X]\n" , this, pBlockToInsert, pBlockToInsert->size)); |
553 | |
554 | // append to the list in the proper position and coalesce if needed |
555 | if (m_pFreeList) |
556 | { |
557 | TrackAllocation *pCurrent = m_pFreeList; |
558 | TrackAllocation *pPrevious = NULL; |
559 | while (pCurrent) |
560 | { |
561 | if (pCurrent > pBlockToInsert) |
562 | { |
563 | // found the point of insertion |
564 | pBlockToInsert->pNext = pCurrent; |
565 | if (pPrevious) |
566 | { |
567 | pPrevious->pNext = pBlockToInsert; |
568 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] -> [%p, 0x%X] -> [%p, 0x%X]\n" , this, |
569 | pPrevious, pPrevious->size, |
570 | pBlockToInsert, pBlockToInsert->size, |
571 | pCurrent, pCurrent->size)); |
572 | } |
573 | else |
574 | { |
575 | m_pFreeList = pBlockToInsert; |
576 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] to head\n" , this, pBlockToInsert, pBlockToInsert->size)); |
577 | } |
578 | |
579 | // check for coalescing |
580 | if ((BYTE*)pBlockToInsert + pBlockToInsert->size == (BYTE*)pCurrent) |
581 | { |
582 | // coalesce with next |
583 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Coalesce block [%p, 0x%X] with [%p, 0x%X] - new size 0x%X\n" , this, |
584 | pBlockToInsert, pBlockToInsert->size, |
585 | pCurrent, pCurrent->size, |
586 | pCurrent->size + pBlockToInsert->size)); |
587 | pBlockToInsert->pNext = pCurrent->pNext; |
588 | pBlockToInsert->size += pCurrent->size; |
589 | } |
590 | |
591 | if (pPrevious && (BYTE*)pPrevious + pPrevious->size == (BYTE*)pBlockToInsert) |
592 | { |
593 | // coalesce with previous |
594 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Coalesce block [%p, 0x%X] with [%p, 0x%X] - new size 0x%X\n" , this, |
595 | pPrevious, pPrevious->size, |
596 | pBlockToInsert, pBlockToInsert->size, |
597 | pPrevious->size + pBlockToInsert->size)); |
598 | pPrevious->pNext = pBlockToInsert->pNext; |
599 | pPrevious->size += pBlockToInsert->size; |
600 | } |
601 | |
602 | return; |
603 | } |
604 | pPrevious = pCurrent; |
605 | pCurrent = pCurrent->pNext; |
606 | } |
607 | _ASSERTE(pPrevious && pCurrent == NULL); |
608 | pBlockToInsert->pNext = NULL; |
609 | // last in the list |
610 | if ((BYTE*)pPrevious + pPrevious->size == (BYTE*)pBlockToInsert) |
611 | { |
612 | // coalesce with previous |
613 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Coalesce block [%p, 0x%X] with [%p, 0x%X] - new size 0x%X\n" , this, |
614 | pPrevious, pPrevious->size, |
615 | pBlockToInsert, pBlockToInsert->size, |
616 | pPrevious->size + pBlockToInsert->size)); |
617 | pPrevious->size += pBlockToInsert->size; |
618 | } |
619 | else |
620 | { |
621 | pPrevious->pNext = pBlockToInsert; |
622 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] to end after [%p, 0x%X]\n" , this, |
623 | pBlockToInsert, pBlockToInsert->size, |
624 | pPrevious, pPrevious->size)); |
625 | } |
626 | |
627 | return; |
628 | |
629 | } |
630 | // first in the list |
631 | pBlockToInsert->pNext = m_pFreeList; |
632 | m_pFreeList = pBlockToInsert; |
633 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Insert block [%p, 0x%X] to head\n" , this, |
634 | m_pFreeList, m_pFreeList->size)); |
635 | } |
636 | |
637 | void* HostCodeHeap::AllocMemForCode_NoThrow(size_t header, size_t size, DWORD alignment, size_t reserveForJumpStubs) |
638 | { |
639 | CONTRACTL |
640 | { |
641 | NOTHROW; |
642 | GC_NOTRIGGER; |
643 | MODE_ANY; |
644 | } |
645 | CONTRACTL_END; |
646 | |
647 | _ASSERTE(header == sizeof(CodeHeader)); |
648 | _ASSERTE(alignment <= HOST_CODEHEAP_SIZE_ALIGN); |
649 | |
650 | // The code allocator has to guarantee that there is only one entrypoint per nibble map entry. |
651 | // It is guaranteed because of HostCodeHeap allocator always aligns the size up to HOST_CODEHEAP_SIZE_ALIGN, |
652 | // and because the size of nibble map entries (BYTES_PER_BUCKET) is smaller than HOST_CODEHEAP_SIZE_ALIGN. |
653 | // Assert the later fact here. |
654 | _ASSERTE(HOST_CODEHEAP_SIZE_ALIGN >= BYTES_PER_BUCKET); |
655 | |
656 | header += sizeof(TrackAllocation*); |
657 | |
658 | TrackAllocation* pTracker = AllocMemory_NoThrow(header, size, alignment, reserveForJumpStubs); |
659 | if (pTracker == NULL) |
660 | return NULL; |
661 | |
662 | BYTE * pCode = ALIGN_UP((BYTE*)(pTracker + 1) + header, alignment); |
663 | |
664 | // Pointer to the TrackAllocation record is stored just before the code header |
665 | CodeHeader * pHdr = (CodeHeader *)pCode - 1; |
666 | *((TrackAllocation **)(pHdr) - 1) = pTracker; |
667 | |
668 | _ASSERTE(pCode + size <= (BYTE*)pTracker + pTracker->size); |
669 | |
670 | // ref count the whole heap |
671 | m_AllocationCount++; |
672 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - ref count %d\n" , this, m_AllocationCount)); |
673 | |
674 | return pCode; |
675 | } |
676 | |
677 | HostCodeHeap::TrackAllocation* HostCodeHeap::AllocMemory_NoThrow(size_t header, size_t size, DWORD alignment, size_t reserveForJumpStubs) |
678 | { |
679 | CONTRACTL |
680 | { |
681 | NOTHROW; |
682 | GC_NOTRIGGER; |
683 | MODE_ANY; |
684 | } |
685 | CONTRACTL_END; |
686 | |
687 | #ifdef _DEBUG |
688 | if (g_pConfig->ShouldInjectFault(INJECTFAULT_DYNAMICCODEHEAP)) |
689 | { |
690 | char *a = new (nothrow) char; |
691 | if (a == NULL) |
692 | return NULL; |
693 | delete a; |
694 | } |
695 | #endif // _DEBUG |
696 | |
697 | // Skip walking the free list if the cached size of the largest block is not enough |
698 | size_t totalRequiredSize = ALIGN_UP(sizeof(TrackAllocation) + header + size + (alignment - 1) + reserveForJumpStubs, sizeof(void*)); |
699 | if (totalRequiredSize > m_ApproximateLargestBlock) |
700 | return NULL; |
701 | |
702 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - Allocation requested 0x%X\n" , this, size)); |
703 | |
704 | TrackAllocation* pTracker = AllocFromFreeList(header, size, alignment, reserveForJumpStubs); |
705 | if (!pTracker) |
706 | { |
707 | // walk free list to end to find available space |
708 | size_t availableInFreeList = 0; |
709 | TrackAllocation *pCurrentBlock = m_pFreeList; |
710 | TrackAllocation *pLastBlock = NULL; |
711 | while (pCurrentBlock) |
712 | { |
713 | pLastBlock = pCurrentBlock; |
714 | pCurrentBlock = pCurrentBlock->pNext; |
715 | } |
716 | if (pLastBlock && (BYTE*)pLastBlock + pLastBlock->size == m_pLastAvailableCommittedAddr) |
717 | { |
718 | availableInFreeList = pLastBlock->size; |
719 | } |
720 | |
721 | _ASSERTE(totalRequiredSize > availableInFreeList); |
722 | size_t sizeToCommit = totalRequiredSize - availableInFreeList; |
723 | sizeToCommit = ROUND_UP_TO_PAGE(sizeToCommit); |
724 | |
725 | if (m_pLastAvailableCommittedAddr + sizeToCommit <= m_pBaseAddr + m_TotalBytesAvailable) |
726 | { |
727 | if (NULL == ClrVirtualAlloc(m_pLastAvailableCommittedAddr, sizeToCommit, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) |
728 | { |
729 | LOG((LF_BCL, LL_ERROR, "CodeHeap [0x%p] - VirtualAlloc failed\n" , this)); |
730 | return NULL; |
731 | } |
732 | |
733 | TrackAllocation *pBlockToInsert = (TrackAllocation*)(void*)m_pLastAvailableCommittedAddr; |
734 | pBlockToInsert->pNext = NULL; |
735 | pBlockToInsert->size = sizeToCommit; |
736 | m_pLastAvailableCommittedAddr += sizeToCommit; |
737 | AddToFreeList(pBlockToInsert); |
738 | pTracker = AllocFromFreeList(header, size, alignment, reserveForJumpStubs); |
739 | _ASSERTE(pTracker != NULL); |
740 | } |
741 | else |
742 | { |
743 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap [0x%p] - allocation failed:\n\tm_pLastAvailableCommittedAddr: 0x%X\n\tsizeToCommit: 0x%X\n\tm_pBaseAddr: 0x%X\n\tm_TotalBytesAvailable: 0x%X\n" , this, m_pLastAvailableCommittedAddr, sizeToCommit, m_pBaseAddr, m_TotalBytesAvailable)); |
744 | // Update largest available block size |
745 | m_ApproximateLargestBlock = totalRequiredSize - 1; |
746 | } |
747 | } |
748 | |
749 | return pTracker; |
750 | } |
751 | |
752 | #endif //!DACCESS_COMPILE |
753 | |
754 | #ifdef DACCESS_COMPILE |
755 | void HostCodeHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
756 | { |
757 | WRAPPER_NO_CONTRACT; |
758 | |
759 | DAC_ENUM_DTHIS(); |
760 | |
761 | TADDR addr = dac_cast<TADDR>(m_pBaseAddr); |
762 | size_t size = dac_cast<TADDR>(m_pLastAvailableCommittedAddr) - addr; |
763 | |
764 | #if (_DEBUG) |
765 | // Test hook: when testing on debug builds, we want an easy way to test that the while |
766 | // correctly terminates in the face of ridiculous stuff from the target. |
767 | if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget) == 1) |
768 | { |
769 | // Pretend the object is very large. |
770 | size |= 0xf0000000; |
771 | } |
772 | #endif // (_DEBUG) |
773 | |
774 | while (size) |
775 | { |
776 | ULONG32 enumSize; |
777 | |
778 | if (size > 0x80000000) |
779 | { |
780 | enumSize = 0x80000000; |
781 | } |
782 | else |
783 | { |
784 | enumSize = (ULONG32)size; |
785 | } |
786 | |
787 | // If we can't read the target memory, stop immediately so we don't work |
788 | // with broken data. |
789 | if (!DacEnumMemoryRegion(addr, enumSize)) |
790 | break; |
791 | |
792 | addr += enumSize; |
793 | size -= enumSize; |
794 | } |
795 | } |
796 | #endif // DACCESS_COMPILE |
797 | |
798 | // static |
799 | struct HostCodeHeap::TrackAllocation * HostCodeHeap::GetTrackAllocation(TADDR codeStart) |
800 | { |
801 | LIMITED_METHOD_CONTRACT; |
802 | |
803 | CodeHeader * pHdr = dac_cast<PTR_CodeHeader>(PCODEToPINSTR(codeStart)) - 1; |
804 | |
805 | // Pointer to the TrackAllocation record is stored just before the code header |
806 | return *((TrackAllocation **)(pHdr) - 1); |
807 | } |
808 | |
809 | HostCodeHeap* HostCodeHeap::GetCodeHeap(TADDR codeStart) |
810 | { |
811 | WRAPPER_NO_CONTRACT; |
812 | return HostCodeHeap::GetTrackAllocation(codeStart)->pHeap; |
813 | } |
814 | |
815 | |
816 | #ifndef DACCESS_COMPILE |
817 | |
818 | void HostCodeHeap::FreeMemForCode(void * codeStart) |
819 | { |
820 | LIMITED_METHOD_CONTRACT; |
821 | |
822 | TrackAllocation *pTracker = HostCodeHeap::GetTrackAllocation((TADDR)codeStart); |
823 | AddToFreeList(pTracker); |
824 | |
825 | m_ApproximateLargestBlock += pTracker->size; |
826 | |
827 | m_AllocationCount--; |
828 | LOG((LF_BCL, LL_INFO100, "Level2 - CodeHeap released [0x%p, vt(0x%x)] - ref count %d\n" , this, *(size_t*)this, m_AllocationCount)); |
829 | |
830 | if (m_AllocationCount == 0) |
831 | { |
832 | m_pJitManager->AddToCleanupList(this); |
833 | } |
834 | } |
835 | |
836 | // |
837 | // Implementation for DynamicMethodDesc declared in method.hpp |
838 | // |
839 | void DynamicMethodDesc::Destroy(BOOL fDomainUnload) |
840 | { |
841 | CONTRACTL |
842 | { |
843 | if (fDomainUnload) NOTHROW; else THROWS; |
844 | GC_TRIGGERS; |
845 | MODE_ANY; |
846 | } |
847 | CONTRACTL_END; |
848 | |
849 | _ASSERTE(IsDynamicMethod()); |
850 | LoaderAllocator *pLoaderAllocator = GetLoaderAllocatorForCode(); |
851 | |
852 | LOG((LF_BCL, LL_INFO1000, "Level3 - Destroying DynamicMethod {0x%p}\n" , this)); |
853 | if (!m_pSig.IsNull()) |
854 | { |
855 | delete[] (BYTE*)m_pSig.GetValue(); |
856 | m_pSig.SetValueMaybeNull(NULL); |
857 | } |
858 | m_cSig = 0; |
859 | if (!m_pszMethodName.IsNull()) |
860 | { |
861 | delete[] m_pszMethodName.GetValue(); |
862 | m_pszMethodName.SetValueMaybeNull(NULL); |
863 | } |
864 | |
865 | GetLCGMethodResolver()->Destroy(fDomainUnload); |
866 | |
867 | if (pLoaderAllocator->IsCollectible() && !fDomainUnload) |
868 | { |
869 | if (pLoaderAllocator->Release()) |
870 | { |
871 | GCX_PREEMP(); |
872 | LoaderAllocator::GCLoaderAllocators(pLoaderAllocator); |
873 | } |
874 | } |
875 | } |
876 | |
877 | // |
878 | // The resolver object is reused when the method is destroyed, |
879 | // this will reset its state for the next use. |
880 | // |
881 | void LCGMethodResolver::Reset() |
882 | { |
883 | m_DynamicStringLiterals = NULL; |
884 | m_recordCodePointer = NULL; |
885 | m_UsedIndCellList = NULL; |
886 | m_pJumpStubCache = NULL; |
887 | m_next = NULL; |
888 | m_Code = NULL; |
889 | } |
890 | |
891 | // |
892 | // Recycle all the indcells in m_UsedIndCellList by adding them to the free list |
893 | // |
894 | void LCGMethodResolver::RecycleIndCells() |
895 | { |
896 | CONTRACTL { |
897 | NOTHROW; |
898 | GC_TRIGGERS; |
899 | MODE_ANY; |
900 | } CONTRACTL_END; |
901 | |
902 | // Append the list of indirection cells used by this dynamic method to the free list |
903 | IndCellList * list = m_UsedIndCellList; |
904 | if (list) |
905 | { |
906 | BYTE * cellhead = list->indcell; |
907 | BYTE * cellprev = NULL; |
908 | BYTE * cellcurr = NULL; |
909 | |
910 | // Build a linked list of indirection cells from m_UsedIndCellList. |
911 | // No need to lock newlist because this method is only called during the finalization of |
912 | // DynamicResolver.DestroyScout and at that time no one else should be modifying m_UsedIndCellList. |
913 | while (list) |
914 | { |
915 | cellcurr = list->indcell; |
916 | _ASSERTE(cellcurr != NULL); |
917 | |
918 | if (cellprev) |
919 | *((BYTE**)cellprev) = cellcurr; |
920 | |
921 | list = list->pNext; |
922 | cellprev = cellcurr; |
923 | } |
924 | |
925 | // Insert the linked list to the free list of the VirtualCallStubManager of the current domain. |
926 | // We should use GetLoaderAllocatorForCode because that is where the ind cell was allocated. |
927 | LoaderAllocator *pLoaderAllocator = GetDynamicMethod()->GetLoaderAllocatorForCode(); |
928 | VirtualCallStubManager *pMgr = pLoaderAllocator->GetVirtualCallStubManager(); |
929 | pMgr->InsertIntoRecycledIndCellList_Locked(cellhead, cellcurr); |
930 | m_UsedIndCellList = NULL; |
931 | } |
932 | } |
933 | |
934 | void LCGMethodResolver::Destroy(BOOL fDomainUnload) |
935 | { |
936 | CONTRACTL { |
937 | NOTHROW; |
938 | GC_TRIGGERS; |
939 | MODE_ANY; |
940 | } CONTRACTL_END; |
941 | |
942 | LOG((LF_BCL, LL_INFO100, "Level2 - Resolver - Destroying Resolver {0x%p}\n" , this)); |
943 | if (m_Code) |
944 | { |
945 | delete[] m_Code; |
946 | m_Code = NULL; |
947 | } |
948 | m_CodeSize = 0; |
949 | if (!m_LocalSig.IsNull()) |
950 | { |
951 | delete[] m_LocalSig.GetPtr(); |
952 | m_LocalSig = SigPointer(); |
953 | } |
954 | |
955 | // Get the global string literal interning map |
956 | GlobalStringLiteralMap* pStringLiteralMap = SystemDomain::GetGlobalStringLiteralMapNoCreate(); |
957 | |
958 | // release references to all the string literals used in this Dynamic Method |
959 | if (pStringLiteralMap != NULL) |
960 | { |
961 | // lock the global string literal interning map |
962 | // we cannot use GetGlobalStringLiteralMap() here because it might throw |
963 | CrstHolder gch(pStringLiteralMap->GetHashTableCrstGlobal()); |
964 | |
965 | // Access to m_DynamicStringLiterals doesn't need to be syncrhonized because |
966 | // this can be run in only one thread: the finalizer thread. |
967 | while (m_DynamicStringLiterals != NULL) |
968 | { |
969 | m_DynamicStringLiterals->m_pEntry->Release(); |
970 | m_DynamicStringLiterals = m_DynamicStringLiterals->m_pNext; |
971 | } |
972 | } |
973 | |
974 | |
975 | if (!fDomainUnload) |
976 | { |
977 | // No need to recycle if the domain is unloading. |
978 | // Note that we need to do this before m_jitTempData is deleted |
979 | RecycleIndCells(); |
980 | } |
981 | |
982 | m_jitMetaHeap.Delete(); |
983 | m_jitTempData.Delete(); |
984 | |
985 | |
986 | // Per-appdomain resources has been reclaimed already if the appdomain is being unloaded. Do not try to |
987 | // release them again. |
988 | if (!fDomainUnload) |
989 | { |
990 | if (m_recordCodePointer) |
991 | { |
992 | #if defined(_TARGET_AMD64_) |
993 | // Remove the unwind information (if applicable) |
994 | UnwindInfoTable::UnpublishUnwindInfoForMethod((TADDR)m_recordCodePointer); |
995 | #endif // defined(_TARGET_AMD64_) |
996 | |
997 | HostCodeHeap *pHeap = HostCodeHeap::GetCodeHeap((TADDR)m_recordCodePointer); |
998 | LOG((LF_BCL, LL_INFO1000, "Level3 - Resolver {0x%p} - Release reference to heap {%p, vt(0x%x)} \n" , this, pHeap, *(size_t*)pHeap)); |
999 | pHeap->m_pJitManager->FreeCodeMemory(pHeap, m_recordCodePointer); |
1000 | |
1001 | m_recordCodePointer = NULL; |
1002 | } |
1003 | |
1004 | if (m_pJumpStubCache != NULL) |
1005 | { |
1006 | JumpStubBlockHeader* current = m_pJumpStubCache->m_pBlocks; |
1007 | while (current) |
1008 | { |
1009 | JumpStubBlockHeader* next = current->m_next; |
1010 | |
1011 | HostCodeHeap *pHeap = current->GetHostCodeHeap(); |
1012 | LOG((LF_BCL, LL_INFO1000, "Level3 - Resolver {0x%p} - Release reference to heap {%p, vt(0x%x)} \n" , current, pHeap, *(size_t*)pHeap)); |
1013 | pHeap->m_pJitManager->FreeCodeMemory(pHeap, current); |
1014 | |
1015 | current = next; |
1016 | } |
1017 | m_pJumpStubCache->m_pBlocks = NULL; |
1018 | |
1019 | delete m_pJumpStubCache; |
1020 | m_pJumpStubCache = NULL; |
1021 | } |
1022 | |
1023 | if (m_managedResolver) |
1024 | { |
1025 | ::DestroyLongWeakHandle(m_managedResolver); |
1026 | m_managedResolver = NULL; |
1027 | } |
1028 | |
1029 | m_DynamicMethodTable->LinkMethod(m_pDynamicMethod); |
1030 | } |
1031 | } |
1032 | |
1033 | void LCGMethodResolver::FreeCompileTimeState() |
1034 | { |
1035 | CONTRACTL { |
1036 | NOTHROW; |
1037 | GC_NOTRIGGER; |
1038 | MODE_ANY; |
1039 | } CONTRACTL_END; |
1040 | |
1041 | //m_jitTempData.Delete(); |
1042 | } |
1043 | |
1044 | |
1045 | |
1046 | void LCGMethodResolver::GetJitContext(SecurityControlFlags * securityControlFlags, |
1047 | TypeHandle *typeOwner) |
1048 | { |
1049 | CONTRACTL { |
1050 | STANDARD_VM_CHECK; |
1051 | PRECONDITION(CheckPointer(securityControlFlags)); |
1052 | PRECONDITION(CheckPointer(typeOwner)); |
1053 | } CONTRACTL_END; |
1054 | |
1055 | GCX_COOP(); |
1056 | GetJitContextCoop(securityControlFlags, typeOwner); |
1057 | } |
1058 | |
1059 | void LCGMethodResolver::GetJitContextCoop(SecurityControlFlags * securityControlFlags, |
1060 | TypeHandle *typeOwner) |
1061 | { |
1062 | CONTRACTL { |
1063 | THROWS; |
1064 | GC_TRIGGERS; |
1065 | MODE_COOPERATIVE; |
1066 | SO_INTOLERANT; |
1067 | INJECT_FAULT(COMPlusThrowOM();); |
1068 | PRECONDITION(CheckPointer(securityControlFlags)); |
1069 | PRECONDITION(CheckPointer(typeOwner)); |
1070 | } CONTRACTL_END; |
1071 | |
1072 | MethodDescCallSite getJitContext(METHOD__RESOLVER__GET_JIT_CONTEXT, m_managedResolver); |
1073 | |
1074 | OBJECTREF resolver = ObjectFromHandle(m_managedResolver); |
1075 | _ASSERTE(resolver); // gc root must be up the stack |
1076 | |
1077 | ARG_SLOT args[] = |
1078 | { |
1079 | ObjToArgSlot(resolver), |
1080 | PtrToArgSlot(securityControlFlags), |
1081 | }; |
1082 | |
1083 | REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)getJitContext.Call_RetOBJECTREF(args); |
1084 | *typeOwner = refType != NULL ? refType->GetType() : TypeHandle(); |
1085 | |
1086 | } |
1087 | |
1088 | ChunkAllocator* LCGMethodResolver::GetJitMetaHeap() |
1089 | { |
1090 | LIMITED_METHOD_CONTRACT; |
1091 | return &m_jitMetaHeap; |
1092 | } |
1093 | |
1094 | BYTE* LCGMethodResolver::GetCodeInfo(unsigned *pCodeSize, unsigned *pStackSize, CorInfoOptions *pOptions, unsigned *pEHSize) |
1095 | { |
1096 | STANDARD_VM_CONTRACT; |
1097 | |
1098 | _ASSERTE(pCodeSize); |
1099 | |
1100 | if (!m_Code) |
1101 | { |
1102 | GCX_COOP(); |
1103 | |
1104 | LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: Getting CodeInfo on resolver 0x%p...\n" , this)); |
1105 | // get the code - Byte[] Resolver.GetCodeInfo(ref ushort stackSize, ref int EHCount) |
1106 | MethodDescCallSite getCodeInfo(METHOD__RESOLVER__GET_CODE_INFO, m_managedResolver); |
1107 | |
1108 | OBJECTREF resolver = ObjectFromHandle(m_managedResolver); |
1109 | VALIDATEOBJECTREF(resolver); // gc root must be up the stack |
1110 | |
1111 | DWORD initLocals = 0, EHSize = 0; |
1112 | unsigned short stackSize = 0; |
1113 | ARG_SLOT args[] = |
1114 | { |
1115 | ObjToArgSlot(resolver), |
1116 | PtrToArgSlot(&stackSize), |
1117 | PtrToArgSlot(&initLocals), |
1118 | PtrToArgSlot(&EHSize), |
1119 | }; |
1120 | U1ARRAYREF dataArray = (U1ARRAYREF) getCodeInfo.Call_RetOBJECTREF(args); |
1121 | DWORD codeSize = dataArray->GetNumComponents(); |
1122 | NewHolder<BYTE> code(new BYTE[codeSize]); |
1123 | memcpy(code, dataArray->GetDataPtr(), codeSize); |
1124 | m_CodeSize = codeSize; |
1125 | _ASSERTE(FitsIn<unsigned short>(stackSize)); |
1126 | m_StackSize = static_cast<unsigned short>(stackSize); |
1127 | m_Options = (initLocals) ? CORINFO_OPT_INIT_LOCALS : (CorInfoOptions)0; |
1128 | _ASSERTE(FitsIn<unsigned short>(EHSize)); |
1129 | m_EHSize = static_cast<unsigned short>(EHSize); |
1130 | m_Code = (BYTE*)code; |
1131 | code.SuppressRelease(); |
1132 | LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: CodeInfo {0x%p} on resolver %p\n" , m_Code, this)); |
1133 | } |
1134 | |
1135 | *pCodeSize = m_CodeSize; |
1136 | if (pStackSize) |
1137 | *pStackSize = m_StackSize; |
1138 | if (pOptions) |
1139 | *pOptions = m_Options; |
1140 | if (pEHSize) |
1141 | *pEHSize = m_EHSize; |
1142 | return m_Code; |
1143 | |
1144 | } |
1145 | |
1146 | //--------------------------------------------------------------------------------------- |
1147 | // |
1148 | SigPointer |
1149 | LCGMethodResolver::GetLocalSig() |
1150 | { |
1151 | STANDARD_VM_CONTRACT; |
1152 | |
1153 | if (m_LocalSig.IsNull()) |
1154 | { |
1155 | GCX_COOP(); |
1156 | |
1157 | LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: Getting LocalSig on resolver 0x%p...\n" , this)); |
1158 | |
1159 | MethodDescCallSite getLocalsSignature(METHOD__RESOLVER__GET_LOCALS_SIGNATURE, m_managedResolver); |
1160 | |
1161 | OBJECTREF resolver = ObjectFromHandle(m_managedResolver); |
1162 | VALIDATEOBJECTREF(resolver); // gc root must be up the stack |
1163 | |
1164 | ARG_SLOT args[] = |
1165 | { |
1166 | ObjToArgSlot(resolver) |
1167 | }; |
1168 | U1ARRAYREF dataArray = (U1ARRAYREF) getLocalsSignature.Call_RetOBJECTREF(args); |
1169 | DWORD localSigSize = dataArray->GetNumComponents(); |
1170 | NewHolder<COR_SIGNATURE> localSig(new COR_SIGNATURE[localSigSize]); |
1171 | memcpy((void *)localSig, dataArray->GetDataPtr(), localSigSize); |
1172 | |
1173 | m_LocalSig = SigPointer((PCCOR_SIGNATURE)localSig, localSigSize); |
1174 | localSig.SuppressRelease(); |
1175 | LOG((LF_BCL, LL_INFO100000, "Level5 - DM-JIT: LocalSig {0x%p} on resolver %p\n" , m_LocalSig.GetPtr(), this)); |
1176 | } |
1177 | |
1178 | return m_LocalSig; |
1179 | } // LCGMethodResolver::GetLocalSig |
1180 | |
1181 | //--------------------------------------------------------------------------------------- |
1182 | // |
1183 | OBJECTHANDLE |
1184 | LCGMethodResolver::ConstructStringLiteral(mdToken metaTok) |
1185 | { |
1186 | STANDARD_VM_CONTRACT; |
1187 | |
1188 | GCX_COOP(); |
1189 | |
1190 | OBJECTHANDLE string = NULL; |
1191 | STRINGREF strRef = GetStringLiteral(metaTok); |
1192 | |
1193 | GCPROTECT_BEGIN(strRef); |
1194 | |
1195 | if (strRef != NULL) |
1196 | { |
1197 | // Instead of storing the string literal in the appdomain specific string literal map, |
1198 | // we store it in the dynamic method specific string liternal list |
1199 | // This way we can release it when the dynamic method is collected. |
1200 | string = (OBJECTHANDLE)GetOrInternString(&strRef); |
1201 | } |
1202 | |
1203 | GCPROTECT_END(); |
1204 | |
1205 | return string; |
1206 | } |
1207 | |
1208 | //--------------------------------------------------------------------------------------- |
1209 | // |
1210 | BOOL |
1211 | LCGMethodResolver::IsValidStringRef(mdToken metaTok) |
1212 | { |
1213 | STANDARD_VM_CONTRACT; |
1214 | |
1215 | GCX_COOP(); |
1216 | |
1217 | return GetStringLiteral(metaTok) != NULL; |
1218 | } |
1219 | |
1220 | //--------------------------------------------------------------------------------------- |
1221 | // |
1222 | STRINGREF |
1223 | LCGMethodResolver::GetStringLiteral( |
1224 | mdToken token) |
1225 | { |
1226 | CONTRACTL { |
1227 | THROWS; |
1228 | GC_TRIGGERS; |
1229 | MODE_COOPERATIVE; |
1230 | } CONTRACTL_END; |
1231 | |
1232 | MethodDescCallSite getStringLiteral(METHOD__RESOLVER__GET_STRING_LITERAL, m_managedResolver); |
1233 | |
1234 | OBJECTREF resolver = ObjectFromHandle(m_managedResolver); |
1235 | VALIDATEOBJECTREF(resolver); // gc root must be up the stack |
1236 | |
1237 | ARG_SLOT args[] = { |
1238 | ObjToArgSlot(resolver), |
1239 | token, |
1240 | }; |
1241 | return getStringLiteral.Call_RetSTRINGREF(args); |
1242 | } |
1243 | |
1244 | // This method will get the interned string by calling GetInternedString on the |
1245 | // global string liternal interning map. It will also store the returned entry |
1246 | // in m_DynamicStringLiterals |
1247 | STRINGREF* LCGMethodResolver::GetOrInternString(STRINGREF *pProtectedStringRef) |
1248 | { |
1249 | CONTRACTL { |
1250 | THROWS; |
1251 | GC_TRIGGERS; |
1252 | MODE_COOPERATIVE; |
1253 | PRECONDITION(CheckPointer(pProtectedStringRef)); |
1254 | } CONTRACTL_END; |
1255 | |
1256 | // Get the global string literal interning map |
1257 | GlobalStringLiteralMap* pStringLiteralMap = SystemDomain::GetGlobalStringLiteralMap(); |
1258 | |
1259 | // Calculating the hash: EEUnicodeHashTableHelper::GetHash |
1260 | EEStringData StringData = EEStringData((*pProtectedStringRef)->GetStringLength(), (*pProtectedStringRef)->GetBuffer()); |
1261 | DWORD dwHash = pStringLiteralMap->GetHash(&StringData); |
1262 | |
1263 | // lock the global string literal interning map |
1264 | CrstHolder gch(pStringLiteralMap->GetHashTableCrstGlobal()); |
1265 | |
1266 | StringLiteralEntryHolder pEntry(pStringLiteralMap->GetInternedString(pProtectedStringRef, dwHash, /* bAddIfNotFound */ TRUE)); |
1267 | |
1268 | DynamicStringLiteral* pStringLiteral = (DynamicStringLiteral*)m_jitTempData.New(sizeof(DynamicStringLiteral)); |
1269 | pStringLiteral->m_pEntry = pEntry.Extract(); |
1270 | |
1271 | // Add to m_DynamicStringLiterals: |
1272 | // we don't need to check for duplicate because the string literal entries in |
1273 | // the global string literal map are ref counted. |
1274 | pStringLiteral->m_pNext = m_DynamicStringLiterals; |
1275 | m_DynamicStringLiterals = pStringLiteral; |
1276 | |
1277 | return pStringLiteral->m_pEntry->GetStringObject(); |
1278 | |
1279 | } |
1280 | |
1281 | // AddToUsedIndCellList adds a IndCellList link to the beginning of m_UsedIndCellList. It is called by |
1282 | // code:CEEInfo::getCallInfo when a indirection cell is allocated for m_pDynamicMethod. |
1283 | // All the indirection cells usded by m_pDynamicMethod will be recycled when this resolver |
1284 | // is finalized, see code:LCGMethodResolver::RecycleIndCells |
1285 | void LCGMethodResolver::AddToUsedIndCellList(BYTE * indcell) |
1286 | { |
1287 | CONTRACTL { |
1288 | STANDARD_VM_CHECK; |
1289 | PRECONDITION(CheckPointer(indcell)); |
1290 | } CONTRACTL_END; |
1291 | |
1292 | IndCellList * link = (IndCellList *)m_jitTempData.New(sizeof(IndCellList)); |
1293 | link->indcell = indcell; |
1294 | |
1295 | // Insert into m_UsedIndCellList |
1296 | while (true) |
1297 | { |
1298 | link->pNext = m_UsedIndCellList; |
1299 | if (InterlockedCompareExchangeT(&m_UsedIndCellList, link, link->pNext) == link->pNext) |
1300 | break; |
1301 | } |
1302 | |
1303 | } |
1304 | |
1305 | void LCGMethodResolver::ResolveToken(mdToken token, TypeHandle * pTH, MethodDesc ** ppMD, FieldDesc ** ppFD) |
1306 | { |
1307 | STANDARD_VM_CONTRACT; |
1308 | |
1309 | GCX_COOP(); |
1310 | |
1311 | PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__RESOLVE_TOKEN, ObjectFromHandle(m_managedResolver)); |
1312 | |
1313 | DECLARE_ARGHOLDER_ARRAY(args, 5); |
1314 | |
1315 | args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver)); |
1316 | args[ARGNUM_1] = DWORD_TO_ARGHOLDER(token); |
1317 | args[ARGNUM_2] = pTH; |
1318 | args[ARGNUM_3] = ppMD; |
1319 | args[ARGNUM_4] = ppFD; |
1320 | |
1321 | CALL_MANAGED_METHOD_NORET(args); |
1322 | |
1323 | _ASSERTE(*ppMD == NULL || *ppFD == NULL); |
1324 | |
1325 | if (pTH->IsNull()) |
1326 | { |
1327 | if (*ppMD != NULL) *pTH = (*ppMD)->GetMethodTable(); |
1328 | else |
1329 | if (*ppFD != NULL) *pTH = (*ppFD)->GetEnclosingMethodTable(); |
1330 | } |
1331 | |
1332 | _ASSERTE(!pTH->IsNull()); |
1333 | } |
1334 | |
1335 | //--------------------------------------------------------------------------------------- |
1336 | // |
1337 | SigPointer |
1338 | LCGMethodResolver::ResolveSignature( |
1339 | mdToken token) |
1340 | { |
1341 | STANDARD_VM_CONTRACT; |
1342 | |
1343 | GCX_COOP(); |
1344 | |
1345 | U1ARRAYREF dataArray = NULL; |
1346 | |
1347 | PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__RESOLVE_SIGNATURE, ObjectFromHandle(m_managedResolver)); |
1348 | |
1349 | DECLARE_ARGHOLDER_ARRAY(args, 3); |
1350 | |
1351 | args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver)); |
1352 | args[ARGNUM_1] = DWORD_TO_ARGHOLDER(token); |
1353 | args[ARGNUM_2] = DWORD_TO_ARGHOLDER(0); |
1354 | |
1355 | CALL_MANAGED_METHOD_RETREF(dataArray, U1ARRAYREF, args); |
1356 | |
1357 | if (dataArray == NULL) |
1358 | COMPlusThrow(kInvalidProgramException); |
1359 | |
1360 | DWORD cbSig = dataArray->GetNumComponents(); |
1361 | PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)m_jitTempData.New(cbSig); |
1362 | memcpy((void *)pSig, dataArray->GetDataPtr(), cbSig); |
1363 | return SigPointer(pSig, cbSig); |
1364 | } // LCGMethodResolver::ResolveSignature |
1365 | |
1366 | //--------------------------------------------------------------------------------------- |
1367 | // |
1368 | SigPointer |
1369 | LCGMethodResolver::ResolveSignatureForVarArg( |
1370 | mdToken token) |
1371 | { |
1372 | STANDARD_VM_CONTRACT; |
1373 | |
1374 | GCX_COOP(); |
1375 | |
1376 | U1ARRAYREF dataArray = NULL; |
1377 | |
1378 | PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__RESOLVE_SIGNATURE, ObjectFromHandle(m_managedResolver)); |
1379 | |
1380 | DECLARE_ARGHOLDER_ARRAY(args, 3); |
1381 | |
1382 | args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver)); |
1383 | args[ARGNUM_1] = DWORD_TO_ARGHOLDER(token); |
1384 | args[ARGNUM_2] = DWORD_TO_ARGHOLDER(1); |
1385 | |
1386 | CALL_MANAGED_METHOD_RETREF(dataArray, U1ARRAYREF, args); |
1387 | |
1388 | if (dataArray == NULL) |
1389 | COMPlusThrow(kInvalidProgramException); |
1390 | |
1391 | DWORD cbSig = dataArray->GetNumComponents(); |
1392 | PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)m_jitTempData.New(cbSig); |
1393 | memcpy((void *)pSig, dataArray->GetDataPtr(), cbSig); |
1394 | return SigPointer(pSig, cbSig); |
1395 | } // LCGMethodResolver::ResolveSignatureForVarArg |
1396 | |
1397 | //--------------------------------------------------------------------------------------- |
1398 | // |
1399 | void LCGMethodResolver::GetEHInfo(unsigned EHnumber, CORINFO_EH_CLAUSE* clause) |
1400 | { |
1401 | STANDARD_VM_CONTRACT; |
1402 | |
1403 | GCX_COOP(); |
1404 | |
1405 | // attempt to get the raw EHInfo first |
1406 | { |
1407 | U1ARRAYREF dataArray; |
1408 | |
1409 | PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__GET_RAW_EH_INFO, ObjectFromHandle(m_managedResolver)); |
1410 | |
1411 | DECLARE_ARGHOLDER_ARRAY(args, 1); |
1412 | |
1413 | args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver)); |
1414 | |
1415 | CALL_MANAGED_METHOD_RETREF(dataArray, U1ARRAYREF, args); |
1416 | |
1417 | if (dataArray != NULL) |
1418 | { |
1419 | COR_ILMETHOD_SECT_EH* pEH = (COR_ILMETHOD_SECT_EH*)dataArray->GetDataPtr(); |
1420 | |
1421 | COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehClause; |
1422 | const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo; |
1423 | ehInfo = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)pEH->EHClause(EHnumber, &ehClause); |
1424 | |
1425 | clause->Flags = (CORINFO_EH_CLAUSE_FLAGS)ehInfo->GetFlags(); |
1426 | clause->TryOffset = ehInfo->GetTryOffset(); |
1427 | clause->TryLength = ehInfo->GetTryLength(); |
1428 | clause->HandlerOffset = ehInfo->GetHandlerOffset(); |
1429 | clause->HandlerLength = ehInfo->GetHandlerLength(); |
1430 | clause->ClassToken = ehInfo->GetClassToken(); |
1431 | clause->FilterOffset = ehInfo->GetFilterOffset(); |
1432 | return; |
1433 | } |
1434 | } |
1435 | |
1436 | // failed, get the info off the ilgenerator |
1437 | { |
1438 | PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__RESOLVER__GET_EH_INFO, ObjectFromHandle(m_managedResolver)); |
1439 | |
1440 | DECLARE_ARGHOLDER_ARRAY(args, 3); |
1441 | |
1442 | args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ObjectFromHandle(m_managedResolver)); |
1443 | args[ARGNUM_1] = DWORD_TO_ARGHOLDER(EHnumber); |
1444 | args[ARGNUM_2] = PTR_TO_ARGHOLDER(clause); |
1445 | |
1446 | CALL_MANAGED_METHOD_NORET(args); |
1447 | } |
1448 | } |
1449 | |
1450 | #endif // !DACCESS_COMPILE |
1451 | |
1452 | |
1453 | // Get the associated managed resolver. This method will be called during a GC so it should not throw, trigger a GC or cause the |
1454 | // object in question to be validated. |
1455 | OBJECTREF LCGMethodResolver::GetManagedResolver() |
1456 | { |
1457 | LIMITED_METHOD_CONTRACT; |
1458 | return ObjectFromHandle(m_managedResolver); |
1459 | } |
1460 | |
1461 | |
1462 | // |
1463 | // ChunkAllocator implementation |
1464 | // |
1465 | ChunkAllocator::~ChunkAllocator() |
1466 | { |
1467 | LIMITED_METHOD_CONTRACT; |
1468 | Delete(); |
1469 | } |
1470 | |
1471 | void ChunkAllocator::Delete() |
1472 | { |
1473 | LIMITED_METHOD_CONTRACT; |
1474 | BYTE *next = NULL; |
1475 | LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - deleting...\n" , this)); |
1476 | while (m_pData) |
1477 | { |
1478 | LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - delete block {0x%p}\n" , this, m_pData)); |
1479 | next = ((BYTE**)m_pData)[0]; |
1480 | delete[] m_pData; |
1481 | m_pData = next; |
1482 | } |
1483 | } |
1484 | |
1485 | void* ChunkAllocator::New(size_t size) |
1486 | { |
1487 | CONTRACTL |
1488 | { |
1489 | THROWS; |
1490 | GC_NOTRIGGER; |
1491 | SO_TOLERANT; |
1492 | MODE_ANY; |
1493 | } |
1494 | CONTRACTL_END; |
1495 | // We need to align it, otherwise we might get DataMisalignedException on IA64 |
1496 | size = ALIGN_UP(size, sizeof(void *)); |
1497 | |
1498 | BYTE *pNewBlock = NULL; |
1499 | LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - allocation requested 0x%X, available 0x%X\n" , this, size, (m_pData) ? ((size_t*)m_pData)[1] : 0)); |
1500 | if (m_pData) |
1501 | { |
1502 | // we may have room available |
1503 | size_t available = ((size_t*)m_pData)[1]; |
1504 | if (size <= available) |
1505 | { |
1506 | LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - reusing block {0x%p}\n" , this, m_pData)); |
1507 | ((size_t*)m_pData)[1] = available - size; |
1508 | pNewBlock = (m_pData + CHUNK_SIZE - available); |
1509 | LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - ptr -> 0x%p, available 0x%X\n" , this, pNewBlock, ((size_t*)m_pData)[1])); |
1510 | return pNewBlock; |
1511 | } |
1512 | } |
1513 | |
1514 | // no available - need to allocate a new buffer |
1515 | if (size + (sizeof(void*) * 2) < CHUNK_SIZE) |
1516 | { |
1517 | // make the allocation |
1518 | NewHolder<BYTE> newBlock(new BYTE[CHUNK_SIZE]); |
1519 | pNewBlock = (BYTE*)newBlock; |
1520 | ((size_t*)pNewBlock)[1] = CHUNK_SIZE - size - (sizeof(void*) * 2); |
1521 | LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - new block {0x%p}\n" , this, pNewBlock)); |
1522 | newBlock.SuppressRelease(); |
1523 | } |
1524 | else |
1525 | { |
1526 | // request bigger than default size this is going to be a single block |
1527 | NewHolder<BYTE> newBlock(new BYTE[size + (sizeof(void*) * 2)]); |
1528 | pNewBlock = (BYTE*)newBlock; |
1529 | ((size_t*)pNewBlock)[1] = 0; // no available bytes left |
1530 | LOG((LF_BCL, LL_INFO10, "Level1 - DM - Allocator [0x%p] - new BIG block {0x%p}\n" , this, pNewBlock)); |
1531 | newBlock.SuppressRelease(); |
1532 | } |
1533 | |
1534 | // all we have left to do is to link the block. |
1535 | // We leave at the top the block with more bytes available |
1536 | if (m_pData) |
1537 | { |
1538 | if (((size_t*)pNewBlock)[1] > ((size_t*)m_pData)[1]) |
1539 | { |
1540 | ((BYTE**)pNewBlock)[0] = m_pData; |
1541 | m_pData = pNewBlock; |
1542 | } |
1543 | else |
1544 | { |
1545 | ((BYTE**)pNewBlock)[0] = ((BYTE**)m_pData)[0]; |
1546 | ((BYTE**)m_pData)[0] = pNewBlock; |
1547 | } |
1548 | } |
1549 | else |
1550 | { |
1551 | // this is the first allocation |
1552 | m_pData = pNewBlock; |
1553 | ((BYTE**)m_pData)[0] = NULL; |
1554 | } |
1555 | |
1556 | pNewBlock += (sizeof(void*) * 2); |
1557 | LOG((LF_BCL, LL_INFO100, "Level2 - DM - Allocator [0x%p] - ptr -> 0x%p, available 0x%X\n" , this, pNewBlock, ((size_t*)m_pData)[1])); |
1558 | return pNewBlock; |
1559 | } |
1560 | |
1561 | |