diff options
Diffstat (limited to 'src/Runtime.Base/src/System/Runtime/TypeCast.cs')
-rw-r--r-- | src/Runtime.Base/src/System/Runtime/TypeCast.cs | 107 |
1 files changed, 72 insertions, 35 deletions
diff --git a/src/Runtime.Base/src/System/Runtime/TypeCast.cs b/src/Runtime.Base/src/System/Runtime/TypeCast.cs index 568892a65..5d6ad5386 100644 --- a/src/Runtime.Base/src/System/Runtime/TypeCast.cs +++ b/src/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -111,7 +111,7 @@ namespace System.Runtime // parameters are compatible. // NOTE: using general assignable path for the cache because of the cost of the variance checks - if (CastCache.AreTypesAssignableInternal(pObjType, pTargetType, AssignmentVariation.BoxedSource)) + if (CastCache.AreTypesAssignableInternal(pObjType, pTargetType, AssignmentVariation.BoxedSource, null)) return obj; return null; } @@ -154,7 +154,7 @@ namespace System.Runtime } [RuntimeExport("RhTypeCast_CheckCastClass")] - public static unsafe object CheckCastClass(Object obj, void* pvTargetEEType) + public static unsafe object CheckCastClass(object obj, void* pvTargetEEType) { // a null value can be cast to anything if (obj == null) @@ -174,7 +174,7 @@ namespace System.Runtime } [RuntimeExport("RhTypeCast_CheckUnbox")] - public static unsafe void CheckUnbox(Object obj, byte expectedCorElementType) + public static unsafe void CheckUnbox(object obj, byte expectedCorElementType) { if (obj == null) { @@ -230,7 +230,7 @@ namespace System.Runtime } if (CastCache.AreTypesAssignableInternal(pObjType->RelatedParameterType, pTargetType->RelatedParameterType, - AssignmentVariation.AllowSizeEquivalence)) + AssignmentVariation.AllowSizeEquivalence, null)) { return obj; } @@ -239,7 +239,7 @@ namespace System.Runtime } [RuntimeExport("RhTypeCast_CheckCastArray")] - public static unsafe object CheckCastArray(Object obj, void* pvTargetEEType) + public static unsafe object CheckCastArray(object obj, void* pvTargetEEType) { // a null value can be cast to anything if (obj == null) @@ -269,7 +269,7 @@ namespace System.Runtime EEType* pTargetType = (EEType*)pvTargetType; EEType* pObjType = obj.EEType; - if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType)) + if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType, null)) return obj; // If object type implements ICastable then there's one more way to check whether it implements @@ -333,7 +333,7 @@ namespace System.Runtime // TODO!! END REMOVE THIS CODE WHEN WE REMOVE ICASTABLE } - internal static unsafe bool ImplementsInterface(EEType* pObjType, EEType* pTargetType) + internal static unsafe bool ImplementsInterface(EEType* pObjType, EEType* pTargetType, EETypePairList* pVisited) { Debug.Assert(!pTargetType->IsParameterizedType, "did not expect paramterized type"); Debug.Assert(pTargetType->IsInterface, "IsInstanceOfInterface called with non-interface EEType"); @@ -413,7 +413,8 @@ namespace System.Runtime pInterfaceInstantiation, pTargetInstantiation, pTargetVarianceInfo, - fArrayCovariance)) + fArrayCovariance, + pVisited)) return true; } } @@ -435,7 +436,7 @@ namespace System.Runtime } // Compare two types to see if they are compatible via generic variance. - private static unsafe bool TypesAreCompatibleViaGenericVariance(EEType* pSourceType, EEType* pTargetType) + private static unsafe bool TypesAreCompatibleViaGenericVariance(EEType* pSourceType, EEType* pTargetType, EETypePairList* pVisited) { EEType* pTargetGenericType = pTargetType->GenericDefinition; EEType* pSourceGenericType = pSourceType->GenericDefinition; @@ -466,7 +467,8 @@ namespace System.Runtime pSourceInstantiation, pTargetInstantiation, pTargetVarianceInfo, - false)) + false, + pVisited)) { return true; } @@ -484,7 +486,8 @@ namespace System.Runtime EETypeRef* pSourceInstantiation, EETypeRef* pTargetInstantiation, GenericVariance* pVarianceInfo, - bool fForceCovariance) + bool fForceCovariance, + EETypePairList* pVisited) { // Walk through the instantiations comparing the cast compatibility of each pair // of type args. @@ -519,7 +522,7 @@ namespace System.Runtime // class Foo : ICovariant<Bar> is ICovariant<IBar> // class Foo : ICovariant<IBar> is ICovariant<Object> - if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.Normal)) + if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.Normal, pVisited)) return false; break; @@ -534,7 +537,7 @@ namespace System.Runtime // This call is just like the call for Covariance above except true is passed // to the fAllowSizeEquivalence parameter to allow the int/uint matching to work - if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.AllowSizeEquivalence)) + if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.AllowSizeEquivalence, pVisited)) return false; break; @@ -549,7 +552,7 @@ namespace System.Runtime // class Foo : IContravariant<IBar> is IContravariant<Bar> // class Foo : IContravariant<Object> is IContravariant<IBar> - if (!CastCache.AreTypesAssignableInternal(pTargetArgType, pSourceArgType, AssignmentVariation.Normal)) + if (!CastCache.AreTypesAssignableInternal(pTargetArgType, pSourceArgType, AssignmentVariation.Normal, pVisited)) return false; break; @@ -594,7 +597,7 @@ namespace System.Runtime return AreTypesEquivalentInternal(pSourceType, pNullableType); } - return CastCache.AreTypesAssignableInternal(pSourceType, pTargetType, AssignmentVariation.BoxedSource); + return CastCache.AreTypesAssignableInternal(pSourceType, pTargetType, AssignmentVariation.BoxedSource, null); } // Internally callable version of the export method above. Has two additional flags: @@ -602,7 +605,7 @@ namespace System.Runtime // compatible with Object, ValueType and Enum (if applicable) // fAllowSizeEquivalence : allow identically sized integral types and enums to be considered // equivalent (currently used only for array element types) - internal static unsafe bool AreTypesAssignableInternal(EEType* pSourceType, EEType* pTargetType, AssignmentVariation variation) + internal static unsafe bool AreTypesAssignableInternal(EEType* pSourceType, EEType* pTargetType, AssignmentVariation variation, EETypePairList* pVisited) { bool fBoxedSource = ((variation & AssignmentVariation.BoxedSource) == AssignmentVariation.BoxedSource); bool fAllowSizeEquivalence = ((variation & AssignmentVariation.AllowSizeEquivalence) == AssignmentVariation.AllowSizeEquivalence); @@ -622,12 +625,12 @@ namespace System.Runtime if (!fBoxedSource && pSourceType->IsValueType) return false; - if (ImplementsInterface(pSourceType, pTargetType)) + if (ImplementsInterface(pSourceType, pTargetType, pVisited)) return true; // Are the types compatible due to generic variance? if (pTargetType->HasGenericVariance && pSourceType->HasGenericVariance) - return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); + return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType, pVisited); return false; } @@ -667,7 +670,7 @@ namespace System.Runtime // here handles array covariance as well as IFoo[] -> Foo[] etc. We are not using // AssignmentVariation.BoxedSource because int[] is not assignable to object[]. return CastCache.AreTypesAssignableInternal(pSourceType->RelatedParameterType, - pTargetType->RelatedParameterType, AssignmentVariation.AllowSizeEquivalence); + pTargetType->RelatedParameterType, AssignmentVariation.AllowSizeEquivalence, pVisited); } } @@ -723,7 +726,7 @@ namespace System.Runtime // deriving from user delegate classes any further all we have to check here is that the // uninstantiated generic delegate definitions are the same and the type parameters are // compatible. - return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); + return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType, pVisited); } // Is the source type derived from the target type? @@ -734,7 +737,7 @@ namespace System.Runtime } [RuntimeExport("RhTypeCast_CheckCastInterface")] - public static unsafe object CheckCastInterface(Object obj, void* pvTargetEEType) + public static unsafe object CheckCastInterface(object obj, void* pvTargetEEType) { // a null value can be cast to anything if (obj == null) @@ -745,7 +748,7 @@ namespace System.Runtime EEType* pTargetType = (EEType*)pvTargetEEType; EEType* pObjType = obj.EEType; - if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType)) + if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType, null)) return obj; Exception castError = null; @@ -779,7 +782,7 @@ namespace System.Runtime Debug.Assert(array.EEType->IsArray, "first argument must be an array"); EEType* arrayElemType = array.EEType->RelatedParameterType; - if (CastCache.AreTypesAssignableInternal(obj.EEType, arrayElemType, AssignmentVariation.BoxedSource)) + if (CastCache.AreTypesAssignableInternal(obj.EEType, arrayElemType, AssignmentVariation.BoxedSource, null)) return; // If object type implements ICastable then there's one more way to check whether it implements @@ -842,7 +845,7 @@ namespace System.Runtime { EEType* arrayElemType = array.EEType->RelatedParameterType; - if (!CastCache.AreTypesAssignableInternal(obj.EEType, arrayElemType, AssignmentVariation.BoxedSource)) + if (!CastCache.AreTypesAssignableInternal(obj.EEType, arrayElemType, AssignmentVariation.BoxedSource, null)) { // If object type implements ICastable then there's one more way to check whether it implements // the interface. @@ -858,7 +861,7 @@ namespace System.Runtime // Both bounds and type check are ok. // Call write barrier directly. Assigning object reference would call slower checked write barrier. - ref Object rawData = ref Unsafe.As<byte, Object>(ref array.GetRawSzArrayData()); + ref object rawData = ref Unsafe.As<byte, object>(ref array.GetRawSzArrayData()); InternalCalls.RhpAssignRef(ref Unsafe.Add(ref rawData, index), obj); } else @@ -870,7 +873,7 @@ namespace System.Runtime } [RuntimeExport("RhpLdelemaRef")] - public static unsafe ref Object LdelemaRef(Array array, int index, IntPtr elementType) + public static unsafe ref object LdelemaRef(Array array, int index, IntPtr elementType) { Debug.Assert(array.EEType->IsArray, "first argument must be an array"); @@ -885,7 +888,7 @@ namespace System.Runtime throw array.EEType->GetClasslibException(ExceptionIDs.ArrayTypeMismatch); } - ref Object rawData = ref Unsafe.As<byte, Object>(ref array.GetRawSzArrayData()); + ref object rawData = ref Unsafe.As<byte, object>(ref array.GetRawSzArrayData()); return ref Unsafe.Add(ref rawData, index); } @@ -976,13 +979,13 @@ namespace System.Runtime } [RuntimeExport("RhTypeCast_CheckCast2")] // Helper with RyuJIT calling convention - public static unsafe object CheckCast2(void* pvTargetType, Object obj) + public static unsafe object CheckCast2(void* pvTargetType, object obj) { return CheckCast(obj, pvTargetType); } [RuntimeExport("RhTypeCast_CheckCast")] - public static unsafe object CheckCast(Object obj, void* pvTargetType) + public static unsafe object CheckCast(object obj, void* pvTargetType) { // @TODO: consider using the cache directly, but beware of ICastable in the interface case EEType* pTargetType = (EEType*)pvTargetType; @@ -1034,6 +1037,33 @@ namespace System.Runtime } } + internal unsafe struct EETypePairList + { + private EEType* _eetype1; + private EEType* _eetype2; + private EETypePairList* _next; + + public EETypePairList(EEType* pEEType1, EEType* pEEType2, EETypePairList* pNext) + { + _eetype1 = pEEType1; + _eetype2 = pEEType2; + _next = pNext; + } + + public static bool Exists(EETypePairList* pList, EEType* pEEType1, EEType* pEEType2) + { + while (pList != null) + { + if (pList->_eetype1 == pEEType1 && pList->_eetype2 == pEEType2) + return true; + if (pList->_eetype1 == pEEType2 && pList->_eetype2 == pEEType1) + return true; + pList = pList->_next; + } + return false; + } + } + // source type + target type + assignment variation -> true/false [System.Runtime.CompilerServices.EagerStaticClassConstructionAttribute] private static class CastCache @@ -1103,7 +1133,7 @@ namespace System.Runtime public EEType* TargetType { get { return (EEType*)_targetType; } } } - public static unsafe bool AreTypesAssignableInternal(EEType* pSourceType, EEType* pTargetType, AssignmentVariation variation) + public static unsafe bool AreTypesAssignableInternal(EEType* pSourceType, EEType* pTargetType, AssignmentVariation variation, EETypePairList* pVisited) { // Important special case -- it breaks infinite recursion in CastCache itself! if (pSourceType == pTargetType) @@ -1112,7 +1142,7 @@ namespace System.Runtime Key key = new Key(pSourceType, pTargetType, variation); Entry entry = LookupInCache(s_cache, ref key); if (entry == null) - return CacheMiss(ref key); + return CacheMiss(ref key, pVisited); return entry.Result; } @@ -1124,13 +1154,13 @@ namespace System.Runtime // 2. Force inlining (This particular variant is only used in a small number of dispatch scenarios that are particularly // high in performance impact.) [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool AreTypesAssignableInternal_SourceNotTarget_BoxedSource(EEType* pSourceType, EEType* pTargetType) + public static unsafe bool AreTypesAssignableInternal_SourceNotTarget_BoxedSource(EEType* pSourceType, EEType* pTargetType, EETypePairList* pVisited) { Debug.Assert(pSourceType != pTargetType, "target is source"); Key key = new Key(pSourceType, pTargetType, AssignmentVariation.BoxedSource); Entry entry = LookupInCache(s_cache, ref key); if (entry == null) - return CacheMiss(ref key); + return CacheMiss(ref key, pVisited); return entry.Result; } @@ -1149,8 +1179,14 @@ namespace System.Runtime return entry; } - private static unsafe bool CacheMiss(ref Key key) + private static unsafe bool CacheMiss(ref Key key, EETypePairList* pVisited) { + // + // First, check if we previously visited the input types pair, to avoid infinite recursions + // + if (EETypePairList.Exists(pVisited, key.SourceType, key.TargetType)) + return false; + bool result = false; bool previouslyCached = false; @@ -1177,7 +1213,8 @@ namespace System.Runtime // if (!previouslyCached) { - result = TypeCast.AreTypesAssignableInternal(key.SourceType, key.TargetType, key.Variation); + EETypePairList newList = new EETypePairList(key.SourceType, key.TargetType, pVisited); + result = TypeCast.AreTypesAssignableInternal(key.SourceType, key.TargetType, key.Variation, &newList); } // |