|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
Imports System
Imports System.Reflection
Imports System.Diagnostics
Imports System.Collections.Generic
Imports Microsoft.VisualBasic.CompilerServices.Symbols
Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution.OperatorCaches
Imports System.Diagnostics.CodeAnalysis
Namespace Microsoft.VisualBasic.CompilerServices
' Implements VB conversion semantics.
Friend NotInheritable Class ConversionResolution
' Prevent creation.
Private Sub New()
End Sub
Friend Enum ConversionClass As SByte
Bad
Identity
[Widening]
[Narrowing]
None
Ambiguous
End Enum
Private Shared ReadOnly s_conversionTable As ConversionClass()()
Friend Shared ReadOnly NumericSpecificityRank As Integer()
Friend Shared ReadOnly ForLoopWidestTypeCode As TypeCode()()
Shared Sub New()
Const max As Integer = TypeCode.String
Const bad_ As ConversionClass = ConversionClass.Bad
Const iden As ConversionClass = ConversionClass.Identity
Const wide As ConversionClass = ConversionClass.Widening
Const narr As ConversionClass = ConversionClass.Narrowing
Const none As ConversionClass = ConversionClass.None
'Columns represent Source type, Rows represent Target type.
' empty obj dbnul bool char sbyte byte short ushrt int uint lng ulng sng dbl dec date str
s_conversionTable = New ConversionClass(max)() _
{
New ConversionClass(max) {bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_},
New ConversionClass(max) {bad_, iden, bad_, wide, wide, wide, wide, wide, wide, wide, wide, wide, wide, wide, wide, wide, wide, bad_, wide},
New ConversionClass(max) {bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_},
New ConversionClass(max) {bad_, narr, bad_, iden, none, narr, narr, narr, narr, narr, narr, narr, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, none, iden, none, none, none, none, none, none, none, none, none, none, none, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, iden, narr, narr, narr, narr, narr, narr, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, narr, iden, narr, narr, narr, narr, narr, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, wide, wide, iden, narr, narr, narr, narr, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, narr, wide, narr, iden, narr, narr, narr, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, wide, wide, wide, wide, iden, narr, narr, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, narr, wide, narr, wide, narr, iden, narr, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, wide, wide, wide, wide, wide, wide, iden, narr, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, narr, wide, narr, wide, narr, wide, narr, iden, narr, narr, narr, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, wide, wide, wide, wide, wide, wide, wide, wide, iden, narr, wide, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, wide, wide, wide, wide, wide, wide, wide, wide, wide, iden, wide, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, narr, none, wide, wide, wide, wide, wide, wide, wide, wide, narr, narr, iden, none, bad_, narr},
New ConversionClass(max) {bad_, narr, bad_, none, none, none, none, none, none, none, none, none, none, none, none, none, iden, bad_, narr},
New ConversionClass(max) {bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_, bad_},
New ConversionClass(max) {bad_, narr, bad_, narr, wide, narr, narr, narr, narr, narr, narr, narr, narr, narr, narr, narr, narr, bad_, iden}
}
'This table is the relative ordering of the specificity of types. It is used during
'overload resolution to answer the question: 'Of numeric types a and b, which is more specific?'.
'
'The general rules encoded in this table are:
' Smaller types are more specific than larger types.
' Signed types are more specific than unsigned types of equal or greater widths,
' with the exception of Byte which is more specific than SByte (for backwards compatibility).
NumericSpecificityRank = New Integer(max) {}
NumericSpecificityRank(TypeCode.Byte) = 1
NumericSpecificityRank(TypeCode.SByte) = 2
NumericSpecificityRank(TypeCode.Int16) = 3
NumericSpecificityRank(TypeCode.UInt16) = 4
NumericSpecificityRank(TypeCode.Int32) = 5
NumericSpecificityRank(TypeCode.UInt32) = 6
NumericSpecificityRank(TypeCode.Int64) = 7
NumericSpecificityRank(TypeCode.UInt64) = 8
NumericSpecificityRank(TypeCode.Decimal) = 9
NumericSpecificityRank(TypeCode.Single) = 10
NumericSpecificityRank(TypeCode.Double) = 11
' This table specifies the "widest" type to be used in For Loops
' It should match the results of the Add Operator.
ForLoopWidestTypeCode = New TypeCode(max)() _
{
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int16, TypeCode.Empty, TypeCode.SByte, TypeCode.Int16, TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.SByte, TypeCode.Empty, TypeCode.SByte, TypeCode.Int16, TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int16, TypeCode.Empty, TypeCode.Int16, TypeCode.Byte, TypeCode.Int16, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int16, TypeCode.Empty, TypeCode.Int16, TypeCode.Int16, TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int32, TypeCode.Empty, TypeCode.Int32, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int32, TypeCode.Empty, TypeCode.Int32, TypeCode.Int32, TypeCode.Int32, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int64, TypeCode.Empty, TypeCode.Int64, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Int64, TypeCode.Empty, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Int64, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Decimal, TypeCode.Empty, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Decimal, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Single, TypeCode.Empty, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Single, TypeCode.Double, TypeCode.Single, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Double, TypeCode.Empty, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Double, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Decimal, TypeCode.Empty, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Decimal, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty},
New TypeCode(max) {TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty, TypeCode.Empty}
}
VerifyTypeCodeEnum()
#If DEBUG Then
VerifyForLoopWidestType()
#End If
End Sub
#If DEBUG Then
<System.Diagnostics.ConditionalAttribute("DEBUG")>
<UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification:="This method only gets called in DEBUG mode.")>
Private Shared Sub VerifyForLoopWidestType()
Const Max As Integer = TypeCode.String
For Index1 As Integer = 0 To Max
Dim tc1 As TypeCode = CType(Index1, TypeCode)
If IsNumericType(tc1) Then
For Index2 As Integer = 0 To Max
Dim tc2 As TypeCode = CType(Index2, TypeCode)
If IsNumericType(tc2) Then
Dim tc As TypeCode = ForLoopWidestTypeCode(tc1)(tc2)
Dim Type1 As Type = MapTypeCodeToType(tc1)
Dim Type2 As Type = MapTypeCodeToType(tc2)
Dim o1 As Object = 0
Dim o2 As Object = 0
o1 = Convert.ChangeType(o1, Type1)
o2 = Convert.ChangeType(o2, Type2)
Dim Result As Object = Operators.AddObject(o1, o2)
Debug.Assert(GetTypeCode(Result.GetType()) = tc, "Widest type is invalid")
End If
Next
End If
Next
End Sub
#End If
<System.Diagnostics.ConditionalAttribute("DEBUG")>
Private Shared Sub VerifyTypeCodeEnum()
Debug.Assert(TypeCode.Empty = 0, "wrong value!")
Debug.Assert(TypeCode.Object = 1, "wrong value!")
Debug.Assert(TypeCode.Boolean = 3, "yte is wrong value!")
Debug.Assert(TypeCode.Char = 4, "wrong value!")
Debug.Assert(TypeCode.SByte = 5, "wrong value!")
Debug.Assert(TypeCode.Byte = 6, "wrong value!")
Debug.Assert(TypeCode.Int16 = 7, "wrong value!")
Debug.Assert(TypeCode.UInt16 = 8, "wrong value!")
Debug.Assert(TypeCode.Int32 = 9, "wrong value!")
Debug.Assert(TypeCode.UInt32 = 10, "wrong value!")
Debug.Assert(TypeCode.Int64 = 11, "wrong value!")
Debug.Assert(TypeCode.UInt64 = 12, "wrong value!")
Debug.Assert(TypeCode.Single = 13, "wrong value!")
Debug.Assert(TypeCode.Double = 14, "wrong value!")
Debug.Assert(TypeCode.Decimal = 15, "wrong value!")
Debug.Assert(TypeCode.DateTime = 16, "wrong value!")
Debug.Assert(TypeCode.String = 18, "wrong value!")
End Sub
<RequiresUnreferencedCode("Calls ClassifyUserDefinedConversion and ClassifyPredefinedConversion")>
Friend Shared Function ClassifyConversion(ByVal targetType As System.Type, ByVal sourceType As System.Type, ByRef operatorMethod As Method) As ConversionClass
'This function classifies the nature of the conversion from the source type to the target
'type. If such a conversion requires a user-defined conversion, it will be supplied as an
'out parameter.
Debug.Assert(Not targetType.IsByRef AndAlso Not sourceType.IsByRef, "ByRef types unexpected.")
Dim result As ConversionClass = ClassifyPredefinedConversion(targetType, sourceType)
If result = ConversionClass.None AndAlso
Not IsInterface(sourceType) AndAlso
Not IsInterface(targetType) AndAlso
(IsClassOrValueType(sourceType) OrElse IsClassOrValueType(targetType)) AndAlso
Not (IsIntrinsicType(sourceType) AndAlso IsIntrinsicType(targetType)) Then
result = ClassifyUserDefinedConversion(targetType, sourceType, operatorMethod)
End If
Return result
End Function
Friend Shared Function ClassifyIntrinsicConversion(ByVal targetTypeCode As TypeCode, ByVal sourceTypeCode As TypeCode) As ConversionClass
Debug.Assert(IsIntrinsicType(targetTypeCode) AndAlso IsIntrinsicType(sourceTypeCode), "expected intrinsics here")
Return s_conversionTable(targetTypeCode)(sourceTypeCode)
End Function
<RequiresUnreferencedCode("Calls GetInterfaceConstraints but does so recursively on various types")>
Friend Shared Function ClassifyPredefinedCLRConversion(ByVal targetType As System.Type, ByVal sourceType As System.Type) As ConversionClass
' This function classifies all intrinsic CLR conversions, such as inheritance,
' implementation, and array covariance.
Debug.Assert(Not targetType.IsByRef AndAlso Not sourceType.IsByRef, "ByRef types unexpected.")
' Could we use IsAssignableFrom to cut out a number of these checks (probably the widening ones)?
' *IDENTITY*
If targetType Is sourceType Then Return ConversionClass.Identity
' *INHERITANCE*
If IsRootObjectType(targetType) OrElse IsOrInheritsFrom(sourceType, targetType) Then
Return ConversionClass.Widening
End If
If IsRootObjectType(sourceType) OrElse IsOrInheritsFrom(targetType, sourceType) Then
Return ConversionClass.Narrowing
End If
' *INTERFACE IMPLEMENTATION*
If IsInterface(sourceType) Then
If IsClass(targetType) OrElse IsArrayType(targetType) OrElse IsGenericParameter(targetType) Then
' Even if a class is marked NotInheritable, it can still be a COM class and implement
' any interface dynamically at runtime, so we must allow a narrowing conversion.
Return ConversionClass.Narrowing
End If
If IsInterface(targetType) Then
Return ConversionClass.Narrowing
End If
If IsValueType(targetType) Then
If [Implements](targetType, sourceType) Then
Return ConversionClass.Narrowing
Else
Return ConversionClass.None
End If
End If
Debug.Assert(False, "all conversions from interface should have been handled by now")
Return ConversionClass.Narrowing
End If
If IsInterface(targetType) Then
If (IsArrayType(sourceType)) Then
Return ClassifyCLRArrayToInterfaceConversion(targetType, sourceType)
End If
If IsValueType(sourceType) Then
If [Implements](sourceType, targetType) Then
Return ConversionClass.Widening
Else
Return ConversionClass.None
End If
End If
If IsClass(sourceType) Then
If [Implements](sourceType, targetType) Then
Return ConversionClass.Widening
Else
Return ConversionClass.Narrowing
End If
End If
'generic params are handled later
End If
' *ENUMERATION*
If IsEnum(sourceType) OrElse IsEnum(targetType) Then
If GetTypeCode(sourceType) = GetTypeCode(targetType) Then
If IsEnum(targetType) Then
Return ConversionClass.Narrowing
Else
Return ConversionClass.Widening
End If
End If
Return ConversionClass.None
End If
' *GENERIC PARAMETERS*
If IsGenericParameter(sourceType) Then
If Not IsClassOrInterface(targetType) Then
Return ConversionClass.None
End If
'Return the best conversion from any constraint type to the target type.
For Each interfaceConstraint As Type In GetInterfaceConstraints(sourceType)
Dim classification As ConversionClass =
ClassifyPredefinedConversion(targetType, interfaceConstraint)
If classification = ConversionClass.Widening OrElse
classification = ConversionClass.Identity Then
'A conversion from a constraint type cannot be an identity conversion
'(because a conversion operation is necessary in the generated code),
'so don't allow it to look any better than Widening.
Return ConversionClass.Widening
End If
Next
Dim classConstraint As Type = GetClassConstraint(sourceType)
If classConstraint IsNot Nothing Then
Dim classification As ConversionClass =
ClassifyPredefinedConversion(targetType, classConstraint)
If classification = ConversionClass.Widening OrElse
classification = ConversionClass.Identity Then
'A conversion from a constraint type cannot be an identity conversion
'(because a conversion operation is necessary in the generated code),
'so don't allow it to look any better than Widening.
Return ConversionClass.Widening
End If
End If
Return IIf(IsInterface(targetType), ConversionClass.Narrowing, ConversionClass.None)
End If
If IsGenericParameter(targetType) Then
Debug.Assert(Not IsInterface(sourceType),
"conversions from interfaces should have been handled by now")
'If one of the constraint types is a class type, a narrowing conversion exists from that class type.
Dim classConstraint As Type = GetClassConstraint(targetType)
If classConstraint IsNot Nothing AndAlso IsOrInheritsFrom(classConstraint, sourceType) Then
Return ConversionClass.Narrowing
End If
Return ConversionClass.None
End If
' *ARRAY COVARIANCE*
If IsArrayType(sourceType) AndAlso IsArrayType(targetType) Then
If sourceType.GetArrayRank = targetType.GetArrayRank Then
' The element types must either be the same or
' the source element type must extend or implement the
' target element type. (VB implements array covariance.)
Return ClassifyCLRConversionForArrayElementTypes(targetType.GetElementType, sourceType.GetElementType)
End If
Return ConversionClass.None
End If
Return ConversionClass.None
End Function
<RequiresUnreferencedCode("Calls ClassifyPredefinedCLRConversion")>
Private Shared Function ClassifyCLRArrayToInterfaceConversion(ByVal targetInterface As System.Type, ByVal sourceArrayType As System.Type) As ConversionClass
Debug.Assert(IsInterface(targetInterface), "Non-Interface type unexpected!!!")
Debug.Assert(IsArrayType(sourceArrayType), "Non-Array type unexpected!!!")
' No need to get to System.Array, [Implements] works for arrays with respect to the interfaces on System.Array
'
If ([Implements](sourceArrayType, targetInterface)) Then
Return ConversionClass.Widening
End If
' Multi-dimensional arrays do not support IList<T>
'
If (sourceArrayType.GetArrayRank > 1) Then
Return ConversionClass.Narrowing
End If
' Check for the conversion from the Array of element type T to
' 1. IList(Of T) - Widening
' 2. Some interface that IList(Of T) inherits from - Widening
' 3. IList(Of SomeType that T inherits from) - Widening
' yes, generics covariance is allowed in the array case
' 4. Some interface that IList(Of SomeType that T inherits from)
' inherits from - Widening
' 5. Some interface that inherits from IList(Of T) - Narrowing
' 6. Some interface that inherits from IList(Of SomeType that T inherits from)
' - Narrowing
'
' 5 and 6 are not checked for explicitly since from array to interface that
' the array does not widen to, we anyway return narrowing.
'
Dim sourceElementType As Type = sourceArrayType.GetElementType
Dim conversion As ConversionClass = ConversionClass.None
If (targetInterface.IsGenericType AndAlso Not targetInterface.IsGenericTypeDefinition) Then
Dim rawTargetInterface As Type = targetInterface.GetGenericTypeDefinition()
If (rawTargetInterface Is GetType(System.Collections.Generic.IList(Of )) OrElse
rawTargetInterface Is GetType(System.Collections.Generic.ICollection(Of )) OrElse
rawTargetInterface Is GetType(System.Collections.Generic.IEnumerable(Of ))) Then
conversion =
ClassifyCLRConversionForArrayElementTypes(
targetInterface.GetGenericArguments()(0),
sourceElementType)
End If
Else
conversion =
ClassifyPredefinedCLRConversion(
targetInterface,
GetType(System.Collections.Generic.IList(Of )).MakeGenericType(New Type() {sourceElementType}))
End If
If (conversion = ConversionClass.Identity OrElse
conversion = ConversionClass.Widening) Then
Return ConversionClass.Widening
End If
Return ConversionClass.Narrowing
End Function
<RequiresUnreferencedCode("Calls ClassifyPredefinedCLRConversion")>
Private Shared Function ClassifyCLRConversionForArrayElementTypes(ByVal targetElementType As System.Type, ByVal sourceElementType As System.Type) As ConversionClass
' The element types must either be the same or
' the source element type must extend or implement the
' target element type. (VB implements array covariance.)
' Generic params are handled correctly here.
If IsReferenceType(sourceElementType) AndAlso
IsReferenceType(targetElementType) Then
Return ClassifyPredefinedCLRConversion(targetElementType, sourceElementType)
End If
If IsValueType(sourceElementType) AndAlso
IsValueType(targetElementType) Then
Return ClassifyPredefinedCLRConversion(targetElementType, sourceElementType)
End If
' Bug VSWhidbey 369131.
' Array co-variance and back-casting special case for generic parameters.
'
If IsGenericParameter(sourceElementType) AndAlso
IsGenericParameter(targetElementType) Then
If sourceElementType Is targetElementType Then
Return ConversionClass.Identity
End If
If IsReferenceType(sourceElementType) AndAlso
IsOrInheritsFrom(sourceElementType, targetElementType) Then
Return ConversionClass.Widening
End If
If IsReferenceType(targetElementType) AndAlso
IsOrInheritsFrom(targetElementType, sourceElementType) Then
Return ConversionClass.Narrowing
End If
End If
Return ConversionClass.None
End Function
<RequiresUnreferencedCode("Calls ClassifyPredefinedCLRConversion")>
Friend Shared Function ClassifyPredefinedConversion(ByVal targetType As System.Type, ByVal sourceType As System.Type) As ConversionClass
' This function classifies all intrinsic language conversions, such as inheritance,
' implementation, array covariance, and conversions between intrinsic types.
Debug.Assert(Not targetType.IsByRef AndAlso Not sourceType.IsByRef, "ByRef types unexpected.")
' Make an easy reference comparison for a common case. More complicated type comparisons will happen later.
If targetType Is sourceType Then Return ConversionClass.Identity
Dim sourceTypeCode As TypeCode = GetTypeCode(sourceType)
Dim targetTypeCode As TypeCode = GetTypeCode(targetType)
If (IsIntrinsicType(sourceTypeCode) AndAlso IsIntrinsicType(targetTypeCode)) Then
If IsEnum(targetType) Then
If IsIntegralType(sourceTypeCode) AndAlso IsIntegralType(targetTypeCode) Then
' Conversion from an integral type (including an Enum type)
' to an Enum type (that has an integral underlying type)
' is narrowing. Enums do not necessarily have integral underlying types.
Return ConversionClass.Narrowing
End If
End If
If sourceTypeCode = targetTypeCode AndAlso IsEnum(sourceType) Then
' Conversion from an Enum to it's underlying type is widening.
' If we used ClassifyIntrinsicConversion, this kind of conversion
' would be classified as identity, and that would not be good.
' Catch this case here.
Return ConversionClass.Widening
End If
Return ClassifyIntrinsicConversion(targetTypeCode, sourceTypeCode)
End If
' Try VB specific conversions from String-->Char() or Char()-->String.
If IsCharArrayRankOne(sourceType) AndAlso IsStringType(targetType) Then
' Array of Char widens to String.
Return ConversionClass.Widening
End If
If IsCharArrayRankOne(targetType) AndAlso IsStringType(sourceType) Then
' String narrows to array of Char.
Return ConversionClass.Narrowing
End If
Return ClassifyPredefinedCLRConversion(targetType, sourceType)
End Function
<RequiresUnreferencedCode("Calls Operators.CollectOperators")>
Private Shared Function CollectConversionOperators(
ByVal targetType As System.Type,
ByVal sourceType As System.Type,
ByRef foundTargetTypeOperators As Boolean,
ByRef foundSourceTypeOperators As Boolean) As List(Of Method)
'Find all Widening and Narrowing conversion operators. Combine the lists
'with the Widening operators grouped at the front.
'From the perspective of VB, intrinsic types have no conversion operators.
'Substitute in Object for these types.
If IsIntrinsicType(targetType) Then targetType = GetType(Object)
If IsIntrinsicType(sourceType) Then sourceType = GetType(Object)
Dim result As List(Of Method) =
Operators.CollectOperators(
UserDefinedOperator.Widen,
targetType,
sourceType,
foundTargetTypeOperators,
foundSourceTypeOperators)
Dim narrowingOperators As List(Of Method) =
Operators.CollectOperators(
UserDefinedOperator.Narrow,
targetType,
sourceType,
foundTargetTypeOperators,
foundSourceTypeOperators)
result.AddRange(narrowingOperators)
Return result
End Function
<RequiresUnreferencedCode("Calls ClassifyPredefinedConversion")>
Private Shared Function Encompasses(ByVal larger As System.Type, ByVal smaller As System.Type) As Boolean
'Definition: LARGER is said to encompass SMALLER if SMALLER widens to or is LARGER.
'Since determining encompasses is quite commonly used,
'and only depends on widening or identity, a special function for classifying
'just predefined widening conversions could be a performance gain.
Dim result As ConversionClass = ClassifyPredefinedConversion(larger, smaller)
Return result = ConversionClass.Widening OrElse result = ConversionClass.Identity
End Function
<RequiresUnreferencedCode("Calls ClassifyPredefinedConversion")>
Private Shared Function NotEncompasses(ByVal larger As System.Type, ByVal smaller As System.Type) As Boolean
'Definition: LARGER is said to not encompass SMALLER if SMALLER narrows to or is LARGER.
'Since determining encompasses is quite commonly used,
'and only depends on widening or identity, a special function for classifying
'just predefined narrowing conversions could be a performance gain.
Dim result As ConversionClass = ClassifyPredefinedConversion(larger, smaller)
Return result = ConversionClass.Narrowing OrElse result = ConversionClass.Identity
End Function
<RequiresUnreferencedCode("Calls Encompasses")>
Private Shared Function MostEncompassing(ByVal types As List(Of System.Type)) As System.Type
'Given a set TYPES, determine the most encompassing type. An element
'CANDIDATE of TYPES is said to be most encompassing if no other element of
'TYPES encompasses CANDIDATE.
Debug.Assert(types.Count > 0, "unexpected empty set")
Dim maxEncompassing As System.Type = types.Item(0)
For index As Integer = 1 To types.Count - 1
Dim candidate As Type = types.Item(index)
If Encompasses(candidate, maxEncompassing) Then
Debug.Assert(candidate Is maxEncompassing OrElse Not Encompasses(maxEncompassing, candidate),
"surprisingly, two types encompass each other")
maxEncompassing = candidate
ElseIf Not Encompasses(maxEncompassing, candidate) Then
'We have detected more than one most encompassing type in the set.
'Return Nothing to indicate this error condition.
Return Nothing
End If
Next
Return maxEncompassing
End Function
<RequiresUnreferencedCode("Calls Encompasses")>
Private Shared Function MostEncompassed(ByVal types As List(Of System.Type)) As System.Type
'Given a set TYPES, determine the most encompassed type. An element
'CANDIDATE of TYPES is said to be most encompassed if CANDIDATE encompasses
'no other element of TYPES.
Debug.Assert(types.Count > 0, "unexpected empty set")
Dim maxEncompassed As System.Type = types.Item(0)
For index As Integer = 1 To types.Count - 1
Dim candidate As Type = types.Item(index)
If Encompasses(maxEncompassed, candidate) Then
Debug.Assert(candidate Is maxEncompassed OrElse Not Encompasses(candidate, maxEncompassed),
"surprisingly, two types encompass each other")
maxEncompassed = candidate
ElseIf Not Encompasses(candidate, maxEncompassed) Then
'We have detected more than one most encompassed type in the set.
'Return Nothing to indicate this error condition.
Return Nothing
End If
Next
Return maxEncompassed
End Function
Private Shared Sub FindBestMatch(
ByVal targetType As Type,
ByVal sourceType As Type,
ByVal searchList As List(Of Method),
ByVal resultList As List(Of Method),
ByRef genericMembersExistInList As Boolean)
'Given a set of conversion operators which convert from INPUT to RESULT, return the set
'of operators for which INPUT is SOURCE and RESULT is TARGET.
For Each item As Method In searchList
Dim current As MethodBase = item.AsMethod
Dim inputType As System.Type = current.GetParameters(0).ParameterType
Dim resultType As System.Type = DirectCast(current, MethodInfo).ReturnType
If inputType Is sourceType AndAlso resultType Is targetType Then
InsertInOperatorListIfLessGenericThanExisting(item, resultList, genericMembersExistInList)
End If
Next
Return
End Sub
Private Shared Sub InsertInOperatorListIfLessGenericThanExisting(
ByVal operatorToInsert As Method,
ByVal operatorList As List(Of Method),
ByRef genericMembersExistInList As Boolean)
If IsGeneric(operatorToInsert.DeclaringType) Then
genericMembersExistInList = True
End If
If genericMembersExistInList Then
For i As Integer = operatorList.Count - 1 To 0 Step -1
Dim existing As Method = operatorList.Item(i)
Dim leastGeneric As Method = OverloadResolution.LeastGenericProcedure(existing, operatorToInsert)
If leastGeneric Is existing Then
' An existing one is less generic than the current operator being
' considered, so skip adding the current operator to the operator
' list.
Return
ElseIf leastGeneric IsNot Nothing Then
' The current operator is less generic than an existing operator,
' so remove the existing operator from the list and continue to
' check if any other existing operator can be removed from the
' result set.
'
operatorList.Remove(existing)
End If
Next
End If
operatorList.Add(operatorToInsert)
End Sub
<RequiresUnreferencedCode("Calls ClassifyPredefinedConversion")>
Private Shared Function ResolveConversion(
ByVal targetType As System.Type,
ByVal sourceType As System.Type,
ByVal operatorSet As List(Of Method),
ByVal wideningOnly As Boolean,
ByRef resolutionIsAmbiguous As Boolean) As List(Of Method)
'This function resolves which user-defined conversion operator contained in the input set
'can be used to perform the conversion from source type S to target type T.
'
'The algorithm defies succinct explanation, but roughly:
'
'Conversions of the form S-->T use only one user-defined conversion at a time, i.e.,
'user-defined conversions are not chained together. It may be necessary to convert to and
'from intermediate types using predefined conversions to match the signature of the
'user-defined conversion exactly, so the conversion "path" is comprised of at most three
'parts:
'
' 1) [ predefined conversion S-->Sx ]
' 2) User-defined conversion Sx-->Tx
' 3) [ predefined conversion Tx-->T ]
'
' Where Sx is the intermediate source type
' and Tx is the intermediate target type
'
' Steps 1 and 3 are optional given S == Sx or Tx == T.
'
'Much of the algorithm below concerns itself with finding Sx and Tx. The rules are:
'
' - If a conversion operator in the set converts from S, then Sx is S.
' - If a conversion operator in the set converts to T, then Tx is T.
' - Otherwise Sx and Tx are the "closest" types to S and T. If multiple types are
' equally close, the conversion is ambiguous.
'
'Each operator presents a possibility for Sx (the parameter type of the operator). Given
'these choices, the "closest" type to S is the smallest (most encompassed) type that S
'widens to. If S widens to none of the possible types, then the "closest" type to S is
'the largest (most encompassing) type that widens to S. In this way, the algorithm
'always prefers widening from S over narrowing from S.
'
'Similarly, each operator presents a possibility for Tx (the return type of the operator).
'Given these choices, the "closest" type to T is the largest (most encompassing) type that
'widens to T. If none of the possible types widen to T, then the "closest" type to T is
'the smallest (most encompassed) type that T widens to. In this way, the algorithm
'always prefers widening to T over narrowing to T.
'
'Upon deciding Sx and Tx, if one operator's operands exactly match Sx and Tx, then that
'operator is chosen. If no operators match, or if multiple operators match, the conversion
'is impossible.
'
'Refer to the language specification as it covers all details of the algorithm.
resolutionIsAmbiguous = False
Dim mostSpecificSourceType As System.Type = Nothing
Dim mostSpecificTargetType As System.Type = Nothing
Dim genericOperatorChoicesFound As Boolean = False
Dim operatorChoices As List(Of Method) = New List(Of Method)(operatorSet.Count)
Dim candidates As List(Of Method) = New List(Of Method)(operatorSet.Count)
Dim sourceBases As List(Of Type) = New List(Of Type)(operatorSet.Count)
Dim targetDeriveds As List(Of Type) = New List(Of Type)(operatorSet.Count)
Dim sourceDeriveds As List(Of Type) = Nothing
Dim targetBases As List(Of Type) = Nothing
If Not wideningOnly Then
sourceDeriveds = New List(Of Type)(operatorSet.Count)
targetBases = New List(Of Type)(operatorSet.Count)
End If
'To minimize the number of calls to Encompasses, we categorize conversions
'into three flavors:
'
' 1) Base of Source to Derived of Target (only flavor that can be completely widening)
' 2) Base of Source to Base of Target
' 3) Derived of Source to Base of Target
'
'For each flavor, we place the input and result type into the corresponding
'type set. Then we calculate most encompassing/encompassed using the type sets.
For Each currentMethod As Method In operatorSet
Dim current As MethodBase = currentMethod.AsMethod
'Performance trick: the operators are grouped by widening and then narrowing
'conversions. If we are iterating over just widening conversions, we are done
'once we find a narrowing conversion.
If wideningOnly AndAlso IsNarrowingConversionOperator(current) Then Exit For
Dim inputType As System.Type = current.GetParameters(0).ParameterType
Dim resultType As System.Type = DirectCast(current, MethodInfo).ReturnType
If (IsGeneric(current) OrElse
IsGeneric(current.DeclaringType)) AndAlso
ClassifyPredefinedConversion(resultType, inputType) <> ConversionClass.None Then
Continue For
End If
If inputType Is sourceType AndAlso resultType Is targetType Then
InsertInOperatorListIfLessGenericThanExisting(currentMethod, operatorChoices, genericOperatorChoicesFound)
ElseIf operatorChoices.Count = 0 Then
If Encompasses(inputType, sourceType) AndAlso Encompasses(targetType, resultType) Then
'Check SourceBase->TargetDerived flavor.
candidates.Add(currentMethod)
If inputType Is sourceType Then mostSpecificSourceType = inputType Else sourceBases.Add(inputType)
If resultType Is targetType Then mostSpecificTargetType = resultType Else targetDeriveds.Add(resultType)
ElseIf Not wideningOnly AndAlso
Encompasses(inputType, sourceType) AndAlso NotEncompasses(targetType, resultType) Then
'Check SourceBase->TargetBase flavor.
candidates.Add(currentMethod)
If inputType Is sourceType Then mostSpecificSourceType = inputType Else sourceBases.Add(inputType)
If resultType Is targetType Then mostSpecificTargetType = resultType Else targetBases.Add(resultType)
ElseIf Not wideningOnly AndAlso
NotEncompasses(inputType, sourceType) AndAlso NotEncompasses(targetType, resultType) Then
'Check SourceDerived->TargetBase flavor.
candidates.Add(currentMethod)
If inputType Is sourceType Then mostSpecificSourceType = inputType Else sourceDeriveds.Add(inputType)
If resultType Is targetType Then mostSpecificTargetType = resultType Else targetBases.Add(resultType)
End If
End If
Next
'Now attempt to find the most specific types Sx and Tx by analyzing the type sets
'we built up in the code above.
If operatorChoices.Count = 0 AndAlso candidates.Count > 0 Then
If mostSpecificSourceType Is Nothing Then
If sourceBases.Count > 0 Then
mostSpecificSourceType = MostEncompassed(sourceBases)
Else
Debug.Assert(Not wideningOnly AndAlso sourceDeriveds.Count > 0, "unexpected state")
mostSpecificSourceType = MostEncompassing(sourceDeriveds)
End If
End If
If mostSpecificTargetType Is Nothing Then
If targetDeriveds.Count > 0 Then
mostSpecificTargetType = MostEncompassing(targetDeriveds)
Else
Debug.Assert(Not wideningOnly AndAlso targetBases.Count > 0, "unexpected state")
mostSpecificTargetType = MostEncompassed(targetBases)
End If
End If
If mostSpecificSourceType Is Nothing OrElse mostSpecificTargetType Is Nothing Then
resolutionIsAmbiguous = True
Return New List(Of Method)
End If
FindBestMatch(mostSpecificTargetType, mostSpecificSourceType, candidates, operatorChoices, genericOperatorChoicesFound)
End If
If operatorChoices.Count > 1 Then
resolutionIsAmbiguous = True
End If
Return operatorChoices
End Function
<RequiresUnreferencedCode("Calls DoClassifyUserDefinedConversion")>
Friend Shared Function ClassifyUserDefinedConversion(
ByVal targetType As System.Type,
ByVal sourceType As System.Type,
ByRef operatorMethod As Method) As ConversionClass
Dim result As ConversionClass
'Check if we have done this classification before.
SyncLock (ConversionCache)
'First check if both types have no user-defined conversion operators. If so, they cannot
'convert to each other with user-defined operators.
If UnconvertibleTypeCache.Lookup(targetType) AndAlso UnconvertibleTypeCache.Lookup(sourceType) Then
Return ConversionClass.None
End If
'Now check if we have recently resolved this conversion.
If ConversionCache.Lookup(targetType, sourceType, result, operatorMethod) Then
Return result
End If
End SyncLock
'Perform the expensive work to resolve the user-defined conversion.
Dim foundTargetTypeOperators As Boolean = False
Dim foundSourceTypeOperators As Boolean = False
result =
DoClassifyUserDefinedConversion(
targetType,
sourceType,
operatorMethod,
foundTargetTypeOperators,
foundSourceTypeOperators)
'Save away the results.
SyncLock (ConversionCache)
'Remember which types have no operators so we can avoid re-doing the work next time.
If Not foundTargetTypeOperators Then
UnconvertibleTypeCache.Insert(targetType)
End If
If Not foundSourceTypeOperators Then
UnconvertibleTypeCache.Insert(sourceType)
End If
If foundTargetTypeOperators OrElse foundSourceTypeOperators Then
'Cache the result of the resolution so we can avoid re-doing the work next time, but
'only when conversion operators were found (otherwise, the type caches will catch this
'the next time).
ConversionCache.Insert(targetType, sourceType, result, operatorMethod)
End If
End SyncLock
Return result
End Function
<RequiresUnreferencedCode("Calls CollectConversionOperators")>
Private Shared Function DoClassifyUserDefinedConversion(
ByVal targetType As System.Type,
ByVal sourceType As System.Type,
ByRef operatorMethod As Method,
ByRef foundTargetTypeOperators As Boolean,
ByRef foundSourceTypeOperators As Boolean) As ConversionClass
'Classifies the conversion from Source to Target using user-defined conversion operators.
'If such a conversion exists, it will be supplied as an out parameter.
'
'The result is a widening conversion from Source to Target if such a conversion exists.
'Otherwise the result is a narrowing conversion if such a conversion exists. Otherwise
'no conversion is possible. We perform this two pass process because the conversion
'"path" is not affected by the user implicitly or explicitly specifying the conversion.
'
'In other words, a safe (widening) conversion is always taken regardless of whether
'Option Strict is on or off.
Debug.Assert(ClassifyPredefinedConversion(targetType, sourceType) = ConversionClass.None,
"predefined conversion is possible, so why try user-defined?")
operatorMethod = Nothing
Dim operatorSet As List(Of Method) =
CollectConversionOperators(
targetType,
sourceType,
foundTargetTypeOperators,
foundSourceTypeOperators)
If operatorSet.Count = 0 Then
'No conversion operators, so no conversion is possible.
Return ConversionClass.None
End If
Dim resolutionIsAmbiguous As Boolean = False
Dim operatorChoices As List(Of Method) =
ResolveConversion(
targetType,
sourceType,
operatorSet,
True,
resolutionIsAmbiguous)
If operatorChoices.Count = 1 Then
operatorMethod = operatorChoices.Item(0)
operatorMethod.ArgumentsValidated = True
'The result from the first pass is necessarily widening.
Return ConversionClass.Widening
ElseIf operatorChoices.Count = 0 AndAlso Not resolutionIsAmbiguous Then
Debug.Assert(operatorSet.Count > 0, "expected operators")
'Second pass: if the first pass failed, attempt to find a conversion
'considering BOTH widening and narrowing.
operatorChoices =
ResolveConversion(
targetType,
sourceType,
operatorSet,
False,
resolutionIsAmbiguous)
If operatorChoices.Count = 1 Then
operatorMethod = operatorChoices.Item(0)
operatorMethod.ArgumentsValidated = True
'The result from the second pass is necessarily narrowing.
Return ConversionClass.Narrowing
ElseIf operatorChoices.Count = 0 Then
'No conversion possible.
Return ConversionClass.None
End If
End If
' If error reporting is improved for conversion resolution,
' create a useful error message here.
' Conversion is ambiguous.
Return ConversionClass.Ambiguous
End Function
Friend NotInheritable Class OperatorCaches
' Prevent creation.
Private Sub New()
End Sub
Friend NotInheritable Class FixedList
Private Structure Entry
Friend TargetType As Type
Friend SourceType As Type
Friend Classification As ConversionClass
Friend OperatorMethod As Method
Friend [Next] As Integer
Friend Previous As Integer
End Structure
Private ReadOnly _list As Entry()
Private ReadOnly _size As Integer
Private _first As Integer
Private _last As Integer
Private _count As Integer
Private Const s_defaultSize As Integer = 50
Friend Sub New()
MyClass.New(s_defaultSize)
End Sub
Friend Sub New(ByVal size As Integer)
'Populate the cache list with the maximum number of entries.
'This simplifies the insertion code for a small upfront cost.
_size = size
_list = New Entry(_size - 1) {}
For index As Integer = 0 To _size - 2
_list(index).Next = index + 1
Next
For index As Integer = _size - 1 To 1 Step -1
_list(index).Previous = index - 1
Next
_list(0).Previous = _size - 1
_last = _size - 1
End Sub
Private Sub MoveToFront(ByVal item As Integer)
'Remove Item from its position in the list and move it to the front.
If item = _first Then Return
Dim [next] As Integer = _list(item).Next
Dim previous As Integer = _list(item).Previous
_list(previous).Next = [next]
_list([next]).Previous = previous
_list(_first).Previous = item
_list(_last).Next = item
_list(item).Next = _first
_list(item).Previous = _last
_first = item
End Sub
Friend Sub Insert(
ByVal targetType As Type,
ByVal sourceType As Type,
ByVal classification As ConversionClass,
ByVal operatorMethod As Method)
If _count < _size Then _count += 1
'Replace the least used conversion in the list with a new conversion, and move
'that entry to the front.
Dim item As Integer = _last
_first = item
_last = _list(_last).Previous
_list(item).TargetType = targetType
_list(item).SourceType = sourceType
_list(item).Classification = classification
_list(item).OperatorMethod = operatorMethod
End Sub
Friend Function Lookup(
ByVal targetType As Type,
ByVal sourceType As Type,
ByRef classification As ConversionClass,
ByRef operatorMethod As Method) As Boolean
Dim item As Integer = _first
Dim iteration As Integer = 0
Do While iteration < _count
If targetType Is _list(item).TargetType AndAlso sourceType Is _list(item).SourceType Then
classification = _list(item).Classification
operatorMethod = _list(item).OperatorMethod
MoveToFront(item)
Return True
End If
item = _list(item).Next
iteration += 1
Loop
classification = ConversionClass.Bad
operatorMethod = Nothing
Return False
End Function
End Class
Friend NotInheritable Class FixedExistenceList
Private Structure Entry
Friend Type As Type
Friend [Next] As Integer
Friend Previous As Integer
End Structure
Private ReadOnly _list As Entry()
Private ReadOnly _size As Integer
Private _first As Integer
Private _last As Integer
Private _count As Integer
Private Const s_defaultSize As Integer = 50
Friend Sub New()
MyClass.New(s_defaultSize)
End Sub
Friend Sub New(ByVal size As Integer)
'Populate the list with the maximum number of entries.
'This simplifies the insertion code for a small upfront cost.
_size = size
_list = New Entry(_size - 1) {}
For index As Integer = 0 To _size - 2
_list(index).Next = index + 1
Next
For index As Integer = _size - 1 To 1 Step -1
_list(index).Previous = index - 1
Next
_list(0).Previous = _size - 1
_last = _size - 1
End Sub
Private Sub MoveToFront(ByVal item As Integer)
'Remove Item from its position in the list and move it to the front.
If item = _first Then Return
Dim [next] As Integer = _list(item).Next
Dim previous As Integer = _list(item).Previous
_list(previous).Next = [next]
_list([next]).Previous = previous
_list(_first).Previous = item
_list(_last).Next = item
_list(item).Next = _first
_list(item).Previous = _last
_first = item
End Sub
Friend Sub Insert(ByVal type As Type)
If _count < _size Then _count += 1
'Replace the least used conversion in the cache with a new conversion, and move
'that entry to the front.
Dim item As Integer = _last
_first = item
_last = _list(_last).Previous
_list(item).Type = type
End Sub
Friend Function Lookup(ByVal type As Type) As Boolean
Dim item As Integer = _first
Dim iteration As Integer = 0
Do While iteration < _count
If type Is _list(item).Type Then
MoveToFront(item)
Return True
End If
item = _list(item).Next
iteration += 1
Loop
Return False
End Function
End Class
Friend Shared ReadOnly ConversionCache As FixedList = New FixedList
Friend Shared ReadOnly UnconvertibleTypeCache As FixedExistenceList = New FixedExistenceList
End Class
End Class
End Namespace
|