summaryrefslogtreecommitdiff
path: root/src/vm/typedesc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/typedesc.cpp')
-rw-r--r--src/vm/typedesc.cpp2458
1 files changed, 2458 insertions, 0 deletions
diff --git a/src/vm/typedesc.cpp b/src/vm/typedesc.cpp
new file mode 100644
index 0000000000..9d84c01488
--- /dev/null
+++ b/src/vm/typedesc.cpp
@@ -0,0 +1,2458 @@
+// 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: typedesc.cpp
+//
+
+
+//
+// This file contains definitions for methods in the code:TypeDesc class and its
+// subclasses
+// code:ParamTypeDesc,
+// code:ArrayTypeDesc,
+// code:TyVarTypeDesc,
+// code:FnPtrTypeDesc
+//
+
+//
+// ============================================================================
+
+#include "common.h"
+#include "typedesc.h"
+#include "typestring.h"
+#if defined(FEATURE_PREJIT)
+#include "compile.h"
+#endif
+#include "array.h"
+#include "stackprobe.h"
+
+
+#ifndef DACCESS_COMPILE
+#ifdef _DEBUG
+
+BOOL ParamTypeDesc::Verify() {
+
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ _ASSERTE(m_TemplateMT.IsNull() || m_TemplateMT.GetValue()->SanityCheck());
+ _ASSERTE(!GetTypeParam().IsNull());
+ BAD_FORMAT_NOTHROW_ASSERT(GetTypeParam().IsTypeDesc() || !GetTypeParam().AsMethodTable()->IsArray());
+ BAD_FORMAT_NOTHROW_ASSERT(CorTypeInfo::IsModifier_NoThrow(GetInternalCorElementType()) ||
+ GetInternalCorElementType() == ELEMENT_TYPE_VALUETYPE);
+ GetTypeParam().Verify();
+ return(true);
+}
+
+BOOL ArrayTypeDesc::Verify() {
+
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // m_TemplateMT == 0 may be null when building types involving TypeVarTypeDesc's
+ BAD_FORMAT_NOTHROW_ASSERT(m_TemplateMT.IsNull() || m_TemplateMT.GetValue()->IsArray());
+ BAD_FORMAT_NOTHROW_ASSERT(CorTypeInfo::IsArray_NoThrow(GetInternalCorElementType()));
+ ParamTypeDesc::Verify();
+ return(true);
+}
+
+#endif
+
+#endif // #ifndef DACCESS_COMPILE
+
+TypeHandle TypeDesc::GetBaseTypeParam()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ _ASSERTE(HasTypeParam());
+
+ TypeHandle th = dac_cast<PTR_ParamTypeDesc>(this)->GetTypeParam();
+ while (th.HasTypeParam())
+ {
+ th = dac_cast<PTR_ParamTypeDesc>(th.AsTypeDesc())->GetTypeParam();
+ }
+ _ASSERTE(!th.IsNull());
+
+ return th;
+}
+
+PTR_Module TypeDesc::GetLoaderModule()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ SUPPORTS_DAC;
+
+ if (HasTypeParam())
+ {
+ return GetBaseTypeParam().GetLoaderModule();
+ }
+ else if (IsGenericVariable())
+ {
+ return dac_cast<PTR_TypeVarTypeDesc>(this)->GetModule();
+ }
+ else
+ {
+ PTR_Module retVal = NULL;
+ BOOL fFail = FALSE;
+
+ _ASSERTE(GetInternalCorElementType() == ELEMENT_TYPE_FNPTR);
+ PTR_FnPtrTypeDesc asFnPtr = dac_cast<PTR_FnPtrTypeDesc>(this);
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), fFail = TRUE );
+ if (!fFail)
+ {
+ retVal = ClassLoader::ComputeLoaderModuleForFunctionPointer(asFnPtr->GetRetAndArgTypesPointer(), asFnPtr->GetNumArgs()+1);
+ }
+ END_SO_INTOLERANT_CODE;
+ return retVal;
+ }
+}
+
+
+PTR_Module TypeDesc::GetZapModule()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ return ExecutionManager::FindZapModule(dac_cast<TADDR>(this));
+}
+
+PTR_BaseDomain TypeDesc::GetDomain()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ Module *pZapModule = GetZapModule();
+ if (pZapModule != NULL)
+ {
+ return pZapModule->GetDomain();
+ }
+
+ if (HasTypeParam())
+ {
+ return GetBaseTypeParam().GetDomain();
+ }
+ if (IsGenericVariable())
+ {
+ PTR_TypeVarTypeDesc asVar = dac_cast<PTR_TypeVarTypeDesc>(this);
+ return asVar->GetModule()->GetDomain();
+ }
+ _ASSERTE(GetInternalCorElementType() == ELEMENT_TYPE_FNPTR);
+ PTR_FnPtrTypeDesc asFnPtr = dac_cast<PTR_FnPtrTypeDesc>(this);
+ return BaseDomain::ComputeBaseDomain(asFnPtr->GetRetAndArgTypesPointer()[0].GetDomain(),
+ Instantiation(asFnPtr->GetRetAndArgTypesPointer(), asFnPtr->GetNumArgs()+1));
+}
+
+PTR_Module TypeDesc::GetModule() {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ SUPPORTS_DAC;
+ // Function pointer types belong to no module
+ //PRECONDITION(GetInternalCorElementType() != ELEMENT_TYPE_FNPTR);
+ }
+ CONTRACTL_END
+
+ // Note here we are making the assumption that a typeDesc lives in
+ // the classloader of its element type.
+
+ if (HasTypeParam())
+ {
+ return GetBaseTypeParam().GetModule();
+ }
+
+ if (IsGenericVariable())
+ {
+ PTR_TypeVarTypeDesc asVar = dac_cast<PTR_TypeVarTypeDesc>(this);
+ return asVar->GetModule();
+ }
+
+ _ASSERTE(GetInternalCorElementType() == ELEMENT_TYPE_FNPTR);
+
+ return GetLoaderModule();
+}
+
+BOOL TypeDesc::IsDomainNeutral()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ return GetDomain()->IsSharedDomain();
+}
+
+BOOL ParamTypeDesc::OwnsTemplateMethodTable()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ CorElementType kind = GetInternalCorElementType();
+
+ // The m_TemplateMT for pointer types is UIntPtr
+ if (!CorTypeInfo::IsArray_NoThrow(kind))
+ {
+ return FALSE;
+ }
+
+ CorElementType elemType = m_Arg.GetSignatureCorElementType();
+
+ // This check matches precisely one in Module::CreateArrayMethodTable
+ //
+ // They indicate if an array TypeDesc is non-canonical (in much the same a a generic
+ // method table being non-canonical), i.e. it is not the primary
+ // owner of the m_TemplateMT (the primary owner is the TypeDesc for object[])
+
+ if (CorTypeInfo::IsGenericVariable_NoThrow(elemType))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+Assembly* TypeDesc::GetAssembly() {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ Module *pModule = GetModule();
+ PREFIX_ASSUME(pModule!=NULL);
+ return pModule->GetAssembly();
+}
+
+void TypeDesc::GetName(SString &ssBuf)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ CorElementType kind = GetInternalCorElementType();
+ TypeHandle th;
+ int rank;
+
+ if (CorTypeInfo::IsModifier(kind))
+ th = GetTypeParam();
+ else
+ th = TypeHandle(this);
+
+ if (kind == ELEMENT_TYPE_ARRAY)
+ rank = ((ArrayTypeDesc*) this)->GetRank();
+ else if (CorTypeInfo::IsGenericVariable(kind))
+ rank = ((TypeVarTypeDesc*) this)->GetIndex();
+ else
+ rank = 0;
+
+ ConstructName(kind, th, rank, ssBuf);
+}
+
+void TypeDesc::ConstructName(CorElementType kind,
+ TypeHandle param,
+ int rank,
+ SString &ssBuff)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(COMPlusThrowOM()); // SString operations can allocate.
+ }
+ CONTRACTL_END
+
+ if (CorTypeInfo::IsModifier(kind))
+ {
+ param.GetName(ssBuff);
+ }
+
+ switch(kind)
+ {
+ case ELEMENT_TYPE_BYREF:
+ ssBuff.Append(W('&'));
+ break;
+
+ case ELEMENT_TYPE_PTR:
+ ssBuff.Append(W('*'));
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ ssBuff.Append(W("[]"));
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ ssBuff.Append(W('['));
+
+ if (rank == 1)
+ {
+ ssBuff.Append(W('*'));
+ }
+ else
+ {
+ while(--rank > 0)
+ {
+ ssBuff.Append(W(','));
+ }
+ }
+
+ ssBuff.Append(W(']'));
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ if (kind == ELEMENT_TYPE_VAR)
+ {
+ ssBuff.Printf(W("!%d"), rank);
+ }
+ else
+ {
+ ssBuff.Printf(W("!!%d"), rank);
+ }
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ ssBuff.Printf(W("FNPTR"));
+ break;
+
+ default:
+ LPCUTF8 namesp = CorTypeInfo::GetNamespace(kind);
+ if(namesp && *namesp) {
+ ssBuff.AppendUTF8(namesp);
+ ssBuff.Append(W('.'));
+ }
+
+ LPCUTF8 name = CorTypeInfo::GetName(kind);
+ BAD_FORMAT_NOTHROW_ASSERT(name);
+ if (name && *name) {
+ ssBuff.AppendUTF8(name);
+ }
+ }
+}
+
+BOOL TypeDesc::IsArray()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return CorTypeInfo::IsArray_NoThrow(GetInternalCorElementType());
+}
+
+BOOL TypeDesc::IsGenericVariable()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return CorTypeInfo::IsGenericVariable_NoThrow(GetInternalCorElementType());
+}
+
+BOOL TypeDesc::IsFnPtr()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (GetInternalCorElementType() == ELEMENT_TYPE_FNPTR);
+}
+
+BOOL TypeDesc::IsNativeValueType()
+{
+ WRAPPER_NO_CONTRACT;
+ return (GetInternalCorElementType() == ELEMENT_TYPE_VALUETYPE);
+}
+
+BOOL TypeDesc::HasTypeParam()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ return CorTypeInfo::IsModifier_NoThrow(GetInternalCorElementType()) ||
+ GetInternalCorElementType() == ELEMENT_TYPE_VALUETYPE;
+}
+
+#ifndef DACCESS_COMPILE
+
+BOOL TypeDesc::CanCastTo(TypeHandle toType, TypeHandlePairList *pVisited)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END
+
+ if (TypeHandle(this) == toType)
+ return TRUE;
+
+ //A boxed variable type can be cast to any of its constraints, or object, if none are specified
+ if (IsGenericVariable())
+ {
+ TypeVarTypeDesc *tyvar = (TypeVarTypeDesc*) this;
+
+ DWORD numConstraints;
+ TypeHandle *constraints = tyvar->GetConstraints(&numConstraints, CLASS_DEPENDENCIES_LOADED);
+
+ if (toType == g_pObjectClass)
+ return TRUE;
+
+ if (toType == g_pValueTypeClass)
+ {
+ mdGenericParam genericParamToken = tyvar->GetToken();
+ DWORD flags;
+ if (FAILED(tyvar->GetModule()->GetMDImport()->GetGenericParamProps(genericParamToken, NULL, &flags, NULL, NULL, NULL)))
+ {
+ return FALSE;
+ }
+ DWORD specialConstraints = flags & gpSpecialConstraintMask;
+ if ((specialConstraints & gpNotNullableValueTypeConstraint) != 0)
+ return TRUE;
+ }
+
+ if (constraints == NULL)
+ return FALSE;
+
+ for (DWORD i = 0; i < numConstraints; i++)
+ {
+ if (constraints[i].CanCastTo(toType, pVisited))
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ // If we're not casting to a TypeDesc (i.e. not to a reference array type, variable type etc.)
+ // then we must be trying to cast to a class or interface type.
+ if (!toType.IsTypeDesc())
+ {
+ MethodTable *pMT = GetMethodTable();
+ if (pMT == 0) {
+ // I don't have an underlying method table, therefore I'm
+ // a variable type, pointer type, function pointer type
+ // etc. I am not an object or value type. Therefore
+ // I can't be cast to an object or value type.
+ return FALSE;
+ }
+
+ // This does the right thing if 'type' == System.Array or System.Object, System.Clonable ...
+ if (pMT->CanCastToClassOrInterface(toType.AsMethodTable(), pVisited) != 0)
+ {
+ return TRUE;
+ }
+
+ if (IsArray() && toType.AsMethodTable()->IsInterface())
+ {
+ if (ArraySupportsBizarreInterface((ArrayTypeDesc*)this, toType.AsMethodTable()))
+ {
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+ }
+
+ TypeDesc* toTypeDesc = toType.AsTypeDesc();
+
+ CorElementType toKind = toTypeDesc->GetInternalCorElementType();
+ CorElementType fromKind = GetInternalCorElementType();
+
+ // The element kinds must match, only exception is that SZARRAY matches a one dimension ARRAY
+ if (!(toKind == fromKind || (toKind == ELEMENT_TYPE_ARRAY && fromKind == ELEMENT_TYPE_SZARRAY)))
+ return FALSE;
+
+ switch (toKind)
+ {
+ case ELEMENT_TYPE_ARRAY:
+ if (dac_cast<PTR_ArrayTypeDesc>(this)->GetRank() != dac_cast<PTR_ArrayTypeDesc>(toTypeDesc)->GetRank())
+ return FALSE;
+ // fall through
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ return TypeDesc::CanCastParam(dac_cast<PTR_ParamTypeDesc>(this)->GetTypeParam(), dac_cast<PTR_ParamTypeDesc>(toTypeDesc)->GetTypeParam(), pVisited);
+
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ case ELEMENT_TYPE_FNPTR:
+ return FALSE;
+
+ default:
+ BAD_FORMAT_NOTHROW_ASSERT(toKind == ELEMENT_TYPE_TYPEDBYREF || CorTypeInfo::IsPrimitiveType(toKind));
+ return TRUE;
+ }
+}
+
+BOOL TypeDesc::CanCastParam(TypeHandle fromParam, TypeHandle toParam, TypeHandlePairList *pVisited)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END
+
+ // While boxed value classes inherit from object their
+ // unboxed versions do not. Parameterized types have the
+ // unboxed version, thus, if the from type parameter is value
+ // class then only an exact match/equivalence works.
+ if (fromParam.IsEquivalentTo(toParam))
+ return TRUE;
+
+ // Object parameters dont need an exact match but only inheritance, check for that
+ CorElementType fromParamCorType = fromParam.GetVerifierCorElementType();
+ if (CorTypeInfo::IsObjRef(fromParamCorType))
+ {
+ return fromParam.CanCastTo(toParam, pVisited);
+ }
+ else if (CorTypeInfo::IsGenericVariable(fromParamCorType))
+ {
+ TypeVarTypeDesc* varFromParam = fromParam.AsGenericVariable();
+
+ if (!varFromParam->ConstraintsLoaded())
+ varFromParam->LoadConstraints(CLASS_DEPENDENCIES_LOADED);
+
+ if (!varFromParam->ConstrainedAsObjRef())
+ return FALSE;
+
+ return fromParam.CanCastTo(toParam, pVisited);
+ }
+ else if(CorTypeInfo::IsPrimitiveType(fromParamCorType))
+ {
+ CorElementType toParamCorType = toParam.GetVerifierCorElementType();
+ if(CorTypeInfo::IsPrimitiveType(toParamCorType))
+ {
+ if (toParamCorType == fromParamCorType)
+ return TRUE;
+
+ // Primitive types such as E_T_I4 and E_T_U4 are interchangeable
+ // Enums with interchangeable underlying types are interchangable
+ // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2
+ if((toParamCorType != ELEMENT_TYPE_BOOLEAN)
+ &&(fromParamCorType != ELEMENT_TYPE_BOOLEAN)
+ &&(toParamCorType != ELEMENT_TYPE_CHAR)
+ &&(fromParamCorType != ELEMENT_TYPE_CHAR))
+ {
+ if ((CorTypeInfo::Size(toParamCorType) == CorTypeInfo::Size(fromParamCorType))
+ && (CorTypeInfo::IsFloat(toParamCorType) == CorTypeInfo::IsFloat(fromParamCorType)))
+ {
+ return TRUE;
+ }
+ }
+ } // end if(CorTypeInfo::IsPrimitiveType(toParamCorType))
+ } // end if(CorTypeInfo::IsPrimitiveType(fromParamCorType))
+
+ // Anything else is not a match.
+ return FALSE;
+}
+
+TypeHandle::CastResult TypeDesc::CanCastToNoGC(TypeHandle toType)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END
+
+ if (TypeHandle(this) == toType)
+ return TypeHandle::CanCast;
+
+ //A boxed variable type can be cast to any of its constraints, or object, if none are specified
+ if (IsGenericVariable())
+ {
+ TypeVarTypeDesc *tyvar = (TypeVarTypeDesc*) this;
+
+ if (!tyvar->ConstraintsLoaded())
+ return TypeHandle::MaybeCast;
+
+ DWORD numConstraints;
+ TypeHandle *constraints = tyvar->GetCachedConstraints(&numConstraints);
+
+ if (toType == g_pObjectClass)
+ return TypeHandle::CanCast;
+
+ if (toType == g_pValueTypeClass)
+ return TypeHandle::MaybeCast;
+
+ if (constraints == NULL)
+ return TypeHandle::CannotCast;
+
+ for (DWORD i = 0; i < numConstraints; i++)
+ {
+ if (constraints[i].CanCastToNoGC(toType) == TypeHandle::CanCast)
+ return TypeHandle::CanCast;
+ }
+ return TypeHandle::MaybeCast;
+ }
+
+ // If we're not casting to a TypeDesc (i.e. not to a reference array type, variable type etc.)
+ // then we must be trying to cast to a class or interface type.
+ if (!toType.IsTypeDesc())
+ {
+ MethodTable *pMT = GetMethodTable();
+ if (pMT == 0) {
+ // I don't have an underlying method table, therefore I'm
+ // a variable type, pointer type, function pointer type
+ // etc. I am not an object or value type. Therefore
+ // I can't be cast to an object or value type.
+ return TypeHandle::CannotCast;
+ }
+
+ // This does the right thing if 'type' == System.Array or System.Object, System.Clonable ...
+ return pMT->CanCastToClassOrInterfaceNoGC(toType.AsMethodTable());
+ }
+
+ TypeDesc* toTypeDesc = toType.AsTypeDesc();
+
+ CorElementType toKind = toTypeDesc->GetInternalCorElementType();
+ CorElementType fromKind = GetInternalCorElementType();
+
+ // The element kinds must match, only exception is that SZARRAY matches a one dimension ARRAY
+ if (!(toKind == fromKind || (toKind == ELEMENT_TYPE_ARRAY && fromKind == ELEMENT_TYPE_SZARRAY)))
+ return TypeHandle::CannotCast;
+
+ switch (toKind)
+ {
+ case ELEMENT_TYPE_ARRAY:
+ if (dac_cast<PTR_ArrayTypeDesc>(this)->GetRank() != dac_cast<PTR_ArrayTypeDesc>(toTypeDesc)->GetRank())
+ return TypeHandle::CannotCast;
+ // fall through
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ return TypeDesc::CanCastParamNoGC(dac_cast<PTR_ParamTypeDesc>(this)->GetTypeParam(), dac_cast<PTR_ParamTypeDesc>(toTypeDesc)->GetTypeParam());
+
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ case ELEMENT_TYPE_FNPTR:
+ return TypeHandle::CannotCast;
+
+ default:
+ BAD_FORMAT_NOTHROW_ASSERT(toKind == ELEMENT_TYPE_TYPEDBYREF || CorTypeInfo::IsPrimitiveType_NoThrow(toKind));
+ return TypeHandle::CanCast;
+ }
+}
+
+TypeHandle::CastResult TypeDesc::CanCastParamNoGC(TypeHandle fromParam, TypeHandle toParam)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END
+
+ // While boxed value classes inherit from object their
+ // unboxed versions do not. Parameterized types have the
+ // unboxed version, thus, if the from type parameter is value
+ // class then only an exact match works.
+ if (fromParam == toParam)
+ return TypeHandle::CanCast;
+
+ // Object parameters dont need an exact match but only inheritance, check for that
+ CorElementType fromParamCorType = fromParam.GetVerifierCorElementType();
+ if (CorTypeInfo::IsObjRef_NoThrow(fromParamCorType))
+ {
+ return fromParam.CanCastToNoGC(toParam);
+ }
+ else if (CorTypeInfo::IsGenericVariable_NoThrow(fromParamCorType))
+ {
+ TypeVarTypeDesc* varFromParam = fromParam.AsGenericVariable();
+
+ if (!varFromParam->ConstraintsLoaded())
+ return TypeHandle::MaybeCast;
+
+ if (!varFromParam->ConstrainedAsObjRef())
+ return TypeHandle::CannotCast;
+
+ return fromParam.CanCastToNoGC(toParam);
+ }
+ else if (CorTypeInfo::IsPrimitiveType_NoThrow(fromParamCorType))
+ {
+ CorElementType toParamCorType = toParam.GetVerifierCorElementType();
+ if(CorTypeInfo::IsPrimitiveType_NoThrow(toParamCorType))
+ {
+ if (toParamCorType == fromParamCorType)
+ return TypeHandle::CanCast;
+
+ // Primitive types such as E_T_I4 and E_T_U4 are interchangeable
+ // Enums with interchangeable underlying types are interchangable
+ // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2
+ if((toParamCorType != ELEMENT_TYPE_BOOLEAN)
+ &&(fromParamCorType != ELEMENT_TYPE_BOOLEAN)
+ &&(toParamCorType != ELEMENT_TYPE_CHAR)
+ &&(fromParamCorType != ELEMENT_TYPE_CHAR))
+ {
+ if ((CorTypeInfo::Size_NoThrow(toParamCorType) == CorTypeInfo::Size_NoThrow(fromParamCorType))
+ && (CorTypeInfo::IsFloat_NoThrow(toParamCorType) == CorTypeInfo::IsFloat_NoThrow(fromParamCorType)))
+ {
+ return TypeHandle::CanCast;
+ }
+ }
+ } // end if(CorTypeInfo::IsPrimitiveType(toParamCorType))
+ } // end if(CorTypeInfo::IsPrimitiveType(fromParamCorType))
+ else
+ {
+ // Types with equivalence need the slow path
+ MethodTable * pFromMT = fromParam.GetMethodTable();
+ if (pFromMT != NULL && pFromMT->HasTypeEquivalence())
+ return TypeHandle::MaybeCast;
+ MethodTable * pToMT = toParam.GetMethodTable();
+ if (pToMT != NULL && pToMT->HasTypeEquivalence())
+ return TypeHandle::MaybeCast;
+ }
+
+ // Anything else is not a match.
+ return TypeHandle::CannotCast;
+}
+
+BOOL TypeDesc::IsEquivalentTo(TypeHandle type COMMA_INDEBUG(TypeHandlePairList *pVisited))
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ if (TypeHandle(this) == type)
+ return TRUE;
+
+ if (!type.IsTypeDesc())
+ return FALSE;
+
+ TypeDesc *pOther = type.AsTypeDesc();
+
+ // bail early for normal types
+ if (!HasTypeEquivalence() || !pOther->HasTypeEquivalence())
+ return FALSE;
+
+ // if the TypeDesc types are different, then they are not equivalent
+ if (GetInternalCorElementType() != pOther->GetInternalCorElementType())
+ return FALSE;
+
+ if (HasTypeParam())
+ {
+ // pointer, byref, array
+
+ // Arrays must have the same rank.
+ if (IsArray())
+ {
+ ArrayTypeDesc *pThisArray = (ArrayTypeDesc *)this;
+ ArrayTypeDesc *pOtherArray = (ArrayTypeDesc *)pOther;
+ if (pThisArray->GetRank() != pOtherArray->GetRank())
+ return FALSE;
+ }
+
+ return GetTypeParam().IsEquivalentTo(pOther->GetTypeParam() COMMA_INDEBUG(pVisited));
+ }
+
+ // var, mvar, fnptr
+ return FALSE;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+
+
+TypeHandle TypeDesc::GetParent() {
+
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ CorElementType kind = GetInternalCorElementType();
+ if (CorTypeInfo::IsArray_NoThrow(kind)) {
+ _ASSERTE(IsArray());
+ BAD_FORMAT_NOTHROW_ASSERT(kind == ELEMENT_TYPE_SZARRAY || kind == ELEMENT_TYPE_ARRAY);
+ return ((ArrayTypeDesc*)this)->GetParent();
+ }
+ if (CorTypeInfo::IsPrimitiveType_NoThrow(kind))
+ return (MethodTable*)g_pObjectClass;
+ return TypeHandle();
+}
+
+#ifndef DACCESS_COMPILE
+
+#ifndef CROSSGEN_COMPILE
+OBJECTREF ParamTypeDesc::GetManagedClassObject()
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+
+ INJECT_FAULT(COMPlusThrowOM());
+
+ PRECONDITION(GetInternalCorElementType() == ELEMENT_TYPE_ARRAY ||
+ GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY ||
+ GetInternalCorElementType() == ELEMENT_TYPE_BYREF ||
+ GetInternalCorElementType() == ELEMENT_TYPE_PTR);
+ }
+ CONTRACTL_END;
+
+ if (m_hExposedClassObject == NULL) {
+ REFLECTCLASSBASEREF refClass = NULL;
+ GCPROTECT_BEGIN(refClass);
+ if (GetAssembly()->IsIntrospectionOnly())
+ refClass = (REFLECTCLASSBASEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__CLASS_INTROSPECTION_ONLY));
+ else
+ refClass = (REFLECTCLASSBASEREF) AllocateObject(g_pRuntimeTypeClass);
+
+ LoaderAllocator *pLoaderAllocator = GetLoaderAllocator();
+ TypeHandle th = TypeHandle(this);
+ ((ReflectClassBaseObject*)OBJECTREFToObject(refClass))->SetType(th);
+ ((ReflectClassBaseObject*)OBJECTREFToObject(refClass))->SetKeepAlive(pLoaderAllocator->GetExposedObject());
+
+ // Let all threads fight over who wins using InterlockedCompareExchange.
+ // Only the winner can set m_hExposedClassObject from NULL.
+ LOADERHANDLE hExposedClassObject = pLoaderAllocator->AllocateHandle(refClass);
+
+ EnsureWritablePages(this);
+ if (FastInterlockCompareExchangePointer(&m_hExposedClassObject, hExposedClassObject, static_cast<LOADERHANDLE>(NULL)))
+ {
+ pLoaderAllocator->ClearHandle(hExposedClassObject);
+ }
+
+ // Log the TypeVarTypeDesc access
+ g_IBCLogger.LogTypeMethodTableWriteableAccess(&th);
+
+ GCPROTECT_END();
+ }
+ return GetManagedClassObjectIfExists();
+}
+#endif // CROSSGEN_COMPILE
+
+#endif // #ifndef DACCESS_COMPILE
+
+BOOL TypeDesc::IsRestored()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+
+ TypeHandle th = TypeHandle(this);
+ g_IBCLogger.LogTypeMethodTableAccess(&th);
+ return IsRestored_NoLogging();
+}
+
+BOOL TypeDesc::IsRestored_NoLogging()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+
+ return (m_typeAndFlags & TypeDesc::enum_flag_Unrestored) == 0;
+}
+
+ClassLoadLevel TypeDesc::GetLoadLevel()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ SUPPORTS_DAC;
+
+ if (m_typeAndFlags & TypeDesc::enum_flag_UnrestoredTypeKey)
+ {
+ return CLASS_LOAD_UNRESTOREDTYPEKEY;
+ }
+ else if (m_typeAndFlags & TypeDesc::enum_flag_Unrestored)
+ {
+ return CLASS_LOAD_UNRESTORED;
+ }
+ else if (m_typeAndFlags & TypeDesc::enum_flag_IsNotFullyLoaded)
+ {
+ if (m_typeAndFlags & TypeDesc::enum_flag_DependenciesLoaded)
+ {
+ return CLASS_DEPENDENCIES_LOADED;
+ }
+ else
+ {
+ return CLASS_LOAD_EXACTPARENTS;
+ }
+ }
+
+ return CLASS_LOADED;
+}
+
+
+// Recursive worker that pumps the transitive closure of a type's dependencies to the specified target level.
+// Dependencies include:
+//
+// - parent
+// - interfaces
+// - canonical type, for non-canonical instantiations
+// - typical type, for non-typical instantiations
+//
+// Parameters:
+//
+// pVisited - used to prevent endless recursion in the case of cyclic dependencies
+//
+// level - target level to pump to - must be CLASS_DEPENDENCIES_LOADED or CLASS_LOADED
+//
+// if CLASS_DEPENDENCIES_LOADED, all transitive dependencies are resolved to their
+// exact types.
+//
+// if CLASS_LOADED, all type-safety checks are done on the type and all its transitive
+// dependencies. Note that for the CLASS_LOADED case, some types may be left
+// on the pending list rather that pushed to CLASS_LOADED in the case of cyclic
+// dependencies - the root caller must handle this.
+//
+//
+// pfBailed - if we or one of our depedencies bails early due to cyclic dependencies, we
+// must set *pfBailed to TRUE. Otherwise, we must *leave it unchanged* (thus, the
+// boolean acts as a cumulative OR.)
+//
+// pPending - if one of our dependencies bailed, the type cannot yet be promoted to CLASS_LOADED
+// as the dependencies will be checked later and may fail a security check then.
+// Instead, DoFullyLoad() will add the type to the pending list - the root caller
+// is responsible for promoting the type after the full transitive closure has been
+// walked. Note that it would be just as correct to always defer to the pending list -
+// however, that is a little less performant.
+//
+// pInstContext - instantiation context created in code:SigPointer.GetTypeHandleThrowing and ultimately
+// passed down to code:TypeVarTypeDesc.SatisfiesConstraints.
+//
+void TypeDesc::DoFullyLoad(Generics::RecursionGraph *pVisited, ClassLoadLevel level,
+ DFLPendingList *pPending, BOOL *pfBailed, const InstantiationContext *pInstContext)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END
+
+ _ASSERTE(level == CLASS_LOADED || level == CLASS_DEPENDENCIES_LOADED);
+ _ASSERTE(pfBailed != NULL);
+ _ASSERTE(!(level == CLASS_LOADED && pPending == NULL));
+
+
+#ifndef DACCESS_COMPILE
+
+ if (Generics::RecursionGraph::HasSeenType(pVisited, TypeHandle(this)))
+ {
+ *pfBailed = TRUE;
+ return;
+ }
+
+ if (GetLoadLevel() >= level)
+ {
+ return;
+ }
+
+ if (level == CLASS_LOADED)
+ {
+ UINT numTH = pPending->Count();
+ TypeHandle *pTypeHndPending = pPending->Table();
+ for (UINT idxPending = 0; idxPending < numTH; idxPending++)
+ {
+ if (pTypeHndPending[idxPending].IsTypeDesc() && pTypeHndPending[idxPending].AsTypeDesc() == this)
+ {
+ *pfBailed = TRUE;
+ return;
+ }
+ }
+
+ }
+
+
+ BOOL fBailed = FALSE;
+
+ // First ensure that we're loaded to just below CLASS_LOADED
+ ClassLoader::EnsureLoaded(TypeHandle(this), (ClassLoadLevel) (level-1));
+
+ Generics::RecursionGraph newVisited(pVisited, TypeHandle(this));
+
+ if (HasTypeParam())
+ {
+ // Fully load the type parameter
+ GetTypeParam().DoFullyLoad(&newVisited, level, pPending, &fBailed, pInstContext);
+
+ ParamTypeDesc* pPTD = (ParamTypeDesc*) this;
+
+ // Fully load the template method table
+ if (!pPTD->m_TemplateMT.IsNull())
+ {
+ pPTD->m_TemplateMT.GetValue()->DoFullyLoad(&newVisited, level, pPending, &fBailed, pInstContext);
+ }
+ }
+
+ switch (level)
+ {
+ case CLASS_DEPENDENCIES_LOADED:
+ FastInterlockOr(&m_typeAndFlags, TypeDesc::enum_flag_DependenciesLoaded);
+ break;
+
+ case CLASS_LOADED:
+ if (fBailed)
+ {
+ // We couldn't complete security checks on some dependency because he is already being processed by one of our callers.
+ // Do not mark this class fully loaded yet. Put him on the pending list and he will be marked fully loaded when
+ // everything unwinds.
+
+ *pfBailed = TRUE;
+
+ TypeHandle* pthPending = pPending->AppendThrowing();
+ *pthPending = TypeHandle(this);
+ }
+ else
+ {
+ // Finally, mark this method table as fully loaded
+ SetIsFullyLoaded();
+ }
+ break;
+
+ default:
+ _ASSERTE(!"Can't get here.");
+ break;
+ }
+#endif
+}
+
+
+#ifdef FEATURE_PREJIT
+void TypeDesc::DoRestoreTypeKey()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END
+
+#ifndef DACCESS_COMPILE
+ if (HasTypeParam())
+ {
+ ParamTypeDesc* pPTD = (ParamTypeDesc*) this;
+ EnsureWritablePages(pPTD);
+
+ // Must have the same loader module, so not encoded
+ CONSISTENCY_CHECK(!pPTD->m_Arg.IsEncodedFixup());
+ ClassLoader::EnsureLoaded(pPTD->m_Arg, CLASS_LOAD_UNRESTORED);
+
+ // Might live somewhere else e.g. Object[] is shared across all ref array types
+ Module::RestoreMethodTablePointer(&(pPTD->m_TemplateMT), NULL, CLASS_LOAD_UNRESTORED);
+ }
+ else
+ {
+ EnsureWritablePages(this);
+ }
+
+ FastInterlockAnd(&m_typeAndFlags, ~TypeDesc::enum_flag_UnrestoredTypeKey);
+#endif
+}
+
+#ifndef DACCESS_COMPILE
+
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+// This just performs a shallow save
+void TypeDesc::Save(DataImage *image)
+{
+ STANDARD_VM_CONTRACT;
+
+ ClassLoader::EnsureLoaded(TypeHandle(this));
+
+ if (LoggingOn(LF_ZAP, LL_INFO10000))
+ {
+ StackSString name;
+ TypeString::AppendType(name, TypeHandle(this));
+ LOG((LF_ZAP, LL_INFO10000, "TypeDesc::Save %S\n", name.GetUnicode()));
+ }
+
+ if (IsGenericVariable())
+ {
+ ((TypeVarTypeDesc*)this)->Save(image);
+ }
+ else if (GetInternalCorElementType() == ELEMENT_TYPE_FNPTR)
+ {
+ ((FnPtrTypeDesc *)this)->Save(image);
+ }
+ else
+ {
+ _ASSERTE(HasTypeParam());
+ ((ParamTypeDesc*)this)->Save(image);
+ }
+
+}
+
+void TypeDesc::Fixup(DataImage *image)
+{
+ STANDARD_VM_CONTRACT;
+
+ if (IsGenericVariable())
+ {
+ TypeVarTypeDesc* tyvar = (TypeVarTypeDesc*) this;
+ tyvar->Fixup(image);
+ }
+ else if (GetInternalCorElementType() == ELEMENT_TYPE_FNPTR)
+ {
+ ((FnPtrTypeDesc*)this)->Fixup(image);
+ }
+ else
+ {
+ // Works for array and PTR/BYREF types, but not function pointers
+ _ASSERTE(HasTypeParam());
+
+ if (IsArray())
+ {
+ ((ArrayTypeDesc*) this)->Fixup(image);
+ }
+ else
+ {
+ ((ParamTypeDesc*) this)->Fixup(image);
+ }
+ }
+
+ if (NeedsRestore(image))
+ {
+ TypeDesc *pTD = (TypeDesc*) image->GetImagePointer(this);
+ _ASSERTE(pTD != NULL);
+ pTD->m_typeAndFlags |= TypeDesc::enum_flag_Unrestored | TypeDesc::enum_flag_UnrestoredTypeKey | TypeDesc::enum_flag_IsNotFullyLoaded;
+ }
+
+}
+
+BOOL TypeDesc::ComputeNeedsRestore(DataImage *image, TypeHandleList *pVisited)
+{
+ STATIC_STANDARD_VM_CONTRACT;
+
+ _ASSERTE(GetAppDomain()->IsCompilationDomain());
+
+ if (HasTypeParam())
+ {
+ return dac_cast<PTR_ParamTypeDesc>(this)->ComputeNeedsRestore(image, pVisited);
+ }
+ else
+ return FALSE;
+}
+
+
+
+void ParamTypeDesc::Save(DataImage *image)
+{
+ STANDARD_VM_CONTRACT;
+
+ if (IsArray())
+ {
+ image->StoreStructure(this, sizeof(ArrayTypeDesc), DataImage::ITEM_ARRAY_TYPEDESC);
+ }
+ else
+ {
+ image->StoreStructure(this, sizeof(ParamTypeDesc), DataImage::ITEM_PARAM_TYPEDESC);
+ }
+
+ // This set of checks matches precisely those in Module::CreateArrayMethodTable
+ // and ParamTypeDesc::ComputeNeedsRestore
+ //
+ // They indicate if an array TypeDesc is non-canonical (in much the same a a generic
+ // method table being non-canonical), i.e. it is not the primary
+ // owner of the m_TemplateMT (the primary owner is the TypeDesc for object[])
+ //
+ if (OwnsTemplateMethodTable())
+ {
+ // This TypeDesc should be the only one saving this MT
+ _ASSERTE(!image->IsStored(m_TemplateMT.GetValue()));
+ Module::SaveMethodTable(image, m_TemplateMT.GetValue(), 0);
+ }
+
+}
+
+
+void ParamTypeDesc::Fixup(DataImage *image)
+{
+ STANDARD_VM_CONTRACT;
+
+ _ASSERTE(image->GetModule()->GetAssembly() ==
+ GetAppDomain()->ToCompilationDomain()->GetTargetAssembly());
+
+ if (LoggingOn(LF_ZAP, LL_INFO10000))
+ {
+ StackSString name;
+ TypeString::AppendType(name, TypeHandle(this));
+ LOG((LF_ZAP, LL_INFO10000, "ParamTypeDesc::Fixup %S\n", name.GetUnicode()));
+ }
+
+ if (!m_TemplateMT.IsNull())
+ {
+ if (OwnsTemplateMethodTable())
+ {
+ // In all other cases the type desc "owns" the m_TemplateMT
+ // and it is always stored in the same module as the TypeDesc (i.e. the
+ // TypeDesc and the MT are "tightly-knit") In other words if one is present in
+ // an NGEN image then then other will be, and if one is "used" at runtime then
+ // the other will be too.
+ image->FixupPointerField(this, offsetof(ParamTypeDesc, m_TemplateMT));
+ m_TemplateMT.GetValue()->Fixup(image);
+ }
+ else
+ {
+ // Fixup the pointer to the possibly-shared m_TemplateMT. This might be in a different module.
+ image->FixupMethodTablePointer(this, &m_TemplateMT);
+ }
+ }
+
+ // Fixup the pointer to the element type.
+ image->HardBindTypeHandlePointer(this, offsetof(ParamTypeDesc, m_Arg));
+
+ // The managed object will get regenerated on demand
+ image->ZeroField(this, offsetof(ParamTypeDesc, m_hExposedClassObject), sizeof(m_hExposedClassObject));
+}
+
+void ArrayTypeDesc::Fixup(DataImage *image)
+{
+ STANDARD_VM_CONTRACT;
+
+ ParamTypeDesc::Fixup(image);
+
+#ifdef FEATURE_COMINTEROP
+ // We don't save CCW templates into ngen images
+ image->ZeroField(this, offsetof(ArrayTypeDesc, m_pCCWTemplate), sizeof(m_pCCWTemplate));
+#endif // FEATURE_COMINTEROP
+}
+
+BOOL ParamTypeDesc::ComputeNeedsRestore(DataImage *image, TypeHandleList *pVisited)
+{
+ STATIC_STANDARD_VM_CONTRACT;
+
+ _ASSERTE(GetAppDomain()->IsCompilationDomain());
+
+ if (m_typeAndFlags & TypeDesc::enum_flag_NeedsRestore)
+ {
+ return TRUE;
+ }
+ if (m_typeAndFlags & TypeDesc::enum_flag_PreRestored)
+ {
+ return FALSE;
+ }
+
+ BOOL res = FALSE;
+ if (!image->CanPrerestoreEagerBindToTypeHandle(m_Arg, pVisited))
+ {
+ res = TRUE;
+ }
+
+ // This set of checks matches precisely those in Module::CreateArrayMethodTable and ParamTypeDesc::Fixup
+ //
+ if (!m_TemplateMT.IsNull())
+ {
+ if (OwnsTemplateMethodTable())
+ {
+ if (m_TemplateMT.GetValue()->ComputeNeedsRestore(image, pVisited))
+ {
+ res = TRUE;
+ }
+ }
+ else
+ {
+ if (!image->CanPrerestoreEagerBindToMethodTable(m_TemplateMT.GetValue(), pVisited))
+ {
+ res = TRUE;
+ }
+ }
+ }
+
+ // Cache the results of running the algorithm.
+ // We can only cache the result if we have not speculatively assumed
+ // that any types are not NeedsRestore, i.e. the visited list is empty
+ if (pVisited == NULL)
+ {
+ if (LoggingOn(LF_ZAP, LL_INFO10000))
+ {
+ StackSString name;
+ TypeString::AppendType(name, TypeHandle(this));
+ LOG((LF_ZAP, LL_INFO10000, "ParamTypeDesc::ComputeNeedsRestore=%d for %S\n", res, name.GetUnicode()));
+ }
+ m_typeAndFlags |= (res ? TypeDesc::enum_flag_NeedsRestore : TypeDesc::enum_flag_PreRestored);
+ }
+ return res;
+}
+#endif // FEATURE_NATIVE_IMAGE_GENERATION
+
+void TypeDesc::SetIsRestored()
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ TypeHandle th = TypeHandle(this);
+ FastInterlockAnd(EnsureWritablePages(&m_typeAndFlags), ~TypeDesc::enum_flag_Unrestored);
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+void TypeDesc::Restore()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ CONSISTENCY_CHECK(!HasUnrestoredTypeKey());
+ }
+ CONTRACTL_END;
+
+#ifndef DACCESS_COMPILE
+ if (HasTypeParam())
+ {
+ ParamTypeDesc *pPTD = dac_cast<PTR_ParamTypeDesc>(this);
+
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOAD_EXACTPARENTS);
+
+ // Must have the same loader module
+ ClassLoader::EnsureLoaded(pPTD->m_Arg, CLASS_LOAD_EXACTPARENTS);
+
+ // Method-table pointer must have been restored by DoRestoreTypeKey
+ Module::RestoreMethodTablePointer(&pPTD->m_TemplateMT, NULL, CLASS_LOAD_EXACTPARENTS);
+ }
+
+ SetIsRestored();
+#else
+ DacNotImpl();
+#endif // #ifndef DACCESS_COMPILE
+}
+
+#endif // FEATURE_PREJIT
+
+
+#ifndef DACCESS_COMPILE
+
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+void TypeVarTypeDesc::Save(DataImage *image)
+{
+ STANDARD_VM_CONTRACT;
+
+ // We don't persist the constraints: instead, load them back on demand
+ m_numConstraints = (DWORD) -1;
+
+ LOG((LF_ZAP, LL_INFO10000, " TypeVarTypeDesc::Save %x (%p)\n", GetToken(), this));
+ image->StoreStructure(this, sizeof(TypeVarTypeDesc),
+ DataImage::ITEM_TYVAR_TYPEDESC);
+}
+
+void TypeVarTypeDesc::Fixup(DataImage *image)
+{
+ STANDARD_VM_CONTRACT;
+
+ LOG((LF_ZAP, LL_INFO10000, " TypeVarTypeDesc::Fixup %x (%p)\n", GetToken(), this));
+ image->FixupPointerField(this, offsetof(TypeVarTypeDesc, m_pModule));
+ image->ZeroField(this, offsetof(TypeVarTypeDesc, m_hExposedClassObject), sizeof(m_hExposedClassObject));
+
+ // We don't persist the constraints: instead, load them back on demand
+ image->ZeroPointerField(this, offsetof(TypeVarTypeDesc, m_constraints));
+
+}
+#endif // FEATURE_NATIVE_IMAGE_GENERATION
+
+MethodDesc * TypeVarTypeDesc::LoadOwnerMethod()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+
+ PRECONDITION(TypeFromToken(m_typeOrMethodDef) == mdtMethodDef);
+ }
+ CONTRACTL_END;
+
+ MethodDesc *pMD = m_pModule->LookupMethodDef(m_typeOrMethodDef);
+ if (pMD == NULL)
+ {
+ pMD = MemberLoader::GetMethodDescFromMethodDef(m_pModule, m_typeOrMethodDef, FALSE);
+ }
+ return pMD;
+}
+
+TypeHandle TypeVarTypeDesc::LoadOwnerType()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+
+ PRECONDITION(TypeFromToken(m_typeOrMethodDef) == mdtTypeDef);
+ }
+ CONTRACTL_END;
+
+ TypeHandle genericType = m_pModule->LookupTypeDef(m_typeOrMethodDef);
+ if (genericType.IsNull())
+ {
+ genericType = ClassLoader::LoadTypeDefThrowing(m_pModule, m_typeOrMethodDef,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::PermitUninstDefOrRef);
+ }
+ return genericType;
+}
+
+TypeHandle* TypeVarTypeDesc::GetCachedConstraints(DWORD *pNumConstraints)
+{
+ LIMITED_METHOD_CONTRACT;
+ PRECONDITION(CheckPointer(pNumConstraints));
+ PRECONDITION(m_numConstraints != (DWORD) -1);
+
+ *pNumConstraints = m_numConstraints;
+ return m_constraints;
+}
+
+
+
+
+TypeHandle* TypeVarTypeDesc::GetConstraints(DWORD *pNumConstraints, ClassLoadLevel level /* = CLASS_LOADED */)
+{
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(CheckPointer(pNumConstraints));
+ PRECONDITION(level == CLASS_DEPENDENCIES_LOADED || level == CLASS_LOADED);
+
+ if (m_numConstraints == (DWORD) -1)
+ LoadConstraints(level);
+
+ *pNumConstraints = m_numConstraints;
+ return m_constraints;
+}
+
+
+void TypeVarTypeDesc::LoadConstraints(ClassLoadLevel level /* = CLASS_LOADED */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+
+ INJECT_FAULT(COMPlusThrowOM());
+
+ PRECONDITION(level == CLASS_DEPENDENCIES_LOADED || level == CLASS_LOADED);
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(((INT_PTR)&m_constraints) % sizeof(m_constraints) == 0);
+ _ASSERTE(((INT_PTR)&m_numConstraints) % sizeof(m_numConstraints) == 0);
+
+ DWORD numConstraints = m_numConstraints;
+
+ if (numConstraints == (DWORD) -1)
+ {
+ EnsureWritablePages(this);
+
+ IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
+
+ HENUMInternalHolder hEnum(pInternalImport);
+ mdGenericParamConstraint tkConstraint;
+
+ SigTypeContext typeContext;
+ mdToken defToken = GetTypeOrMethodDef();
+
+ MethodTable *pMT = NULL;
+ if (TypeFromToken(defToken) == mdtMethodDef)
+ {
+ MethodDesc *pMD = LoadOwnerMethod();
+ _ASSERTE(pMD->IsGenericMethodDefinition());
+
+ SigTypeContext::InitTypeContext(pMD,&typeContext);
+
+ _ASSERTE(!typeContext.m_methodInst.IsEmpty());
+ pMT = pMD->GetMethodTable();
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(defToken) == mdtTypeDef);
+ TypeHandle genericType = LoadOwnerType();
+ _ASSERTE(genericType.IsGenericTypeDefinition());
+
+ SigTypeContext::InitTypeContext(genericType,&typeContext);
+ }
+
+ hEnum.EnumInit(mdtGenericParamConstraint, GetToken());
+ numConstraints = pInternalImport->EnumGetCount(&hEnum);
+ if (numConstraints != 0)
+ {
+ LoaderAllocator* pAllocator=m_pModule->GetLoaderAllocator();
+ // If there is a single class constraint we put in in element 0 of the array
+ AllocMemHolder<TypeHandle> constraints
+ (pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(numConstraints) * S_SIZE_T(sizeof(TypeHandle))));
+
+ DWORD i = 0;
+ while (pInternalImport->EnumNext(&hEnum, &tkConstraint))
+ {
+ _ASSERTE(i <= numConstraints);
+ mdToken tkConstraintType, tkParam;
+ if (FAILED(pInternalImport->GetGenericParamConstraintProps(tkConstraint, &tkParam, &tkConstraintType)))
+ {
+ GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, pMT->GetCl(), IDS_CLASSLOAD_BADFORMAT);
+ }
+ _ASSERTE(tkParam == GetToken());
+ TypeHandle thConstraint = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(GetModule(), tkConstraintType,
+ &typeContext,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::FailIfUninstDefOrRef,
+ ClassLoader::LoadTypes,
+ level);
+
+ constraints[i++] = thConstraint;
+
+ // Method type constraints behave contravariantly
+ // (cf Bounded polymorphism e.g. see
+ // Cardelli & Wegner, On understanding types, data abstraction and polymorphism, Computing Surveys 17(4), Dec 1985)
+ if (pMT != NULL && pMT->HasVariance() && TypeFromToken(tkConstraintType) == mdtTypeSpec)
+ {
+ ULONG cSig;
+ PCCOR_SIGNATURE pSig;
+ if (FAILED(pInternalImport->GetTypeSpecFromToken(tkConstraintType, &pSig, &cSig)))
+ {
+ GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, pMT->GetCl(), IDS_CLASSLOAD_BADFORMAT);
+ }
+ if (!EEClass::CheckVarianceInSig(pMT->GetNumGenericArgs(),
+ pMT->GetClass()->GetVarianceInfo(),
+ pMT->GetModule(),
+ SigPointer(pSig, cSig),
+ gpContravariant))
+ {
+ GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, pMT->GetCl(), IDS_CLASSLOAD_VARIANCE_IN_CONSTRAINT);
+ }
+ }
+ }
+
+ if (InterlockedCompareExchangeT(&m_constraints, constraints.operator->(), NULL) == NULL)
+ {
+ constraints.SuppressRelease();
+ }
+ }
+
+ m_numConstraints = numConstraints;
+ }
+
+ for (DWORD i = 0; i < numConstraints; i++)
+ {
+ ClassLoader::EnsureLoaded(m_constraints[i], level);
+ }
+}
+
+BOOL TypeVarTypeDesc::ConstrainedAsObjRef()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(ConstraintsLoaded());
+ }
+ CONTRACTL_END;
+
+ IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
+ mdGenericParam genericParamToken = GetToken();
+ DWORD flags;
+ if (FAILED(pInternalImport->GetGenericParamProps(genericParamToken, NULL, &flags, NULL, NULL, NULL)))
+ {
+ return FALSE;
+ }
+ DWORD specialConstraints = flags & gpSpecialConstraintMask;
+
+ if ((specialConstraints & gpReferenceTypeConstraint) != 0)
+ return TRUE;
+
+ return ConstrainedAsObjRefHelper();
+}
+
+// A recursive helper that helps determine whether this variable is constrained as ObjRef.
+// Please note that we do not check the gpReferenceTypeConstraint special constraint here
+// because this property does not propagate up the constraining hierarchy.
+// (e.g. "class A<S, T> where S : T, where T : class" does not guarantee that S is ObjRef)
+BOOL TypeVarTypeDesc::ConstrainedAsObjRefHelper()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ DWORD dwNumConstraints = 0;
+ TypeHandle* constraints = GetCachedConstraints(&dwNumConstraints);
+
+ for (DWORD i = 0; i < dwNumConstraints; i++)
+ {
+ TypeHandle constraint = constraints[i];
+
+ if (constraint.IsGenericVariable() && constraint.AsGenericVariable()->ConstrainedAsObjRefHelper())
+ return TRUE;
+
+ if (!constraint.IsInterface() && CorTypeInfo::IsObjRef_NoThrow(constraint.GetInternalCorElementType()))
+ {
+ // Object, ValueType, and Enum are ObjRefs but they do not constrain the var to ObjRef!
+ MethodTable *mt = constraint.GetMethodTable();
+
+ if (mt != g_pObjectClass &&
+ mt != g_pValueTypeClass &&
+ mt != g_pEnumClass)
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL TypeVarTypeDesc::ConstrainedAsValueType()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(ConstraintsLoaded());
+ }
+ CONTRACTL_END;
+
+ IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
+ mdGenericParam genericParamToken = GetToken();
+ DWORD flags;
+ if (FAILED(pInternalImport->GetGenericParamProps(genericParamToken, NULL, &flags, NULL, NULL, NULL)))
+ {
+ return FALSE;
+ }
+ DWORD specialConstraints = flags & gpSpecialConstraintMask;
+
+ if ((specialConstraints & gpNotNullableValueTypeConstraint) != 0)
+ return TRUE;
+
+ DWORD dwNumConstraints = 0;
+ TypeHandle* constraints = GetCachedConstraints(&dwNumConstraints);
+
+ for (DWORD i = 0; i < dwNumConstraints; i++)
+ {
+ TypeHandle constraint = constraints[i];
+
+ if (constraint.IsGenericVariable())
+ {
+ if (constraint.AsGenericVariable()->ConstrainedAsValueType())
+ return TRUE;
+ }
+ else
+ {
+ // the following condition will also disqualify interfaces
+ if (!CorTypeInfo::IsObjRef_NoThrow(constraint.GetInternalCorElementType()))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// Loads the type of a constraint given the constraint token and instantiation context. If pInstContext is
+// not NULL and the constraint's type is a typespec, pInstContext will be used to instantiate the typespec.
+// Otherwise typical instantiation is returned if the constraint type is generic.
+//---------------------------------------------------------------------------------------------------------------------
+static
+TypeHandle LoadTypeVarConstraint(TypeVarTypeDesc *pTypeVar, mdGenericParamConstraint tkConstraint,
+ const InstantiationContext *pInstContext)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pTypeVar));
+ }
+ CONTRACTL_END;
+
+ Module *pTyModule = pTypeVar->GetModule();
+ IMDInternalImport* pInternalImport = pTyModule->GetMDImport();
+
+ mdToken tkConstraintType, tkParam;
+ IfFailThrow(pInternalImport->GetGenericParamConstraintProps(tkConstraint, &tkParam, &tkConstraintType));
+ _ASSERTE(tkParam == pTypeVar->GetToken());
+ mdToken tkOwnerToken = pTypeVar->GetTypeOrMethodDef();
+
+ if (TypeFromToken(tkConstraintType) == mdtTypeSpec && pInstContext != NULL)
+ {
+ if(pInstContext->m_pSubstChain == NULL)
+ {
+ // The substitution chain will be null in situations
+ // where we are instantiating types that are open, and therefore
+ // we should be using the fully open TypeVar constraint instantiation code
+ // below. However, in the case of a open method on a closed generic class
+ // we will also have a null substitution chain. In this case, if we can ensure
+ // that the instantiation type parameters are non type-var types, it is valid
+ // to use the passed in instantiation when instantiating the type var constraint.
+ BOOL fContextContainsValidGenericTypeParams = FALSE;
+
+ if (TypeFromToken(tkOwnerToken) == mdtMethodDef)
+ {
+ SigTypeContext sigTypeContext;
+
+ MethodDesc *pMD = pTypeVar->LoadOwnerMethod();
+
+ SigTypeContext::InitTypeContext(pMD, &sigTypeContext);
+ fContextContainsValidGenericTypeParams = SigTypeContext::IsValidTypeOnlyInstantiationOf(&sigTypeContext, pInstContext->m_pArgContext);
+ }
+
+ if (!fContextContainsValidGenericTypeParams)
+ goto LoadConstraintOnOpenType;
+ }
+
+ // obtain the constraint type's signature if it's a typespec
+ ULONG cbSig;
+ PCCOR_SIGNATURE ptr;
+
+ IfFailThrow(pInternalImport->GetSigFromToken(tkConstraintType, &cbSig, &ptr));
+
+ SigPointer pSig(ptr, cbSig);
+
+ // instantiate the signature using the current InstantiationContext
+ return pSig.GetTypeHandleThrowing(pTyModule,
+ pInstContext->m_pArgContext,
+ ClassLoader::LoadTypes, CLASS_DEPENDENCIES_LOADED, FALSE,
+ pInstContext->m_pSubstChain);
+ }
+ else
+ {
+LoadConstraintOnOpenType:
+
+ SigTypeContext sigTypeContext;
+
+ switch (TypeFromToken(tkOwnerToken))
+ {
+ case mdtTypeDef:
+ {
+ // the type variable is declared by a type - load the handle of the type
+ TypeHandle thOwner = pTyModule->GetClassLoader()->LoadTypeDefThrowing(pTyModule,
+ tkOwnerToken,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::PermitUninstDefOrRef,
+ tdNoTypes,
+ CLASS_LOAD_APPROXPARENTS
+ );
+
+ SigTypeContext::InitTypeContext(thOwner, &sigTypeContext);
+ break;
+ }
+
+ case mdtMethodDef:
+ {
+ // the type variable is declared by a method - load its method desc
+ MethodDesc *pMD = pTyModule->LookupMethodDef(tkOwnerToken);
+
+ SigTypeContext::InitTypeContext(pMD, &sigTypeContext);
+ break;
+ }
+
+ default:
+ {
+ COMPlusThrow(kBadImageFormatException);
+ }
+ }
+
+ // load the (typical instantiation of) constraint type
+ return ClassLoader::LoadTypeDefOrRefOrSpecThrowing(pTyModule,
+ tkConstraintType,
+ &sigTypeContext,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::FailIfUninstDefOrRef,
+ ClassLoader::LoadTypes,
+ CLASS_DEPENDENCIES_LOADED);
+ }
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// We come here only if a type parameter with a special constraint is instantiated by an argument that is itself
+// a type parameter. In this case, we'll need to examine *its* constraints to see if the range of types that would satisfy its
+// constraints is a subset of the range of types that would satisfy the special constraint.
+//
+// This routine will return TRUE if it can prove that argument "pTyArg" has a constraint that will satisfy the special constraint.
+//
+// (NOTE: It does not check against anything other than one specific specialConstraint (it doesn't even know what they are.) This is
+// just one step in the checking of constraints.)
+//---------------------------------------------------------------------------------------------------------------------
+static
+BOOL SatisfiesSpecialConstraintRecursive(TypeVarTypeDesc *pTyArg, DWORD specialConstraint, TypeHandleList *pVisitedVars = NULL)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pTyArg));
+ }
+ CONTRACTL_END;
+
+ // The caller must invoke for all special constraints that apply - this fcn can only reliably test against one
+ // constraint at a time.
+ _ASSERTE(specialConstraint == gpNotNullableValueTypeConstraint
+ || specialConstraint == gpReferenceTypeConstraint
+ || specialConstraint == gpDefaultConstructorConstraint);
+
+ IMDInternalImport* pInternalImport = pTyArg->GetModule()->GetMDImport();
+
+ // Get the argument type's own special constraints
+ DWORD argFlags;
+ IfFailThrow(pTyArg->GetModule()->GetMDImport()->GetGenericParamProps(pTyArg->GetToken(), NULL, &argFlags, NULL, NULL, NULL));
+
+ DWORD argSpecialConstraints = argFlags & gpSpecialConstraintMask;
+
+ // First, if the argument's own special constraints match the parameter's special constraints,
+ // we can safely conclude the constraint is satisfied.
+ switch (specialConstraint)
+ {
+ case gpNotNullableValueTypeConstraint:
+ {
+ if ((argSpecialConstraints & gpNotNullableValueTypeConstraint) != 0)
+ {
+ return TRUE;
+ }
+ break;
+ }
+
+ case gpReferenceTypeConstraint:
+ {
+ // gpReferenceTypeConstraint is not "inherited" so ignore it if pTyArg is a variable
+ // constraining the argument rather than the argument itself.
+
+ if (pVisitedVars == NULL && (argSpecialConstraints & gpReferenceTypeConstraint) != 0)
+ {
+ return TRUE;
+ }
+ break;
+ }
+
+ case gpDefaultConstructorConstraint:
+ {
+ // gpDefaultConstructorConstraint is not "inherited" so ignore it if pTyArg is a variable
+ // constraining the argument rather than the argument itself.
+
+ if ((pVisitedVars == NULL && (argSpecialConstraints & gpDefaultConstructorConstraint) != 0) ||
+ (argSpecialConstraints & gpNotNullableValueTypeConstraint) != 0)
+ {
+ return TRUE;
+ }
+ break;
+ }
+ }
+
+ // The special constraints did not match. However, we may find a primary type constraint
+ // that would always satisfy the special constraint.
+ HENUMInternalHolder hEnum(pInternalImport);
+ hEnum.EnumInit(mdtGenericParamConstraint, pTyArg->GetToken());
+
+ mdGenericParamConstraint tkConstraint;
+ while (pInternalImport->EnumNext(&hEnum, &tkConstraint))
+ {
+ // We pass NULL instantiation context here because when checking for special constraints, it makes
+ // no difference whether we load a typical (e.g. A<T>) or concrete (e.g. A<string>) instantiation.
+ TypeHandle thConstraint = LoadTypeVarConstraint(pTyArg, tkConstraint, NULL);
+
+ if (thConstraint.IsGenericVariable())
+ {
+ // The variable is constrained by another variable, which we need to check recursively. An
+ // example of why this is necessary follows:
+ //
+ // class A<T> where T : class
+ // { }
+ // class B<S, R> : A<S> where S : R where R : EventArgs
+ // { }
+ //
+ if (!TypeHandleList::Exists(pVisitedVars, thConstraint))
+ {
+ TypeHandleList newVisitedVars(thConstraint, pVisitedVars);
+ if (SatisfiesSpecialConstraintRecursive(thConstraint.AsGenericVariable(),
+ specialConstraint,
+ &newVisitedVars))
+ {
+ return TRUE;
+ }
+ }
+ }
+ else if (thConstraint.IsInterface())
+ {
+ // This is a secondary constraint - this tells us nothing about the eventual instantiation that
+ // we can use here.
+ }
+ else
+ {
+ // This is a type constraint. Remember that the eventual instantiation is only guaranteed to be
+ // something *derived* from this type, not the actual type itself. To emphasize, we rename the local.
+
+ TypeHandle thAncestorOfType = thConstraint;
+
+ if (specialConstraint == gpNotNullableValueTypeConstraint)
+ {
+ if (thAncestorOfType.IsValueType() && !(thAncestorOfType.AsMethodTable()->IsNullable()))
+ {
+ return TRUE;
+ }
+ }
+
+ if (specialConstraint == gpReferenceTypeConstraint)
+ {
+
+ if (!thAncestorOfType.IsTypeDesc())
+ {
+ MethodTable *pAncestorMT = thAncestorOfType.AsMethodTable();
+
+ if ((!(pAncestorMT->IsValueType())) && pAncestorMT != g_pObjectClass && pAncestorMT != g_pValueTypeClass)
+ {
+ // ValueTypes are sealed except when they aren't (cough, cough, System.Enum...). Sigh.
+ // Don't put all our trust in IsValueType() here - check the ancestry chain as well.
+ BOOL fIsValueTypeAnAncestor = FALSE;
+ MethodTable *pParentMT = pAncestorMT->GetParentMethodTable();
+ while (pParentMT)
+ {
+ if (pParentMT == g_pValueTypeClass)
+ {
+ fIsValueTypeAnAncestor = TRUE;
+ break;
+ }
+ pParentMT = pParentMT->GetParentMethodTable();
+ }
+
+ if (!fIsValueTypeAnAncestor)
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ if (specialConstraint == gpDefaultConstructorConstraint)
+ {
+ // If a valuetype, just check to ensure that doesn't have a private default ctor.
+ // If not a valuetype, not much we can conclude knowing just an ancestor class.
+ if (thAncestorOfType.IsValueType() && thAncestorOfType.GetMethodTable()->HasExplicitOrImplicitPublicDefaultConstructor())
+ {
+ return TRUE;
+ }
+ }
+
+ }
+ }
+
+ // If we got here, we found no evidence that the argument's constraints are strict enough to satisfy the parameter's constraints.
+ return FALSE;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// Walks the "constraining chain" of a type variable and appends all concrete constraints as well as type vars
+// to the provided ArrayList. Upon leaving the function, the list contains all types that the type variable is
+// known to be assignable to.
+//
+// E.g.
+// class A<S, T> where S : T, IComparable where T : EventArgs
+// {
+// void f<U>(U u) where U : S, IDisposable { }
+// }
+// This would put 5 types to the U's list: S, T, IDisposable, IComparable, and EventArgs.
+//---------------------------------------------------------------------------------------------------------------------
+static
+void GatherConstraintsRecursive(TypeVarTypeDesc *pTyArg, ArrayList *pArgList, const InstantiationContext *pInstContext,
+ TypeHandleList *pVisitedVars = NULL)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pTyArg));
+ PRECONDITION(CheckPointer(pArgList));
+ }
+ CONTRACTL_END;
+
+ IMDInternalImport* pInternalImport = pTyArg->GetModule()->GetMDImport();
+
+ // enumerate constraints of the pTyArg
+ HENUMInternalHolder hEnum(pInternalImport);
+ hEnum.EnumInit(mdtGenericParamConstraint, pTyArg->GetToken());
+
+ mdGenericParamConstraint tkConstraint;
+ while (pInternalImport->EnumNext(&hEnum, &tkConstraint))
+ {
+ TypeHandle thConstraint = LoadTypeVarConstraint(pTyArg, tkConstraint, pInstContext);
+
+ if (thConstraint.IsGenericVariable())
+ {
+ // see if it's safe to recursively call ourselves
+ if (!TypeHandleList::Exists(pVisitedVars, thConstraint))
+ {
+ pArgList->Append(thConstraint.AsPtr());
+
+ TypeHandleList newVisitedVars(thConstraint, pVisitedVars);
+ GatherConstraintsRecursive(thConstraint.AsGenericVariable(), pArgList, pInstContext, &newVisitedVars);
+ }
+
+ // Note: circular type parameter constraints will be detected and reported later in
+ // MethodTable::DoFullyLoad, we just have to avoid SO here.
+ }
+ else
+ {
+ pArgList->Append(thConstraint.AsPtr());
+ }
+ }
+}
+
+// pTypeContextOfConstraintDeclarer = type context of the generic type that declares the constraint
+// This is needed to load the "X" type when the constraint is the frm
+// "where T:X".
+// Caution: Do NOT use it to load types or constraints attached to "thArg".
+//
+// thArg = typehandle of the type being substituted for the type parameter.
+//
+// pInstContext = the instantiation context (type context + substitution chain) to be
+// used when loading constraints attached to "thArg".
+//
+BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstraintDeclarer, TypeHandle thArg,
+ const InstantiationContext *pInstContext/*=NULL*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+
+ PRECONDITION(!thArg.IsNull());
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END;
+
+ IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
+ mdGenericParamConstraint tkConstraint;
+
+ INDEBUG(mdToken defToken = GetTypeOrMethodDef());
+ _ASSERTE(TypeFromToken(defToken) == mdtMethodDef || TypeFromToken(defToken) == mdtTypeDef);
+
+ // prepare for the enumeration of this variable's general constraints
+ mdGenericParam genericParamToken = GetToken();
+
+ HENUMInternalHolder hEnum(pInternalImport);
+ hEnum.EnumInit(mdtGenericParamConstraint, genericParamToken);
+
+ ArrayList argList;
+
+ // First check special constraints (must-be-reference-type, must-be-value-type, and must-have-default-constructor)
+ DWORD flags;
+ IfFailThrow(pInternalImport->GetGenericParamProps(genericParamToken, NULL, &flags, NULL, NULL, NULL));
+
+ DWORD specialConstraints = flags & gpSpecialConstraintMask;
+
+ if (thArg.IsGenericVariable())
+ {
+ TypeVarTypeDesc *pTyVar = thArg.AsGenericVariable();
+
+ if ((specialConstraints & gpNotNullableValueTypeConstraint) != 0)
+ {
+ if (!SatisfiesSpecialConstraintRecursive(pTyVar, gpNotNullableValueTypeConstraint))
+ {
+ return FALSE;
+ }
+ }
+
+ if ((specialConstraints & gpReferenceTypeConstraint) != 0)
+ {
+ if (!SatisfiesSpecialConstraintRecursive(pTyVar, gpReferenceTypeConstraint))
+ {
+ return FALSE;
+ }
+ }
+
+ if ((specialConstraints & gpDefaultConstructorConstraint) != 0)
+ {
+ if (!SatisfiesSpecialConstraintRecursive(pTyVar, gpDefaultConstructorConstraint))
+ {
+ return FALSE;
+ }
+ }
+
+ if (hEnum.EnumGetCount() == 0)
+ {
+ // return immediately if there are no general constraints to satisfy (fast path)
+ return TRUE;
+ }
+
+ // Now walk the "constraining chain" of type variables and gather all constraint types.
+ //
+ // This work should not be left to code:TypeHandle.CanCastTo because we need typespec constraints
+ // to be instantiated in pInstContext. If we just do thArg.CanCastTo(thConstraint), it would load
+ // typical instantiations of the constraints and the can-cast-to check may fail. In addition,
+ // code:TypeHandle.CanCastTo will SO if the constraints are circular.
+ //
+ // Consider:
+ //
+ // class A<T>
+ // {
+ // void f<U>(B<U, T> b) where U : A<T> { }
+ // }
+ // class B<S, R> where S : A<R> { }
+ //
+ // If we load the signature of, say, A<int>.f<U> (concrete class but typical method), and end up
+ // here verifying that S : A<R> is satisfied by U : A<T>, we must instantiate the constraint type
+ // A<T> using pInstContext so that it becomes A<int>. Otherwise the constraint check fails.
+ //
+ GatherConstraintsRecursive(pTyVar, &argList, pInstContext);
+ }
+ else
+ {
+ if ((specialConstraints & gpNotNullableValueTypeConstraint) != 0)
+ {
+ if (!thArg.IsValueType())
+ return FALSE;
+ else
+ {
+ // the type argument is a value type, however if it is any kind of Nullable we want to fail
+ // as the constraint accepts any value type except Nullable types (Nullable itself is a value type)
+ if (thArg.AsMethodTable()->IsNullable())
+ return FALSE;
+ }
+ }
+
+ if ((specialConstraints & gpReferenceTypeConstraint) != 0)
+ {
+ if (thArg.IsValueType())
+ return FALSE;
+ }
+
+ if ((specialConstraints & gpDefaultConstructorConstraint) != 0)
+ {
+ if (thArg.IsTypeDesc() || (!thArg.AsMethodTable()->HasExplicitOrImplicitPublicDefaultConstructor()))
+ return FALSE;
+ }
+ }
+
+ // Complete the list by adding thArg itself. If thArg is not a generic variable this will be the only
+ // item in the list. If it is a generic variable, we need it in the list as well in addition to all the
+ // constraints gathered by GatherConstraintsRecursive, because e.g. class A<S, T> : where S : T
+ // can be instantiated using A<U, U>.
+ argList.Append(thArg.AsPtr());
+
+ // At this point argList contains all types that thArg is known to be assignable to. The list may
+ // contain duplicates and it consists of zero or more type variables, zero or more possibly generic
+ // interfaces, and at most one possibly generic class.
+
+ // Now check general subtype constraints
+ while (pInternalImport->EnumNext(&hEnum, &tkConstraint))
+ {
+ mdToken tkConstraintType, tkParam;
+ IfFailThrow(pInternalImport->GetGenericParamConstraintProps(tkConstraint, &tkParam, &tkConstraintType));
+
+ _ASSERTE(tkParam == GetToken());
+ TypeHandle thConstraint = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(GetModule(),
+ tkConstraintType,
+ pTypeContextOfConstraintDeclarer,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::FailIfUninstDefOrRef,
+ ClassLoader::LoadTypes,
+ CLASS_DEPENDENCIES_LOADED);
+
+ // System.Object constraint will be always satisfied - even if argList is empty
+ if (!thConstraint.IsObjectType())
+ {
+ BOOL fCanCast = FALSE;
+
+ // loop over all types that we know the arg will be assignable to
+ ArrayList::Iterator iter = argList.Iterate();
+ while (iter.Next())
+ {
+ TypeHandle thElem = TypeHandle::FromPtr(iter.GetElement());
+
+ if (thElem.IsGenericVariable())
+ {
+ // if a generic variable equals to the constraint, then this constraint will be satisfied
+ if (thElem == thConstraint)
+ {
+ fCanCast = TRUE;
+ break;
+ }
+
+ // and any variable with the gpNotNullableValueTypeConstraint special constraint
+ // satisfies the "derived from System.ValueType" general subtype constraint
+ if (thConstraint == g_pValueTypeClass)
+ {
+ TypeVarTypeDesc *pTyElem = thElem.AsGenericVariable();
+ IfFailThrow(pTyElem->GetModule()->GetMDImport()->GetGenericParamProps(
+ pTyElem->GetToken(),
+ NULL,
+ &flags,
+ NULL,
+ NULL,
+ NULL));
+
+ if ((flags & gpNotNullableValueTypeConstraint) != 0)
+ {
+ fCanCast = TRUE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // if a concrete type can be cast to the constraint, then this constraint will be satisifed
+ if (thElem.CanCastTo(thConstraint))
+ {
+ fCanCast = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!fCanCast)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+#ifndef CROSSGEN_COMPILE
+OBJECTREF TypeVarTypeDesc::GetManagedClassObject()
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+
+ INJECT_FAULT(COMPlusThrowOM());
+
+ PRECONDITION(IsGenericVariable());
+ }
+ CONTRACTL_END;
+
+ if (m_hExposedClassObject == NULL) {
+ REFLECTCLASSBASEREF refClass = NULL;
+ GCPROTECT_BEGIN(refClass);
+ if (GetAssembly()->IsIntrospectionOnly())
+ refClass = (REFLECTCLASSBASEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__CLASS_INTROSPECTION_ONLY));
+ else
+ refClass = (REFLECTCLASSBASEREF) AllocateObject(g_pRuntimeTypeClass);
+
+ LoaderAllocator *pLoaderAllocator = GetLoaderAllocator();
+ TypeHandle th = TypeHandle(this);
+ ((ReflectClassBaseObject*)OBJECTREFToObject(refClass))->SetType(th);
+ ((ReflectClassBaseObject*)OBJECTREFToObject(refClass))->SetKeepAlive(pLoaderAllocator->GetExposedObject());
+
+ // Let all threads fight over who wins using InterlockedCompareExchange.
+ // Only the winner can set m_hExposedClassObject from NULL.
+ LOADERHANDLE hExposedClassObject = pLoaderAllocator->AllocateHandle(refClass);
+
+ if (FastInterlockCompareExchangePointer(EnsureWritablePages(&m_hExposedClassObject), hExposedClassObject, static_cast<LOADERHANDLE>(NULL)))
+ {
+ pLoaderAllocator->ClearHandle(hExposedClassObject);
+ }
+
+ GCPROTECT_END();
+ }
+ return GetManagedClassObjectIfExists();
+}
+#endif // CROSSGEN_COMPILE
+
+#endif //!DACCESS_COMPILE
+
+TypeHandle *
+FnPtrTypeDesc::GetRetAndArgTypes()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Decode encoded type handles on demand
+#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
+ for (DWORD i = 0; i <= m_NumArgs; i++)
+ {
+ Module::RestoreTypeHandlePointerRaw(&m_RetAndArgTypes[i]);
+ }
+#endif //defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
+
+ return m_RetAndArgTypes;
+} // FnPtrTypeDesc::GetRetAndArgTypes
+
+#ifndef DACCESS_COMPILE
+
+// Returns TRUE if all return and argument types are externally visible.
+BOOL
+FnPtrTypeDesc::IsExternallyVisible() const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ const TypeHandle * rgRetAndArgTypes = GetRetAndArgTypes();
+ for (DWORD i = 0; i <= m_NumArgs; i++)
+ {
+ if (!rgRetAndArgTypes[i].IsExternallyVisible())
+ {
+ return FALSE;
+ }
+ }
+ // All return/arguments types are externally visible
+ return TRUE;
+} // FnPtrTypeDesc::IsExternallyVisible
+
+// Returns TRUE if any of return or argument types is part of an assembly loaded for introspection.
+BOOL
+FnPtrTypeDesc::IsIntrospectionOnly() const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ const TypeHandle * rgRetAndArgTypes = GetRetAndArgTypes();
+ for (DWORD i = 0; i <= m_NumArgs; i++)
+ {
+ if (rgRetAndArgTypes[i].IsIntrospectionOnly())
+ {
+ return TRUE;
+ }
+ }
+ // None of the return/arguments type was loaded for introspection
+ return FALSE;
+} // FnPtrTypeDesc::IsIntrospectionOnly
+
+// Returns TRUE if any of return or argument types is part of an assembly loaded for introspection.
+// Instantiations of generic types are also recursively checked.
+BOOL
+FnPtrTypeDesc::ContainsIntrospectionOnlyTypes() const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ const TypeHandle * rgRetAndArgTypes = GetRetAndArgTypes();
+ for (DWORD i = 0; i <= m_NumArgs; i++)
+ {
+ if (rgRetAndArgTypes[i].ContainsIntrospectionOnlyTypes())
+ {
+ return TRUE;
+ }
+ }
+ // None of the return/arguments type contains types loaded for introspection
+ return FALSE;
+} // FnPtrTypeDesc::ContainsIntrospectionOnlyTypes
+
+#endif //DACCESS_COMPILE
+
+#if defined(FEATURE_NATIVE_IMAGE_GENERATION) && !defined(DACCESS_COMPILE)
+
+void FnPtrTypeDesc::Save(DataImage * image)
+{
+ STANDARD_VM_CONTRACT;
+
+ image->StoreStructure(
+ this,
+ sizeof(FnPtrTypeDesc) + (m_NumArgs * sizeof(TypeHandle)),
+ DataImage::ITEM_FPTR_TYPEDESC);
+}
+
+void FnPtrTypeDesc::Fixup(DataImage * image)
+{
+ STANDARD_VM_CONTRACT;
+
+ for (DWORD i = 0; i <= m_NumArgs; i++)
+ {
+ image->FixupTypeHandlePointerInPlace(
+ this,
+ (BYTE *)&m_RetAndArgTypes[i] - (BYTE *)this);
+ }
+}
+
+#endif //defined(FEATURE_NATIVE_IMAGE_GENERATION) && !defined(DACCESS_COMPILE)
+
+#ifdef DACCESS_COMPILE
+
+void
+ParamTypeDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ DAC_ENUM_DTHIS();
+
+ PTR_MethodTable pTemplateMT = m_TemplateMT.GetValue();
+ if (pTemplateMT.IsValid())
+ {
+ pTemplateMT->EnumMemoryRegions(flags);
+ }
+
+ m_Arg.EnumMemoryRegions(flags);
+}
+
+void
+TypeVarTypeDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ DAC_ENUM_DTHIS();
+
+ if (m_pModule.IsValid())
+ {
+ m_pModule->EnumMemoryRegions(flags, true);
+ }
+
+ if (m_numConstraints != (DWORD)-1)
+ {
+ PTR_TypeHandle constraint = m_constraints;
+ for (DWORD i = 0; i < m_numConstraints; i++)
+ {
+ if (constraint.IsValid())
+ {
+ constraint->EnumMemoryRegions(flags);
+ }
+ constraint++;
+ }
+ }
+} // TypeVarTypeDesc::EnumMemoryRegions
+
+void
+FnPtrTypeDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ DAC_ENUM_DTHIS();
+
+ for (DWORD i = 0; i < m_NumArgs; i++)
+ {
+ m_RetAndArgTypes[i].EnumMemoryRegions(flags);
+ }
+}
+
+#endif //DACCESS_COMPILE