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// File: class.cpp
8//
9//*****************************************************************************
10#include "stdafx.h"
11
12// We have an assert in ceemain.cpp that validates this assumption
13#define FIELD_OFFSET_NEW_ENC_DB 0x07FFFFFB
14
15#include "winbase.h"
16#include "corpriv.h"
17
18
19
20//-----------------------------------------------------------------------------
21// class CordbClass
22// Represents a IL-Class in the debuggee, eg: List<T>, System.Console, etc
23//
24// Parameters:
25// m - module that the class is contained in.
26// classMetadataToken - metadata token for the class, scoped to module m.
27//-----------------------------------------------------------------------------
28CordbClass::CordbClass(CordbModule *m, mdTypeDef classMetadataToken)
29 : CordbBase(m->GetProcess(), classMetadataToken, enumCordbClass),
30 m_loadLevel(Constructed),
31 m_fLoadEventSent(FALSE),
32 m_fHasBeenUnloaded(false),
33 m_pModule(m),
34 m_token(classMetadataToken),
35 m_fIsValueClassKnown(false),
36 m_fIsValueClass(false),
37 m_fHasTypeParams(false),
38 m_continueCounterLastSync(0),
39 m_fCustomNotificationsEnabled(false)
40{
41 m_classInfo.Clear();
42}
43
44
45/*
46 A list of which resources owned by this object are accounted for.
47
48 HANDLED:
49 CordbModule* m_module; // Assigned w/o AddRef()
50 FieldData *m_fields; // Deleted in ~CordbClass
51 CordbHangingFieldTable m_hangingFieldsStatic; // by value, ~CHashTableAndData frees
52*/
53
54
55//-----------------------------------------------------------------------------
56// Destructor for CordbClass
57//-----------------------------------------------------------------------------
58CordbClass::~CordbClass()
59{
60 // We should have been explicitly neutered before our internal ref went to 0.
61 _ASSERTE(IsNeutered());
62}
63
64//-----------------------------------------------------------------------------
65// Neutered by CordbModule
66// See CordbBase::Neuter for semantics.
67//-----------------------------------------------------------------------------
68void CordbClass::Neuter()
69{
70 // Reduce the reference count on the type object for this class
71 m_type.Clear();
72 CordbBase::Neuter();
73}
74
75
76
77
78//-----------------------------------------------------------------------------
79// Standard IUnknown::QI implementation.
80// See IUnknown::QI for standard semantics.
81//-----------------------------------------------------------------------------
82HRESULT CordbClass::QueryInterface(REFIID id, void **pInterface)
83{
84 if (id == IID_ICorDebugClass)
85 {
86 *pInterface = static_cast<ICorDebugClass*>(this);
87 }
88 else if (id == IID_ICorDebugClass2)
89 {
90 *pInterface = static_cast<ICorDebugClass2*>(this);
91 }
92 else if (id == IID_IUnknown)
93 {
94 *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugClass*>(this));
95 }
96 else
97 {
98 *pInterface = NULL;
99 return E_NOINTERFACE;
100 }
101
102 ExternalAddRef();
103 return S_OK;
104}
105
106//-----------------------------------------------------------------------------
107// Get a ICorDebugValue for a static field on this class.
108//
109// Parameters:
110// fieldDef - metadata token for field on this class. Can not be from an
111// inherited class.
112// pFrame - frame used to resolve Thread-static, AppDomain-static, etc.
113// ppValue - OUT: gets value of the field.
114//
115// Returns:
116// S_OK on success.
117// CORDBG_E_STATIC_VAR_NOT_AVAILABLE
118//-----------------------------------------------------------------------------
119HRESULT CordbClass::GetStaticFieldValue(mdFieldDef fieldDef,
120 ICorDebugFrame *pFrame,
121 ICorDebugValue **ppValue)
122{
123 PUBLIC_REENTRANT_API_ENTRY(this);
124 FAIL_IF_NEUTERED(this);
125 VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **);
126 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
127
128 HRESULT hr = S_OK;
129 *ppValue = NULL;
130 BOOL fEnCHangingField = FALSE;
131
132
133 IMetaDataImport * pImport = NULL;
134 EX_TRY
135 {
136 pImport = GetModule()->GetMetaDataImporter(); // throws
137
138 // Validate the token.
139 if (!pImport->IsValidToken(fieldDef) || (TypeFromToken(fieldDef) != mdtFieldDef))
140 {
141 ThrowHR(E_INVALIDARG);
142 }
143
144 // Make sure we have enough info about the class.
145 Init();
146
147 // Uninstantiated generics (eg, Foo<T>) don't have static data. Must use instantiated (eg Foo<int>)
148 // But all CordbClass instances are uninstantiated. So this should fail for all generic types.
149 // Normally, debuggers should be using ICorDebugType instead.
150 // Though in the forward compat case, they'll hit this.
151 if (HasTypeParams())
152 {
153 ThrowHR(CORDBG_E_STATIC_VAR_NOT_AVAILABLE);
154 }
155
156
157 // Lookup the field given its metadata token.
158 FieldData *pFieldData;
159
160 hr = GetFieldInfo(fieldDef, &pFieldData);
161
162 // This field was added by EnC, need to use EnC specific code path
163 if (hr == CORDBG_E_ENC_HANGING_FIELD)
164 {
165 // Static fields added with EnC hang off the EnCFieldDesc
166 hr = GetEnCHangingField(fieldDef,
167 &pFieldData,
168 NULL);
169
170 if (SUCCEEDED(hr))
171 {
172 fEnCHangingField = TRUE;
173 }
174 // Note: the FieldOffset in pFieldData has been cooked to produce
175 // the correct address of the field in the syncBlock.
176 // @todo: extend Debugger_IPCEFieldData so we don't have to cook the offset here
177 }
178
179 IfFailThrow(hr);
180
181 {
182 Instantiation emptyInst;
183
184 hr = CordbClass::GetStaticFieldValue2(GetModule(),
185 pFieldData,
186 fEnCHangingField,
187 &emptyInst,
188 pFrame,
189 ppValue);
190 // Let hr fall through
191 }
192 }
193 EX_CATCH_HRESULT(hr);
194
195 // Translate Failure HRs.
196 if (pImport != NULL)
197 {
198 hr = CordbClass::PostProcessUnavailableHRESULT(hr, pImport, fieldDef);
199 }
200
201 return hr;
202
203}
204
205//-----------------------------------------------------------------------------
206// Common helper for accessing statics from both CordbClass and CordbType.
207//
208// Arguments:
209// pModule - module containing the class
210// pFieldData - field data describing the field (this is correlated to a
211// mdFieldDef, but has more specific data)
212// fEnCHangingField - field storage hangs off the FieldDesc for EnC
213// pInst - generic instantiation.
214// pFrame - frame used for context for Thread-static, AD-static, etc.
215// ppValue - OUT: out parameter to get value.
216//
217// Returns:
218// S_OK on success.
219// CORDBG_E_FIELD_NOT_STATIC - if field isn't static.
220// CORDBG_E_STATIC_VAR_NOT_AVAILABLE - if field storage is not available.
221// Else some other failure.
222/* static */
223HRESULT CordbClass::GetStaticFieldValue2(CordbModule * pModule,
224 FieldData * pFieldData,
225 BOOL fEnCHangingField,
226 const Instantiation * pInst,
227 ICorDebugFrame * pFrame,
228 ICorDebugValue ** ppValue)
229{
230 FAIL_IF_NEUTERED(pModule);
231 INTERNAL_SYNC_API_ENTRY(pModule->GetProcess());
232 _ASSERTE((pModule->GetProcess()->GetShim() == NULL) || pModule->GetProcess()->GetSynchronized());
233 HRESULT hr = S_OK;
234
235 if (!pFieldData->m_fFldIsStatic)
236 {
237 return CORDBG_E_FIELD_NOT_STATIC;
238 }
239
240 CORDB_ADDRESS pRmtStaticValue = NULL;
241 CordbProcess * pProcess = pModule->GetProcess();
242
243 if (!pFieldData->m_fFldIsTLS)
244 {
245 if (pFieldData->m_fFldIsCollectibleStatic)
246 {
247 EX_TRY
248 {
249 pRmtStaticValue = pProcess->GetDAC()->GetCollectibleTypeStaticAddress(pFieldData->m_vmFieldDesc,
250 pModule->GetAppDomain()->GetADToken());
251 }
252 EX_CATCH_HRESULT(hr);
253 if(FAILED(hr))
254 {
255 return hr;
256 }
257 }
258 else
259 {
260 // Statics never move, so we always address them using their absolute address.
261 _ASSERTE(pFieldData->OkToGetOrSetStaticAddress());
262 pRmtStaticValue = pFieldData->GetStaticAddress();
263 }
264 }
265 else
266 {
267 // We've got a thread local static
268
269 if( fEnCHangingField )
270 {
271 // fEnCHangingField is set for fields added with EnC which hang off the FieldDesc.
272 // Thread-local statics cannot be added with EnC, so we shouldn't be here
273 // if this is an EnC field is thread-local.
274 _ASSERTE(!pFieldData->m_fFldIsTLS );
275 }
276 else
277 {
278 if (pFrame == NULL)
279 {
280 return E_INVALIDARG;
281 }
282
283 CordbFrame * pRealFrame = CordbFrame::GetCordbFrameFromInterface(pFrame);
284 _ASSERTE(pRealFrame != NULL);
285
286 // Get the thread we are working on
287 CordbThread * pThread = pRealFrame->m_pThread;
288
289 EX_TRY
290 {
291 pRmtStaticValue = pProcess->GetDAC()->GetThreadStaticAddress(pFieldData->m_vmFieldDesc,
292 pThread->m_vmThreadToken);
293 }
294 EX_CATCH_HRESULT(hr);
295 if(FAILED(hr))
296 {
297 return hr;
298 }
299
300 }
301 }
302
303 if (pRmtStaticValue == NULL)
304 {
305 // type probably wasn't loaded yet.
306 // The debugger may chose to func-eval the creation of an instance of this type and try again.
307 return CORDBG_E_STATIC_VAR_NOT_AVAILABLE;
308 }
309
310 SigParser sigParser;
311 hr = S_OK;
312 EX_TRY
313 {
314 hr = pFieldData->GetFieldSignature(pModule, &sigParser);
315 }
316 EX_CATCH_HRESULT(hr);
317 IfFailRet(hr);
318
319 CordbType * pType;
320 IfFailRet (CordbType::SigToType(pModule, &sigParser, pInst, &pType));
321
322 bool fIsValueClass = false;
323 EX_TRY
324 {
325 fIsValueClass = pType->IsValueType(); // throws
326 }
327 EX_CATCH_HRESULT(hr);
328 IfFailRet(hr);
329
330 // Static value classes are stored as handles so that GC can deal with them properly. Thus, we need to follow the
331 // handle like an objectref. Do this by forcing CreateValueByType to think this is an objectref. Note: we don't do
332 // this for value classes that have an RVA, since they're layed out at the RVA with no handle.
333 bool fIsBoxed = (fIsValueClass &&
334 !pFieldData->m_fFldIsRVA &&
335 !pFieldData->m_fFldIsPrimitive &&
336 !pFieldData->m_fFldIsTLS);
337
338 TargetBuffer remoteValue(pRmtStaticValue, CordbValue::GetSizeForType(pType, fIsBoxed ? kBoxed : kUnboxed));
339 ICorDebugValue * pValue;
340
341 EX_TRY
342 {
343 CordbValue::CreateValueByType(pModule->GetAppDomain(),
344 pType,
345 fIsBoxed,
346 remoteValue,
347 MemoryRange(NULL, 0),
348 NULL,
349 &pValue); // throws
350 }
351 EX_CATCH_HRESULT(hr);
352
353 if (SUCCEEDED(hr))
354 {
355 *ppValue = pValue;
356 }
357
358 return hr;
359}
360
361
362//-----------------------------------------------------------------------------
363// Public method to build a CordbType from a CordbClass.
364// This is used to build up generic types. Eg, build:
365// List<T> + { int } --> List<int>
366//
367// Arguments:
368// elementType - element type. Either ELEMENT_TYPE_CLASS, or ELEMENT_TYPE_VALUETYPE.
369// We could technically figure this out from the metadata (by looking if it derives
370// from System.ValueType).
371// cTypeArgs - number of elements in rgpTypeArgs array
372// rgpTypeArgs - array for type args.
373// ppType - OUT: out parameter to hold resulting type.
374//
375// Returns:
376// S_OK on success. Else false.
377//
378HRESULT CordbClass::GetParameterizedType(CorElementType elementType,
379 ULONG32 cTypeArgs,
380 ICorDebugType * rgpTypeArgs[],
381 ICorDebugType ** ppType)
382{
383 PUBLIC_API_ENTRY(this);
384 FAIL_IF_NEUTERED(this);
385 VALIDATE_POINTER_TO_OBJECT(ppType, ICorDebugType **);
386 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
387
388 // Note: Do not call Init() to find out if its a VC or not.
389 // Rather expect the client to tell us. This means the debug client
390 // can describe type instantiations not yet seen in the EE.
391
392 if ((elementType != ELEMENT_TYPE_CLASS) && (elementType != ELEMENT_TYPE_VALUETYPE))
393 {
394 return E_INVALIDARG;
395 }
396
397 // Prefast overflow check:
398 S_UINT32 allocSize = S_UINT32( cTypeArgs ) * S_UINT32( sizeof(CordbType *) );
399
400 if (allocSize.IsOverflow())
401 {
402 return E_INVALIDARG;
403 }
404
405 CordbAppDomain * pClassAppDomain = GetAppDomain();
406
407 // Note: casting from (ICorDebugType **) to (CordbType **) is not valid.
408 // Offsets may differ. Copy and validate the type array.
409 CordbType ** ppArgTypes = reinterpret_cast<CordbType **>(_alloca( allocSize.Value()));
410
411 for (unsigned int i = 0; i < cTypeArgs; i++)
412 {
413 ppArgTypes[i] = static_cast<CordbType *>( rgpTypeArgs[i] );
414
415 CordbAppDomain * pArgAppDomain = ppArgTypes[i]->GetAppDomain();
416
417 if ((pArgAppDomain != NULL) && (pArgAppDomain != pClassAppDomain))
418 {
419 return CORDBG_E_APPDOMAIN_MISMATCH;
420 }
421 }
422
423 {
424 CordbType * pResultType;
425
426 Instantiation typeInstantiation(cTypeArgs, ppArgTypes);
427
428 HRESULT hr = CordbType::MkType(pClassAppDomain, elementType, this, &typeInstantiation, &pResultType);
429
430 if (FAILED(hr))
431 {
432 return hr;
433 }
434
435 *ppType = pResultType;
436 }
437
438 _ASSERTE(*ppType);
439
440 if (*ppType)
441 {
442 (*ppType)->AddRef();
443 }
444 return S_OK;
445}
446
447//-----------------------------------------------------------------------------
448// Returns true if the field is a static literal.
449// In this case, the debugger should get the value from the metadata.
450//-----------------------------------------------------------------------------
451bool IsFieldStaticLiteral(IMetaDataImport *pImport, mdFieldDef fieldDef)
452{
453 DWORD dwFieldAttr;
454 HRESULT hr2 = pImport->GetFieldProps(
455 fieldDef,
456 NULL,
457 NULL,
458 0,
459 NULL,
460 &dwFieldAttr,
461 NULL,
462 0,
463 NULL,
464 NULL,
465 0);
466
467 if (SUCCEEDED(hr2) && IsFdLiteral(dwFieldAttr))
468 {
469 return true;
470 }
471
472 return false;
473}
474
475
476//-----------------------------------------------------------------------------
477// Filter to determine a more descriptive failing HResult for a field lookup.
478//
479// Parameters:
480// hr - incoming ambiguous HR.
481// pImport - metadata importer for this class.
482// feildDef - field being looked up.
483//
484// Returns:
485// hr - the incoming HR if no further HR can be determined.
486// else another failing HR that it judged to be more specific that the incoming HR.
487//-----------------------------------------------------------------------------
488HRESULT CordbClass::PostProcessUnavailableHRESULT(HRESULT hr,
489 IMetaDataImport *pImport,
490 mdFieldDef fieldDef)
491{
492 CONTRACTL
493 {
494 NOTHROW; // just translates an HR. shouldn't need to throw.
495 }
496 CONTRACTL_END;
497
498 if (hr == CORDBG_E_FIELD_NOT_AVAILABLE)
499 {
500 if (IsFieldStaticLiteral(pImport, fieldDef))
501 {
502 return CORDBG_E_VARIABLE_IS_ACTUALLY_LITERAL;
503 }
504 }
505
506 return hr;
507}
508
509//-----------------------------------------------------------------------------
510// Public method to get the Module that this class lives in.
511//
512// Parameters:
513// ppModule - OUT: holds module that this class gets in.
514//
515// Returns:
516// S_OK on success.
517//-----------------------------------------------------------------------------
518HRESULT CordbClass::GetModule(ICorDebugModule **ppModule)
519{
520 FAIL_IF_NEUTERED(this);
521 VALIDATE_POINTER_TO_OBJECT(ppModule, ICorDebugModule **);
522
523 *ppModule = static_cast<ICorDebugModule*> (m_pModule);
524 m_pModule->ExternalAddRef();
525
526 return S_OK;
527}
528
529//-----------------------------------------------------------------------------
530// Get the mdTypeDef token that this class corresponds to.
531//
532// Parameters:
533// pTypeDef - OUT: out param to get typedef token.
534//
535// Returns:
536// S_OK - on success.
537//-----------------------------------------------------------------------------
538HRESULT CordbClass::GetToken(mdTypeDef *pTypeDef)
539{
540 FAIL_IF_NEUTERED(this);
541 VALIDATE_POINTER_TO_OBJECT(pTypeDef, mdTypeDef *);
542
543 _ASSERTE(TypeFromToken(m_token) == mdtTypeDef);
544
545 *pTypeDef = m_token;
546
547 return S_OK;
548}
549
550//-----------------------------------------------------------------------------
551// Set the JMC status on all of our member functions.
552// The current implementation just uses the metadata to enumerate all
553// methods and then calls SetJMCStatus on each method.
554// This isn't great perf, but this should never be needed in a
555// perf-critical situation.
556//
557// Parameters:
558// fIsUserCode - true to set entire class to user code. False to set to
559// non-user code.
560//
561// Returns:
562// S_OK on success. On failure, the user-code status of the methods in the
563// class is random.
564//-----------------------------------------------------------------------------
565HRESULT CordbClass::SetJMCStatus(BOOL fIsUserCode)
566{
567 PUBLIC_REENTRANT_API_ENTRY(this);
568 FAIL_IF_NEUTERED(this);
569 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
570
571 // Get the member functions via a meta data interface
572 CordbModule * pModule = GetModule();
573
574 // Ensure that our process is in a sane state.
575 CordbProcess * pProcess = pModule->GetProcess();
576 _ASSERTE(pProcess != NULL);
577
578 IMetaDataImport * pImport = NULL;
579 HCORENUM phEnum = 0;
580
581 HRESULT hr = S_OK;
582
583 mdMethodDef rTokens[100];
584 ULONG i;
585 ULONG count;
586
587 EX_TRY
588 {
589 pImport = pModule->GetMetaDataImporter();
590 do
591 {
592 hr = pImport->EnumMethods(&phEnum, m_token, rTokens, NumItems(rTokens), &count);
593 IfFailThrow(hr);
594
595 for (i = 0; i < count; i++)
596 {
597 RSLockHolder lockHolder(pProcess->GetProcessLock());
598 // Need the ICorDebugFunction to query for JMC status.
599 CordbFunction * pFunction = pModule->LookupOrCreateFunctionLatestVersion(rTokens[i]);
600
601 lockHolder.Release(); // Must release before sending an IPC event
602 hr = pFunction->SetJMCStatus(fIsUserCode);
603 IfFailThrow(hr);
604 }
605 }
606 while (count > 0);
607
608 _ASSERTE(SUCCEEDED(hr));
609 }
610 EX_CATCH_HRESULT(hr);
611
612 if ((pImport != NULL) && (phEnum != 0))
613 {
614 pImport->CloseEnum(phEnum);
615 }
616
617 return hr;
618
619}
620
621//-----------------------------------------------------------------------------
622// We have to go the the EE to find out if a class is a value
623// class or not. This is because there is no flag for this, but rather
624// it depends on whether the class subclasses System.ValueType (apart
625// from System.Enum...). Replicating all that resoultion logic
626// does not seem like a good plan.
627//
628// We also accept other "evidence" that the class is or isn't a VC, in
629// particular:
630// - It is definitely a VC if it has been used after a
631// E_T_VALUETYPE in a signature.
632// - It is definitely not a VC if it has been used after a
633// E_T_CLASS in a signature.
634// - It is definitely a VC if it has been used in combination with
635// E_T_VALUETYPE in one of COM API operations that take both
636// a ICorDebugClass and a CorElementType (e.g. GetParameterizedType)
637//
638// !!!Note the following!!!!
639// - A class may still be a VC even if it has been
640// used in combination with E_T_CLASS in one of COM API operations that take both
641// a ICorDebugClass and a CorElementType (e.g. GetParameterizedType).
642// We allow the user of the API to specify E_T_CLASS when the VC status
643// is not known or is not important.
644//
645// Return Value:
646// indicates whether this is a value-class
647//
648// Notes:
649// Throws CORDBG_E_CLASS_NOT_LOADED or synchronization errors on failure
650//-----------------------------------------------------------------------------
651bool CordbClass::IsValueClass()
652{
653 INTERNAL_API_ENTRY(this);
654 THROW_IF_NEUTERED(this);
655
656 if (!m_fIsValueClassKnown)
657 {
658 ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(GetProcess(), ThrowHR);
659 Init();
660 }
661 return m_fIsValueClass;
662}
663
664//-----------------------------------------------------------------------------
665// Get a CordbType for the 'this' pointer of a method in a CordbClass.
666// The 'this' pointer is argument #0 in an instance method.
667//
668// For ReferenceTypes (ELEMENT_TYPE_CLASS), the 'this' pointer is just a
669// normal reference, and so GetThisType() behaves like GetParameterizedType().
670// For ValueTypes, the 'this' pointer is a byref.
671//
672// Arguments:
673// pInst - instantiation info (eg, the type parameters) to produce CordbType
674// ppResultType - OUT: out parameter to hold outgoing CordbType.
675//
676// Returns:
677// S_OK on success. Else failure.
678//
679HRESULT CordbClass::GetThisType(const Instantiation * pInst, CordbType ** ppResultType)
680{
681 FAIL_IF_NEUTERED(this);
682
683 HRESULT hr = S_OK;
684 // Note: We have to call Init() here to find out if it really a VC or not.
685 bool fIsValueClass = false;
686 EX_TRY
687 {
688 fIsValueClass = IsValueClass();
689 }
690 EX_CATCH_HRESULT(hr);
691
692 if (FAILED(hr))
693 {
694 return hr;
695 }
696
697 if (fIsValueClass)
698 {
699 CordbType *pType;
700
701 hr = CordbType::MkType(GetAppDomain(), // OK: this E_T_VALUETYPE will be normalized by MkType
702 ELEMENT_TYPE_VALUETYPE,
703 this,
704 pInst,
705 &pType);
706
707 if (!SUCCEEDED(hr))
708 {
709 return hr;
710 }
711
712 hr = CordbType::MkType(GetAppDomain(), ELEMENT_TYPE_BYREF, 0, pType, ppResultType);
713
714 if (!SUCCEEDED(hr))
715 {
716 return hr;
717 }
718 }
719 else
720 {
721 hr = CordbType::MkType(GetAppDomain(), ELEMENT_TYPE_CLASS, this, pInst, ppResultType);
722
723 if (!SUCCEEDED(hr))
724 {
725 return hr;
726 }
727 }
728
729 return hr;
730}
731
732
733//-----------------------------------------------------------------------------
734// Initialize the CordbClass.
735// This will collect all the field information via the DAC, and also determine
736// whether this Type is a ReferenceType or ValueType.
737//
738// Parameters:
739// fForceInit - if true, always reinitialize. If false, may skip
740// initialization if we believe we already have the info.
741//
742// Note:
743// Throws CORDBG_E_CLASS_NOT_LOADED on failure
744//-----------------------------------------------------------------------------
745void CordbClass::Init(ClassLoadLevel desiredLoadLevel)
746{
747 INTERNAL_SYNC_API_ENTRY(this->GetProcess());
748
749 CordbProcess * pProcess = GetProcess();
750 IDacDbiInterface* pDac = pProcess->GetDAC();
751
752 // If we've done a continue since the last time we got hanging static fields,
753 // we should clear out our cache, since everything may have moved.
754 if (m_continueCounterLastSync < GetProcess()->m_continueCounter)
755 {
756 m_hangingFieldsStatic.Clear();
757 m_continueCounterLastSync = GetProcess()->m_continueCounter;
758 }
759
760 if (m_loadLevel < desiredLoadLevel)
761 {
762 // reset everything
763 m_loadLevel = Constructed;
764 m_fIsValueClass = false;
765 m_fIsValueClassKnown = false;
766 m_fHasTypeParams = false;
767 m_classInfo.Clear();
768 // @dbgtodo Microsoft inspection: declare a constant to replace badbad
769 m_classInfo.m_objectSize = 0xbadbad;
770 VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr();
771
772 // basic info load level
773 if(desiredLoadLevel >= BasicInfo)
774 {
775 vmTypeHandle = pDac->GetTypeHandle(m_pModule->GetRuntimeModule(), GetToken());
776 SetIsValueClass(pDac->IsValueType(vmTypeHandle));
777 m_fHasTypeParams = !!pDac->HasTypeParams(vmTypeHandle);
778 m_loadLevel = BasicInfo;
779 }
780
781 // full info load level
782 if(desiredLoadLevel == FullInfo)
783 {
784 VMPTR_AppDomain vmAppDomain = VMPTR_AppDomain::NullPtr();
785 VMPTR_DomainFile vmDomainFile = m_pModule->GetRuntimeDomainFile();
786 if (!vmDomainFile.IsNull())
787 {
788 DomainFileInfo info;
789 pDac->GetDomainFileData(vmDomainFile, &info);
790 vmAppDomain = info.vmAppDomain;
791 }
792 pDac->GetClassInfo(vmAppDomain, vmTypeHandle, &m_classInfo);
793
794 BOOL fGotUnallocatedStatic = GotUnallocatedStatic(&m_classInfo.m_fieldList);
795
796 // if we have an unallocated static don't record that we reached FullInfo stage
797 // this seems pretty ugly but I don't want to bite off cleaning this up just yet
798 // Not saving the FullInfo stage effectively means future calls to Init() will
799 // re-init everything and some parts of DBI may be depending on that re-initialization
800 // with alternate data in order to operate correctly
801 if(!fGotUnallocatedStatic)
802 m_loadLevel = FullInfo;
803 }
804 }
805} // CordbClass::Init
806
807// determine if any fields for a type are unallocated statics
808BOOL CordbClass::GotUnallocatedStatic(DacDbiArrayList<FieldData> * pFieldList)
809{
810 BOOL fGotUnallocatedStatic = FALSE;
811 int count = 0;
812 while ((count < pFieldList->Count()) && !fGotUnallocatedStatic )
813 {
814 if ((*pFieldList)[count].OkToGetOrSetStaticAddress() &&
815 (*pFieldList)[count].GetStaticAddress() == NULL )
816 {
817 // The address for a regular static field isn't available yet
818 // How can this happen? Statics appear to get allocated during domain load.
819 // There may be some laziness or a race-condition involved.
820 fGotUnallocatedStatic = TRUE;
821 }
822 ++count;
823 }
824 return fGotUnallocatedStatic;
825} // CordbClass::GotUnallocatedStatic
826
827/*
828 * FieldData::GetFieldSignature
829 *
830 * Get the field's full metadata signature. This may be cached, but for dynamic modules we'll always read it from
831 * the metadata.
832 *
833 * Parameters:
834 * pModule - pointer to the module that contains the field
835 *
836 * pSigParser - OUT: the full signature for the field.
837 *
838 * Returns:
839 * HRESULT for success or failure.
840 *
841 */
842HRESULT FieldData::GetFieldSignature(CordbModule *pModule,
843 SigParser *pSigParser)
844{
845 CONTRACTL
846 {
847 THROWS;
848 }
849 CONTRACTL_END;
850
851 INTERNAL_SYNC_API_ENTRY(pModule->GetProcess());
852
853 HRESULT hr = S_OK;
854
855 IMetaDataImport * pImport = pModule->GetMetaDataImporter(); // throws;
856
857 PCCOR_SIGNATURE fieldSignature = NULL;
858 ULONG size = ((ULONG) -1);
859
860 _ASSERTE(pSigParser != NULL);
861
862 // If the module is dynamic, there had better not be a cached field signature.
863 _ASSERTE(!pModule->IsDynamic() || (m_fldSignatureCache == NULL));
864
865 // If the field signature cache is null, or if this is a dynamic module, then go read the signature from the
866 // matadata. We always read from the metadata for dynamic modules because our metadata blob is constantly
867 // getting deleted and re-allocated. If we kept a pointer to the signature, we'd end up pointing to bad data.
868 if (m_fldSignatureCache == NULL)
869 {
870 // Go to the metadata for all fields: previously the left-side tranferred over
871 // single-byte signatures as part of the field info. Since the left-side
872 // goes to the metadata anyway, and we already fetch plenty of other metadata,
873 // I don't believe that fetching it here instead of transferring it over
874 // is going to slow things down at all, and
875 // in any case will not be where the primary optimizations lie...
876
877 IfFailRet(pImport->GetFieldProps(m_fldMetadataToken, NULL, NULL, 0, NULL, NULL,
878 &fieldSignature,
879 &size,
880 NULL, NULL, NULL));
881
882 // Point past the calling convention
883 CorCallingConvention conv;
884
885 // Move pointer,
886 BYTE * pOldPtr = (BYTE*) fieldSignature;
887 conv = (CorCallingConvention) CorSigUncompressData(fieldSignature);
888 _ASSERTE(conv == IMAGE_CEE_CS_CALLCONV_FIELD);
889 size -= (ULONG) (((BYTE*) fieldSignature) - pOldPtr); // since we updated filedSignature, adjust size
890
891 // Although the pointer will keep updating, the size should be the same. So we assert that.
892 _ASSERTE((m_fldSignatureCacheSize == 0) || (m_fldSignatureCacheSize == size));
893
894 // Cache the value for non-dynamic modules, so this is faster later.
895 // Since we're caching in a FieldData, we can't store the actual SigParser object.
896 if (!pModule->IsDynamic())
897 {
898 m_fldSignatureCache = fieldSignature;
899 m_fldSignatureCacheSize = size;
900 }
901 }
902 else
903 {
904 // We have a cached value, so return it. Note: we should never have a cached value for a field in a dynamic
905 // module.
906 CONSISTENCY_CHECK_MSGF((!pModule->IsDynamic()),
907 ("We should never cache a field signature in a dynamic module! Module=%p This=%p",
908 pModule, this));
909
910 fieldSignature = m_fldSignatureCache;
911 size = m_fldSignatureCacheSize;
912 }
913
914 _ASSERTE(fieldSignature != NULL);
915 _ASSERTE(size != ((ULONG) -1));
916 *pSigParser = SigParser(fieldSignature, size);
917 return hr;
918}
919
920// CordbClass::InitEnCFieldInfo
921// Initializes an instance of EnCHangingFieldInfo.
922// Arguments:
923// input: fStatic - flag to indicate whether the EnC field is static
924// pObject - For instance fields, the Object instance containing the the sync-block.
925// For static fields (if this is being called from GetStaticFieldValue) object is NULL.
926// fieldToken - token for the EnC field
927// metadataToken - metadata token for this instance of CordbClass
928// output: pEncField - the fields of this class will be appropriately initialized
929void CordbClass::InitEnCFieldInfo(EnCHangingFieldInfo * pEncField,
930 BOOL fStatic,
931 CordbObjectValue * pObject,
932 mdFieldDef fieldToken,
933 mdTypeDef classToken)
934{
935 IDacDbiInterface * pInterface = GetProcess()->GetDAC();
936
937 if (fStatic)
938 {
939 // the field is static, we don't need any additional data
940 pEncField->Init(VMPTR_Object::NullPtr(), /* vmObject */
941 NULL, /* offsetToVars */
942 fieldToken,
943 ELEMENT_TYPE_MAX,
944 classToken,
945 m_pModule->GetRuntimeDomainFile());
946 }
947 else
948 {
949 // This is an instance field, we need to pass a bunch of type information back
950 _ASSERTE(pObject != NULL);
951
952 pEncField->Init(pInterface->GetObject(pObject->m_id), // VMPTR to the object instance of interest.
953 pObject->GetInfo().objOffsetToVars, // The offset from the beginning of the object
954 // to the beginning of the fields. Fields added
955 // with EnC don't actually reside in the object
956 // (they hang off the sync block instead), so
957 // this is used to compute the returned field
958 // offset (fieldData.m_fldInstanceOffset). This
959 // makes it appear to be an offset from the object.
960 // Ideally we wouldn't do any of this, and just
961 // explicitly deal with absolute addresses (instead
962 // of "offsets") for EnC hanging instance fields.
963 fieldToken, // Field token for the added field.
964 pObject->GetInfo().objTypeData.elementType, // An indication of the type of object to which
965 // we're adding a field (specifically,
966 // whether it's a value type or a class).
967 // This is used only for log messages, and could
968 // be removed.
969 classToken, // metadata token for the class
970 m_pModule->GetRuntimeDomainFile()); // Domain file for the class
971 }
972} // CordbClass::InitFieldData
973
974// CordbClass::GetEnCFieldFromDac
975// Get information via the DAC about a field added with Edit and Continue.
976// Arguments:
977// input: fStatic - flag to indicate whether the EnC field is static
978// pObject - For instance fields, the Object instance containing the the sync-block.
979// For static fields (if this is being called from GetStaticFieldValue) object is NULL.
980// fieldToken - token for the EnC field
981// output: pointer to an initialized instance of FieldData that has been added to the appropriate table
982// in our cache
983FieldData * CordbClass::GetEnCFieldFromDac(BOOL fStatic,
984 CordbObjectValue * pObject,
985 mdFieldDef fieldToken)
986{
987 EnCHangingFieldInfo encField;
988 mdTypeDef metadataToken;
989 FieldData fieldData,
990 * pInfo = NULL;
991 BOOL fDacStatic;
992 CordbProcess * pProcess = GetModule()->GetProcess();
993
994 _ASSERTE(pProcess != NULL);
995 IfFailThrow(GetToken(&metadataToken));
996 InitEnCFieldInfo(&encField, fStatic, pObject, fieldToken, metadataToken);
997
998 // Go get this particular field.
999 pProcess->GetDAC()->GetEnCHangingFieldInfo(&encField, &fieldData, &fDacStatic);
1000 _ASSERTE(fStatic == fDacStatic);
1001
1002 // Save the field results in our cache and get a stable pointer to the data
1003 if (fStatic)
1004 {
1005 pInfo = m_hangingFieldsStatic.AddFieldInfo(&fieldData);
1006 }
1007 else
1008 {
1009 pInfo = pObject->GetHangingFieldTable()->AddFieldInfo(&fieldData);
1010 }
1011
1012 // We should have a fresh copy of the data (don't want to return a pointer to data on our stack)
1013 _ASSERTE((void *)pInfo != (void *)&fieldData);
1014 _ASSERTE(pInfo->m_fFldIsStatic == (fStatic == TRUE));
1015 _ASSERTE(pInfo->m_fldMetadataToken == fieldToken);
1016
1017 // Pass a pointer to the data out.
1018 return pInfo;
1019} // CordbClass::GetEnCFieldFromDac
1020
1021//-----------------------------------------------------------------------------
1022// Internal helper to get a FieldData for fields added by EnC after the type
1023// was loaded. Since object and MethodTable layout has already been fixed,
1024// such added fields are "hanging" off some other data structure. For instance
1025// fields, they're stored in a syncblock off the object. For static fields
1026// they're stored off the EnCFieldDesc.
1027//
1028// The caller must have already determined this is a hanging field (i.e.
1029// GetFieldInfo returned CORDBG_E_ENC_HANGING_FIELDF).
1030//
1031// Arguments:
1032// input: fldToken - field of interest to get.
1033// pObject - For instance fields, the Object instance containing the the sync-block.
1034// For static fields (if this is being called from GetStaticFieldValue) object is NULL.
1035// output: ppFieldData - the FieldData matching the fldToken.
1036//
1037// Returns:
1038// S_OK on success, failure code otherwise.
1039//-----------------------------------------------------------------------------
1040HRESULT CordbClass::GetEnCHangingField(mdFieldDef fldToken,
1041 FieldData **ppFieldData,
1042 CordbObjectValue * pObject)
1043{
1044 FAIL_IF_NEUTERED(this);
1045 INTERNAL_SYNC_API_ENTRY(GetProcess());
1046
1047 HRESULT hr = S_OK;
1048 _ASSERTE(pObject == NULL || !pObject->IsNeutered() );
1049
1050 if (HasTypeParams())
1051 {
1052 _ASSERTE(!"EnC hanging field not yet implemented on constructed types!");
1053 return E_FAIL;
1054 }
1055
1056 // This must be a static field if no object was supplied
1057 BOOL fStatic = (pObject == NULL);
1058
1059 // Look for cached field information
1060 FieldData *pInfo = NULL;
1061 if (fStatic)
1062 {
1063 // Static fields should _NOT_ be cleared, since they stick around. Thus
1064 // the separate tables.
1065 pInfo = m_hangingFieldsStatic.GetFieldInfo(fldToken);
1066 }
1067 else
1068 {
1069 // We must get new copies each time we call continue b/c we get the
1070 // actual Object ptr from the left side, which can move during a GC.
1071 pInfo = pObject->GetHangingFieldTable()->GetFieldInfo(fldToken);
1072 }
1073
1074 // We've found a previously located entry
1075 if (pInfo != NULL)
1076 {
1077 *ppFieldData = pInfo;
1078 return S_OK;
1079 }
1080
1081 // Field information not already available - go get it
1082 EX_TRY
1083 {
1084
1085 // We're not going to be able to get the instance-specific field
1086 // if we can't get the instance.
1087 if (!fStatic && pObject->GetInfo().objRefBad)
1088 {
1089 ThrowHR(CORDBG_E_INVALID_OBJECT);
1090 }
1091
1092 *ppFieldData = GetEnCFieldFromDac(fStatic, pObject, fldToken);
1093 }
1094 EX_CATCH_HRESULT(hr);
1095 return hr;
1096}
1097
1098//-----------------------------------------------------------------------------
1099// Get a FieldData (which rich information, including details about storage)
1100// from a metadata token.
1101//
1102// Parameters:
1103// fldToken - incoming metadata token specifying the field.
1104// ppFieldData - OUT: resulting FieldData structure.
1105//
1106// Returns:
1107// S_OK on success. else failure.
1108//-----------------------------------------------------------------------------
1109HRESULT CordbClass::GetFieldInfo(mdFieldDef fldToken, FieldData **ppFieldData)
1110{
1111 INTERNAL_SYNC_API_ENTRY(GetProcess());
1112
1113 Init();
1114 return SearchFieldInfo(GetModule(), &m_classInfo.m_fieldList, m_token, fldToken, ppFieldData);
1115}
1116
1117
1118//-----------------------------------------------------------------------------
1119// Search an array of FieldData (pFieldList) for a field (fldToken).
1120// The FieldData array must match the class supplied by classToken, and live
1121// in the supplied module.
1122//
1123// Internal helper used by CordbType::GetFieldInfo, CordbClass::GetFieldInfo
1124//
1125// Parameters:
1126// module - module containing the class that the FieldData array matches.
1127// pFieldList - array of fields to search through and the number of elements in
1128// the array.
1129// classToken - class that the data array matches. class must live in
1130// the supplied moudle.
1131// fldToken - metadata token of the field to search for. This field should be
1132// on the class supplied by classToken.
1133//
1134// Returns:
1135// CORDBG_E_ENC_HANGING_FIELD for "hanging fields" (fields added via Enc) (common error).
1136// Returns S_OK, set ppFieldData = pointer into data array for matching field. (*retval)->m_fldMetadataToken == fldToken)
1137// Throws on other errors.
1138//-----------------------------------------------------------------------------
1139/* static */
1140HRESULT CordbClass::SearchFieldInfo(
1141 CordbModule * pModule,
1142 DacDbiArrayList<FieldData> * pFieldList,
1143 mdTypeDef classToken,
1144 mdFieldDef fldToken,
1145 FieldData **ppFieldData
1146)
1147{
1148 int i;
1149
1150 IMetaDataImport * pImport = pModule->GetMetaDataImporter(); // throws
1151
1152 HRESULT hr = S_OK;
1153 for (i = 0; i < pFieldList->Count(); i++)
1154 {
1155 if ((*pFieldList)[i].m_fldMetadataToken == fldToken)
1156 {
1157 // If the storage for this field isn't yet available (i.e. it is newly added with EnC)
1158 if (!(*pFieldList)[i].m_fFldStorageAvailable)
1159 {
1160 // If we're a static literal, then return special HR to let
1161 // debugger know that it should look it up via the metadata.
1162 // Check m_fFldIsStatic first b/c that's fast.
1163 if ((*pFieldList)[i].m_fFldIsStatic)
1164 {
1165 if (IsFieldStaticLiteral(pImport, fldToken))
1166 {
1167 ThrowHR(CORDBG_E_VARIABLE_IS_ACTUALLY_LITERAL);
1168 }
1169 }
1170
1171 // This is a field added by EnC, caller needs to get instance-specific info.
1172 return CORDBG_E_ENC_HANGING_FIELD;
1173 }
1174
1175 *ppFieldData = &((*pFieldList)[i]);
1176 return S_OK;
1177 }
1178 }
1179
1180 // Hmmm... we didn't find the field on this class. See if the field really belongs to this class or not.
1181 mdTypeDef classTok;
1182
1183 hr = pImport->GetFieldProps(fldToken, &classTok, NULL, 0, NULL, NULL, NULL, 0, NULL, NULL, NULL);
1184 IfFailThrow(hr);
1185
1186 if (classTok == (mdTypeDef) classToken)
1187 {
1188 // Well, the field belongs in this class. The assumption is that the Runtime optimized the field away.
1189 ThrowHR(CORDBG_E_FIELD_NOT_AVAILABLE);
1190 }
1191
1192 // Well, the field doesn't even belong to this class...
1193 ThrowHR(E_INVALIDARG);
1194}
1195
1196