// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. //***************************************************************************** // File: rstype.cpp // // // Define implementation of ICorDebugType //***************************************************************************** #include "stdafx.h" #include "winbase.h" #include "corpriv.h" //----------------------------------------------------------------------------- // Public method to get the static field from a type. // // Parameters: // fieldDef - metadata token for which field on this type to retrieve. // pFrame - context for Thread/AppDomains statics. // ppValue - OUT: out-parameter to get value. // // Returns: // S_OK on success. // HRESULT CordbType::GetStaticFieldValue(mdFieldDef fieldDef, ICorDebugFrame * pFrame, ICorDebugValue ** ppValue) { PUBLIC_REENTRANT_API_ENTRY(this); FAIL_IF_NEUTERED(this); VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); HRESULT hr = S_OK; IMetaDataImport * pImport = NULL; EX_TRY { // Ensure we were actually passed a mdFieldDef. This is especially useful to protect // against an accidental mdPropertyDef, because properties look like fields. // if (TypeFromToken(fieldDef) != mdtFieldDef) { ThrowHR(E_INVALIDARG); } pImport = m_pClass->GetModule()->GetMetaDataImporter(); // throws if (((m_elementType != ELEMENT_TYPE_CLASS) && (m_elementType != ELEMENT_TYPE_VALUETYPE)) || (m_pClass == NULL)) { ThrowHR(E_INVALIDARG); } BOOL fSyncBlockField = FALSE; // If non generic type, then degenerate to CordbClass implementation. if (m_inst.m_cInst == 0) { hr = m_pClass->GetStaticFieldValue(fieldDef, pFrame, ppValue); } else { *ppValue = NULL; // Validate the token. if (!pImport->IsValidToken(fieldDef)) { ThrowHR(hr = E_INVALIDARG); } // Make sure we have enough info about the class. hr = Init(FALSE); IfFailThrow(hr); // Lookup the field given its metadata token. FieldData * pFieldData; hr = GetFieldInfo(fieldDef, &pFieldData); if (hr == CORDBG_E_ENC_HANGING_FIELD) { // Generics + EnC is Not supported. hr = CORDBG_E_STATIC_VAR_NOT_AVAILABLE; } IfFailThrow(hr); hr = CordbClass::GetStaticFieldValue2(m_pClass->GetModule(), pFieldData, fSyncBlockField, &m_inst, pFrame, ppValue); // fall through to translate HR } } EX_CATCH_HRESULT(hr); if (pImport != NULL) { hr = CordbClass::PostProcessUnavailableHRESULT(hr, pImport, fieldDef); } return hr; } // Combine E_T_s and rank together to get an id for the m_sharedtypes table #define CORDBTYPE_ID(elementType,rank) ((unsigned int) elementType * (rank + 1) + 1) //----------------------------------------------------------------------------- // Constructor // Builds a CordbType around a primitive. //----------------------------------------------------------------------------- CordbType::CordbType(CordbAppDomain *appdomain, CorElementType et, unsigned int rank) : CordbBase(appdomain->GetProcess(), CORDBTYPE_ID(et,rank) , enumCordbType), m_elementType(et), m_appdomain(appdomain), m_pClass(NULL), m_rank(rank), m_spinetypes(2), m_objectSize(0), m_fieldInfoNeedsInit(TRUE) { m_typeHandleExact = VMPTR_TypeHandle::NullPtr(); _ASSERTE(m_elementType != ELEMENT_TYPE_VALUETYPE); HRESULT hr = S_OK; EX_TRY { m_appdomain->AddToTypeList(this); } EX_CATCH_HRESULT(hr); SetUnrecoverableIfFailed(GetProcess(), hr); } //----------------------------------------------------------------------------- // Constructor // Builds a CordbType around a class. This is an Open CordbType. // For a generic type, this CordbType will not have the generic parameters, // but it will be a subordinate type to another Closed (instantiated) CordbType //----------------------------------------------------------------------------- CordbType::CordbType(CordbAppDomain *appdomain, CorElementType et, CordbClass *cls) : CordbBase(appdomain->GetProcess(), et, enumCordbType), m_elementType(et), m_appdomain(appdomain), m_pClass(cls), m_rank(0), m_spinetypes(2), m_objectSize(0), m_fieldInfoNeedsInit(TRUE) { m_typeHandleExact = VMPTR_TypeHandle::NullPtr(); _ASSERTE(m_elementType != ELEMENT_TYPE_VALUETYPE); HRESULT hr = S_OK; EX_TRY { m_appdomain->AddToTypeList(this); } EX_CATCH_HRESULT(hr); SetUnrecoverableIfFailed(GetProcess(), hr); } //----------------------------------------------------------------------------- // Constructor // Builds a Partial-Type, instantiation is tycon's instantation plus tyarg. // Eg, if tycon is "Dict", and tyarg is "string", then this yields // "Dict" //----------------------------------------------------------------------------- CordbType::CordbType(CordbType *tycon, CordbType *tyarg) : CordbBase(tycon->GetProcess(), (UINT_PTR)tyarg, enumCordbType), m_elementType(tycon->m_elementType), m_appdomain(tycon->m_appdomain), m_pClass(tycon->m_pClass), m_rank(tycon->m_rank), m_spinetypes(2), m_objectSize(0), m_fieldInfoNeedsInit(TRUE) // tyarg is added as part of instantiation -see below... { m_typeHandleExact = VMPTR_TypeHandle::NullPtr(); _ASSERTE(m_elementType != ELEMENT_TYPE_VALUETYPE); HRESULT hr = S_OK; EX_TRY { m_appdomain->AddToTypeList(this); } EX_CATCH_HRESULT(hr); SetUnrecoverableIfFailed(GetProcess(), hr); } ULONG STDMETHODCALLTYPE CordbType::AddRef() { // This AddRef/Release pair creates a weak ref-counted reference to the class for this // type. This avoids a circularity in ref-counted references between // classes and types - if we had a circularity the objects would never get // collected at all... //if (m_class) // m_class->AddRef(); return (BaseAddRef()); } ULONG STDMETHODCALLTYPE CordbType::Release() { // if (m_class) // m_class->Release(); return (BaseRelease()); } /* A list of which resources owened by this object are accounted for. HANDLED: CordbClass *m_class; Weakly referenced by increasing count directly in AddRef() and Release() Instantiation m_inst; // Internal pointers to CordbClass released in CordbClass::Neuter CordbHashTable m_spinetypes; // Neutered CordbHashTable m_fields; // Deleted in ~CordbType */ //----------------------------------------------------------------------------- // Cleanup memory for CordbTypes. //----------------------------------------------------------------------------- CordbType::~CordbType() { _ASSERTE(IsNeutered()); } //----------------------------------------------------------------------------- // Neutered by CordbModule // See CordbBase::Neuter for neuter semantics. //----------------------------------------------------------------------------- void CordbType::Neuter() { _ASSERTE(GetProcess()->GetProcessLock()->HasLock()); // We have some direct releases below. If we call Neuter twice, that could // result in double-releases. So check if we're already neutered, and // if so, no work left to do. if (IsNeutered()) { return; } for (unsigned int i = 0; i < m_inst.m_cInst; i++) { m_inst.m_ppInst[i]->Release(); } m_spinetypes.NeuterAndClear(GetProcess()->GetProcessLock()); if(m_inst.m_ppInst) { delete [] m_inst.m_ppInst; m_inst.m_ppInst = NULL; } m_fieldList.Dealloc(); CordbBase::Neuter(); } //----------------------------------------------------------------------------- // Public method for IUnknown::QueryInterface. // Has standard QI semantics. //----------------------------------------------------------------------------- HRESULT CordbType::QueryInterface(REFIID id, void **pInterface) { if (id == IID_ICorDebugType) *pInterface = static_cast(this); else if (id == IID_ICorDebugType2) *pInterface = static_cast(this); else if (id == IID_IUnknown) *pInterface = static_cast(static_cast(this)); else { *pInterface = NULL; return E_NOINTERFACE; } ExternalAddRef(); return S_OK; } //----------------------------------------------------------------------------- // Make a simple type with no type arguments by specifying a CorElementType, // e.g. ELEMENT_TYPE_I1 // // CordbType's are effectively a full representation of // structured types. They are hashed via a combination of their constituent // elements (e.g. CordbClass's or CordbType's) and the element type that is used to // combine the elements, or if they have no elements then via // the element type alone. The following is used to create all CordbTypes. // // An AppDomain holds a cache of CordbTypes for each of the basic CorElementTypes. // // Arguments: // pAppDomain - the AppDomain that the type lives in. // elementType - element_type to create the CordbType around. // ppResultType - OUT: out-parameter to get the CordbType. // // Returns: // S_OK on success. // // HRESULT CordbType::MkType(CordbAppDomain * pAppDomain, CorElementType elementType, CordbType ** ppResultType) { _ASSERTE(pAppDomain != NULL); _ASSERTE(ppResultType != NULL); RSLockHolder lockHolder(pAppDomain->GetProcess()->GetProcessLock()); // Some points in the code create types via element types that are clearly objects but where // no further information is given. This is always done when creating a CordbValue, prior // to actually going over to the EE to discover what kind of value it is. In all these // cases we can just use the type for "Object" - the code for dereferencing the value // will update the type correctly once it has been determined. We don't do this for ELEMENT_TYPE_STRING // as that is actually a NullaryType and at other places in the code we will want exactly that type! if ((elementType == ELEMENT_TYPE_CLASS) || (elementType == ELEMENT_TYPE_SZARRAY) || (elementType == ELEMENT_TYPE_ARRAY)) { elementType = ELEMENT_TYPE_OBJECT; } switch (elementType) { // this one is included because we need a "seed" type to uniquely hash FNPTR types, // i.e. the nullary FNPTR type is used as the type constructor for all function pointer types, // when combined with an approproiate instantiation. case ELEMENT_TYPE_FNPTR: // fall through ... case ELEMENT_TYPE_VOID: case ELEMENT_TYPE_BOOLEAN: case ELEMENT_TYPE_CHAR: case ELEMENT_TYPE_I1: case ELEMENT_TYPE_U1: case ELEMENT_TYPE_I2: case ELEMENT_TYPE_U2: case ELEMENT_TYPE_I4: case ELEMENT_TYPE_U4: case ELEMENT_TYPE_I8: case ELEMENT_TYPE_U8: case ELEMENT_TYPE_R4: case ELEMENT_TYPE_R8: case ELEMENT_TYPE_STRING: case ELEMENT_TYPE_OBJECT: case ELEMENT_TYPE_TYPEDBYREF: case ELEMENT_TYPE_I: case ELEMENT_TYPE_U: *ppResultType = pAppDomain->m_sharedtypes.GetBase(CORDBTYPE_ID(elementType, 0)); if (*ppResultType == NULL) { CordbType * pNewType = new (nothrow) CordbType(pAppDomain, elementType, (unsigned int) 0); if (pNewType == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pAppDomain->m_sharedtypes.AddBase(pNewType); if (SUCCEEDED(hr)) { *ppResultType = pNewType; } else { _ASSERTE(!"unexpected failure!"); delete pNewType; } return hr; } return S_OK; default: _ASSERTE(!"unexpected element type!"); return E_FAIL; } } //----------------------------------------------------------------------------- // Internal method to make a type with exactly one type argument by specifying // ELEMENT_TYPE_PTR, ELEMENT_TYPE_BYREF, ELEMENT_TYPE_SZARRAY or // ELEMENT_TYPE_ARRAY. // // Arguments: // pAppDomain - appdomain containing the type. // elementType - element type to create around. This is limited to: ELEMENT_TYPE_PTR, // ELEMENT_TYPE_BYREF, ELEMENT_TYPE_SZARRAY or ELEMENT_TYPE_ARRAY. // rank - for non-arrays, this must be 0. For szarray, this must be 1. // For multi-dimensional arrays, this is the rank. // pType - the single input type-parameter required for the specified element type. // ppResultType - OUT: the output parameter to get the corresponding CordbType // // Returns: // S_OK on success. // HRESULT CordbType::MkType(CordbAppDomain *pAppDomain, CorElementType elementType, ULONG rank, CordbType * pType, CordbType ** ppResultType) { _ASSERTE(pAppDomain != NULL); _ASSERTE(ppResultType != NULL); RSLockHolder lockHolder(pAppDomain->GetProcess()->GetProcessLock()); switch (elementType) { case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_BYREF: _ASSERTE(rank == 0); goto LUnary; case ELEMENT_TYPE_SZARRAY: _ASSERTE(rank == 1); goto LUnary; case ELEMENT_TYPE_ARRAY: LUnary: { CordbType * pFoundType = pAppDomain->m_sharedtypes.GetBase(CORDBTYPE_ID(elementType, rank)); if (pFoundType == NULL) { pFoundType = new (nothrow) CordbType(pAppDomain, elementType, rank); if (pFoundType == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pAppDomain->m_sharedtypes.AddBase(pFoundType); if (FAILED(hr)) { _ASSERTE(!"unexpected failure!"); delete pFoundType; return hr; } } Instantiation inst(1, &pType); return MkTyAppType(pAppDomain, pFoundType, &inst, ppResultType); } default: _ASSERTE(!"unexpected element type!"); return E_FAIL; } } //----------------------------------------------------------------------------- // Internal method to make a type for an instantiation of a class or value type, or just for the // class or value type if it accepts no type parameters. // Creates a CordbType instantiation around an uninstantiated CordbType and TypeParameter list. // In other words, this does: // CordbType(List) + Instantiation({T=int}) --> CordbType(List) // // This will create the subordinate types. Eg, for Triple, it will create: // CordbType(Triple), CordbType(Triple), and CordbType(Triple) // pInst - instantiation parameters. // ppResultType - OUT: out parameter to hold resulting type. // // Returns: // S_OK on success. // HRESULT CordbType::MkTyAppType(CordbAppDomain * pAppDomain, CordbType * pType, const Instantiation * pInst, CordbType ** ppResultType) { _ASSERTE(pAppDomain == pType->GetAppDomain()); CordbType * pCordbType = pType; // Loop through and create each of the subordinate types, building up to the final fully Closed type. for (unsigned int i = 0; i < pInst->m_cClassTyPars; i++) { CordbType * pCordbBaseType = pCordbType->m_spinetypes.GetBase((UINT_PTR) (pInst->m_ppInst[i])); if (pCordbBaseType == NULL) { pCordbBaseType = new (nothrow) CordbType(pCordbType, pInst->m_ppInst[i]); if (pCordbBaseType == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pCordbType->m_spinetypes.AddBase(pCordbBaseType); if (FAILED(hr)) { _ASSERTE(!"unexpected failure!"); delete pCordbBaseType; // @dbgtodo Microsoft leaks: Release the previously created types if this fails later in the loop return hr; } pCordbBaseType->m_inst.m_cInst = i + 1; pCordbBaseType->m_inst.m_cClassTyPars = i + 1; pCordbBaseType->m_inst.m_ppInst = new (nothrow) CordbType *[i+1]; if (pCordbBaseType->m_inst.m_ppInst == NULL) { delete pCordbBaseType; // @dbgtodo Microsoft leaks: Doesn't release the previously created types if this fails later in the loop return E_OUTOFMEMORY; } for (unsigned int j = 0; j < (i + 1); j++) { // Constructed types include pointers across to other types - increase // the reference counts on these.... pInst->m_ppInst[j]->AddRef(); pCordbBaseType->m_inst.m_ppInst[j] = pInst->m_ppInst[j]; } } pCordbType = pCordbBaseType; } *ppResultType = pCordbType; return S_OK; } //----------------------------------------------------------------------------- // Creates a CordbType instantation around a cordbClass and TypeParameter list. // In other words, this does: // CordbClass(List) + Instantiation({T=int}) --> CordbType(List) // // This really just converts CordbClass(List) --> CordbType(List), and then calls CordbType::MkTyAppType // // Arguments: // pAppDomain - the AD that the class lives in. // elementType - element type of the class. Either ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUETYPE // pClass - the uninstantiated class (eg, List). This function will fill out the tycon->m_type field // to an uninstantiated CordbType (eg CordbType(List)) // pInst - the list of type parameters to instantiate with. // ppResultType - OUT: the CordbType instantiated with the type parameters (eg, CordbType(List)) // // Returns: // S_OK on success. // HRESULT CordbType::MkType(CordbAppDomain * pAppDomain, CorElementType elementType, CordbClass * pClass, const Instantiation * pInst, CordbType ** ppResultType) { _ASSERTE(pAppDomain != NULL); _ASSERTE(ppResultType != NULL); switch (elementType) { // Normalize E_T_VALUETYPE away, so types do not record whether they are VCs or not, but CorDebugClass does. // Update our view of whether a class is a VC based on the evidence we have here. case ELEMENT_TYPE_VALUETYPE: _ASSERTE(((pClass != NULL) && (!pClass->IsValueClassKnown() || pClass->IsValueClassNoInit())) || !"A non-value class is being used with ELEMENT_TYPE_VALUETYPE"); pClass->SetIsValueClass(true); pClass->SetIsValueClassKnown(true); // drop through case ELEMENT_TYPE_CLASS: { // This probably isn't needed... if (pClass == NULL) { elementType = ELEMENT_TYPE_OBJECT; goto LReallyObject; } CordbType * pType = NULL; pType = pClass->GetType(); if (pType == NULL) { pType = new (nothrow) CordbType(pAppDomain, ELEMENT_TYPE_CLASS, pClass); if (pType == NULL) { return E_OUTOFMEMORY; } pClass->SetType(pType); } _ASSERTE(pClass->GetType() != NULL); return CordbType::MkTyAppType(pAppDomain, pType, pInst, ppResultType); } default: LReallyObject: _ASSERTE(pInst->m_cInst == 0); return MkType(pAppDomain, elementType, ppResultType); } } //----------------------------------------------------------------------------- // Make a CordbType for a function pointer type (ELEMENT_TYPE_FNPTR). // // Arguments: // pAppDomain - the Appdomian the type lives in. // elementType - must be ELEMENT_TYPE_FNPTR. // pInst - instantiation information. // ppResultType - OUT: out-parameter to hold resulting CordbType // // Return: // S_OK on success. // HRESULT CordbType::MkType(CordbAppDomain * pAppDomain, CorElementType elementType, const Instantiation * pInst, CordbType ** ppResultType) { CordbType * pType; _ASSERTE(elementType == ELEMENT_TYPE_FNPTR); HRESULT hr = MkType(pAppDomain, elementType, &pType); if (!SUCCEEDED(hr)) { return hr; } return CordbType::MkTyAppType(pAppDomain, pType, pInst, ppResultType); } //----------------------------------------------------------------------------- // Public API to get the CorElementType of the type. // // Parameters: // pType - OUT: on return, gets the CorElementType // // Returns: // S_OK on success. CORDBG_E_CLASS_NOT_LOADED or synchronization errors on failure //----------------------------------------------------------------------------- HRESULT CordbType::GetType(CorElementType *pType) { PUBLIC_REENTRANT_API_ENTRY(this); FAIL_IF_NEUTERED(this); // See if this E_T_CLASS is really a value type? if (m_elementType == ELEMENT_TYPE_CLASS) { _ASSERTE(m_pClass); bool isVC = false; // Determining if something is a VC or not can involve asking the EE. // We could do it ourselves based on the metadata but it's non-trivial // determining if a class has System.ValueType as a parent (we have // to find and OpenScope the mscorlib.dll which we don't currently do // on the right-side). But the IsValueClass call can fail if the // class is not yet loaded on the right side. In that case we // ignore the failure and return ELEMENT_TYPE_CLASS HRESULT hr = S_OK; EX_TRY { isVC = m_pClass->IsValueClass(); } EX_CATCH_HRESULT(hr); if (!FAILED(hr) && isVC) { *pType = ELEMENT_TYPE_VALUETYPE; return S_OK; } } *pType = m_elementType; return S_OK; } //----------------------------------------------------------------------------- // Public method to get the ICorDebugClass that matches this type. // ICorDebugType has instantiated type-params (eg, List), whereas // ICorDebugClass is open (eg, List). // // Parameters: // pClass - OUT: gets class on return. // Returns: // S_OK on success. CORDBG_E_CLASS_NOT_LOADED if the class is not loaded. // Else some other error. //----------------------------------------------------------------------------- HRESULT CordbType::GetClass(ICorDebugClass **pClass) { PUBLIC_REENTRANT_API_ENTRY(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); if ((m_pClass == NULL) && (m_elementType == ELEMENT_TYPE_STRING || m_elementType == ELEMENT_TYPE_OBJECT)) { Init(FALSE); } if (m_pClass == NULL) { *pClass = NULL; return CORDBG_E_CLASS_NOT_LOADED; } *pClass = m_pClass; m_pClass->ExternalAddRef(); return S_OK; } //----------------------------------------------------------------------------- // Public method to get array rank. This is only valid for arrays. // // Parameters: // pnRank - OUT: *pnRank is set to rank on return // // Return: // S_OK if success. E_INVALIDARG is this Type doesn't have a rank. //----------------------------------------------------------------------------- HRESULT CordbType::GetRank(ULONG32 *pnRank) { PUBLIC_REENTRANT_API_ENTRY(this); VALIDATE_POINTER_TO_OBJECT(pnRank, ULONG32 *); if (m_elementType != ELEMENT_TYPE_SZARRAY && m_elementType != ELEMENT_TYPE_ARRAY) return E_INVALIDARG; *pnRank = (ULONG32) m_rank; return S_OK; } //----------------------------------------------------------------------------- // Public convenience method to get the first type parameter. // This is purely to avoid needing to call EnumerateTypeParameters for // the set of types that only have 1 type-parameter. // // Parameters: // pType - OUT: get the ICorDebugType for the first type-parameter. // Returns: // S_OK on success. //----------------------------------------------------------------------------- HRESULT CordbType::GetFirstTypeParameter(ICorDebugType **pType) { PUBLIC_REENTRANT_API_ENTRY(this); VALIDATE_POINTER_TO_OBJECT(pType, ICorDebugType **); // Since this is a public API, make sure there actually is at least 1 type-parameter. if (m_inst.m_cInst == 0) { return E_INVALIDARG; } _ASSERTE(m_inst.m_ppInst != NULL); _ASSERTE(m_inst.m_ppInst[0] != NULL); *pType = m_inst.m_ppInst[0]; if (*pType) (*pType)->AddRef(); return S_OK; } //----------------------------------------------------------------------------- // Internal worker to create a CordbType around a CordbClass. // Parameters: // appdomain - AD that the type lives in. // et - CorElementType of the incoming CordbClass // cl - CordbClass representing the type to build a CordbType for // pRes - OUT: out parameter to return the newly created CordbType object. // // Return: // S_OK on success. //----------------------------------------------------------------------------- HRESULT CordbType::MkUnparameterizedType(CordbAppDomain *appdomain, CorElementType et, CordbClass *cl,CordbType **pRes) { // Pass in empty instantiation since CordbClass has no generic info. // We should make some assert between et and cl->GetType(). Instantiation emptyInstantiation; return CordbType::MkType(appdomain, et, cl, &emptyInstantiation, pRes); } //----------------------------------------------------------------------------- // Internal helper to get the First type parameter. // This is an internal convenience function for the public GetFirstTypeParameter. // // Parameters: // pRes - OUT: out-param to get the unary type-parameter. //----------------------------------------------------------------------------- void CordbType::DestUnaryType(CordbType **pRes) { _ASSERTE(m_elementType == ELEMENT_TYPE_PTR || m_elementType == ELEMENT_TYPE_BYREF || m_elementType == ELEMENT_TYPE_ARRAY || m_elementType == ELEMENT_TYPE_SZARRAY); _ASSERTE(m_inst.m_cInst == 1); _ASSERTE(m_inst.m_ppInst != NULL); *pRes = m_inst.m_ppInst[0]; } //----------------------------------------------------------------------------- // Internal method to get the Class and type-parameters from a CordbType. //----------------------------------------------------------------------------- void CordbType::DestConstructedType(CordbClass **cls, Instantiation *inst) { ASSERT(m_elementType == ELEMENT_TYPE_CLASS); *cls = m_pClass; *inst = m_inst; } //----------------------------------------------------------------------------- // Internal method to get all the type-parameters for a FnPtr //----------------------------------------------------------------------------- void CordbType::DestNaryType(Instantiation *inst) { ASSERT(m_elementType == ELEMENT_TYPE_FNPTR); *inst = m_inst; } //----------------------------------------------------------------------------- // CordbType::SigToType // Internal helper to create a CordbType from a Metadata signature (SigParser) // // This parses a metadata signature in the context of a module to return a CordbType. // This heavily relies on the metadata and signature format. See ECMA Partition II for details. // Since signatures may be recursive, this function can be called recursively. // Since metadata signatures exist all over, this can be called in many different scenarios, including // resolving a TypeSpec, looking up a field. // // pModule - module that the signature lives in. // pSigParse - Signature, positioned at the point to read the Type from. // This will not change the SigParser's current position. // inst - instantiation containing Type Params for the context of the SigParser. // For a local var or argument lookup, this would be the type-params from the Frame. // For a field lookup, this would be the type-params for the containing type. // pRes - OUT: yields the CordbType for this signature. // // Returns: // S_OK on success //----------------------------------------------------------------------------- HRESULT CordbType::SigToType(CordbModule * pModule, SigParser * pSigParser, const Instantiation * pInst, CordbType ** ppResultType) { FAIL_IF_NEUTERED(pModule); INTERNAL_SYNC_API_ENTRY(pModule->GetProcess()); _ASSERTE(pSigParser != NULL); // // Make a local copy of the SigParser since we are going to mutate it. // SigParser sigParser = *pSigParser; CorElementType elementType; HRESULT hr; IfFailRet(sigParser.GetElemType(&elementType)); switch (elementType) { case ELEMENT_TYPE_VAR: case ELEMENT_TYPE_MVAR: { ULONG tyvar_num; IfFailRet(sigParser.GetData(&tyvar_num)); if (elementType == ELEMENT_TYPE_VAR) { // ELEMENT_TYPE_VAR refers to an indexed type-parameter in the containing Type. // Eg, we may be doing a field lookup on 'List { T m_head}', and the field's return type 'T' is Type-parameter #0. // Or this maybe part of a base class's TypeSpec. _ASSERTE (tyvar_num < (pInst->m_cClassTyPars)); if (tyvar_num >= (pInst->m_cClassTyPars)) return E_FAIL; _ASSERTE (pInst->m_ppInst != NULL); *ppResultType = pInst->m_ppInst[tyvar_num]; } else { //ELEMENT_TYPE_MVAR refers to an indexed type-parameter in the containing Method. // Eg, we may be in Class::Func and refering to T. // The Instantiation array has Type type-parameters first, and then any Method Type-parameters. // The m_cClassTyPars field indicats where the split is between Type and Method type-parameters. Type type-params // come first. _ASSERTE(elementType == ELEMENT_TYPE_MVAR); _ASSERTE (tyvar_num < (pInst->m_cInst - pInst->m_cClassTyPars)); if (tyvar_num >= (pInst->m_cInst - pInst->m_cClassTyPars)) return E_FAIL; _ASSERTE (pInst->m_ppInst != NULL); *ppResultType = pInst->m_ppInst[tyvar_num + pInst->m_cClassTyPars]; } return S_OK; } case ELEMENT_TYPE_GENERICINST: { //ELEMENT_TYPE_GENERICINST is that start of a instantiated generic type. //Format for the signature blob is: // 1) CorElementType, Token - this is the uninstantiated type (eg, for Pair, it would be token for Pair) // 2) int - Count of generic args - eg, for Pair, it would be "2". // 3) type1,type2, ... - meteadata representation for generic args. For example above, it would be Type(int), Type(string). // ignore "WITH", look at next ELEMENT_TYPE to get CLASS or VALUE IfFailRet(sigParser.GetElemType(&elementType)); mdToken token; IfFailRet(sigParser.GetToken(&token)); CordbClass * pClass; IfFailRet( pModule->ResolveTypeRefOrDef(token, &pClass)); // The use of a class in a signature provides definite evidence as to whether it is a VC or not. _ASSERTE(!pClass->IsValueClassKnown() || (pClass->IsValueClassNoInit() == (elementType == ELEMENT_TYPE_VALUETYPE)) || !"A value class is being used with ELEMENT_TYPE_GENERICINST"); pClass->SetIsValueClass(elementType == ELEMENT_TYPE_VALUETYPE); pClass->SetIsValueClassKnown(true); // Build up the array of generic arguments. ULONG cArgs; // number of generic arguments in the type. IfFailRet(sigParser.GetData(&cArgs)); S_UINT32 allocSize = S_UINT32( cArgs ) * S_UINT32( sizeof(CordbType *) ); if (allocSize.IsOverflow()) { IfFailRet(E_OUTOFMEMORY); } CordbType ** ppTypeInstantiations = reinterpret_cast(_alloca( allocSize.Value())); for (unsigned int i = 0; i < cArgs;i++) { IfFailRet(CordbType::SigToType(pModule, &sigParser, pInst, &ppTypeInstantiations[i])); IfFailRet(sigParser.SkipExactlyOne()); } // Now we have the Open type (eg, Pair) and the instantiation list, so create the Closed CordbType.. Instantiation typeInstantiation(cArgs, ppTypeInstantiations); return CordbType::MkType(pModule->GetAppDomain(), elementType, pClass, &typeInstantiation, ppResultType); } case ELEMENT_TYPE_CLASS: case ELEMENT_TYPE_VALUETYPE: // OK: this E_T_VALUETYPE comes from signature { // Path for non-generic types mdToken token; IfFailRet(sigParser.GetToken(&token)); CordbClass * pClass; IfFailRet(pModule->ResolveTypeRefOrDef(token, &pClass)); // The use of a class in a signature provides definite evidence as to whether it is a VC or not. _ASSERTE(!pClass->IsValueClassKnown() || (pClass->IsValueClassNoInit() == (elementType == ELEMENT_TYPE_VALUETYPE)) || !"A non-value class is being used with ELEMENT_TYPE_VALUETYPE"); pClass->SetIsValueClass(elementType == ELEMENT_TYPE_VALUETYPE); pClass->SetIsValueClassKnown(true); return CordbType::MkUnparameterizedType(pModule->GetAppDomain(), elementType, pClass, ppResultType); } case ELEMENT_TYPE_SENTINEL: case ELEMENT_TYPE_MODIFIER: case ELEMENT_TYPE_PINNED: { IfFailRet(CordbType::SigToType(pModule, &sigParser, pInst, ppResultType)); // Throw away SENTINELS on all CordbTypes... return S_OK; } case ELEMENT_TYPE_CMOD_REQD: case ELEMENT_TYPE_CMOD_OPT: { mdToken token; IfFailRet(sigParser.GetToken(&token)); IfFailRet(CordbType::SigToType(pModule, &sigParser, pInst, ppResultType)); // Throw away CMOD on all CordbTypes... return S_OK; } case ELEMENT_TYPE_ARRAY: { CordbType * pType; IfFailRet(CordbType::SigToType(pModule, &sigParser, pInst, &pType)); IfFailRet(sigParser.SkipExactlyOne()); ULONG rank; IfFailRet(sigParser.GetData(&rank)); return CordbType::MkType(pModule->GetAppDomain(), elementType, rank, pType, ppResultType); } case ELEMENT_TYPE_SZARRAY: { CordbType * pType; IfFailRet(CordbType::SigToType(pModule, &sigParser, pInst, &pType)); return CordbType::MkType(pModule->GetAppDomain(), elementType, 1, pType, ppResultType); } case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_BYREF: { CordbType * pType; IfFailRet(CordbType::SigToType(pModule, &sigParser, pInst, &pType)); return CordbType::MkType(pModule->GetAppDomain(),elementType, 0, pType, ppResultType); } case ELEMENT_TYPE_FNPTR: { ULONG cArgs; IfFailRet(sigParser.GetData(&cArgs)); // Skip callingConv IfFailRet(sigParser.GetData(&cArgs)); // Get number of parameters S_UINT32 allocSize = ( S_UINT32(cArgs) + S_UINT32(1) ) * S_UINT32( sizeof(CordbType *) ); if (allocSize.IsOverflow()) { IfFailRet(E_OUTOFMEMORY); } CordbType ** ppTypeInstantiations = (CordbType **) _alloca( allocSize.Value() ); for (unsigned int i = 0; i <= cArgs; i++) { IfFailRet(CordbType::SigToType(pModule, &sigParser, pInst, &ppTypeInstantiations[i])); IfFailRet(sigParser.SkipExactlyOne()); } Instantiation typeInstantiation(cArgs + 1, ppTypeInstantiations); return CordbType::MkType(pModule->GetAppDomain(), elementType, &typeInstantiation, ppResultType); } case ELEMENT_TYPE_VOID: case ELEMENT_TYPE_BOOLEAN: case ELEMENT_TYPE_CHAR: case ELEMENT_TYPE_I1: case ELEMENT_TYPE_U1: case ELEMENT_TYPE_I2: case ELEMENT_TYPE_U2: case ELEMENT_TYPE_I4: case ELEMENT_TYPE_U4: case ELEMENT_TYPE_I8: case ELEMENT_TYPE_U8: case ELEMENT_TYPE_R4: case ELEMENT_TYPE_R8: case ELEMENT_TYPE_STRING: case ELEMENT_TYPE_TYPEDBYREF: case ELEMENT_TYPE_OBJECT: case ELEMENT_TYPE_I: case ELEMENT_TYPE_U: return CordbType::MkType(pModule->GetAppDomain(), elementType, ppResultType); default: _ASSERTE(!"unexpected element type!"); return E_FAIL; } } // CordbType::SigToType //----------------------------------------------------------------------------- // Marshal a DebuggerIPCE_BasicTypeData --> CordbType. // // This will build up a DebuggerIPCE_ExpandedTypeData and convert that into // a CordbType. This may send additional IPC events if needed to // go from Basic --> Expanded data. Note that this is designed to handle generics. // // Parameters: // pAppDomain - the AppDomain the type lives in. // data - DebuggerIPCE_BasicTypeData from Left-Side containing type description. // pRes - OUT: out-parameter to hold built type. // // Returns: // S_OK on success. //----------------------------------------------------------------------------- HRESULT CordbType::TypeDataToType(CordbAppDomain *pAppDomain, DebuggerIPCE_BasicTypeData *data, CordbType **pRes) { FAIL_IF_NEUTERED(pAppDomain); INTERNAL_SYNC_API_ENTRY(pAppDomain->GetProcess()); // HRESULT hr = S_OK; CorElementType et = data->elementType; switch (et) { case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_BYREF: // For these element types the "Basic" type data only contains the type handle. // So we fetch some more data, and the go onto the "Expanded" case... { EX_TRY { DebuggerIPCE_ExpandedTypeData typeInfo; CordbProcess * pProcess = pAppDomain->GetProcess(); { RSLockHolder lockHolder(pProcess->GetProcessLock()); pProcess->GetDAC()->TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, // could be generics // which are never boxed pAppDomain->GetADToken(), data->vmTypeHandle, &typeInfo); } IfFailThrow(CordbType::TypeDataToType(pAppDomain,&typeInfo, pRes)); } EX_CATCH_HRESULT(hr); return hr; } case ELEMENT_TYPE_FNPTR: { DebuggerIPCE_ExpandedTypeData e; e.elementType = et; e.NaryTypeData.typeHandle = data->vmTypeHandle; return CordbType::TypeDataToType(pAppDomain, &e, pRes); } default: // For all other element types the "Basic" view of a type // contains the same information as the "expanded" // view, so just reuse the code for the Expanded view... DebuggerIPCE_ExpandedTypeData e; e.elementType = et; e.ClassTypeData.metadataToken = data->metadataToken; e.ClassTypeData.vmDomainFile = data->vmDomainFile; e.ClassTypeData.vmModule = data->vmModule; e.ClassTypeData.typeHandle = data->vmTypeHandle; return CordbType::TypeDataToType(pAppDomain, &e, pRes); } } //----------------------------------------------------------------------------- // Marshal DebuggerIPCE_ExpandedTypeData --> CordbType // The ExpandedTypeData just contains top level generic info, and so // the RS may need to send more IPC events to fill out details. // // Parameters: // pAppDomain - the appdomain that all the types live in. // data - data used to build up CordbType // pRes - OUT: out param to get back CordbType on return. // // Returns: // S_OK on success. //----------------------------------------------------------------------------- HRESULT CordbType::TypeDataToType(CordbAppDomain *pAppDomain, DebuggerIPCE_ExpandedTypeData *data, CordbType **pRes) { INTERNAL_SYNC_API_ENTRY(pAppDomain->GetProcess()); // CorElementType et = data->elementType; HRESULT hr = S_OK; switch (et) { case ELEMENT_TYPE_OBJECT: case ELEMENT_TYPE_VOID: case ELEMENT_TYPE_BOOLEAN: case ELEMENT_TYPE_CHAR: case ELEMENT_TYPE_I1: case ELEMENT_TYPE_U1: case ELEMENT_TYPE_I2: case ELEMENT_TYPE_U2: case ELEMENT_TYPE_I4: case ELEMENT_TYPE_U4: case ELEMENT_TYPE_I8: case ELEMENT_TYPE_U8: case ELEMENT_TYPE_R4: case ELEMENT_TYPE_R8: case ELEMENT_TYPE_STRING: case ELEMENT_TYPE_TYPEDBYREF: case ELEMENT_TYPE_I: case ELEMENT_TYPE_U: ETObject: // It's a primitive (therefore non-generic) type, so we can just create it immediately. IfFailRet (CordbType::MkType(pAppDomain, et, pRes)); break; case ELEMENT_TYPE_CLASS: case ELEMENT_TYPE_VALUETYPE: // OK: this E_T_VALUETYPE comes from the EE { // if (data->ClassTypeData.metadataToken == mdTokenNil) { et = ELEMENT_TYPE_OBJECT; goto ETObject; } CordbModule * pClassModule = NULL; EX_TRY { pClassModule = pAppDomain->LookupOrCreateModule(data->ClassTypeData.vmModule, data->ClassTypeData.vmDomainFile); } EX_CATCH_HRESULT(hr); if( pClassModule == NULL ) { // We don't know anything about this module - shouldn't happen. // This can be hit by the issue described in VSWhidbey 465120 _ASSERTE(!"Unrecognized module"); return CORDBG_E_MODULE_NOT_LOADED; } CordbClass *tycon; IfFailRet (pClassModule->LookupOrCreateClass(data->ClassTypeData.metadataToken,&tycon)); if (!(data->ClassTypeData.typeHandle.IsNull())) { // It's a generic type. We have the typehandle, use that to query for the rest of // the tyeparameters and build up the instantiation for the CordbType. IfFailRet (CordbType::InstantiateFromTypeHandle(pAppDomain, data->ClassTypeData.typeHandle, et, tycon, pRes)); // Set the type handle regardless of how we found // the type. For example if type was already // constructed without the type handle still set // it here. if (*pRes) { (*pRes)->m_typeHandleExact = data->ClassTypeData.typeHandle; } break; } else { // Non generic type. Since we already have the CordbClass for it, we can trivially create the CordbType IfFailRet (CordbType::MkUnparameterizedType(pAppDomain, et,tycon,pRes)); break; } } case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: { CordbType *argty; IfFailRet (CordbType::TypeDataToType(pAppDomain, &(data->ArrayTypeData.arrayTypeArg), &argty)); IfFailRet (CordbType::MkType(pAppDomain, et, data->ArrayTypeData.arrayRank, argty, pRes)); break; } case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_BYREF: { CordbType *argty; IfFailRet (CordbType::TypeDataToType(pAppDomain, &(data->UnaryTypeData.unaryTypeArg), &argty)); IfFailRet (CordbType::MkType(pAppDomain, et, 0, argty, pRes)); break; } case ELEMENT_TYPE_FNPTR: { IfFailRet (CordbType::InstantiateFromTypeHandle(pAppDomain, data->NaryTypeData.typeHandle, et, NULL, pRes)); if (*pRes) { (*pRes)->m_typeHandleExact = data->NaryTypeData.typeHandle; } break; } case ELEMENT_TYPE_END: *pRes = NULL; return E_FAIL; default: _ASSERTE(!"unexpected element type!"); return E_FAIL; } return S_OK; } //----------------------------------------------------------------------------- // CordbType::InstantiateFromTypeHandle // Internal helper method. // Builds (Left-Side) TypeHandle --> (Right-Side) CordbType // This is very useful when we get a typehandle from the LeftSide. A common // scenario is when we get an Object back from the LS, which happens when // we build the CordbType corresponding to a Cordb*Value. // // Parameters: // pAppdomain - the appdomain the type lives in. // vmTypeHandle - a Left-Side typehandle describing the type. // elementType - convenient way to indicate whether we've got ELEMENT_TYPE_FNPTR or // something else. We should be able to retrieve this from the TypeHandle, // but our caller already has it available. // typeConstructor - CordbClass corresponding to the typeHandle. This could be built // up from typehandle, but our caller already has it. // Will be NULL for ELEMENT_TYPE_FNPTR // pResultType - OUT: out parameter to yield CordbType for the TypeHandle. // // Returns: // S_OK on success. //----------------------------------------------------------------------------- HRESULT CordbType::InstantiateFromTypeHandle(CordbAppDomain * pAppDomain, VMPTR_TypeHandle vmTypeHandle, CorElementType elementType, CordbClass * typeConstructor, CordbType ** pResultType) { HRESULT hr = S_OK; // Should already by synced by caller. INTERNAL_SYNC_API_ENTRY(pAppDomain->GetProcess()); // _ASSERTE((pAppDomain->GetProcess()->GetShim() == NULL) || (pAppDomain->GetProcess()->GetSynchronized())); EX_TRY { CordbProcess * pProcess = pAppDomain->GetProcess(); // // Step 1) Ask DacDbi interface for a list of type-parameters given a TypeHandle. // TypeParamsList params; { RSLockHolder lockHolder(pProcess->GetProcessLock()); pProcess->GetDAC()->GetTypeHandleParams(pAppDomain->GetADToken(), vmTypeHandle, ¶ms); } // convert the parameter type information to a list of CordbTypeInstances (one for each parameter) // note: typeList will be destroyed on exit, running destructors for each element. In this case, that // means it will simply assert IsNeutered. DacDbiArrayList typeList; typeList.Alloc(params.Count()); for (int i = 0; i < params.Count(); ++i) { IfFailThrow(TypeDataToType(pAppDomain, &(params[i]), &(typeList[i]))); } // now make an instance of CordbType from an instantiation Instantiation instantiation(params.Count(), &(typeList[0])); if (elementType == ELEMENT_TYPE_FNPTR) { IfFailThrow(CordbType::MkType(pAppDomain, elementType, &instantiation, pResultType)); } else { IfFailThrow(CordbType::MkType(pAppDomain, elementType, typeConstructor, &instantiation, pResultType)); } } EX_CATCH_HRESULT(hr); return hr; } // CordbType::InstantiateFromTypeHandle //----------------------------------------------------------------------------- // Initialize the CordbType. // This will involve a lot of queries to the Left-side. // This means finding the type-handle, getting / creating associated CordbClass, // filling out the instantiation, getting field info, etc. // // Parameters: // fForceInit - if false, may skip initialization if TypeHandle already known. // // Returns: // S_OK if success, CORDBG_E_CLASS_NOT_LOADED, E_INVALIDARG, OOM on failure //----------------------------------------------------------------------------- HRESULT CordbType::Init(BOOL fForceInit) { INTERNAL_SYNC_API_ENTRY(GetProcess()); // HRESULT hr = S_OK; if (m_pClass && m_pClass->GetLoadLevel() != CordbClass::FullInfo) fForceInit = TRUE; // Step 1. initialize the type constructor (if one exists) // and the (class) type parameters.... if (m_elementType == ELEMENT_TYPE_CLASS) { // start by initing only enough so that we can determine whether // or not this is a generic class. When dealing with generic // type instantiations there is no guarantee the open generic // type is fully restored. If we load too eagerly it might fail // and we wouldn't actually need that extra data anyways. _ASSERTE(m_pClass != NULL); EX_TRY { m_pClass->Init(CordbClass::BasicInfo); } EX_CATCH_HRESULT(hr); IfFailRet(hr); // non-generic classes need the class object to be fully inited // in the generic case we won't ever use that data if (!m_pClass->HasTypeParams()) { EX_TRY { m_pClass->Init(CordbClass::FullInfo); } EX_CATCH_HRESULT(hr); IfFailRet(hr); return S_OK; // Non-generic, that's all - no clean-up required } } _ASSERTE(m_elementType != ELEMENT_TYPE_CLASS || m_pClass->HasTypeParams()); for (unsigned int i = 0; iInit(fForceInit) ); } // Step 2. Try to fetch the type handle if necessary (only // for instantiated class types, pointer types etc.) // We do this by preparing an event specifying the type and // then fetching the type handle from the left-side. This // will not always succeed, as forcing the load of the type handle would be the // equivalent of doing a FuncEval, i.e. the instantiation may // not have been created. But we try anyway to reduce the number of // failures. // // Note that in the normal case we will have the type handle from the EE // anyway, e.g. if the CordbType was created when reporting the type // of an actual object. // Initialize m_typeHandleExact if it needs it if (m_elementType == ELEMENT_TYPE_ARRAY || m_elementType == ELEMENT_TYPE_SZARRAY || m_elementType == ELEMENT_TYPE_BYREF || m_elementType == ELEMENT_TYPE_PTR || m_elementType == ELEMENT_TYPE_FNPTR || (m_elementType == ELEMENT_TYPE_CLASS && m_pClass->HasTypeParams())) { // It is OK if getting an exact type handle // fails with CORDBG_E_CLASS_NOT_LOADED. In that case we leave // the type information incomplete and subsequent operations // will try to call Init() again. The immediate operation will fail later if // TypeToBasicTypeData requests the exact type information for this type. hr = InitInstantiationTypeHandle(fForceInit); if (hr != CORDBG_E_CLASS_NOT_LOADED) IfFailRet(hr); } // For OBJECT and STRING we may not have a value for m_class // object. Go try and get it. if (m_elementType == ELEMENT_TYPE_STRING || m_elementType == ELEMENT_TYPE_OBJECT) { IfFailRet(InitStringOrObjectClass(fForceInit)); } // Step 3. Fetch the information that is specific to the type where necessary... // Now we have the type handle for the constructed type, we can ask for the size of // the object. Only do this for constructed value types. // // Note that the exact and/or approximate type handles may not be available. if ((m_elementType == ELEMENT_TYPE_CLASS) && m_pClass->HasTypeParams()) { IfFailRet(InitInstantiationFieldInfo(fForceInit)); } return S_OK; } //----------------------------------------------------------------------------- // Internal function to communicate with Left-Side to get an exact TypeHandle // (runtime type representation) for this CordbType. // // Parameters: // fForceInit - if false, may skip initialization if TypeHandle already known. // // Returns: // S_OK on success or failure HR E_INVALIDARG, OOM, CORDBG_E_CLASS_NOT_LOADED // on failure //----------------------------------------------------------------------------- HRESULT CordbType::InitInstantiationTypeHandle(BOOL fForceInit) { // Check if we've already done this Init if (!fForceInit && !m_typeHandleExact.IsNull()) return S_OK; HRESULT hr = S_OK; // Create an array of DebuggerIPCE_BasicTypeData structures from the array of type parameters. // First, get a buffer to hold the information CordbProcess *pProcess = GetProcess(); S_UINT32 bufferSize = S_UINT32(sizeof(DebuggerIPCE_BasicTypeData)) * S_UINT32(m_inst.m_cClassTyPars); EX_TRY { if( bufferSize.IsOverflow() ) { ThrowHR(E_INVALIDARG); } NewHolder pArgTypeData(new DebuggerIPCE_BasicTypeData[bufferSize.Value()]); // We will have already called Init on each of the type parameters further above. Now we build a // list of type information for each type parameter. for (unsigned int i = 0; i < m_inst.m_cClassTyPars; i++) { _ASSERTE(m_inst.m_ppInst != NULL); _ASSERTE(m_inst.m_ppInst[i] != NULL); IfFailThrow(m_inst.m_ppInst[i]->TypeToBasicTypeData(&pArgTypeData[i])); } DebuggerIPCE_ExpandedTypeData typeData; // get the top-level type information TypeToExpandedTypeData(&typeData); ArgInfoList argInfo(pArgTypeData, m_inst.m_cClassTyPars); { // Get the TypeHandle based on the type data RSLockHolder lockHolder(GetProcess()->GetProcessLock()); hr = pProcess->GetDAC()->GetExactTypeHandle(&typeData, &argInfo, m_typeHandleExact); } } EX_CATCH_HRESULT(hr); return hr; } // CordbType::InitInstantiationTypeHandle //----------------------------------------------------------------------------- // Internal helper for CordbType::Init to finish initialize types for // System.String or System.Object. // This just needs to set the m_class field. // // Parameters: // fForceInit - force re-initialization if already initialized. // // Returns: // S_OK on success or CORDBG_E_CLASS_NOT_LOADED on failure. // // Note: verification with IPC result may assert //----------------------------------------------------------------------------- HRESULT CordbType::InitStringOrObjectClass(BOOL fForceInit) { // This CordbType is a non-generic class, either System.String or System.Object. // Need to find the CordbClass instance (in the proper AppDomain) that matches that type. // Check if we've already done this Init if (!fForceInit && m_pClass != NULL) { return S_OK; } HRESULT hr = S_OK; EX_TRY { // // Step 1a) Send a request to the DAC to map: CorElementType --> {token, Module} // CordbProcess *pProcess = GetProcess(); mdTypeDef metadataToken; VMPTR_DomainFile vmDomainFile = VMPTR_DomainFile::NullPtr(); VMPTR_Module vmModule = VMPTR_Module::NullPtr(); { RSLockHolder lockHolder(GetProcess()->GetProcessLock()); pProcess->GetDAC()->GetSimpleType(m_appdomain->GetADToken(), m_elementType, &metadataToken, &vmModule, &vmDomainFile); } // // Step 2) Lookup CordbClass based off token + Module. // CordbModule * pTypeModule = m_appdomain->LookupOrCreateModule(vmModule, vmDomainFile); _ASSERTE(pTypeModule != NULL); IfFailThrow(pTypeModule->LookupOrCreateClass(metadataToken, &m_pClass)); _ASSERTE(m_pClass != NULL); _ASSERTE(SUCCEEDED(hr)); m_pClass->AddRef(); } EX_CATCH_HRESULT(hr); return hr; } // CordbType::InitStringOrObjectClass //----------------------------------------------------------------------------- // Internal helper for CordbType::Init to get FieldInfos for a generic Type. // Non-generic types can use the FieldInfos off their associated CordbClass. // // Parameters: // fForceInit - force re-initialization if already initialized? // // Returns: // S_OK on success. //----------------------------------------------------------------------------- HRESULT CordbType::InitInstantiationFieldInfo(BOOL fForceInit) { HRESULT hr = S_OK; // Check if we've already done this Init if (!m_fieldInfoNeedsInit && !fForceInit) { return hr; } _ASSERTE(m_elementType == ELEMENT_TYPE_CLASS); _ASSERTE(m_pClass->HasTypeParams()); VMPTR_TypeHandle typeHandleApprox = m_typeHandleExact; // If the exact type handle is not available then get the approximate type handle. if (typeHandleApprox.IsNull()) { // set up a buffer to hold type parameter information for the type. (See // code:CordbType::GatherTypeData for more information). First, compute its size. unsigned int typeDataNodeCount = 0; this->CountTypeDataNodes(&typeDataNodeCount); EX_TRY { // allocate a buffer to hold the parameter data TypeInfoList typeData; typeData.Alloc(typeDataNodeCount); // fill the buffer DebuggerIPCE_TypeArgData * pCurrent = &(typeData[0]); GatherTypeData(this, &pCurrent); // request the type handle from the DAC CordbProcess *pProcess = GetProcess(); { RSLockHolder lockHolder(pProcess->GetProcessLock()); typeHandleApprox = pProcess->GetDAC()->GetApproxTypeHandle(&typeData); } } EX_CATCH_HRESULT(hr); if(FAILED(hr)) return hr; } // OK, now get the field info if we can. CordbProcess *pProcess = GetProcess(); EX_TRY { { // this may be called multiple times. Each call will discard previous values in m_fieldList and reinitialize // the list with updated information RSLockHolder lockHolder(pProcess->GetProcessLock()); pProcess->GetDAC()->GetInstantiationFieldInfo(m_pClass->GetModule()->GetRuntimeDomainFile(), m_typeHandleExact, typeHandleApprox, &m_fieldList, &m_objectSize); } } EX_CATCH_HRESULT(hr); return hr; } HRESULT CordbType::ReturnedByValue() { HRESULT hr = S_OK; if (!IsValueType()) return S_OK; ULONG32 unboxedSize = 0; IfFailRet(GetUnboxedObjectSize(&unboxedSize)); if (unboxedSize > sizeof(SIZE_T)) return S_FALSE; mdToken mdClass = m_pClass->GetToken(); int fieldCount = 0; bool unsupported = false; HCORENUM fields = 0; ULONG fetched = 0; mdToken mdField; IMetaDataImport *pImport = m_pClass->GetModule()->GetMetaDataImporter(); IfFailRet(pImport->EnumFields(&fields, mdClass, &mdField, 1, &fetched)); while (hr == S_OK && fetched == 1) { DWORD attr = 0; PCCOR_SIGNATURE sigBlob = 0; ULONG sigLen = 0; hr = pImport->GetFieldProps(mdField, NULL, NULL, 0, NULL, &attr, &sigBlob, &sigLen, NULL, NULL, NULL); if (SUCCEEDED(hr)) { // !static if ((attr & 0x10) == 0) { if (fieldCount++) break; CorElementType et; SigParser parser(sigBlob, sigLen); parser.GetByte(NULL); // 0x6, field signature parser.SkipCustomModifiers(); hr = parser.GetElemType(&et); if (SUCCEEDED(hr)) { switch (et) { case ELEMENT_TYPE_R4: case ELEMENT_TYPE_R8: unsupported = true; break; case ELEMENT_TYPE_CLASS: case ELEMENT_TYPE_STRING: case ELEMENT_TYPE_PTR: // OK break; default: if (!CorIsPrimitiveType(et)) unsupported = true; break; } if (unsupported) break; } } hr = pImport->EnumFields(&fields, mdClass, &mdField, 1, &fetched); } if (FAILED(hr)) { pImport->CloseEnum(fields); return hr; } } pImport->CloseEnum(fields); if (unsupported) return S_FALSE; return fieldCount <= 1 ? S_OK : S_FALSE; } //----------------------------------------------------------------------------- // Internal helper to get the size (in bytes) of the unboxed object. // For a generic type, the size of the type depends on the size of the // type-parameters. // This is commonly used by Cordb*Value in their Initialization when they // need to cache the size of the Target object they refer to. // // This should only be called on Value-types and Primitives (eg, i4, FnPtr). // It should not be called on Reference types. // // Parameters: // pObjectSize - OUT: out-parameter to get the size in bytes. // // Returns: // S_OK on success. //----------------------------------------------------------------------------- HRESULT CordbType::GetUnboxedObjectSize(ULONG32 *pObjectSize) { INTERNAL_SYNC_API_ENTRY(GetProcess()); // HRESULT hr = S_OK; bool isVC = false; EX_TRY { isVC = IsValueType(); } EX_CATCH_HRESULT(hr); IfFailRet(hr); if (isVC) { *pObjectSize = 0; hr = Init(FALSE); if (!SUCCEEDED(hr)) return hr; *pObjectSize = (ULONG) ((!m_pClass->HasTypeParams()) ? m_pClass->ObjectSize() : this->m_objectSize); return hr; } else { // Caller gaurantees that we're not a class. And the check above guarantees we're not a value-type. // So we're some sort of primitive, and thus we can determine size from the signature. // // @dbgtodo inspection - We didn't have this assert in Whidbey, and it's firing in vararg // scenarios even though it's returning the right value for reference types (i.e. 4 on x86 and 8 on // 64-bit). Commenting it out for now. //_ASSERTE(m_elementType != ELEMENT_TYPE_CLASS); // We need to use a temporary variable here -- attempting to cast among pointer types // (i.e., (PCCOR_SIGNATURE) &m_elementType) yields incorrect results on big-endian machines COR_SIGNATURE corSig = (COR_SIGNATURE) m_elementType; SigParser sigParser(&corSig, sizeof(corSig)); ULONG size; IfFailRet(sigParser.PeekElemTypeSize(&size)); *pObjectSize = size; return hr; } } VMPTR_DomainFile CordbType::GetDomainFile() { if (m_pClass != NULL) { CordbModule * pModule = m_pClass->GetModule(); if (pModule) { return pModule->m_vmDomainFile; } else { return VMPTR_DomainFile::NullPtr(); } } else { return VMPTR_DomainFile::NullPtr(); } } VMPTR_Module CordbType::GetModule() { if (m_pClass != NULL) { CordbModule * pModule = m_pClass->GetModule(); if (pModule) { return pModule->GetRuntimeModule(); } else { return VMPTR_Module::NullPtr(); } } else { return VMPTR_Module::NullPtr(); } } //----------------------------------------------------------------------------- // Internal method to Marshal: CordbType --> DebuggerIPCE_BasicTypeData // Nb. CordbType::Init will call this. The operation // fails if the exact type information has been requested but was not available // // Parameters: // data - OUT: BasicTypeData instance to fill out. // // Returns: // S_OK on success, CORDBG_E_CLASS_NOT_LOADED on failure //----------------------------------------------------------------------------- HRESULT CordbType::TypeToBasicTypeData(DebuggerIPCE_BasicTypeData *data) { switch (m_elementType) { case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: case ELEMENT_TYPE_BYREF: case ELEMENT_TYPE_PTR: data->elementType = m_elementType; data->metadataToken = mdTokenNil; data->vmDomainFile = VMPTR_DomainFile::NullPtr(); data->vmTypeHandle = m_typeHandleExact; if (data->vmTypeHandle.IsNull()) { return CORDBG_E_CLASS_NOT_LOADED; } _ASSERTE(!data->vmTypeHandle.IsNull()); break; case ELEMENT_TYPE_CLASS: _ASSERTE(m_pClass != NULL); data->elementType = m_pClass->IsValueClassNoInit() ? ELEMENT_TYPE_VALUETYPE : ELEMENT_TYPE_CLASS; data->metadataToken = m_pClass->MDToken(); data->vmDomainFile = GetDomainFile(); data->vmTypeHandle = m_typeHandleExact; if (m_pClass->HasTypeParams() && data->vmTypeHandle.IsNull()) { return CORDBG_E_CLASS_NOT_LOADED; } break; default: // This includes all the "primitive" types, in which CorElementType is a sufficient description. data->elementType = m_elementType; data->metadataToken = mdTokenNil; data->vmDomainFile = VMPTR_DomainFile::NullPtr(); data->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); break; } return S_OK; } //----------------------------------------------------------------------------- // Internal method to marshal: CordbType --> ExpandedTypeData // // Nb. CordbType::Init need NOT have been called before this... // Also, this does not write the type arguments. How this is done depends // depends on where this is called from. // // Parameters: // data - OUT: outgoing ExpandedTypeData to fill in with stats about CordbType. //----------------------------------------------------------------------------- void CordbType::TypeToExpandedTypeData(DebuggerIPCE_ExpandedTypeData *data) { switch (m_elementType) { case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: data->ArrayTypeData.arrayRank = m_rank; data->elementType = m_elementType; break; case ELEMENT_TYPE_BYREF: case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_FNPTR: data->elementType = m_elementType; break; case ELEMENT_TYPE_CLASS: { data->elementType = m_pClass->IsValueClassNoInit() ? ELEMENT_TYPE_VALUETYPE : ELEMENT_TYPE_CLASS; data->ClassTypeData.metadataToken = m_pClass->GetToken(); data->ClassTypeData.vmDomainFile = GetDomainFile(); data->ClassTypeData.vmModule = GetModule(); data->ClassTypeData.typeHandle = VMPTR_TypeHandle::NullPtr(); break; } case ELEMENT_TYPE_END: _ASSERTE(!"bad element type!"); default: data->elementType = m_elementType; break; } } void CordbType::TypeToTypeArgData(DebuggerIPCE_TypeArgData *data) { TypeToExpandedTypeData(&(data->data)); data->numTypeArgs = m_inst.m_cClassTyPars; } //----------------------------------------------------------------------------- // Query if this CordbType represents a ValueType (Does not include primitives). // Since CordbType doesn't record ValueType status, this may involve querying // the CordbClass or even asking the Left-Side (if the CordbClass is not init) // // Return Value: // indicates whether this is a value type // Note: // Throws. //----------------------------------------------------------------------------- bool CordbType::IsValueType() { if (m_elementType == ELEMENT_TYPE_CLASS) { return m_pClass->IsValueClass(); } else return false; } //------------------------------------------------------------------------ // If this is a ptr type, get the CordbType that it points to. // Eg, for CordbType("Int*") or CordbType("Int&"), returns CordbType("Int"). // If not a ptr type, returns null. // Since it's all internal, no reference counting. // This is effectively a specialized version of DestUnaryType. //------------------------------------------------------------------------ CordbType * CordbType::GetPointerElementType() { if ((m_elementType != ELEMENT_TYPE_PTR) && (m_elementType != ELEMENT_TYPE_BYREF)) { return NULL; } CordbType * pOut; DestUnaryType(&pOut); _ASSERTE(pOut != NULL); return pOut; } //------------------------------------------------------------------------ // Helper for IsGcRoot. // Determine if the element type is a non GC-root candidate. // Updating GC-roots requires coordinating with the GC's write-barrier. // Whereas non-GC roots can be updated more freely. // // Parameters: // et - An element type. // Returns: // True if variables of et can be used as a GC root. //------------------------------------------------------------------------ static inline bool IsElementTypeNonGcRoot(CorElementType et) { // Functon ptrs are raw data, not GC-roots. if (et == ELEMENT_TYPE_FNPTR) { return true; } // This is almost exactly if we're a primitive, but // primitives include some things that could be GC-roots, so we strip those out, return CorIsPrimitiveType(et) && (et != ELEMENT_TYPE_STRING) && (et != ELEMENT_TYPE_VOID); // exlcude these from primitives } //------------------------------------------------------------------------ // Helper for IsGcRoot // Non-gc roots include Value types + non-gc elemement types (like E_T_I4, E_T_FNPTR) // // Parameters: // pType - type to check whether it's a GC-root. // Returns: // true if we know we're not a GC-root // false if we still might be (so caller must do further checkin) //------------------------------------------------------------------------ static inline bool _IsNonGCRootHelper(CordbType * pType) { _ASSERTE(pType != NULL); CorElementType et = pType->GetElementType(); if (IsElementTypeNonGcRoot(et)) { return true; } HRESULT hr = S_OK; bool fValueClass = false; // If we are a value-type, then we can't be a Gc-root. EX_TRY { fValueClass = pType->IsValueType(); } EX_CATCH_HRESULT(hr); if (FAILED(hr) || fValueClass) { return true; } // Don't know return false; } //----------------------------------------------------------------------------- // Is this type a GC-root. (Not to be confused w/ "does this contain embedded GC roots") // All object references are GC-roots. E_T_PTR are actually not GC-roots. // // Returns: // True - if this is a GC-root. // False - not a GC root. //----------------------------------------------------------------------------- bool CordbType::IsGCRoot() { // If it's a E_T_PTR type, then look at what it's a a pointer of. CordbType * pPtr = this->GetPointerElementType(); if (pPtr == NULL) { // If non pointer, than we can just look at our current type. return !_IsNonGCRootHelper(this); } return !_IsNonGCRootHelper(pPtr); } //------------------------------------------------------------------------ // Public function to enumerate type-parameters. // Parameters: // ppTypeParameterEnum - OUT: on return, get an enumerator. // Returns: // S_OK on success. //------------------------------------------------------------------------ HRESULT CordbType::EnumerateTypeParameters(ICorDebugTypeEnum **ppTypeParameterEnum) { PUBLIC_API_ENTRY(this); VALIDATE_POINTER_TO_OBJECT(ppTypeParameterEnum, ICorDebugTypeEnum **); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); CordbTypeEnum *icdTPE = CordbTypeEnum::Build(m_appdomain, m_appdomain->GetLongExitNeuterList(), this->m_inst.m_cInst, this->m_inst.m_ppInst); if ( icdTPE == NULL ) { (*ppTypeParameterEnum) = NULL; return E_OUTOFMEMORY; } (*ppTypeParameterEnum) = static_cast (icdTPE); icdTPE->ExternalAddRef(); return S_OK; } //----------------------------------------------------------------------------- // CordbType::GetBase // Public convenience method to get the instantiated base type. // // Parameters: // ppType - OUT: yields the base type for the current type. // // Returns: // S_OK if succeeded. // HRESULT CordbType::GetBase(ICorDebugType ** ppType) { PUBLIC_REENTRANT_API_ENTRY(this); ATT_ALLOW_LIVE_DO_STOPGO(this->GetProcess()); // @todo - can this by RequiredStopped? HRESULT hr = S_OK; LOG((LF_CORDB, LL_EVERYTHING, "CordbType::GetBase called\n")); VALIDATE_POINTER_TO_OBJECT(ppType, ICorDebugType **); if (m_elementType != ELEMENT_TYPE_CLASS) { return E_INVALIDARG; } EX_TRY { CordbType * pType = NULL; _ASSERTE(m_pClass != NULL); // Get the supertype from metadata for m_class mdToken extendsToken; IMetaDataImport * pImport = m_pClass->GetModule()->GetMetaDataImporter(); // throws hr = pImport->GetTypeDefProps(m_pClass->MDToken(), NULL, 0, NULL, NULL, &extendsToken); IfFailThrow(hr); // Now create a CordbType instance for the base type that has the same type parameters as the derived type. if ((extendsToken == mdTypeDefNil) || (extendsToken == mdTypeRefNil) || (extendsToken == mdTokenNil)) { // No base class. pType = NULL; } else if (TypeFromToken(extendsToken) == mdtTypeSpec) { // TypeSpec has a signature. So get the sig and convert it to a CordbType. // generic base class of a generic type is a TypeSpec. // If we have: // class Triple derives from Pair, // then the base class for Triple would be a TypeSpec: // Class(Pair), 2 args, ELEMENT_TYPE_VAR #0, ELEMENT_TYPE_VAR#2. // m_inst provides the type-parameters to resolve the ELEMENT_TYPE_VAR types. PCCOR_SIGNATURE pSig; ULONG sigSize; // Get the signature for the constructed supertype... hr = pImport->GetTypeSpecFromToken(extendsToken, &pSig, &sigSize); IfFailThrow(hr); _ASSERTE(pSig != NULL); SigParser sigParser(pSig, sigSize); // Instantiate the signature of the supertype using the type instantiation for // the current type.... hr = SigToType(m_pClass->GetModule(), &sigParser, &m_inst, &pType); IfFailThrow(hr); } else if ((TypeFromToken(extendsToken) == mdtTypeRef) || (TypeFromToken(extendsToken) == mdtTypeDef)) { // TypeDef/TypeRef for non-generic base-class class. CordbClass * pSuperClass; hr = m_pClass->GetModule()->ResolveTypeRefOrDef(extendsToken, &pSuperClass); IfFailThrow(hr); _ASSERTE(pSuperClass != NULL); hr = MkUnparameterizedType(m_appdomain, ELEMENT_TYPE_CLASS, pSuperClass, &pType); IfFailThrow(hr); } else { pType = NULL; _ASSERTE(!"unexpected token!"); } // At this point, we've succeeded _ASSERTE(SUCCEEDED(hr)); (*ppType) = pType; if (*ppType) { pType->AddRef(); } } EX_CATCH_HRESULT(hr); return hr; } //----------------------------------------------------------------------------- // CordbType::GetTypeID // Method to get the COR_TYPEID corresponding to this CordbType. // // Parameters: // pId - OUT: gives the COR_TYPEID for this CordbType // // Returns: // S_OK if succeeded. // CORDBG_E_CLASS_NOT_LOADED if the type which this CordbType represents has // not been loaded in the runtime. // E_POINTER if pId is NULL // CORDBG_E_UNSUPPORTED for unsupported types. // HRESULT CordbType::GetTypeID(COR_TYPEID *pId) { LOG((LF_CORDB, LL_INFO1000, "GetTypeID\n")); if (pId == NULL) return E_POINTER; HRESULT hr = S_OK; PUBLIC_API_ENTRY(this); RSLockHolder stopGoLock(GetProcess()->GetStopGoLock()); RSLockHolder procLock(GetProcess()->GetProcessLock()); EX_TRY { hr = Init(FALSE); IfFailThrow(hr); VMPTR_TypeHandle vmTypeHandle; CorElementType et = GetElementType(); switch (et) { case ELEMENT_TYPE_OBJECT: case ELEMENT_TYPE_VOID: case ELEMENT_TYPE_BOOLEAN: case ELEMENT_TYPE_CHAR: case ELEMENT_TYPE_I1: case ELEMENT_TYPE_U1: case ELEMENT_TYPE_I2: case ELEMENT_TYPE_U2: case ELEMENT_TYPE_I4: case ELEMENT_TYPE_U4: case ELEMENT_TYPE_I8: case ELEMENT_TYPE_U8: case ELEMENT_TYPE_R4: case ELEMENT_TYPE_R8: case ELEMENT_TYPE_STRING: case ELEMENT_TYPE_TYPEDBYREF: case ELEMENT_TYPE_I: case ELEMENT_TYPE_U: { mdTypeDef mdToken; VMPTR_Module vmModule = VMPTR_Module::NullPtr(); VMPTR_DomainFile vmDomainFile = VMPTR_DomainFile::NullPtr(); // get module and token of the simple type GetProcess()->GetDAC()->GetSimpleType(GetAppDomain()->GetADToken(), et, &mdToken, &vmModule, &vmDomainFile); vmTypeHandle = GetProcess()->GetDAC()->GetTypeHandle(vmModule, mdToken); } break; case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: { LOG((LF_CORDB, LL_INFO1000, "GetTypeID: parameterized type\n")); if (m_typeHandleExact.IsNull()) { hr = InitInstantiationTypeHandle(FALSE); IfFailThrow(hr); } vmTypeHandle = m_typeHandleExact; } break; case ELEMENT_TYPE_CLASS: { ICorDebugClass *pICDClass = NULL; hr = GetClass(&pICDClass); IfFailThrow(hr); CordbClass *pClass = (CordbClass*)pICDClass; _ASSERTE(pClass != NULL); if (pClass->HasTypeParams()) { vmTypeHandle = m_typeHandleExact; } else { mdTypeDef mdToken; hr = pClass->GetToken(&mdToken); IfFailThrow(hr); VMPTR_Module vmModule = GetModule(); vmTypeHandle = GetProcess()->GetDAC()->GetTypeHandle(vmModule, mdToken); } } break; case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_BYREF: case ELEMENT_TYPE_FNPTR: IfFailThrow(CORDBG_E_UNSUPPORTED); default: _ASSERTE(!"unexpected element type!"); IfFailThrow(CORDBG_E_UNSUPPORTED); break; } GetProcess()->GetDAC()->GetTypeIDForType(vmTypeHandle, pId); } EX_CATCH_HRESULT(hr); return hr; } //----------------------------------------------------------------------------- // Get rich field information given a token. // // Parameters: // fldToken - metadata field token specifying a field on this Type. // ppFieldData - OUT: get the rich field information for the given field // // Returns: // S_OK on success. CORDBG_E_ENC_HANGING_FIELD for EnC fields (common case) // Other errors on failure case. //----------------------------------------------------------------------------- HRESULT CordbType::GetFieldInfo(mdFieldDef fldToken, FieldData ** ppFieldData) { INTERNAL_SYNC_API_ENTRY(GetProcess()); // HRESULT hr = S_OK; *ppFieldData = NULL; EX_TRY { if (m_elementType != ELEMENT_TYPE_CLASS) { ThrowHR(E_INVALIDARG); } // Initialize so that the field information is up-to-date. hr = Init(FALSE); IfFailThrow(hr); if (m_pClass->HasTypeParams()) { if (m_fieldList.IsEmpty()) { ThrowHR(CORDBG_E_FIELD_NOT_AVAILABLE); } else { // Use a static helper function in CordbClass, though we're really // searching through this->m_fields hr = CordbClass::SearchFieldInfo(m_pClass->GetModule(), &m_fieldList, m_pClass->MDToken(), fldToken, ppFieldData); // fall through and return. // Let possible CORDBG_E_ENC_HANGING_FIELD errors propogate } } else { hr = m_pClass->GetFieldInfo(fldToken, ppFieldData); // this is for non-generic types.... // Let possible CORDBG_E_ENC_HANGING_FIELD errors propogate } } EX_CATCH_HRESULT(hr); _ASSERTE(SUCCEEDED(hr) == (*ppFieldData != NULL)); return hr; } //----------------------------------------------------------------------------- // Class is a class somewhere on the hierarchy for m_type. Search for // a CordbType corresponding to the CordbClass, but which has the type-parameters // from the current CordbType. // In other words, instantiate a CordbType from baseClass, using the type-params // in the current Type. // // For example, given: // class C // class D : C // then if the CordbObjectValue is of type D and pClass is the class // for "C", then searching will set relevantType to C. This // type is then used to fetch fields from the object. // // Adds a reference to the resulting type. Since this is for internal // use only we probably don't need todo this... // // Parameters: // baseClass - open Type that needs to be instantiated with this CordbType's params. // ppRes - OUT: out-parameter to get CordbType. ppRes->GetClass() should equal baseClass. // // Returns: // S_OK on success. CORDBG_E_OBJECT_NEUTERED, CORDBG_E_CLASS_NOT_LOADED, E_INVALIDARG, OOM //----------------------------------------------------------------------------- HRESULT CordbType::GetParentType(CordbClass *baseClass, CordbType **ppRes) { INTERNAL_SYNC_API_ENTRY(GetProcess()); // // Ensure that we're not trying to match up against a neutered class. if (baseClass->IsNeutered()) { return CORDBG_E_OBJECT_NEUTERED; } HRESULT hr = S_OK; _ASSERTE(ppRes); *ppRes = NULL; CordbType *res = this; res->AddRef(); int safety = 20000; // no inheritance hierarchy is 20000 deep... we include this just in case there's a issue below and we don't terminate while (safety--) { if (res->m_pClass == NULL) { if (FAILED(hr = res->Init(FALSE))) { res->Release(); return hr; } } _ASSERTE(res->m_pClass); if (res->m_pClass == baseClass) { // Found it! break; } // Another way to determine if we're talking about the // same class... Compare tokens and module. mdTypeDef tok; mdTypeDef targetTok; if (FAILED(hr = res->m_pClass->GetToken(&tok)) || FAILED(hr = baseClass->GetToken(&targetTok))) { res->Release(); return hr; } if (tok == targetTok && res->m_pClass->GetModule() == baseClass->GetModule()) { // Found it! break; } // OK, this is not the right class so look up the inheritance chain ICorDebugType *nextType = NULL; if (FAILED(hr = res->GetBase(&nextType))) { res->Release(); return hr; } res->Release(); // matches the AddRef above and/or the one implicit in GetBase, for all but last time around the loop res = static_cast (nextType); if (!res || res->m_elementType == ELEMENT_TYPE_OBJECT) { // Did not find it... break; } } // We exit the loop above owning one reference to res. // Upon exit res will either be the appropriate type for the // class we're looking for or will be the CordbType for System.Object // or will be NULL // If it's System.Object then assume something's gone wrong with // the way we did the search and bail out to an old fashioned // MkUnparameterizedType on the class given originally if (!res || res->m_elementType == ELEMENT_TYPE_OBJECT) { if (res) res->Release(); // matches the one left over from the loop IfFailRet(CordbType::MkUnparameterizedType(baseClass->GetAppDomain(), ELEMENT_TYPE_CLASS, baseClass, &res)); res->AddRef(); } *ppRes = res; return hr; } //----------------------------------------------------------------------------- // Walk a type tree, writing the number of type args including internal nodes. // // Parameters: // count - IN/OUT: counter to update. //----------------------------------------------------------------------------- void CordbType::CountTypeDataNodes(unsigned int *count) { (*count)++; for (unsigned int i = 0; i < this->m_inst.m_cClassTyPars; i++) { this->m_inst.m_ppInst[i]->CountTypeDataNodes(count); } } //----------------------------------------------------------------------------- // Internal helper method. // Counts the total generic args (including sub-args) for an Instantiation. // Eg, for List>, it would return 3. // // Parameters: // genericArgsCount - size of the genericArgs array in elements. // genericArgs - array of type parameters. // count - IN/OUT - will increment with total number of generic args. // caller must intialize this (likely to 0). //----------------------------------------------------------------------------- void CordbType::CountTypeDataNodesForInstantiation(unsigned int genericArgsCount, ICorDebugType *genericArgs[], unsigned int *count) { for (unsigned int i = 0; i < genericArgsCount; i++) { (static_cast(genericArgs[i]))->CountTypeDataNodes(count); } } //----------------------------------------------------------------------------- // Recursively walk a type tree, writing the type args into a linear. // Eg, for List>, this will write the TypeArgData buffer // for { A, B, C }. // // Parameters: // curr_tyargData - IN/OUT: Pointer into buffer of TypeArgData structures. // Caller must ensure this buffer is large enough (probably by calling // CountTypeDataNodes). // On output, set to the next element in the buffer. //----------------------------------------------------------------------------- void CordbType::GatherTypeData(CordbType *type, DebuggerIPCE_TypeArgData **curr_tyargData) { type->TypeToTypeArgData(*curr_tyargData); (*curr_tyargData)++; for (unsigned int i = 0; i < type->m_inst.m_cClassTyPars; i++) { GatherTypeData(type->m_inst.m_ppInst[i], curr_tyargData); } } //----------------------------------------------------------------------------- // Flatten Instantiation into a linear buffer of TypeArgData // Use CountTypeDataNodesForInstantiation on the instantiation to get a large // enough buffer. // // Parameters: // genericArgsCount - size of genericArgs array in elements. // genericArgs - incoming array to walk // curr_tyargData - IN/OUT: Pointer into buffer of TypeArgData structures. // Caller must ensure this buffer is large enough (probably by calling // CountTypeDataNodes). // On output, set to the next element in the buffer. // //----------------------------------------------------------------------------- void CordbType::GatherTypeDataForInstantiation(unsigned int genericArgsCount, ICorDebugType *genericArgs[], DebuggerIPCE_TypeArgData **curr_tyargData) { for (unsigned int i = 0; i < genericArgsCount; i++) { GatherTypeData(static_cast (genericArgs[i]), curr_tyargData); } } #ifdef FEATURE_64BIT_ALIGNMENT // checks if the type requires 8-byte alignment. the algorithm used here // was adapted from AdjustArgPtrForAlignment() in bcltype/VarArgsNative.cpp HRESULT CordbType::RequiresAlign8(BOOL* isRequired) { if (isRequired == NULL) return E_INVALIDARG; HRESULT hr = S_OK; EX_TRY { *isRequired = FALSE; ULONG32 size = 0; GetUnboxedObjectSize(&size); if (size >= 8) { CorElementType type; GetType(&type); if (type != ELEMENT_TYPE_TYPEDBYREF) { if (type == ELEMENT_TYPE_VALUETYPE) { if (m_typeHandleExact.IsNull()) InitInstantiationTypeHandle(FALSE); *isRequired = GetProcess()->GetDAC()->RequiresAlign8(m_typeHandleExact); } else { *isRequired = TRUE; } } } } EX_CATCH_HRESULT(hr); return hr; } #endif /* ------------------------------------------------------------------------- * * TypeParameter Enumerator class * ------------------------------------------------------------------------- */ // Factory methods CordbTypeEnum* CordbTypeEnum::Build(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, CordbType **ppTypars) { return BuildImpl( pAppDomain, pNeuterList, cTypars, ppTypars ); } CordbTypeEnum* CordbTypeEnum::Build(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, RSSmartPtr *ppTypars) { return BuildImpl( pAppDomain, pNeuterList, cTypars, ppTypars ); } //----------------------------------------------------------------------------- // We need to support taking both an array of CordbType* and an array of RSSmartPtr, // but the code is identical in both cases. Rather than duplicate any code explicity, it's better to // have the compiler do it for us using this template method. // Another option would be to create an IList interface and implementations for both arrays // of T* and arrays of RSSmartPtr. This would be more generally useful, but much more code. //----------------------------------------------------------------------------- template CordbTypeEnum* CordbTypeEnum::BuildImpl(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, T* ppTypars) { CordbTypeEnum* newEnum = new (nothrow) CordbTypeEnum( pAppDomain, pNeuterList ); if( NULL == newEnum ) { return NULL; } _ASSERTE( newEnum->m_ppTypars == NULL ); newEnum->m_ppTypars = new (nothrow) RSSmartPtr [cTypars]; if( newEnum->m_ppTypars == NULL ) { delete newEnum; return NULL; } newEnum->m_iMax = cTypars; for (unsigned int i = 0; i < cTypars; i++) { newEnum->m_ppTypars[i].Assign(ppTypars[i]); } return newEnum; } // Private, called only by Build above CordbTypeEnum::CordbTypeEnum(CordbAppDomain * pAppDomain, NeuterList * pNeuterList) : CordbBase(pAppDomain->GetProcess(), 0), m_ppTypars(NULL), m_iCurrent(0), m_iMax(0) { _ASSERTE(pAppDomain != NULL); _ASSERTE(pNeuterList != NULL); m_pAppDomain = pAppDomain; HRESULT hr = S_OK; EX_TRY { pNeuterList->Add(GetProcess(), this); } EX_CATCH_HRESULT(hr); SetUnrecoverableIfFailed(GetProcess(), hr); } CordbTypeEnum::~CordbTypeEnum() { _ASSERTE(this->IsNeutered()); } void CordbTypeEnum::Neuter() { delete [] m_ppTypars; m_ppTypars = NULL; m_pAppDomain = NULL; CordbBase::Neuter(); } HRESULT CordbTypeEnum::QueryInterface(REFIID id, void **pInterface) { if (id == IID_ICorDebugEnum) *pInterface = static_cast(this); else if (id == IID_ICorDebugTypeEnum) *pInterface = static_cast(this); else if (id == IID_IUnknown) *pInterface = static_cast(static_cast(this)); else { *pInterface = NULL; return E_NOINTERFACE; } ExternalAddRef(); return S_OK; } HRESULT CordbTypeEnum::Skip(ULONG celt) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); HRESULT hr = E_FAIL; if ( (m_iCurrent+celt) < m_iMax || celt == 0) { m_iCurrent += celt; hr = S_OK; } return hr; } HRESULT CordbTypeEnum::Reset(void) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); m_iCurrent = 0; return S_OK; } HRESULT CordbTypeEnum::Clone(ICorDebugEnum **ppEnum) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **); HRESULT hr = S_OK; CordbTypeEnum *pCVE = CordbTypeEnum::Build( m_pAppDomain, m_pAppDomain->GetLongExitNeuterList(), m_iMax, m_ppTypars ); if ( pCVE == NULL ) { (*ppEnum) = NULL; hr = E_OUTOFMEMORY; goto LExit; } pCVE->AddRef(); (*ppEnum) = (ICorDebugEnum*)pCVE; LExit: return hr; } HRESULT CordbTypeEnum::GetCount(ULONG *pcelt) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *); if( pcelt == NULL) return E_INVALIDARG; (*pcelt) = m_iMax; return S_OK; } // // In the event of failure, the current pointer will be left at // one element past the troublesome element. Thus, if one were // to repeatedly ask for one element to iterate through the // array, you would iterate exactly m_iMax times, regardless // of individual failures. HRESULT CordbTypeEnum::Next(ULONG celt, ICorDebugType *values[], ULONG *pceltFetched) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); VALIDATE_POINTER_TO_OBJECT_ARRAY(values, ICorDebugClass *, celt, true, true); VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *); if ((pceltFetched == NULL) && (celt != 1)) { return E_INVALIDARG; } if (celt == 0) { if (pceltFetched != NULL) { *pceltFetched = 0; } return S_OK; } HRESULT hr = S_OK; int iMax = min( m_iMax, m_iCurrent+celt); int i; for (i = m_iCurrent; i < iMax; i++) { //printf("CordbTypeEnum::Next, returning = 0x%08x.\n", m_ppTypars[i]); values[i-m_iCurrent] = m_ppTypars[i]; values[i-m_iCurrent]->AddRef(); } int count = (i - m_iCurrent); if ( FAILED( hr ) ) { //we failed: +1 pushes us past troublesome element m_iCurrent += 1 + count; } else { m_iCurrent += count; } if (pceltFetched != NULL) { *pceltFetched = count; } // // If we reached the end of the enumeration, but not the end // of the number of requested items, we return S_FALSE. // if (((ULONG)count) < celt) { return S_FALSE; } return hr; }