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 | //----------------------------------------------------------------------------- |
28 | CordbClass::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 | //----------------------------------------------------------------------------- |
58 | CordbClass::~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 | //----------------------------------------------------------------------------- |
68 | void 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 | //----------------------------------------------------------------------------- |
82 | HRESULT 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 | //----------------------------------------------------------------------------- |
119 | HRESULT 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 */ |
223 | HRESULT 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 | // |
378 | HRESULT 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 | //----------------------------------------------------------------------------- |
451 | bool 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 | //----------------------------------------------------------------------------- |
488 | HRESULT 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 | //----------------------------------------------------------------------------- |
518 | HRESULT 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 | //----------------------------------------------------------------------------- |
538 | HRESULT 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 | //----------------------------------------------------------------------------- |
565 | HRESULT 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 | //----------------------------------------------------------------------------- |
651 | bool 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 | // |
679 | HRESULT 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 | //----------------------------------------------------------------------------- |
745 | void 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 |
808 | BOOL 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 | */ |
842 | HRESULT 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 |
929 | void 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 |
983 | FieldData * 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 | //----------------------------------------------------------------------------- |
1040 | HRESULT 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 | //----------------------------------------------------------------------------- |
1109 | HRESULT 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 */ |
1140 | HRESULT 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 | |