1 ' Licensed to the .NET Foundation under one or more agreements.
2 ' The .NET Foundation licenses this file to you under the MIT license.
3 ' See the LICENSE file in the project root for more information.
6 Imports System.ComponentModel
7 Imports System.Diagnostics
8 Imports System.Reflection
10 Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution
11 Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils
12 Imports Microsoft.VisualBasic.CompilerServices.Symbols
13 Imports Microsoft.VisualBasic.CompilerServices.Utils
15 Namespace Microsoft.VisualBasic.CompilerServices
17 ' Provides runtime support for loop iterators on various types such as
18 ' object, decimal, etc.
19 <EditorBrowsable(EditorBrowsableState.Never)>
20 Public NotInheritable Class ObjectFlowControl
25 Public Shared Sub CheckForSyncLockOnValueType(ByVal Expression As Object)
26 If Expression IsNot Nothing AndAlso Expression.GetType.IsValueType() Then
27 Throw New ArgumentException(
28 SR.Format(SR.SyncLockRequiresReferenceType1, VBFriendlyName(Expression.GetType)))
34 <System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)>
35 Public NotInheritable Class ForLoopControl
36 Private _counter As Object
37 Private _limit As Object
38 Private _stepValue As Object
39 Private _positiveStep As Boolean
40 Private _enumType As Type
41 Private _widestType As Type
42 Private _widestTypeCode As TypeCode
43 Private _useUserDefinedOperators As Boolean
44 Private _operatorPlus As Method
45 Private _operatorGreaterEqual As Method
46 Private _operatorLessEqual As Method
52 Private Shared Function GetWidestType(ByVal type1 As System.Type, ByVal type2 As System.Type) As Type
53 If type1 Is Nothing OrElse type2 Is Nothing Then Return Nothing
55 If Not type1.IsEnum AndAlso Not type2.IsEnum Then
56 Dim tc1 As TypeCode = GetTypeCode(type1)
57 Dim tc2 As TypeCode = GetTypeCode(type2)
59 If IsNumericType(tc1) AndAlso IsNumericType(tc2) Then
60 Return MapTypeCodeToType(ForLoopWidestTypeCode(tc1)(tc2))
64 Dim leftToRight As ConversionClass = ClassifyConversion(type2, type1, Nothing)
65 If leftToRight = ConversionClass.Identity OrElse leftToRight = ConversionClass.Widening Then
69 Dim rightToLeft As ConversionClass = ClassifyConversion(type1, type2, Nothing)
70 If rightToLeft = ConversionClass.Widening Then
77 Private Shared Function GetWidestType(ByVal type1 As System.Type, ByVal type2 As System.Type, ByVal type3 As System.Type) As Type
78 Return GetWidestType(type1, GetWidestType(type2, type3))
81 Private Shared Function ConvertLoopElement(ByVal elementName As String, ByVal value As Object, ByVal sourceType As Type, ByVal targetType As Type) As Object
83 Return Conversions.ChangeType(value, targetType)
84 Catch ex As OutOfMemoryException
87 Throw New ArgumentException(SR.Format(SR.ForLoop_ConvertToType3, elementName, VBFriendlyName(sourceType), VBFriendlyName(targetType)))
91 Private Shared Function VerifyForLoopOperator(
92 ByVal op As UserDefinedOperator,
93 ByVal forLoopArgument As Object,
94 ByVal forLoopArgumentType As Type) As Method
96 Dim operatorMethod As Method = Operators.GetCallableUserDefinedOperator(op, forLoopArgument, forLoopArgument)
98 If operatorMethod Is Nothing Then
99 Throw New ArgumentException(SR.Format(
100 SR.ForLoop_OperatorRequired2,
101 VBFriendlyNameOfType(forLoopArgumentType, fullName:=True),
102 Symbols.OperatorNames(op)))
105 Dim operatorInfo As MethodInfo = TryCast(operatorMethod.AsMethod, MethodInfo)
106 Dim parameters As ParameterInfo() = operatorInfo.GetParameters
110 Case UserDefinedOperator.Plus, UserDefinedOperator.Minus
111 If parameters.Length <> 2 OrElse
112 parameters(0).ParameterType IsNot forLoopArgumentType OrElse
113 parameters(1).ParameterType IsNot forLoopArgumentType OrElse
114 operatorInfo.ReturnType IsNot forLoopArgumentType Then
115 Throw New ArgumentException(SR.Format(
116 SR.ForLoop_UnacceptableOperator2,
117 operatorMethod.ToString,
118 VBFriendlyNameOfType(forLoopArgumentType, fullName:=True)))
121 Case UserDefinedOperator.LessEqual, UserDefinedOperator.GreaterEqual
122 If parameters.Length <> 2 OrElse
123 parameters(0).ParameterType IsNot forLoopArgumentType OrElse
124 parameters(1).ParameterType IsNot forLoopArgumentType Then
125 Throw New ArgumentException(SR.Format(
126 SR.ForLoop_UnacceptableRelOperator2,
127 operatorMethod.ToString,
128 VBFriendlyNameOfType(forLoopArgumentType, fullName:=True)))
132 Return operatorMethod
135 Public Shared Function ForLoopInitObj(ByVal Counter As Object, ByVal Start As Object, ByVal Limit As Object, ByVal StepValue As Object, ByRef LoopForResult As Object, ByRef CounterResult As Object) As Boolean
136 Dim loopFor As ForLoopControl
138 If (Start Is Nothing) Then
139 Throw New ArgumentException(SR.Format(SR.Argument_InvalidNullValue1, "Start"))
140 ElseIf (Limit Is Nothing) Then
141 Throw New ArgumentException(SR.Format(SR.Argument_InvalidNullValue1, "Limit"))
142 ElseIf (StepValue Is Nothing) Then
143 Throw New ArgumentException(SR.Format(SR.Argument_InvalidNullValue1, "Step"))
146 Dim startType As Type = Start.GetType()
147 Dim limitType As Type = Limit.GetType()
148 Dim stepType As Type = StepValue.GetType()
150 Dim widestType As Type = GetWidestType(stepType, startType, limitType)
152 If widestType Is Nothing Then
153 Throw New ArgumentException(SR.Format(SR.ForLoop_CommonType3, VBFriendlyName(startType), VBFriendlyName(limitType), VBFriendlyName(StepValue)))
156 loopFor = New ForLoopControl
158 Dim widestTypeCode As TypeCode = GetTypeCode(widestType)
160 ' If the widest typecode is Object, try to use user defined conversions.
161 If widestTypeCode = TypeCode.Object Then
162 loopFor._useUserDefinedOperators = True
165 If widestTypeCode = TypeCode.String Then
166 widestTypeCode = TypeCode.Double
169 Dim startTypeCode As TypeCode = startType.GetTypeCode
170 Dim limitTypeCode As TypeCode = limitType.GetTypeCode
171 Dim stepTypeCode As TypeCode = stepType.GetTypeCode
173 ' If one or more of the three values is an enum of the same underlying
174 ' type as the loop, and all of the enum types are the same, then make the type
175 ' of the loop the enum.
176 Dim currentEnumType As Type = Nothing
178 If (startTypeCode = widestTypeCode) AndAlso startType.IsEnum Then
179 currentEnumType = startType
182 If (limitTypeCode = widestTypeCode) AndAlso limitType.IsEnum Then
183 If (Not currentEnumType Is Nothing) AndAlso
184 (Not currentEnumType Is limitType) Then
185 currentEnumType = Nothing
189 currentEnumType = limitType
192 If (stepTypeCode = widestTypeCode) AndAlso stepType.IsEnum Then
193 If (Not currentEnumType Is Nothing) AndAlso
194 (Not currentEnumType Is stepType) Then
195 currentEnumType = Nothing
199 currentEnumType = stepType
202 loopFor._enumType = currentEnumType
204 If Not loopFor._useUserDefinedOperators Then
205 loopFor._widestType = MapTypeCodeToType(widestTypeCode)
207 loopFor._widestType = widestType
210 loopFor._widestTypeCode = widestTypeCode
212 loopFor._counter = ConvertLoopElement("Start", Start, startType, loopFor._widestType)
213 loopFor._limit = ConvertLoopElement("Limit", Limit, limitType, loopFor._widestType)
214 loopFor._stepValue = ConvertLoopElement("Step", StepValue, stepType, loopFor._widestType)
216 ' Verify that the required operators are present.
217 If loopFor._useUserDefinedOperators Then
218 loopFor._operatorPlus = VerifyForLoopOperator(UserDefinedOperator.Plus, loopFor._counter, loopFor._widestType)
219 VerifyForLoopOperator(UserDefinedOperator.Minus, loopFor._counter, loopFor._widestType)
220 loopFor._operatorLessEqual = VerifyForLoopOperator(UserDefinedOperator.LessEqual, loopFor._counter, loopFor._widestType)
221 loopFor._operatorGreaterEqual = VerifyForLoopOperator(UserDefinedOperator.GreaterEqual, loopFor._counter, loopFor._widestType)
224 'Important: a Zero step is considered Positive. This is consistent with the early-bound behavior.
225 loopFor._positiveStep = Operators.ConditionalCompareObjectGreaterEqual(
227 Operators.SubtractObject(loopFor._stepValue, loopFor._stepValue),
230 LoopForResult = loopFor
232 If Not loopFor._enumType Is Nothing Then
233 CounterResult = System.Enum.ToObject(loopFor._enumType, loopFor._counter)
235 CounterResult = loopFor._counter
238 Return CheckContinueLoop(loopFor)
241 Public Shared Function ForNextCheckObj(ByVal Counter As Object, ByVal LoopObj As Object, ByRef CounterResult As Object) As Boolean
243 Dim loopFor As ForLoopControl
245 If LoopObj Is Nothing Then
246 Throw VbMakeIllegalForException()
249 If Counter Is Nothing Then
250 Throw New NullReferenceException(SR.Format(SR.Argument_InvalidNullValue1, "Counter"))
253 loopFor = CType(LoopObj, ForLoopControl)
255 Dim needToChangeType As Boolean = False
257 If Not loopFor._useUserDefinedOperators Then
258 ' At this point, we know it's IConvertible
259 Dim counterTypeCode As TypeCode = Counter.GetType.GetTypeCode
261 If counterTypeCode <> loopFor._widestTypeCode OrElse counterTypeCode = TypeCode.String Then
262 If counterTypeCode = TypeCode.Object Then
263 Throw New ArgumentException(SR.Format(SR.ForLoop_CommonType2, VBFriendlyName(MapTypeCodeToType(counterTypeCode)), VBFriendlyName(loopFor._widestType)))
265 Dim widestType As Type = GetWidestType(MapTypeCodeToType(counterTypeCode), loopFor._widestType)
266 Dim widestTypeCode As TypeCode = GetTypeCode(widestType)
268 If widestTypeCode = TypeCode.String Then
269 widestTypeCode = TypeCode.Double
272 loopFor._widestTypeCode = widestTypeCode
273 loopFor._widestType = MapTypeCodeToType(widestTypeCode)
274 needToChangeType = True
279 If needToChangeType OrElse loopFor._useUserDefinedOperators Then
280 Counter = ConvertLoopElement("Start", Counter, Counter.GetType(), loopFor._widestType)
282 If Not loopFor._useUserDefinedOperators Then
283 loopFor._limit = ConvertLoopElement("Limit", loopFor._limit, loopFor._limit.GetType(), loopFor._widestType)
284 loopFor._stepValue = ConvertLoopElement("Step", loopFor._stepValue, loopFor._stepValue.GetType(), loopFor._widestType)
288 If Not loopFor._useUserDefinedOperators Then
289 loopFor._counter = Operators.AddObject(Counter, loopFor._stepValue)
291 Dim resultTypeCode As TypeCode = loopFor._counter.GetType.GetTypeCode ' CType(LoopFor.Counter, IConvertible).GetTypeCode()
293 If Not loopFor._enumType Is Nothing Then
294 counterResult = System.Enum.ToObject(loopFor._enumType, loopFor._counter)
296 counterResult = loopFor._counter
299 If resultTypeCode <> loopFor._widestTypeCode Then
300 'Overflow to bigger type occurred
301 loopFor._limit = Conversions.ChangeType(loopFor._limit, MapTypeCodeToType(resultTypeCode))
302 loopFor._stepValue = Conversions.ChangeType(loopFor._stepValue, MapTypeCodeToType(resultTypeCode))
303 'If we overflow, then we should always be at the end of the loop
308 loopFor._counter = Operators.InvokeUserDefinedOperator(
309 loopFor._operatorPlus,
314 If loopFor._counter.GetType() IsNot loopFor._widestType Then
315 loopFor._counter = ConvertLoopElement("Start", loopFor._counter, loopFor._counter.GetType(), loopFor._widestType)
318 counterResult = loopFor._counter
321 Return CheckContinueLoop(loopFor)
324 Public Shared Function ForNextCheckR4(ByVal count As Single, ByVal limit As Single, ByVal StepValue As Single) As Boolean
325 'Important: a Zero step is considered Positive. This is consistent with integral For loops.
326 If StepValue >= 0 Then
327 Return count <= limit
329 Return count >= limit
333 Public Shared Function ForNextCheckR8(ByVal count As Double, ByVal limit As Double, ByVal StepValue As Double) As Boolean
334 'Important: a Zero step is considered Positive. This is consistent with integral For loops.
335 If StepValue >= 0 Then
336 Return count <= limit
338 Return count >= limit
342 Public Shared Function ForNextCheckDec(ByVal count As Decimal, ByVal limit As Decimal, ByVal StepValue As Decimal) As Boolean
343 'Important: a Zero step is considered Positive. This is consistent with integral For loops.
344 If StepValue >= 0 Then
345 Return count <= limit
347 Return count >= limit
351 Private Shared Function CheckContinueLoop(ByVal loopFor As ForLoopControl) As Boolean
353 If Not loopFor._useUserDefinedOperators Then
354 Dim icompare As IComparable
355 Dim compareResult As Integer
358 icompare = CType(loopFor._counter, IComparable)
359 compareResult = icompare.CompareTo(loopFor._limit)
361 If loopFor._positiveStep Then
362 Return compareResult <= 0
364 Return compareResult >= 0
367 Catch ex As InvalidCastException
368 Throw New ArgumentException(SR.Format(SR.Argument_IComparable2, "loop control variable", VBFriendlyName(loopFor._counter)))
371 If loopFor._positiveStep Then
372 Return CBool(Operators.InvokeUserDefinedOperator(
373 loopFor._operatorLessEqual,
378 Return CBool(Operators.InvokeUserDefinedOperator(
379 loopFor._operatorGreaterEqual,