//******************************************************************************* // Helper: Set the specified attributes on the given ExportedType token. //******************************************************************************* HRESULT RegMeta::_SetExportedTypeProps( // S_OK or error. mdExportedType ct, // [IN] ExportedType token. mdToken tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. mdTypeDef tkTypeDef, // [IN] TypeDef token within the file. DWORD dwExportedTypeFlags) // [IN] Flags. { ExportedTypeRec *pRecord; HRESULT hr = S_OK; IfFailGo(m_pStgdb->m_MiniMd.GetExportedTypeRecord(RidFromToken(ct), &pRecord)); if(! IsNilToken(tkImplementation)) IfFailGo(m_pStgdb->m_MiniMd.PutToken(TBL_ExportedType, ExportedTypeRec::COL_Implementation, pRecord, tkImplementation)); if (! IsNilToken(tkTypeDef)) { _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef); pRecord->SetTypeDefId(tkTypeDef); } if (dwExportedTypeFlags != ULONG_MAX) pRecord->SetFlags(dwExportedTypeFlags); IfFailGo(UpdateENCLog(ct)); ErrExit: return hr; } // RegMeta::_SetExportedTypeProps
int GetVersionResilientTypeHashCode(IMDInternalImport *pMDImport, mdExportedType token) { _ASSERTE(TypeFromToken(token) == mdtTypeDef || TypeFromToken(token) == mdtTypeRef || TypeFromToken(token) == mdtExportedType); _ASSERTE(!IsNilToken(token)); HRESULT hr; LPCUTF8 szNamespace; LPCUTF8 szName; bool hasTypeToken = true; int hashcode = 0; while (hasTypeToken) { if (IsNilToken(token)) ThrowHR(COR_E_BADIMAGEFORMAT); switch (TypeFromToken(token)) { case mdtTypeDef: if (FAILED(pMDImport->GetNameOfTypeDef(token, &szName, &szNamespace))) ThrowHR(COR_E_BADIMAGEFORMAT); hr = pMDImport->GetNestedClassProps(token, &token); if (hr == CLDB_E_RECORD_NOTFOUND) hasTypeToken = false; else if (FAILED(hr)) ThrowHR(COR_E_BADIMAGEFORMAT); break; case mdtTypeRef: if (FAILED(pMDImport->GetNameOfTypeRef(token, &szNamespace, &szName))) ThrowHR(COR_E_BADIMAGEFORMAT); if (FAILED(pMDImport->GetResolutionScopeOfTypeRef(token, &token))) ThrowHR(COR_E_BADIMAGEFORMAT); hasTypeToken = (TypeFromToken(token) == mdtTypeRef); break; case mdtExportedType: if (FAILED(pMDImport->GetExportedTypeProps(token, &szNamespace, &szName, &token, NULL, NULL))) ThrowHR(COR_E_BADIMAGEFORMAT); hasTypeToken = (TypeFromToken(token) == mdtExportedType); break; default: ThrowHR(COR_E_BADIMAGEFORMAT); } hashcode ^= ComputeNameHashCode(szNamespace, szName); } return hashcode; }
void QCALLTYPE COMDynamicWrite::TermCreateClass(QCall::ModuleHandle pModule, INT32 tk, QCall::ObjectHandleOnStack retType) { QCALL_CONTRACT; TypeHandle typeHnd; BEGIN_QCALL; _ASSERTE(pModule->GetReflectionModule()->GetClassWriter()); // Use the same service, regardless of whether we are generating a normal // class, or the special class for the module that holds global functions // & methods. pModule->GetReflectionModule()->AddClass(tk); // manually load the class if it is not the global type if (!IsNilToken(tk)) { TypeKey typeKey(pModule, tk); typeHnd = pModule->GetClassLoader()->LoadTypeHandleForTypeKey(&typeKey, TypeHandle()); } if (!typeHnd.IsNull()) { GCX_COOP(); retType.Set(typeHnd.GetManagedClassObject()); } END_QCALL; return; }
int GetVersionResilientTypeHashCode(TypeHandle type) { if (!type.IsTypeDesc()) { MethodTable *pMT = type.AsMethodTable(); _ASSERTE(!pMT->IsArray()); _ASSERTE(!IsNilToken(pMT->GetCl())); LPCUTF8 szNamespace; LPCUTF8 szName; IfFailThrow(pMT->GetMDImport()->GetNameOfTypeDef(pMT->GetCl(), &szName, &szNamespace)); int hashcode = ComputeNameHashCode(szNamespace, szName); MethodTable *pMTEnclosing = pMT->LoadEnclosingMethodTable(CLASS_LOAD_UNRESTOREDTYPEKEY); if (pMTEnclosing != NULL) { hashcode = ComputeNestedTypeHashCode(GetVersionResilientTypeHashCode(TypeHandle(pMTEnclosing)), hashcode); } if (!pMT->IsGenericTypeDefinition() && pMT->HasInstantiation()) { return ComputeGenericInstanceHashCode(hashcode, pMT->GetInstantiation().GetNumArgs(), pMT->GetInstantiation(), GetVersionResilientTypeHashCode); } else { return hashcode; } } else if (type.IsArray()) { ArrayTypeDesc *pArray = type.AsArray(); return ComputeArrayTypeHashCode(GetVersionResilientTypeHashCode(pArray->GetArrayElementTypeHandle()), pArray->GetRank()); } else if (type.IsPointer()) { return ComputePointerTypeHashCode(GetVersionResilientTypeHashCode(type.AsTypeDesc()->GetTypeParam())); } else if (type.IsByRef()) { return ComputeByrefTypeHashCode(GetVersionResilientTypeHashCode(type.AsTypeDesc()->GetTypeParam())); } assert(false); return 0; }
MethodTable *Binder::LookupClass(BinderClassID id, BOOL fLoad) { _ASSERTE(m_pModule != NULL); _ASSERTE(id != CLASS__NIL); _ASSERTE(id <= m_cClassRIDs); MethodTable *pMT; const ClassDescription *d = m_classDescriptions + id - 1; NameHandle nh(d->name); if (!fLoad) { nh.SetTokenNotToLoad(tdAllTypes); pMT = m_pModule->GetClassLoader()->FindTypeHandle(&nh).AsMethodTable(); if (pMT == NULL) return NULL; } else { THROWSCOMPLUSEXCEPTION(); BEGIN_ENSURE_COOPERATIVE_GC(); OBJECTREF pThrowable = NULL; GCPROTECT_BEGIN(pThrowable); pMT = m_pModule->GetClassLoader()->FindTypeHandle(&nh, &pThrowable).AsMethodTable(); if (pMT == NULL) { _ASSERTE(!"EE expects class to exist"); COMPlusThrow(pThrowable); } GCPROTECT_END(); END_ENSURE_COOPERATIVE_GC(); } _ASSERTE(pMT->GetModule() == m_pModule); mdTypeDef td = pMT->GetClass()->GetCl(); _ASSERTE(!IsNilToken(td)); _ASSERTE(RidFromToken(td) <= USHRT_MAX); m_pClassRIDs[id-1] = (USHORT) RidFromToken(td); m_pModule->StoreTypeDef(td, pMT); return pMT; }
//***************************************************************************** // Given a namespace and a class name, return the typedef //***************************************************************************** STDMETHODIMP RegMeta::FindTypeDefByName(// S_OK or error. LPCWSTR wzTypeDef, // [IN] Name of the Type. mdToken tkEnclosingClass, // [IN] Enclosing class. mdTypeDef *ptd) // [OUT] Put the TypeDef token here. { HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW LOG((LOGMD, "{%08x} RegMeta::FindTypeDefByName(%S, 0x%08x, 0x%08x)\n", this, MDSTR(wzTypeDef), tkEnclosingClass, ptd)); START_MD_PERF(); LOCKREAD(); if (wzTypeDef == NULL) IfFailGo(E_INVALIDARG); PREFIX_ASSUME(wzTypeDef != NULL); LPSTR szTypeDef; UTF8STR(wzTypeDef, szTypeDef); LPCSTR szNamespace; LPCSTR szName; _ASSERTE(ptd); _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef || TypeFromToken(tkEnclosingClass) == mdtTypeRef || IsNilToken(tkEnclosingClass)); // initialize output parameter *ptd = mdTypeDefNil; ns::SplitInline(szTypeDef, szNamespace, szName); hr = ImportHelper::FindTypeDefByName(&(m_pStgdb->m_MiniMd), szNamespace, szName, tkEnclosingClass, ptd); ErrExit: STOP_MD_PERF(FindTypeDefByName); END_ENTRYPOINT_NOTHROW; return hr; } // STDMETHODIMP RegMeta::FindTypeDefByName()
//***************************************************************************** // Implementation of IMetaDataImport::ResolveTypeRef to resolve a typeref across scopes. // // Arguments: // tr - typeref within this scope to resolve // riid - interface on ppIScope to support // ppIScope - out-parameter to get metadata scope for typedef (*ptd) // ptd - out-parameter to get typedef that the ref resolves to. // // Notes: // TypeDefs define a type within a scope. TypeRefs refer to type-defs in other scopes // and allow you to import a type from another scope. This function attempts to determine // which type-def a type-ref points to. // // This resolve (type-ref, this cope) --> (type-def=*ptd, other scope=*ppIScope) // // However, this resolution requires knowing what modules have been loaded, which is not decided // until runtime via loader / fusion policy. Thus this interface can't possibly be correct since // it doesn't have that knowledge. Furthermore, when inspecting metadata from another process // (such as a debugger inspecting the debuggee's metadata), this API can be truly misleading. // // This API usage should be avoided. // //***************************************************************************** STDMETHODIMP RegMeta::ResolveTypeRef( mdTypeRef tr, REFIID riid, IUnknown ** ppIScope, mdTypeDef * ptd) { #ifdef FEATURE_METADATA_IN_VM HRESULT hr; BEGIN_ENTRYPOINT_NOTHROW; TypeRefRec * pTypeRefRec; WCHAR wzNameSpace[_MAX_PATH]; CMiniMdRW * pMiniMd = NULL; LOG((LOGMD, "{%08x} RegMeta::ResolveTypeRef(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", this, tr, riid, ppIScope, ptd)); START_MD_PERF(); LOCKREAD(); pMiniMd = &(m_pStgdb->m_MiniMd); _ASSERTE((ppIScope != NULL) && (ptd != NULL)); // Init the output values. *ppIScope = NULL; *ptd = 0; if (IsNilToken(tr)) { if (ptd != NULL) { *ptd = mdTypeDefNil; } if (ppIScope != NULL) { *ppIScope = NULL; } STOP_MD_PERF(ResolveTypeRef); hr = E_INVALIDARG; goto ErrExit; } if (TypeFromToken(tr) == mdtTypeDef) { // Shortcut when we receive a TypeDef token *ptd = tr; STOP_MD_PERF(ResolveTypeRef); hr = this->QueryInterface(riid, (void **)ppIScope); goto ErrExit; } // Get the class ref row. _ASSERTE(TypeFromToken(tr) == mdtTypeRef); IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec)); IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, wzNameSpace, lengthof(wzNameSpace), NULL)); if (hr != NOERROR) { _ASSERTE(hr == CLDB_S_TRUNCATION); // Truncate the namespace string wzNameSpace[lengthof(wzNameSpace) - 1] = 0; } //*********************** // before we go off to CORPATH, check the loaded modules! //*********************** if (LOADEDMODULES::ResolveTypeRefWithLoadedModules( tr, this, pMiniMd, riid, ppIScope, ptd) == NOERROR) { // Done!! We found one match among the loaded modules. goto ErrExit; } IfFailGo(META_E_CANNOTRESOLVETYPEREF); ErrExit: STOP_MD_PERF(ResolveTypeRef); END_ENTRYPOINT_NOTHROW; return hr; #else // FEATURE_METADATA_IN_VM return E_NOTIMPL; #endif // FEATURE_METADATA_IN_VM } // RegMeta::ResolveTypeRef
//PrettyPrinting type names PCCOR_SIGNATURE PrettyPrintType( PCCOR_SIGNATURE typePtr, // type to convert, CQuickBytes *out, // where to put the pretty printed string IMDInternalImport *pIMDI, // ptr to IMDInternal class with ComSig DWORD formatFlags /*= formatILDasm*/) { mdToken tk; const char* str; int typ; CQuickBytes tmp; CQuickBytes Appendix; BOOL Reiterate; int n; do { Reiterate = FALSE; switch(typ = *typePtr++) { case ELEMENT_TYPE_VOID : str = "void"; goto APPEND; case ELEMENT_TYPE_BOOLEAN : str = "bool"; goto APPEND; case ELEMENT_TYPE_CHAR : str = "char"; goto APPEND; case ELEMENT_TYPE_I1 : str = "int8"; goto APPEND; case ELEMENT_TYPE_U1 : str = "uint8"; goto APPEND; case ELEMENT_TYPE_I2 : str = "int16"; goto APPEND; case ELEMENT_TYPE_U2 : str = "uint16"; goto APPEND; case ELEMENT_TYPE_I4 : str = "int32"; goto APPEND; case ELEMENT_TYPE_U4 : str = "uint32"; goto APPEND; case ELEMENT_TYPE_I8 : str = "int64"; goto APPEND; case ELEMENT_TYPE_U8 : str = "uint64"; goto APPEND; case ELEMENT_TYPE_R4 : str = "float32"; goto APPEND; case ELEMENT_TYPE_R8 : str = "float64"; goto APPEND; case ELEMENT_TYPE_U : str = "native uint"; goto APPEND; case ELEMENT_TYPE_I : str = "native int"; goto APPEND; case ELEMENT_TYPE_OBJECT : str = "object"; goto APPEND; case ELEMENT_TYPE_STRING : str = "string"; goto APPEND; case ELEMENT_TYPE_TYPEDBYREF : str = "typedref"; goto APPEND; APPEND: appendStr(out, (char*)str); break; case ELEMENT_TYPE_VALUETYPE : if ((formatFlags & FormatKwInNames) != 0) str = "valuetype "; else str = ""; goto DO_CLASS; case ELEMENT_TYPE_CLASS : if ((formatFlags & FormatKwInNames) != 0) str = "class "; else str = ""; goto DO_CLASS; DO_CLASS: appendStr(out, (char*)str); typePtr += CorSigUncompressToken(typePtr, &tk); if(IsNilToken(tk)) { appendStr(out, "[ERROR! NIL TOKEN]"); } else PrettyPrintClass(out, tk, pIMDI, formatFlags); break; case ELEMENT_TYPE_SZARRAY : insertStr(&Appendix,"[]"); Reiterate = TRUE; break; case ELEMENT_TYPE_ARRAY : { typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags); unsigned rank = CorSigUncompressData(typePtr); // <TODO> what is the syntax for the rank 0 case? </TODO> if (rank == 0) { appendStr(out, "[BAD: RANK == 0!]"); } else { _ASSERTE(rank != 0); #ifdef _PREFAST_ #pragma warning(push) #pragma warning(disable:22009) // "Suppress PREfast warnings about integer overflow" // PREFAST warns about using _alloca in a loop. However when we're in this switch case we do NOT // set Reiterate to true, so we only execute through the loop once! #pragma warning(disable:6263) // "Suppress PREfast warnings about stack overflow due to _alloca in a loop." #endif int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank); int* sizes = &lowerBounds[rank]; memset(lowerBounds, 0, sizeof(int)*2*rank); unsigned numSizes = CorSigUncompressData(typePtr); _ASSERTE(numSizes <= rank); unsigned i; for(i =0; i < numSizes; i++) sizes[i] = CorSigUncompressData(typePtr); unsigned numLowBounds = CorSigUncompressData(typePtr); _ASSERTE(numLowBounds <= rank); for(i = 0; i < numLowBounds; i++) typePtr+=CorSigUncompressSignedInt(typePtr,&lowerBounds[i]); appendChar(out, '['); if (rank == 1 && numSizes == 0 && numLowBounds == 0) appendStr(out, "..."); else { for(i = 0; i < rank; i++) { //if (sizes[i] != 0 || lowerBounds[i] != 0) { if (lowerBounds[i] == 0 && i < numSizes) appendStrNum(out, sizes[i]); else { if(i < numLowBounds) { appendStrNum(out, lowerBounds[i]); appendStr(out, "..."); if (/*sizes[i] != 0 && */i < numSizes) appendStrNum(out, lowerBounds[i] + sizes[i] - 1); } } } if (i < rank-1) appendChar(out, ','); } } appendChar(out, ']'); #ifdef _PREFAST_ #pragma warning(pop) #endif } } break; case ELEMENT_TYPE_VAR : appendChar(out, '!'); n = CorSigUncompressData(typePtr); appendStrNum(out, n); break; case ELEMENT_TYPE_MVAR : appendChar(out, '!'); appendChar(out, '!'); n = CorSigUncompressData(typePtr); appendStrNum(out, n); break; case ELEMENT_TYPE_FNPTR : appendStr(out, "method "); appendStr(out, "METHOD"); // was: typePtr = PrettyPrintSignature(typePtr, 0x7FFF, "*", out, pIMDI, NULL); break; case ELEMENT_TYPE_GENERICINST : { typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags); if ((formatFlags & FormatSignature) == 0) break; if ((formatFlags & FormatAngleBrackets) != 0) appendStr(out, "<"); else appendStr(out,"["); unsigned numArgs = CorSigUncompressData(typePtr); bool needComma = false; while(numArgs--) { if (needComma) appendChar(out, ','); typePtr = PrettyPrintType(typePtr, out, pIMDI, formatFlags); needComma = true; } if ((formatFlags & FormatAngleBrackets) != 0) appendStr(out, ">"); else appendStr(out,"]"); break; } case ELEMENT_TYPE_PINNED : str = " pinned"; goto MODIFIER; case ELEMENT_TYPE_PTR : str = "*"; goto MODIFIER; case ELEMENT_TYPE_BYREF : str = "&"; goto MODIFIER; MODIFIER: insertStr(&Appendix, str); Reiterate = TRUE; break; default: case ELEMENT_TYPE_SENTINEL : case ELEMENT_TYPE_END : //_ASSERTE(!"Unknown Type"); if(typ) { char sz[64]; sprintf_s(sz,COUNTOF(sz),"/* UNKNOWN TYPE (0x%X)*/",typ); appendStr(out, sz); } break; } // end switch } while(Reiterate); if (Appendix.Size() > 0) appendStr(out,asString(&Appendix)); return(typePtr); }
// emit the section (best format); unsigned __stdcall SectEH_Emit(unsigned size, unsigned ehCount, IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clauses, BOOL moreSections, BYTE* outBuff, ULONG* ehTypeOffsets) { if (size == 0) return(0); _ASSERTE((((size_t) outBuff) & 3) == 0); // header is dword aligned BYTE* origBuff = outBuff; if (ehCount <= 0) return 0; // Initialize the ehTypeOffsets array. if (ehTypeOffsets) { for (unsigned int i = 0; i < ehCount; i++) ehTypeOffsets[i] = (ULONG) -1; } if (COR_ILMETHOD_SECT_EH_SMALL::Size(ehCount) < COR_ILMETHOD_SECT_SMALL_MAX_DATASIZE) { COR_ILMETHOD_SECT_EH_SMALL* EHSect = (COR_ILMETHOD_SECT_EH_SMALL*) outBuff; unsigned i; for (i = 0; i < ehCount; i++) { COR_ILMETHOD_SECT_EH_CLAUSE_FAT* fatClause = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)&clauses[i]; if (fatClause->GetTryOffset() > 0xFFFF || fatClause->GetTryLength() > 0xFF || fatClause->GetHandlerOffset() > 0xFFFF || fatClause->GetHandlerLength() > 0xFF) { break; // fall through and generate as FAT } _ASSERTE((fatClause->GetFlags() & ~0xFFFF) == 0); _ASSERTE((fatClause->GetTryOffset() & ~0xFFFF) == 0); _ASSERTE((fatClause->GetTryLength() & ~0xFF) == 0); _ASSERTE((fatClause->GetHandlerOffset() & ~0xFFFF) == 0); _ASSERTE((fatClause->GetHandlerLength() & ~0xFF) == 0); COR_ILMETHOD_SECT_EH_CLAUSE_SMALL* smallClause = (COR_ILMETHOD_SECT_EH_CLAUSE_SMALL*)&EHSect->Clauses[i]; smallClause->SetFlags((CorExceptionFlag) fatClause->GetFlags()); smallClause->SetTryOffset(fatClause->GetTryOffset()); smallClause->SetTryLength(fatClause->GetTryLength()); smallClause->SetHandlerOffset(fatClause->GetHandlerOffset()); smallClause->SetHandlerLength(fatClause->GetHandlerLength()); smallClause->SetClassToken(fatClause->GetClassToken()); } if (i >= ehCount) { // if actually got through all the clauses and they are small enough EHSect->Kind = CorILMethod_Sect_EHTable; if (moreSections) EHSect->Kind |= CorILMethod_Sect_MoreSects; EHSect->DataSize = EHSect->Size(ehCount); EHSect->Reserved = 0; _ASSERTE(EHSect->DataSize == EHSect->Size(ehCount)); // make sure didn't overflow outBuff = (BYTE*) &EHSect->Clauses[ehCount]; // Set the offsets for the exception type tokens. if (ehTypeOffsets) { for (i = 0; i < ehCount; i++) { COR_ILMETHOD_SECT_EH_CLAUSE_SMALL* smallClause = (COR_ILMETHOD_SECT_EH_CLAUSE_SMALL*)&EHSect->Clauses[i]; if (smallClause->GetFlags() == COR_ILEXCEPTION_CLAUSE_NONE) { _ASSERTE(! IsNilToken(smallClause->GetClassToken())); ehTypeOffsets[i] = (ULONG)((BYTE *)&smallClause->ClassToken - origBuff); } } } return(size); } } // either total size too big or one of constituent elements too big (eg. offset or length) COR_ILMETHOD_SECT_EH_FAT* EHSect = (COR_ILMETHOD_SECT_EH_FAT*) outBuff; EHSect->SetKind(CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat); if (moreSections) EHSect->SetKind(EHSect->GetKind() | CorILMethod_Sect_MoreSects); EHSect->SetDataSize(EHSect->Size(ehCount)); memcpy(EHSect->Clauses, clauses, ehCount * sizeof(COR_ILMETHOD_SECT_EH_CLAUSE_FAT)); outBuff = (BYTE*) &EHSect->Clauses[ehCount]; _ASSERTE(&origBuff[size] == outBuff); // Set the offsets for the exception type tokens. if (ehTypeOffsets) { for (unsigned int i = 0; i < ehCount; i++) { COR_ILMETHOD_SECT_EH_CLAUSE_FAT* fatClause = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)&EHSect->Clauses[i]; if (fatClause->GetFlags() == COR_ILEXCEPTION_CLAUSE_NONE) { _ASSERTE(! IsNilToken(fatClause->GetClassToken())); ehTypeOffsets[i] = (ULONG)((BYTE *)&fatClause->ClassToken - origBuff); } } } return(size); }
//***************************************************************************** // Enumerate the CustomAttributes for a given token. //***************************************************************************** STDMETHODIMP RegMeta::EnumCustomAttributes( HCORENUM *phEnum, // Pointer to the enum. mdToken tk, // Token to scope the enumeration. mdToken tkType, // Type to limit the enumeration. mdCustomAttribute rCustomAttributes[], // Put CustomAttributes here. ULONG cMax, // Max CustomAttributes to put. ULONG *pcCustomAttributes) // Put # tokens returned here. { HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; HENUMInternal **ppmdEnum = reinterpret_cast<HENUMInternal **> (phEnum); ULONG ridStart; ULONG ridEnd; HENUMInternal *pEnum = *ppmdEnum; CustomAttributeRec *pRec; ULONG index; LOG((LOGMD, "RegMeta::EnumCustomAttributes(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, tk, tkType, rCustomAttributes, cMax, pcCustomAttributes)); START_MD_PERF(); LOCKREAD(); if ( pEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); CLookUpHash *pHashTable = pMiniMd->m_pLookUpHashs[TBL_CustomAttribute]; // Does caller want all custom Values? if (IsNilToken(tk)) { IfFailGo( HENUMInternal::CreateSimpleEnum(mdtCustomAttribute, 1, pMiniMd->getCountCustomAttributes()+1, &pEnum) ); } else { // Scope by some object. if ( pMiniMd->IsSorted( TBL_CustomAttribute ) ) { // Get CustomAttributes for the object. IfFailGo(pMiniMd->getCustomAttributeForToken(tk, &ridEnd, &ridStart)); if (IsNilToken(tkType)) { // Simple enumerator for object's entire list. IfFailGo( HENUMInternal::CreateSimpleEnum( mdtCustomAttribute, ridStart, ridEnd, &pEnum) ); } else { // Dynamic enumerator for subsetted list. IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) ); for (index = ridStart; index < ridEnd; index ++ ) { IfFailGo(pMiniMd->GetCustomAttributeRecord(index, &pRec)); if (tkType == pMiniMd->getTypeOfCustomAttribute(pRec)) { IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtCustomAttribute) ) ); } } } } else { if (pHashTable) { // table is not sorted but hash is built // We want to create dynmaic array to hold the dynamic enumerator. TOKENHASHENTRY *p; ULONG iHash; int pos; mdToken tkParentTmp; mdToken tkTypeTmp; // Hash the data. iHash = pMiniMd->HashCustomAttribute(tk); IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) ); // Go through every entry in the hash chain looking for ours. for (p = pHashTable->FindFirst(iHash, pos); p; p = pHashTable->FindNext(pos)) { CustomAttributeRec *pCustomAttribute; IfFailGo(pMiniMd->GetCustomAttributeRecord(RidFromToken(p->tok), &pCustomAttribute)); tkParentTmp = pMiniMd->getParentOfCustomAttribute(pCustomAttribute); tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pCustomAttribute); if (tkParentTmp == tk) { if (IsNilToken(tkType) || tkType == tkTypeTmp) { // compare the blob value IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(p->tok, mdtCustomAttribute )) ); } } } } else { // table is not sorted and hash is not built so we have to create dynmaic array // create the dynamic enumerator and loop through CA table linearly // ridStart = 1; ridEnd = pMiniMd->getCountCustomAttributes() + 1; IfFailGo( HENUMInternal::CreateDynamicArrayEnum( mdtCustomAttribute, &pEnum) ); for (index = ridStart; index < ridEnd; index ++ ) { IfFailGo(pMiniMd->GetCustomAttributeRecord(index, &pRec)); if ( tk == pMiniMd->getParentOfCustomAttribute(pRec) && (tkType == pMiniMd->getTypeOfCustomAttribute(pRec) || IsNilToken(tkType))) { IfFailGo( HENUMInternal::AddElementToEnum(pEnum, TokenFromRid(index, mdtCustomAttribute) ) ); } } } } } // set the output parameter *ppmdEnum = pEnum; } // fill the output token buffer hr = HENUMInternal::EnumWithCount(pEnum, cMax, rCustomAttributes, pcCustomAttributes); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); STOP_MD_PERF(EnumCustomAttributes); END_ENTRYPOINT_NOTHROW; return hr; } // STDMETHODIMP RegMeta::EnumCustomAttributes()