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// File: contractimpl.cpp
6//
7// Keeps track of contract implementations, used primarily in stub dispatch.
8//
9
10
11//
12
13//
14// ============================================================================
15
16#include "common.h" // Precompiled header
17
18#include "contractimpl.h"
19#include "virtualcallstub.h"
20#include "decodemd.h"
21
22#ifdef FEATURE_PREJIT
23#include "compile.h"
24#endif
25
26#if defined(_DEBUG)
27DummyGlobalContract ___contract;
28#endif
29
30#ifdef LOGGING
31//----------------------------------------------------------------------------
32StubDispatchStats g_sdStats = {0};
33#endif // LOGGING
34
35#ifndef DACCESS_COMPILE
36
37//----------------------------------------------------------------------------
38MethodDesc * DispatchSlot::GetMethodDesc()
39{
40 WRAPPER_NO_CONTRACT;
41 if (IsNull())
42 return NULL;
43 else
44 return MethodTable::GetMethodDescForSlotAddress(GetTarget());
45}
46
47//------------------------------------------------------------------------
48void TypeIDMap::Init(UINT32 idStartValue, UINT32 idIncrementValue, BOOL fUseFatTokensForUniqueness)
49{
50 STANDARD_VM_CONTRACT;
51
52 LockOwner lock = {&m_lock, IsOwnerOfCrst};
53 m_idMap.Init(11, TRUE, &lock);
54 m_mtMap.Init(11, TRUE, &lock);
55 m_idProvider.Init(idStartValue, idIncrementValue);
56 m_entryCount = 0;
57 m_fUseFatIdsForUniqueness = fUseFatTokensForUniqueness;
58}
59
60#endif // !DACCESS_COMPILE
61
62//------------------------------------------------------------------------
63// Returns the ID of the type if found. If not found, returns INVALID_TYPE_ID
64UINT32 TypeIDMap::LookupTypeID(PTR_MethodTable pMT)
65{
66 CONTRACTL {
67 NOTHROW;
68 SO_TOLERANT;
69 PRECONDITION(CheckPointer(GetThread()));
70 if (GetThread()->PreemptiveGCDisabled()) { GC_NOTRIGGER; } else { GC_TRIGGERS; }
71 } CONTRACTL_END;
72
73 UINT32 id = (UINT32) m_mtMap.LookupValue((UPTR)dac_cast<TADDR>(pMT), 0);
74 _ASSERTE(!m_fUseFatIdsForUniqueness || !pMT->RequiresFatDispatchTokens() || (DispatchToken::RequiresDispatchTokenFat(id, 0)));
75
76 return id;
77}
78
79//------------------------------------------------------------------------
80// Returns the ID of the type if found. If not found, returns INVALID_TYPE_ID
81PTR_MethodTable TypeIDMap::LookupType(UINT32 id)
82{
83 CONTRACTL {
84 NOTHROW;
85 SO_TOLERANT;
86 PRECONDITION(CheckPointer(GetThread()));
87 if (GetThread()->PreemptiveGCDisabled()) { GC_NOTRIGGER; } else { GC_TRIGGERS; }
88 PRECONDITION(id <= TypeIDProvider::MAX_TYPE_ID);
89 } CONTRACTL_END;
90
91 if (!m_idProvider.OwnsID(id))
92 return NULL;
93
94 UPTR ret = m_idMap.LookupValue((UPTR)id, 0);
95 if (ret == static_cast<UPTR>(INVALIDENTRY))
96 return NULL;
97
98 ret <<= 1;
99
100 return PTR_MethodTable(ret);
101}
102
103//------------------------------------------------------------------------
104// Returns the ID of the type if found. If not found, assigns the ID and
105// returns the new ID.
106UINT32 TypeIDMap::GetTypeID(PTR_MethodTable pMT)
107{
108 CONTRACTL {
109 THROWS;
110 GC_TRIGGERS;
111 } CONTRACTL_END;
112
113 // Lookup the value.
114 UINT32 id = LookupTypeID(pMT);
115#ifndef DACCESS_COMPILE
116 // If the value is not in the table, take the lock, get a new ID, and
117 // insert the new pair.
118 if (id == TypeIDProvider::INVALID_TYPE_ID)
119 {
120 // Take the lock
121 CrstHolder lh(&m_lock);
122 // Check to see if someone beat us to the punch
123 id = LookupTypeID(pMT);
124 if (id != TypeIDProvider::INVALID_TYPE_ID)
125 {
126 return id;
127 }
128 // Get the next ID
129 if (m_fUseFatIdsForUniqueness && pMT->RequiresFatDispatchTokens())
130 {
131 id = GetNextFatID();
132 }
133 else
134 {
135 id = GetNextID();
136 }
137
138 CONSISTENCY_CHECK(id <= TypeIDProvider::MAX_TYPE_ID);
139 // Insert the pair, with lookups in both directions
140 CONSISTENCY_CHECK((((UPTR)pMT) & 0x1) == 0);
141 m_idMap.InsertValue((UPTR)id, (UPTR)pMT >> 1);
142 m_mtMap.InsertValue((UPTR)pMT, (UPTR)id);
143 m_entryCount++;
144 CONSISTENCY_CHECK(GetThread()->GetDomain()->IsCompilationDomain() ||
145 (LookupType(id) == pMT));
146 }
147#else // DACCESS_COMPILE
148 if (id == TypeIDProvider::INVALID_TYPE_ID)
149 DacError(E_FAIL);
150#endif // DACCESS_COMPILE
151 // Return the ID for this type.
152 return id;
153} // TypeIDMap::GetTypeID
154
155#ifndef DACCESS_COMPILE
156
157//------------------------------------------------------------------------
158// If TRUE, it points to a matching entry.
159// If FALSE, it is at the insertion point.
160BOOL
161DispatchMapBuilder::Find(
162 DispatchMapTypeID typeID,
163 UINT32 slotNumber,
164 Iterator & it)
165{
166 WRAPPER_NO_CONTRACT;
167 for (; it.IsValid(); it.Next())
168 {
169 if (typeID == it.GetTypeID())
170 {
171 if (slotNumber == it.GetSlotNumber())
172 {
173 return TRUE;
174 }
175 if (slotNumber < it.GetSlotNumber())
176 {
177 return FALSE;
178 }
179 }
180 else if (typeID < it.GetTypeID())
181 {
182 return FALSE;
183 }
184 }
185
186 return FALSE;
187} // DispatchMapBuilder::Find
188
189//------------------------------------------------------------------------
190// If TRUE, contains such an entry.
191// If FALSE, no such entry exists.
192BOOL DispatchMapBuilder::Contains(DispatchMapTypeID typeID, UINT32 slotNumber)
193{
194 WRAPPER_NO_CONTRACT;
195 Iterator it(this);
196 return Find(typeID, slotNumber, it);
197}
198
199//------------------------------------------------------------------------
200void
201DispatchMapBuilder::InsertMDMapping(
202 DispatchMapTypeID typeID,
203 UINT32 slotNumber,
204 MethodDesc * pMDTarget,
205 BOOL fIsMethodImpl)
206{
207 CONTRACTL {
208 THROWS;
209 GC_NOTRIGGER;
210 } CONTRACTL_END;
211
212 // Find a matching entry, or move the iterator to insertion point.
213 Iterator it(this);
214 BOOL fFound = Find(typeID, slotNumber, it);
215
216 // If we find an existing matching entry, fail.
217 if (fFound)
218 {
219 _ASSERTE(false);
220 COMPlusThrowHR(COR_E_TYPELOAD);
221 }
222
223 // Create and initialize a new entry
224 DispatchMapBuilderNode * pNew = NewEntry();
225 pNew->Init(typeID, slotNumber, pMDTarget);
226 if (fIsMethodImpl)
227 pNew->SetIsMethodImpl();
228
229 // Insert at the point of the iterator
230 pNew->m_next = NULL;
231 if (it.IsValid())
232 {
233 pNew->m_next = it.EntryNode();
234 }
235 *(it.EntryNodePtr()) = pNew;
236 m_cEntries++;
237
238} // DispatchMapBuilder::InsertMDMapping
239
240//--------------------------------------------------------------------
241UINT32 DispatchMapBuilder::Iterator::GetTargetSlot()
242{
243 WRAPPER_NO_CONTRACT;
244 CONSISTENCY_CHECK(IsValid());
245 if (GetTargetMD() != NULL)
246 {
247 return EntryNode()->m_pMDTarget->GetSlot();
248 }
249 else
250 {
251 return 0;
252 }
253}
254
255//------------------------------------------------------------------------
256DispatchMapBuilderNode * DispatchMapBuilder::NewEntry()
257{
258 CONTRACTL {
259 THROWS;
260 GC_NOTRIGGER;
261 INJECT_FAULT(COMPlusThrowOM());
262 } CONTRACTL_END;
263
264 return new (m_pAllocator) DispatchMapBuilderNode();
265}
266
267//----------------------------------------------------------------------------
268DispatchMap::DispatchMap(
269 BYTE * pMap,
270 UINT32 cbMap)
271{
272 LIMITED_METHOD_CONTRACT;
273 CONSISTENCY_CHECK(CheckPointer(pMap));
274 memcpyNoGCRefs(m_rgMap, pMap, cbMap);
275}
276
277//----------------------------------------------------------------------------
278// This mapping consists of a list of the following entries.
279// <type, [<slot, (index | slot)>]>. This is implemented as
280//
281// flag: 0 if the map is a part of a JIT'd module
282// 1 if the map is a part of an NGEN'd module.
283// count: number of types that have entries
284// {
285// type: The ID current type being mapped
286// count: Number of subentries for the current type
287// bool: Whether or not the target slot/index values can be negative.
288// {
289// slot: The slot of type that is being mapped
290// index/slot: This is a slot mapping for the current type. The implementation search is
291// modified to <this, slot> and the search is restarted from the initial type.
292// }
293// }
294void
295DispatchMap::CreateEncodedMapping(
296 MethodTable * pMT,
297 DispatchMapBuilder * pMapBuilder,
298 StackingAllocator * pAllocator,
299 BYTE ** ppbMap,
300 UINT32 * pcbMap)
301{
302 CONTRACTL {
303 THROWS;
304 GC_TRIGGERS;
305 INJECT_FAULT(COMPlusThrowOM());
306 PRECONDITION(CheckPointer(pMT));
307 PRECONDITION(CheckPointer(pMapBuilder));
308 PRECONDITION(CheckPointer(pAllocator));
309 PRECONDITION(CheckPointer(ppbMap));
310 PRECONDITION(CheckPointer(pcbMap));
311 } CONTRACTL_END;
312
313 /////////////////////////////////
314 // Phase 1 - gather entry counts
315
316 UINT32 cNumTypes = 0;
317 UINT32 cNumEntries = 0;
318
319 {
320 DispatchMapBuilder::Iterator it(pMapBuilder);
321 // We don't want to record overrides or methodImpls in the dispatch map since
322 // we have vtables to track this information.
323 it.SkipThisTypeEntries();
324 if (it.IsValid())
325 {
326 DispatchMapTypeID curType = DispatchMapTypeID::FromUINT32(INVALIDENTRY);
327 do
328 {
329 cNumEntries++;
330 if (curType != it.GetTypeID())
331 {
332 cNumTypes++;
333 curType = it.GetTypeID();
334 }
335 }
336 while (it.Next());
337 }
338 }
339
340 /////////////////////////////////
341 // Phase 2 - allocate space
342
343 // Now that we have stats about the overall absolute maximum map size, we can allocate
344 // some working space for createing the encoded map in.
345 // Sizes: flag==UINT32, typeID==UINT32, slot==UINT32, index/slot==UINT32
346
347 S_UINT32 scbMap = S_UINT32(sizeof(UINT32)) +
348 S_UINT32(cNumTypes) * S_UINT32(sizeof(UINT32)) +
349 S_UINT32(cNumEntries) * S_UINT32((sizeof(UINT32) + sizeof(UINT32)));
350
351 BYTE * pbMap = (BYTE *)pAllocator->Alloc(scbMap);
352
353 /////////////////////////////////
354 // Phase 3 - encode the map
355
356 {
357 // Create the encoder over the newly allocated memory
358 Encoder e(pbMap);
359 // Encode the count of type entries
360 e.Encode((unsigned)cNumTypes);
361 // Start encoding the map
362 DispatchMapBuilder::Iterator it(pMapBuilder);
363 it.SkipThisTypeEntries();
364
365 INT32 curType = -1;
366 INT32 prevType;
367 INT32 deltaType;
368 while (it.IsValid())
369 {
370 // Encode the type ID
371 prevType = curType;
372 curType = (INT32)it.GetTypeID().ToUINT32();
373 deltaType = curType - prevType - ENCODING_TYPE_DELTA;
374 CONSISTENCY_CHECK(0 <= deltaType);
375 e.Encode((unsigned)deltaType);
376 // Variables for slot delta calculations
377 BOOL fHasNegatives = FALSE;
378 // Source slot
379 INT32 curSlot = -1;
380 INT32 prevSlot = -1;
381 // Target slot for virtual mappings
382 INT32 curTargetSlot = -1;
383 INT32 prevTargetSlot = -1;
384 // Count and encode the number of sub entries for this type
385 UINT32 cSubEntries = 0;
386 DispatchMapBuilder::Iterator subIt(it);
387 do
388 {
389 prevTargetSlot = curTargetSlot;
390 curTargetSlot = (INT32)subIt.GetTargetSlot();
391 INT32 deltaTargetSlot = curTargetSlot - prevTargetSlot - ENCODING_TARGET_SLOT_DELTA;
392 if (deltaTargetSlot < 0)
393 {
394 fHasNegatives = TRUE;
395 }
396 cSubEntries++;
397 }
398 while (subIt.Next() && (subIt.GetTypeID().ToUINT32() == (UINT32)curType));
399
400 e.Encode((unsigned)cSubEntries);
401 e.Encode((unsigned)fHasNegatives);
402 e.ContainsNegatives(fHasNegatives);
403 // Iterate each subentry and encode it
404 curTargetSlot = -1;
405 do
406 {
407 // Only virtual targets can be mapped virtually.
408 CONSISTENCY_CHECK((it.GetTargetMD() == NULL) ||
409 it.GetTargetMD()->IsVirtual());
410 // Encode the slot
411 prevSlot = curSlot;
412 curSlot = it.GetSlotNumber();
413 INT32 deltaSlot = curSlot - prevSlot - ENCODING_SLOT_DELTA;
414 CONSISTENCY_CHECK(0 <= deltaSlot);
415 e.Encode((unsigned)deltaSlot);
416
417 // Calculate and encode the target slot delta
418 prevTargetSlot = curTargetSlot;
419 curTargetSlot = (INT32)it.GetTargetSlot();
420 INT32 delta = curTargetSlot - prevTargetSlot - ENCODING_TARGET_SLOT_DELTA;
421
422 if (fHasNegatives)
423 {
424 e.EncodeSigned((signed)delta);
425 }
426 else
427 {
428 CONSISTENCY_CHECK(0 <= delta);
429 e.Encode((unsigned)delta);
430 }
431 }
432 while (it.Next() && it.GetTypeID().ToUINT32() == (UINT32)curType);
433 } // while (it.IsValid())
434
435 // Finish and finalize the map, and set the out params.
436 e.Done();
437 *pcbMap = e.Contents(ppbMap);
438 }
439
440#ifdef _DEBUG
441 // Let's verify the mapping
442 {
443 EncodedMapIterator itMap(*ppbMap);
444 DispatchMapBuilder::Iterator itBuilder(pMapBuilder);
445 itBuilder.SkipThisTypeEntries();
446
447 while (itMap.IsValid())
448 {
449 CONSISTENCY_CHECK(itBuilder.IsValid());
450 DispatchMapEntry * pEntryMap = itMap.Entry();
451 CONSISTENCY_CHECK(pEntryMap->GetTypeID() == itBuilder.GetTypeID());
452 CONSISTENCY_CHECK(pEntryMap->GetTargetSlotNumber() == itBuilder.GetTargetSlot());
453 itMap.Next();
454 itBuilder.Next();
455 }
456
457 CONSISTENCY_CHECK(!itBuilder.IsValid());
458 }
459#endif //_DEBUG
460} // DispatchMap::CreateEncodedMapping
461
462#ifdef FEATURE_NATIVE_IMAGE_GENERATION
463//------------------------------------------------------------------------
464void DispatchMap::Save(DataImage * image)
465{
466 STANDARD_VM_CONTRACT;
467
468 CONSISTENCY_CHECK(!image->IsStored(this));
469
470 UINT32 cbMap = GetMapSize();
471 UINT32 cbObj = GetObjectSize(cbMap);
472
473 image->StoreInternedStructure(
474 this,
475 cbObj,
476 DataImage::ITEM_DISPATCH_MAP,
477 sizeof(void *));
478
479#ifdef LOGGING
480 g_sdStats.m_cNGENDispatchMap++;
481 g_sdStats.m_cbNGENDispatchMap += cbObj;
482#endif //LOGGING
483}
484
485//------------------------------------------------------------------------
486void DispatchMap::Fixup(DataImage *image)
487{
488 STANDARD_VM_CONTRACT;
489}
490
491#endif //FEATURE_NATIVE_IMAGE_GENERATION
492
493#endif //!DACCESS_COMPILE
494
495//------------------------------------------------------------------------
496UINT32 DispatchMap::GetMapSize()
497{
498 WRAPPER_NO_CONTRACT;
499 SUPPORTS_DAC;
500 EncodedMapIterator it(this);
501 for (; it.IsValid(); it.Next())
502 {
503 }
504 CONSISTENCY_CHECK(dac_cast<TADDR>(it.m_d.End()) > PTR_HOST_MEMBER_TADDR(DispatchMap, this, m_rgMap));
505 return (UINT32)(dac_cast<TADDR>(it.m_d.End()) - PTR_HOST_MEMBER_TADDR(DispatchMap, this, m_rgMap));
506}
507
508#ifdef DACCESS_COMPILE
509
510//------------------------------------------------------------------------
511void DispatchMap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
512{
513 WRAPPER_NO_CONTRACT;
514 SUPPORTS_DAC;
515
516 DAC_ENUM_DTHIS();
517
518 EMEM_OUT(("MEM: %p DispatchMap\n", dac_cast<TADDR>(this)));
519
520 DacEnumMemoryRegion(PTR_HOST_MEMBER_TADDR(DispatchMap,this,m_rgMap), GetMapSize());
521}
522
523#endif // DACCESS_COMPILE
524
525//--------------------------------------------------------------------
526void DispatchMap::EncodedMapIterator::Invalidate()
527{
528 LIMITED_METHOD_DAC_CONTRACT;
529 m_numTypes = 0;
530 m_curType = 0;
531 m_numEntries = 0;
532 m_curEntry = 0;
533}
534
535//--------------------------------------------------------------------
536void DispatchMap::EncodedMapIterator::Init(PTR_BYTE pbMap)
537{
538 CONTRACTL {
539 GC_NOTRIGGER;
540 NOTHROW;
541 INSTANCE_CHECK;
542 PRECONDITION(CheckPointer(pbMap, NULL_OK));
543 SUPPORTS_DAC;
544 } CONTRACTL_END;
545
546 if (pbMap != NULL)
547 {
548 // Initialize the map decoder
549 m_d.Init(pbMap);
550 m_numTypes = m_d.Next();
551 m_curType = -1;
552 m_curTypeId = DispatchMapTypeID::FromUINT32(static_cast<UINT32>(-1));
553 m_numEntries = 0;
554 m_curEntry = -1;
555 m_curTargetSlot = static_cast<UINT32>(-1);
556 }
557 else
558 {
559 Invalidate();
560 }
561
562 Next();
563}
564
565//--------------------------------------------------------------------
566DispatchMap::EncodedMapIterator::EncodedMapIterator(MethodTable * pMT)
567{
568 CONTRACTL {
569 GC_NOTRIGGER;
570 NOTHROW;
571 INSTANCE_CHECK;
572 SUPPORTS_DAC;
573 } CONTRACTL_END;
574
575 if (pMT->HasDispatchMap())
576 {
577 DispatchMap * pMap = pMT->GetDispatchMap();
578 Init(PTR_BYTE(PTR_HOST_MEMBER_TADDR(DispatchMap, pMap, m_rgMap)));
579 }
580 else
581 {
582 Init(NULL);
583 }
584}
585
586//--------------------------------------------------------------------
587// This should be used only when a dispatch map needs to be used
588// separately from its MethodTable.
589DispatchMap::EncodedMapIterator::EncodedMapIterator(DispatchMap * pMap)
590{
591 LIMITED_METHOD_CONTRACT;
592 SUPPORTS_DAC;
593 PTR_BYTE pBytes = NULL;
594 if (pMap != NULL)
595 {
596 pBytes = PTR_BYTE(PTR_HOST_MEMBER_TADDR(DispatchMap, pMap,m_rgMap));
597 }
598 Init(pBytes);
599}
600
601//--------------------------------------------------------------------
602DispatchMap::EncodedMapIterator::EncodedMapIterator(PTR_BYTE pbMap)
603{
604 LIMITED_METHOD_CONTRACT;
605
606 Init(pbMap);
607}
608
609//--------------------------------------------------------------------
610BOOL DispatchMap::EncodedMapIterator::Next()
611{
612 CONTRACTL {
613 GC_NOTRIGGER;
614 NOTHROW;
615 INSTANCE_CHECK;
616 SUPPORTS_DAC;
617 } CONTRACTL_END;
618
619 if (!IsValid())
620 {
621 return FALSE;
622 }
623
624 m_curEntry++;
625 if (m_curEntry == m_numEntries)
626 {
627 m_curType++;
628 if (m_curType == m_numTypes)
629 {
630 return FALSE;
631 }
632 m_curTypeId =
633 DispatchMapTypeID::FromUINT32(
634 (UINT32)((INT32)m_curTypeId.ToUINT32() +
635 (INT32)m_d.Next() +
636 ENCODING_TYPE_DELTA));
637 _ASSERTE(!m_curTypeId.IsThisClass());
638 m_curEntry = 0;
639 m_numEntries = m_d.Next();
640 m_fCurTypeHasNegativeEntries = (BOOL)m_d.Next();
641 m_curSlot = static_cast<UINT32>(-1);
642 m_curTargetSlot = static_cast<UINT32>(-1);
643 CONSISTENCY_CHECK(m_numEntries != 0);
644 }
645
646 // Now gather enough info to initialize the dispatch entry
647
648 // Get the source slot
649 m_curSlot = (UINT32)((INT32)m_curSlot + (INT32)m_d.Next() + ENCODING_SLOT_DELTA);
650
651 // If virtual, get the target virtual slot number
652 m_curTargetSlot =
653 (UINT32)((INT32)m_curTargetSlot +
654 ENCODING_TARGET_SLOT_DELTA +
655 (INT32)(m_fCurTypeHasNegativeEntries ? m_d.NextSigned() : m_d.Next()));
656 m_e.InitVirtualMapping(m_curTypeId, m_curSlot, m_curTargetSlot);
657
658 CONSISTENCY_CHECK(IsValid());
659 return TRUE;
660} // DispatchMap::EncodedMapIterator::Next
661
662//--------------------------------------------------------------------
663DispatchMap::Iterator::Iterator(MethodTable * pMT)
664 : m_mapIt(pMT)
665{
666 CONTRACTL {
667 THROWS;
668 GC_TRIGGERS;
669 } CONTRACTL_END;
670}
671
672//--------------------------------------------------------------------
673BOOL DispatchMap::Iterator::IsValid()
674{
675 WRAPPER_NO_CONTRACT;
676 return m_mapIt.IsValid();
677}
678
679//--------------------------------------------------------------------
680BOOL DispatchMap::Iterator::Next()
681{
682 WRAPPER_NO_CONTRACT;
683 CONSISTENCY_CHECK(!m_mapIt.Entry()->GetTypeID().IsThisClass());
684 if (m_mapIt.IsValid())
685 {
686 m_mapIt.Next();
687 CONSISTENCY_CHECK(!m_mapIt.IsValid() || !m_mapIt.Entry()->GetTypeID().IsThisClass());
688 }
689 return IsValid();
690}
691
692//--------------------------------------------------------------------
693DispatchMapEntry * DispatchMap::Iterator::Entry()
694{
695/*
696 CONTRACTL {
697 INSTANCE_CHECK;
698 MODE_ANY;
699 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
700 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
701 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
702 PRECONDITION(IsValid());
703 } CONTRACTL_END;
704*/
705 WRAPPER_NO_CONTRACT;
706 CONSISTENCY_CHECK(IsValid());
707
708 DispatchMapEntry * pEntry = NULL;
709 if (m_mapIt.IsValid())
710 {
711 pEntry = m_mapIt.Entry();
712 }
713 CONSISTENCY_CHECK(CheckPointer(pEntry));
714 return pEntry;
715}
716