diff options
Diffstat (limited to 'src/vm/typestring.cpp')
-rw-r--r-- | src/vm/typestring.cpp | 1678 |
1 files changed, 1678 insertions, 0 deletions
diff --git a/src/vm/typestring.cpp b/src/vm/typestring.cpp new file mode 100644 index 0000000000..ba103c42fa --- /dev/null +++ b/src/vm/typestring.cpp @@ -0,0 +1,1678 @@ +// 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. +// --------------------------------------------------------------------------- +// typestring.cpp +// --------------------------------------------------------------------------- +// + +// +// This module contains a helper function used to produce string +// representations of types, with options to control the appearance of +// namespace and assembly information. Its primary use is in +// reflection (Type.Name, Type.FullName, Type.ToString, etc) but over +// time it could replace the use of TypeHandle.GetName etc for +// diagnostic messages. +// +// See the header file for more details +// --------------------------------------------------------------------------- + + +#include "common.h" +#include "class.h" +#include "typehandle.h" +#include "sstring.h" +#include "sigformat.h" +#include "typeparse.h" +#include "typestring.h" +#include "ex.h" +#include "typedesc.h" + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +TypeNameBuilder * QCALLTYPE TypeNameBuilder::_CreateTypeNameBuilder() +{ + QCALL_CONTRACT; + + TypeNameBuilder * retVal = NULL; + BEGIN_QCALL; + retVal = new TypeNameBuilder(); + END_QCALL; + + return retVal; +} + +void QCALLTYPE TypeNameBuilder::_ReleaseTypeNameBuilder(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + delete pTnb; + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_ToString(TypeNameBuilder * pTnb, QCall::StringHandleOnStack retString) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + retString.Set(*pTnb->GetString()); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_AddName(TypeNameBuilder * pTnb, LPCWSTR wszName) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->AddName(wszName); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_AddAssemblySpec(TypeNameBuilder * pTnb, LPCWSTR wszAssemblySpec) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->AddAssemblySpec(wszAssemblySpec); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_OpenGenericArguments(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->OpenGenericArguments(); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_CloseGenericArguments(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->CloseGenericArguments(); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_OpenGenericArgument(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->OpenGenericArgument(); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_CloseGenericArgument(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->CloseGenericArgument(); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_AddPointer(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->AddPointer(); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_AddByRef(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->AddByRef(); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_AddSzArray(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->AddSzArray(); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_AddArray(TypeNameBuilder * pTnb, DWORD dwRank) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->AddArray(dwRank); + END_QCALL; +} + +void QCALLTYPE TypeNameBuilder::_Clear(TypeNameBuilder * pTnb) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + pTnb->Clear(); + END_QCALL; +} + +#endif + +// +// TypeNameBuilder +// +TypeNameBuilder::TypeNameBuilder(SString* pStr, ParseState parseState /*= ParseStateSTART*/) : + m_pStr(NULL) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + Clear(); + m_pStr = pStr; + m_parseState = parseState; +} + +void TypeNameBuilder::PushOpenGenericArgument() +{ + WRAPPER_NO_CONTRACT; + + m_stack.Push(m_pStr->GetCount()); +} + +void TypeNameBuilder::PopOpenGenericArgument() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + COUNT_T index = m_stack.Pop(); + + if (!m_bHasAssemblySpec) + m_pStr->Delete(m_pStr->Begin() + index - 1, 1); + + m_bHasAssemblySpec = FALSE; +} + +/* This method escapes szName and appends it to this TypeNameBuilder */ +void TypeNameBuilder::EscapeName(LPCWSTR szName) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (TypeString::ContainsReservedChar(szName)) + { + while (* szName) + { + WCHAR c = * szName ++; + + if (IsTypeNameReservedChar(c)) + Append(W('\\')); + + Append(c); + } + } + else + { + Append(szName); + } +} + +void TypeNameBuilder::EscapeAssemblyName(LPCWSTR szName) +{ + WRAPPER_NO_CONTRACT; + + Append(szName); +} + +void TypeNameBuilder::EscapeEmbeddedAssemblyName(LPCWSTR szName) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + LPCWSTR itr = szName; + bool bContainsReservedChar = false; + + while (*itr) + { + if (W(']') == *itr++) + { + bContainsReservedChar = true; + break; + } + } + + if (bContainsReservedChar) + { + itr = szName; + while (*itr) + { + WCHAR c = *itr++; + if (c == ']') + Append(W('\\')); + + Append(c); + } + } + else + { + Append(szName); + } +} + +HRESULT TypeNameBuilder::OpenGenericArgument() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!CheckParseState(ParseStateSTART)) + return Fail(); + + if (m_instNesting == 0) + return Fail(); + + HRESULT hr = S_OK; + + m_parseState = ParseStateSTART; + m_bNestedName = FALSE; + + if (!m_bFirstInstArg) + Append(W(',')); + + m_bFirstInstArg = FALSE; + + if (m_bUseAngleBracketsForGenerics) + Append(W('<')); + else + Append(W('[')); + PushOpenGenericArgument(); + + return hr; +} + +HRESULT TypeNameBuilder::AddName(LPCWSTR szName) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!szName) + return Fail(); + + if (!CheckParseState(ParseStateSTART | ParseStateNAME)) + return Fail(); + + HRESULT hr = S_OK; + + m_parseState = ParseStateNAME; + + if (m_bNestedName) + Append(W('+')); + + m_bNestedName = TRUE; + + EscapeName(szName); + + return hr; +} + +HRESULT TypeNameBuilder::AddName(LPCWSTR szName, LPCWSTR szNamespace) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!szName) + return Fail(); + + if (!CheckParseState(ParseStateSTART | ParseStateNAME)) + return Fail(); + + HRESULT hr = S_OK; + + m_parseState = ParseStateNAME; + + if (m_bNestedName) + Append(W('+')); + + m_bNestedName = TRUE; + + if (szNamespace && *szNamespace) + { + EscapeName(szNamespace); + Append(W('.')); + } + + EscapeName(szName); + + return hr; +} + +HRESULT TypeNameBuilder::OpenGenericArguments() +{ + WRAPPER_NO_CONTRACT; + + if (!CheckParseState(ParseStateNAME)) + return Fail(); + + HRESULT hr = S_OK; + + m_parseState = ParseStateSTART; + m_instNesting ++; + m_bFirstInstArg = TRUE; + + if (m_bUseAngleBracketsForGenerics) + Append(W('<')); + else + Append(W('[')); + + return hr; +} + +HRESULT TypeNameBuilder::CloseGenericArguments() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!m_instNesting) + return Fail(); + if (!CheckParseState(ParseStateSTART)) + return Fail(); + + HRESULT hr = S_OK; + + m_parseState = ParseStateGENARGS; + + m_instNesting --; + + if (m_bFirstInstArg) + { + m_pStr->Truncate(m_pStr->End() - 1); + } + else + { + if (m_bUseAngleBracketsForGenerics) + Append(W('>')); + else + Append(W(']')); + } + + return hr; +} + +HRESULT TypeNameBuilder::AddPointer() +{ + WRAPPER_NO_CONTRACT; + + if (!CheckParseState(ParseStateNAME | ParseStateGENARGS | ParseStatePTRARR)) + return Fail(); + + m_parseState = ParseStatePTRARR; + + Append(W('*')); + + return S_OK; +} + +HRESULT TypeNameBuilder::AddByRef() +{ + WRAPPER_NO_CONTRACT; + + if (!CheckParseState(ParseStateNAME | ParseStateGENARGS | ParseStatePTRARR)) + return Fail(); + + m_parseState = ParseStateBYREF; + + Append(W('&')); + + return S_OK; +} + +HRESULT TypeNameBuilder::AddSzArray() +{ + WRAPPER_NO_CONTRACT; + + if (!CheckParseState(ParseStateNAME | ParseStateGENARGS | ParseStatePTRARR)) + return Fail(); + + m_parseState = ParseStatePTRARR; + + Append(W("[]")); + + return S_OK; +} + +HRESULT TypeNameBuilder::AddArray(DWORD rank) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!CheckParseState(ParseStateNAME | ParseStateGENARGS | ParseStatePTRARR)) + return Fail(); + + m_parseState = ParseStatePTRARR; + + if (rank <= 0) + return E_INVALIDARG; + + if (rank == 1) + Append(W("[*]")); + else if (rank > 64) + { + // Only taken in an error path, runtime will not load arrays of more than 32 dimentions + WCHAR wzDim[128]; + _snwprintf_s(wzDim, 128, _TRUNCATE, W("[%d]"), rank); + Append(wzDim); + } + else + { + WCHAR* wzDim = new (nothrow) WCHAR[rank+3]; + + if(wzDim == NULL) // allocation failed, do it the long way (each Append -> memory realloc) + { + Append(W('[')); + for(COUNT_T i = 1; i < rank; i ++) + Append(W(',')); + Append(W(']')); + } + else // allocation OK, do it the fast way + { + WCHAR* pwz = wzDim+1; + *wzDim = '['; + for(COUNT_T i = 1; i < rank; i++, pwz++) *pwz=','; + *pwz = ']'; + *(++pwz) = 0; + Append(wzDim); + delete [] wzDim; + } + } + + return S_OK; +} + +HRESULT TypeNameBuilder::CloseGenericArgument() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!CheckParseState(ParseStateNAME | ParseStateGENARGS | ParseStatePTRARR | ParseStateBYREF | ParseStateASSEMSPEC)) + return Fail(); + + if (m_instNesting == 0) + return Fail(); + + m_parseState = ParseStateSTART; + + if (m_bHasAssemblySpec) + { + if (m_bUseAngleBracketsForGenerics) + Append(W('>')); + else + Append(W(']')); + } + + PopOpenGenericArgument(); + + return S_OK; +} + +HRESULT TypeNameBuilder::AddAssemblySpec(LPCWSTR szAssemblySpec) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + if (!CheckParseState(ParseStateNAME | ParseStateGENARGS | ParseStatePTRARR | ParseStateBYREF)) + return Fail(); + + HRESULT hr = S_OK; + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + + m_parseState = ParseStateASSEMSPEC; + + if (szAssemblySpec && *szAssemblySpec) + { + Append(W(", ")); + + if (m_instNesting > 0) + { + EscapeEmbeddedAssemblyName(szAssemblySpec); + } + else + { + EscapeAssemblyName(szAssemblySpec); + } + + m_bHasAssemblySpec = TRUE; + hr = S_OK; + } + + END_SO_INTOLERANT_CODE; + + return hr; +} + +HRESULT TypeNameBuilder::ToString(BSTR* pszStringRepresentation) +{ + WRAPPER_NO_CONTRACT; + + if (!CheckParseState(ParseStateNAME | ParseStateGENARGS | ParseStatePTRARR | ParseStateBYREF | ParseStateASSEMSPEC)) + return Fail(); + + if (m_instNesting) + return Fail(); + + *pszStringRepresentation = SysAllocString(m_pStr->GetUnicode()); + + return S_OK; +} + +HRESULT TypeNameBuilder::Clear() +{ + CONTRACTL + { + THROWS; // TypeNameBuilder::Stack::Clear might throw. + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + CONTRACT_VIOLATION(SOToleranceViolation); + + if (m_pStr) + { + m_pStr->Clear(); + } + m_bNestedName = FALSE; + m_instNesting = 0; + m_bFirstInstArg = FALSE; + m_parseState = ParseStateSTART; + m_bHasAssemblySpec = FALSE; + m_bUseAngleBracketsForGenerics = FALSE; + m_stack.Clear(); + + return S_OK; +} + + + +// Append the name of the type td to the string +// The following flags in the FormatFlags argument are significant: FormatNamespace +void TypeString::AppendTypeDef(SString& ss, IMDInternalImport *pImport, mdTypeDef td, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + GC_NOTRIGGER; + THROWS; + } + CONTRACT_END + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO()); + { + TypeNameBuilder tnb(&ss, TypeNameBuilder::ParseStateNAME); + AppendTypeDef(tnb, pImport, td, format); + } + END_SO_INTOLERANT_CODE; + + RETURN; +} + + +void TypeString::AppendTypeDef(TypeNameBuilder& tnb, IMDInternalImport *pImport, mdTypeDef td, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + GC_NOTRIGGER; + THROWS; + PRECONDITION(CheckPointer(pImport)); + PRECONDITION(TypeFromToken(td) == mdtTypeDef); + } + CONTRACT_END + + LPCUTF8 szName; + LPCUTF8 szNameSpace; + IfFailThrow(pImport->GetNameOfTypeDef(td, &szName, &szNameSpace)); + + const WCHAR *wszNameSpace = NULL; + + InlineSString<128> ssName(SString::Utf8, szName); + InlineSString<128> ssNameSpace; + + if (format & FormatNamespace) + { + ssNameSpace.SetUTF8(szNameSpace); + wszNameSpace = ssNameSpace.GetUnicode(); + } + + tnb.AddName(ssName.GetUnicode(), wszNameSpace); + + RETURN; +} + +void TypeString::AppendNestedTypeDef(TypeNameBuilder& tnb, IMDInternalImport *pImport, mdTypeDef td, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + GC_NOTRIGGER; + THROWS; + PRECONDITION(CheckPointer(pImport)); + PRECONDITION(TypeFromToken(td) == mdtTypeDef); + } + CONTRACT_END + + DWORD dwAttr; + IfFailThrow(pImport->GetTypeDefProps(td, &dwAttr, NULL)); + + StackSArray<mdTypeDef> arNames; + arNames.Append(td); + if (format & FormatNamespace && IsTdNested(dwAttr)) + { + while (SUCCEEDED(pImport->GetNestedClassProps(td, &td))) + arNames.Append(td); + } + + for(SCOUNT_T i = arNames.GetCount() - 1; i >= 0; i --) + AppendTypeDef(tnb, pImport, arNames[i], format); + + RETURN; +} + +// Append a square-bracket-enclosed, comma-separated list of n type parameters in inst to the string s +// and enclose each parameter in square brackets to disambiguate the commas +// The following flags in the FormatFlags argument are significant: FormatNamespace FormatFullInst FormatAssembly FormatNoVersion +void TypeString::AppendInst(SString& ss, Instantiation inst, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + if (format & (FormatAssembly|FormatFullInst)) GC_TRIGGERS; else GC_NOTRIGGER; + THROWS; + } + CONTRACT_END + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO()); + { + TypeNameBuilder tnb(&ss, TypeNameBuilder::ParseStateNAME); + if ((format & FormatAngleBrackets) != 0) + tnb.SetUseAngleBracketsForGenerics(TRUE); + AppendInst(tnb, inst, format); + } + END_SO_INTOLERANT_CODE; + + RETURN; +} + +void TypeString::AppendInst(TypeNameBuilder& tnb, Instantiation inst, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + THROWS; + if (format & (FormatAssembly|FormatFullInst)) GC_TRIGGERS; else GC_NOTRIGGER; + PRECONDITION(!inst.IsEmpty()); + } + CONTRACT_END + + tnb.OpenGenericArguments(); + + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + tnb.OpenGenericArgument(); + + TypeHandle thArg = inst[i]; + + if ((format & FormatFullInst) != 0 && !thArg.IsGenericVariable()) + { + AppendType(tnb, thArg, Instantiation(), format | FormatNamespace | FormatAssembly); + } + else + { + AppendType(tnb, thArg, Instantiation(), format & (FormatNamespace | FormatAngleBrackets +#ifdef _DEBUG + | FormatDebug +#endif + )); + } + + tnb.CloseGenericArgument(); + } + + tnb.CloseGenericArguments(); + + RETURN; +} + +void TypeString::AppendParamTypeQualifier(TypeNameBuilder& tnb, CorElementType kind, DWORD rank) +{ + CONTRACTL + { + MODE_ANY; + THROWS; + GC_NOTRIGGER; + PRECONDITION(CorTypeInfo::IsModifier(kind)); + } + CONTRACTL_END + + switch (kind) + { + case ELEMENT_TYPE_BYREF : + tnb.AddByRef(); + break; + case ELEMENT_TYPE_PTR : + tnb.AddPointer(); + break; + case ELEMENT_TYPE_SZARRAY : + tnb.AddSzArray(); + break; + case ELEMENT_TYPE_ARRAY : + tnb.AddArray(rank); + break; + default : + break; + } +} + +// Append a representation of the type t to the string s +// The following flags in the FormatFlags argument are significant: FormatNamespace FormatFullInst FormatAssembly FormatNoVersion + +void TypeString::AppendType(SString& ss, TypeHandle ty, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + if (format & (FormatAssembly|FormatFullInst)) GC_TRIGGERS; else GC_NOTRIGGER; + THROWS; + } + CONTRACT_END + + AppendType(ss, ty, Instantiation(), format); + + RETURN; +} + +void TypeString::AppendType(SString& ss, TypeHandle ty, Instantiation typeInstantiation, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + if (format & (FormatAssembly|FormatFullInst)) GC_TRIGGERS; else GC_NOTRIGGER; + THROWS; + } + CONTRACT_END + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO()); + { + TypeNameBuilder tnb(&ss); + if ((format & FormatAngleBrackets) != 0) + tnb.SetUseAngleBracketsForGenerics(TRUE); + AppendType(tnb, ty, typeInstantiation, format); + } + END_SO_INTOLERANT_CODE; + + RETURN; +} + +void TypeString::AppendType(TypeNameBuilder& tnb, TypeHandle ty, Instantiation typeInstantiation, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + + /* This method calls Assembly::GetDisplayName. Since that function + uses Fusion which takes some Crsts in some places, it is GC_TRIGGERS. + It could be made GC_NOTRIGGER by factoring out Assembly::GetDisplayName. + However, its better to leave stuff as GC_TRIGGERS unless really needed, + as GC_NOTRIGGER ties your hands up. */ + if (format & (FormatAssembly|FormatFullInst)) GC_TRIGGERS; else GC_NOTRIGGER; + THROWS; + } + CONTRACT_END + + INTERIOR_STACK_PROBE_FOR_CHECK_THREAD(10); + + BOOL bToString = (format & (FormatNamespace|FormatFullInst|FormatAssembly)) == FormatNamespace; + + // It's null! + if (ty.IsNull()) + { + tnb.AddName(W("(null)")); + } + else + // It's not restored yet! + if (ty.IsEncodedFixup()) + { + tnb.AddName(W("(fixup)")); + } + else + + // It's an array, with format + // element_ty[] (1-d, SZARRAY) + // element_ty[*] (1-d, ARRAY) + // element_ty[,] (2-d, ARRAY) etc + // or a pointer (*) or byref (&) + if (ty.HasTypeParam() || (!ty.IsTypeDesc() && ty.AsMethodTable()->IsArray())) + { + if (ty.GetSignatureCorElementType() != ELEMENT_TYPE_VALUETYPE) + { + DWORD rank; + TypeHandle elemType; + if (ty.HasTypeParam()) + { + rank = ty.IsArray() ? ty.AsArray()->GetRank() : 0; + elemType = ty.GetTypeParam(); + } + else + { + MethodTable *pMT = ty.GetMethodTable(); + PREFIX_ASSUME(pMT != NULL); + rank = pMT->GetRank(); + elemType = pMT->GetApproxArrayElementTypeHandle(); + } + + _ASSERTE(!elemType.IsNull()); + AppendType(tnb, elemType, Instantiation(), format & ~FormatAssembly); + AppendParamTypeQualifier(tnb, ty.GetSignatureCorElementType(), rank); + } + else + { + tnb.Append(W("VALUETYPE")); + TypeHandle elemType = ty.GetTypeParam(); + AppendType(tnb, elemType, Instantiation(), format & ~FormatAssembly); + } + } + + // ...or type parameter + else if (ty.IsGenericVariable()) + { + PTR_TypeVarTypeDesc tyvar = dac_cast<PTR_TypeVarTypeDesc>(ty.AsTypeDesc()); + + mdGenericParam token = tyvar->GetToken(); + + LPCSTR szName = NULL; + mdToken mdOwner; + + IfFailThrow(ty.GetModule()->GetMDImport()->GetGenericParamProps(token, NULL, NULL, &mdOwner, NULL, &szName)); + + _ASSERTE(TypeFromToken(mdOwner) == mdtTypeDef || TypeFromToken(mdOwner) == mdtMethodDef); + + LPCSTR szPrefix; + if (!(format & FormatGenericParam)) + szPrefix = ""; + else if (TypeFromToken(mdOwner) == mdtTypeDef) + szPrefix = "!"; + else + szPrefix = "!!"; + + SmallStackSString pName(SString::Utf8, szPrefix); + pName.AppendUTF8(szName); + tnb.AddName(pName.GetUnicode()); + + format &= ~FormatAssembly; + } + + // ...or function pointer + else if (ty.IsFnPtrType()) + { + // Don't attempt to format this currently, it may trigger GC due to fixups. + tnb.AddName(W("(fnptr)")); + } + + // ...otherwise it's just a plain type def or an instantiated type + else + { + // Get the TypeDef token and attributes + IMDInternalImport *pImport = ty.GetMethodTable()->GetMDImport(); + mdTypeDef td = ty.GetCl(); + if (IsNilToken(td)) { + // This type does not exist in metadata. Simply append "dynamicClass". + tnb.AddName(W("(dynamicClass)")); + } + else + { +#ifdef _DEBUG + if (format & FormatDebug) + { + WCHAR wzAddress[128]; + _snwprintf_s(wzAddress, 128, _TRUNCATE, W("(%p)"), dac_cast<TADDR>(ty.AsPtr())); + tnb.AddName(wzAddress); + } +#endif + AppendNestedTypeDef(tnb, pImport, td, format); + } + + // Append the instantiation + if ((format & (FormatNamespace|FormatAssembly)) && ty.HasInstantiation() && (!ty.IsGenericTypeDefinition() || bToString)) + { + if (typeInstantiation.IsEmpty()) + AppendInst(tnb, ty.GetInstantiation(), format); + else + AppendInst(tnb, typeInstantiation, format); + } + } + + // Now append the assembly + if (format & FormatAssembly) + { + Assembly* pAssembly = ty.GetAssembly(); + _ASSERTE(pAssembly != NULL); + + StackSString pAssemblyName; +#ifdef DACCESS_COMPILE + pAssemblyName.SetUTF8(pAssembly->GetSimpleName()); +#else + pAssembly->GetDisplayName(pAssemblyName, + ASM_DISPLAYF_PUBLIC_KEY_TOKEN | ASM_DISPLAYF_CONTENT_TYPE | + (format & FormatNoVersion ? 0 : ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE)); +#endif + + tnb.AddAssemblySpec(pAssemblyName.GetUnicode()); + + } + + END_INTERIOR_STACK_PROBE; + + + RETURN; +} + +void TypeString::AppendMethod(SString& s, MethodDesc *pMD, Instantiation typeInstantiation, const DWORD format) +{ + CONTRACTL + { + MODE_ANY; + GC_TRIGGERS; + THROWS; + PRECONDITION(CheckPointer(pMD)); + PRECONDITION(pMD->IsRestored_NoLogging()); + PRECONDITION(s.Check()); + } + CONTRACTL_END + + AppendMethodImpl(s, pMD, typeInstantiation, format); +} + +void TypeString::AppendMethodInternal(SString& s, MethodDesc *pMD, const DWORD format) +{ + CONTRACTL + { + MODE_ANY; + GC_TRIGGERS; + SUPPORTS_DAC; + THROWS; + PRECONDITION(CheckPointer(pMD)); + PRECONDITION(pMD->IsRestored_NoLogging()); + PRECONDITION(s.Check()); + } + CONTRACTL_END + + AppendMethodImpl(s, pMD, Instantiation(), format); +} + +void TypeString::AppendMethodImpl(SString& ss, MethodDesc *pMD, Instantiation typeInstantiation, const DWORD format) +{ + CONTRACTL + { + MODE_ANY; + GC_TRIGGERS; + SUPPORTS_DAC; + THROWS; + PRECONDITION(CheckPointer(pMD)); + PRECONDITION(pMD->IsRestored_NoLogging()); + PRECONDITION(ss.Check()); + } + CONTRACTL_END + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO()); + { + TypeHandle th; + + if (pMD->IsDynamicMethod()) + { + if (pMD->IsLCGMethod()) + { + SString sss(SString::Literal, "DynamicClass"); + ss += sss; + } + else if (pMD->IsILStub()) + { + SString sss(SString::Literal, ILStubResolver::GetStubClassName(pMD)); + ss += sss; + } + } + else + { + th = TypeHandle(pMD->GetMethodTable()); + AppendType(ss, th, typeInstantiation, format); + } + + SString sss1(SString::Literal, NAMESPACE_SEPARATOR_STR); + ss += sss1; + SString sss2(SString::Utf8, pMD->GetName()); + ss += sss2; + + if (pMD->HasMethodInstantiation() && !pMD->IsGenericMethodDefinition()) + { + AppendInst(ss, pMD->GetMethodInstantiation(), format); + } + + if (format & FormatSignature) + { + // @TODO: The argument list should be formatted nicely using AppendType() + + SigFormat sigFormatter(pMD, th); + const char* sigStr = sigFormatter.GetCStringParmsOnly(); + SString sss(SString::Utf8, sigStr); + ss += sss; + } + + if (format & FormatStubInfo) { + if (pMD->IsInstantiatingStub()) + { + SString sss(SString::Literal, "{inst-stub}"); + ss += sss; + } + if (pMD->IsUnboxingStub()) + { + SString sss(SString::Literal, "{unbox-stub}"); + ss += sss; + } + if (pMD->IsSharedByGenericMethodInstantiations()) + { + SString sss(SString::Literal, "{method-shared}"); + ss += sss; + } + else if (pMD->IsSharedByGenericInstantiations()) + { + SString sss(SString::Literal, "{shared}"); + ss += sss; + } + if (pMD->RequiresInstMethodTableArg()) + { + SString sss(SString::Literal, "{requires-mt-arg}"); + ss += sss; + } + if (pMD->RequiresInstMethodDescArg()) + { + SString sss(SString::Literal, "{requires-mdesc-arg}"); + ss += sss; + } + } + } + END_SO_INTOLERANT_CODE; +} + +void TypeString::AppendField(SString& s, FieldDesc *pFD, Instantiation typeInstantiation, const DWORD format /* = FormatNamespace */) +{ + CONTRACTL + { + MODE_ANY; + GC_TRIGGERS; + THROWS; + PRECONDITION(CheckPointer(pFD)); + PRECONDITION(s.Check()); + } + CONTRACTL_END; + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO()); + { + TypeHandle th(pFD->GetApproxEnclosingMethodTable()); + AppendType(s, th, typeInstantiation, format); + + s.AppendUTF8(NAMESPACE_SEPARATOR_STR); + s.AppendUTF8(pFD->GetName()); + } + END_SO_INTOLERANT_CODE; +} + +#ifdef _DEBUG +void TypeString::AppendMethodDebug(SString& ss, MethodDesc *pMD) +{ + CONTRACTL + { + MODE_ANY; + GC_TRIGGERS; + NOTHROW; + PRECONDITION(CheckPointer(pMD)); + PRECONDITION(pMD->IsRestored_NoLogging()); + PRECONDITION(ss.Check()); + } + CONTRACTL_END + +#ifndef DACCESS_COMPILE + EX_TRY + { + AppendMethodInternal(ss, pMD, FormatSignature | FormatNamespace); + } + EX_CATCH + { + // This function is only used as diagnostic aid in debug builds. + // If we run out of memory or hit some other problem, + // tough luck for the debugger. + + // Should we set ss to Empty + } + EX_END_CATCH(SwallowAllExceptions); +#endif +} + +void TypeString::AppendTypeDebug(SString& ss, TypeHandle t) +{ + CONTRACTL + { + MODE_ANY; + GC_NOTRIGGER; + NOTHROW; + PRECONDITION(CheckPointer(t)); + PRECONDITION(ss.Check()); + SO_NOT_MAINLINE; + } + CONTRACTL_END + +#ifndef DACCESS_COMPILE + { + EX_TRY + { + AppendType(ss, t, FormatNamespace | FormatDebug); + } + EX_CATCH + { + // This function is only used as diagnostic aid in debug builds. + // If we run out of memory or hit some other problem, + // tough luck for the debugger. + } + EX_END_CATCH(SwallowAllExceptions); + } +#endif +} + +void TypeString::AppendTypeKeyDebug(SString& ss, TypeKey *pTypeKey) +{ + CONTRACTL + { + MODE_ANY; + GC_NOTRIGGER; + NOTHROW; + PRECONDITION(CheckPointer(pTypeKey)); + PRECONDITION(ss.Check()); + SO_NOT_MAINLINE; + } + CONTRACTL_END + +#ifndef DACCESS_COMPILE + { + EX_TRY + { + AppendTypeKey(ss, pTypeKey, FormatNamespace | FormatDebug); + } + EX_CATCH + { + // This function is only used as diagnostic aid in debug builds. + // If we run out of memory or hit some other problem, + // tough luck for the debugger. + } + EX_END_CATCH(SwallowAllExceptions); + } +#endif +} + +#endif // _DEBUG + + +void TypeString::AppendTypeKey(TypeNameBuilder& tnb, TypeKey *pTypeKey, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + THROWS; + if (format & (FormatAssembly|FormatFullInst)) GC_TRIGGERS; else GC_NOTRIGGER; + PRECONDITION(CheckPointer(pTypeKey)); + SO_INTOLERANT; + } + CONTRACT_END + + Module *pModule = NULL; + + // It's an array, with format + // element_ty[] (1-d, SZARRAY) + // element_ty[*] (1-d, ARRAY) + // element_ty[,] (2-d, ARRAY) etc + // or a pointer (*) or byref (&) + CorElementType kind = pTypeKey->GetKind(); + if (CorTypeInfo::IsModifier(kind)) + { + DWORD rank = 0; + TypeHandle elemType = pTypeKey->GetElementType(); + if (CorTypeInfo::IsArray(kind)) + { + rank = pTypeKey->GetRank(); + } + + AppendType(tnb, elemType, Instantiation(), format); + AppendParamTypeQualifier(tnb, kind, rank); + pModule = elemType.GetModule(); + } + else if (kind == ELEMENT_TYPE_VALUETYPE) + { + tnb.Append(W("VALUETYPE")); + TypeHandle elemType = pTypeKey->GetElementType(); + AppendType(tnb, elemType, Instantiation(), format); + pModule = elemType.GetModule(); + } + else if (kind == ELEMENT_TYPE_FNPTR) + { + RETURN; + } + + // ...otherwise it's just a plain type def or an instantiated type + else + { + // Get the TypeDef token and attributes + pModule = pTypeKey->GetModule(); + if (pModule != NULL) + { + IMDInternalImport *pImport = pModule->GetMDImport(); + mdTypeDef td = pTypeKey->GetTypeToken(); + _ASSERTE(!IsNilToken(td)); + + AppendNestedTypeDef(tnb, pImport, td, format); + + // Append the instantiation + if ((format & (FormatNamespace|FormatAssembly)) && pTypeKey->HasInstantiation()) + AppendInst(tnb, pTypeKey->GetInstantiation(), format); + } + + } + + // Now append the assembly + if (pModule != NULL && (format & FormatAssembly)) + { + Assembly* pAssembly = pModule->GetAssembly(); + _ASSERTE(pAssembly != NULL); + + StackSString pAssemblyName; +#ifdef DACCESS_COMPILE + pAssemblyName.SetUTF8(pAssembly->GetSimpleName()); +#else + pAssembly->GetDisplayName(pAssemblyName, + ASM_DISPLAYF_PUBLIC_KEY_TOKEN | ASM_DISPLAYF_CONTENT_TYPE | + (format & FormatNoVersion ? 0 : ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE)); +#endif + tnb.AddAssemblySpec(pAssemblyName.GetUnicode()); + } + + RETURN; +} + +void TypeString::AppendTypeKey(SString& ss, TypeKey *pTypeKey, DWORD format) +{ + CONTRACT_VOID + { + MODE_ANY; + if (format & (FormatAssembly|FormatFullInst)) GC_TRIGGERS; else GC_NOTRIGGER; + THROWS; + PRECONDITION(CheckPointer(pTypeKey)); + } + CONTRACT_END + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO()); + { + TypeNameBuilder tnb(&ss); + AppendTypeKey(tnb, pTypeKey, format); + } + END_SO_INTOLERANT_CODE; + + RETURN; +} + +/*static*/ +void TypeString::EscapeSimpleTypeName(SString* ssTypeName, SString* ssEscapedTypeName) +{ + SString::Iterator itr = ssTypeName->Begin(); + WCHAR c; + while ((c = *itr++) != W('\0')) + { + if (IsTypeNameReservedChar(c)) + ssEscapedTypeName->Append(W("\\")); + + ssEscapedTypeName->Append(c); + } +} + +/*static*/ +bool TypeString::ContainsReservedChar(LPCWSTR pTypeName) +{ + CONTRACTL + { + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + WCHAR c; + while ((c = * pTypeName++) != W('\0')) + { + if (IsTypeNameReservedChar(c)) + { + return true; + } + } + + return false; +} + +HRESULT __stdcall TypeNameBuilderWrapper::QueryInterface(REFIID riid, void **ppUnk) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_TOLERANT; + } + CONTRACTL_END; + + *ppUnk = 0; + + if (riid == IID_IUnknown) + *ppUnk = (IUnknown *)this; + else if (riid == IID_ITypeNameBuilder) + *ppUnk = (ITypeNameBuilder*)this; + else + return (E_NOINTERFACE); + + AddRef(); + return S_OK; +} + +ULONG __stdcall TypeNameBuilderWrapper::AddRef() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_TOLERANT; + } + CONTRACTL_END; + + return InterlockedIncrement(&m_ref); +} + +ULONG __stdcall TypeNameBuilderWrapper::Release() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_TOLERANT; + SUPPORTS_DAC_HOST_ONLY; + } + CONTRACTL_END; + + LONG ref = 0; + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + + ref = InterlockedDecrement(&m_ref); + if (ref == 0) + delete this; + + END_SO_INTOLERANT_CODE; + + return ref; +} + + +HRESULT __stdcall TypeNameBuilderWrapper::OpenGenericArguments() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.OpenGenericArguments(); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::CloseGenericArguments() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.CloseGenericArguments(); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::OpenGenericArgument() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.OpenGenericArgument(); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::CloseGenericArgument() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.CloseGenericArgument(); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::AddName(LPCWSTR szName) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.AddName(szName); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::AddPointer() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.AddPointer(); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::AddByRef() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.AddByRef(); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::AddSzArray() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.AddSzArray(); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::AddArray(DWORD rank) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.AddArray(rank); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::AddAssemblySpec(LPCWSTR szAssemblySpec) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.AddAssemblySpec(szAssemblySpec); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::ToString(BSTR* pszStringRepresentation) +{ + WRAPPER_NO_CONTRACT; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.ToString(pszStringRepresentation); + END_SO_INTOLERANT_CODE; + return hr; +} + +HRESULT __stdcall TypeNameBuilderWrapper::Clear() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); + hr = m_tnb.Clear(); + END_SO_INTOLERANT_CODE; + return hr; +} |