9902ac25da27b2ce271132bd33f777efc3d088b0
[platform/upstream/dotnet/runtime.git] /
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.
4
5 Imports System
6 Imports System.ComponentModel
7 Imports System.Diagnostics
8 Imports System.Reflection
9
10 Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution
11 Imports Microsoft.VisualBasic.CompilerServices.ExceptionUtils
12 Imports Microsoft.VisualBasic.CompilerServices.Symbols
13 Imports Microsoft.VisualBasic.CompilerServices.Utils
14
15 Namespace Microsoft.VisualBasic.CompilerServices
16
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
21
22         Private Sub New()
23         End Sub
24
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)))
29             End If
30         End Sub
31
32
33
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
47
48
49             Private Sub New()
50             End Sub
51
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
54
55                 If Not type1.IsEnum AndAlso Not type2.IsEnum Then
56                     Dim tc1 As TypeCode = GetTypeCode(type1)
57                     Dim tc2 As TypeCode = GetTypeCode(type2)
58
59                     If IsNumericType(tc1) AndAlso IsNumericType(tc2) Then
60                         Return MapTypeCodeToType(ForLoopWidestTypeCode(tc1)(tc2))
61                     End If
62                 End If
63
64                 Dim leftToRight As ConversionClass = ClassifyConversion(type2, type1, Nothing)
65                 If leftToRight = ConversionClass.Identity OrElse leftToRight = ConversionClass.Widening Then
66                     Return type2
67                 End If
68
69                 Dim rightToLeft As ConversionClass = ClassifyConversion(type1, type2, Nothing)
70                 If rightToLeft = ConversionClass.Widening Then
71                     Return type1
72                 End If
73
74                 Return Nothing
75             End Function
76
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))
79             End Function
80
81             Private Shared Function ConvertLoopElement(ByVal elementName As String, ByVal value As Object, ByVal sourceType As Type, ByVal targetType As Type) As Object
82                 Try
83                     Return Conversions.ChangeType(value, targetType)
84                 Catch ex As OutOfMemoryException
85                     Throw ex
86                 Catch
87                     Throw New ArgumentException(SR.Format(SR.ForLoop_ConvertToType3, elementName, VBFriendlyName(sourceType), VBFriendlyName(targetType)))
88                 End Try
89             End Function
90
91             Private Shared Function VerifyForLoopOperator(
92                 ByVal op As UserDefinedOperator,
93                 ByVal forLoopArgument As Object,
94                 ByVal forLoopArgumentType As Type) As Method
95
96                 Dim operatorMethod As Method = Operators.GetCallableUserDefinedOperator(op, forLoopArgument, forLoopArgument)
97
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)))
103                 End If
104
105                 Dim operatorInfo As MethodInfo = TryCast(operatorMethod.AsMethod, MethodInfo)
106                 Dim parameters As ParameterInfo() = operatorInfo.GetParameters
107
108                 ' Validate the types
109                 Select Case op
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)))
119                         End If
120
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)))
129                         End If
130                 End Select
131
132                 Return operatorMethod
133             End Function
134
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
137
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"))
144                 End If
145
146                 Dim startType As Type = Start.GetType()
147                 Dim limitType As Type = Limit.GetType()
148                 Dim stepType As Type = StepValue.GetType()
149
150                 Dim widestType As Type = GetWidestType(stepType, startType, limitType)
151
152                 If widestType Is Nothing Then
153                     Throw New ArgumentException(SR.Format(SR.ForLoop_CommonType3, VBFriendlyName(startType), VBFriendlyName(limitType), VBFriendlyName(StepValue)))
154                 End If
155
156                 loopFor = New ForLoopControl
157
158                 Dim widestTypeCode As TypeCode = GetTypeCode(widestType)
159
160                 ' If the widest typecode is Object, try to use user defined conversions.
161                 If widestTypeCode = TypeCode.Object Then
162                     loopFor._useUserDefinedOperators = True
163                 End If
164
165                 If widestTypeCode = TypeCode.String Then
166                     widestTypeCode = TypeCode.Double
167                 End If
168
169                 Dim startTypeCode As TypeCode = startType.GetTypeCode
170                 Dim limitTypeCode As TypeCode = limitType.GetTypeCode
171                 Dim stepTypeCode As TypeCode = stepType.GetTypeCode
172
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
177
178                 If (startTypeCode = widestTypeCode) AndAlso startType.IsEnum Then
179                     currentEnumType = startType
180                 End If
181
182                 If (limitTypeCode = widestTypeCode) AndAlso limitType.IsEnum Then
183                     If (Not currentEnumType Is Nothing) AndAlso
184                        (Not currentEnumType Is limitType) Then
185                         currentEnumType = Nothing
186                         GoTo NotEnumType
187                     End If
188
189                     currentEnumType = limitType
190                 End If
191
192                 If (stepTypeCode = widestTypeCode) AndAlso stepType.IsEnum Then
193                     If (Not currentEnumType Is Nothing) AndAlso
194                        (Not currentEnumType Is stepType) Then
195                         currentEnumType = Nothing
196                         GoTo NotEnumType
197                     End If
198
199                     currentEnumType = stepType
200                 End If
201 NotEnumType:
202                 loopFor._enumType = currentEnumType
203
204                 If Not loopFor._useUserDefinedOperators Then
205                     loopFor._widestType = MapTypeCodeToType(widestTypeCode)
206                 Else
207                     loopFor._widestType = widestType
208                 End If
209
210                 loopFor._widestTypeCode = widestTypeCode
211
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)
215
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)
222                 End If
223
224                 'Important: a Zero step is considered Positive. This is consistent with the early-bound behavior.
225                 loopFor._positiveStep = Operators.ConditionalCompareObjectGreaterEqual(
226                         loopFor._stepValue,
227                         Operators.SubtractObject(loopFor._stepValue, loopFor._stepValue),
228                         False)
229
230                 LoopForResult = loopFor
231
232                 If Not loopFor._enumType Is Nothing Then
233                     CounterResult = System.Enum.ToObject(loopFor._enumType, loopFor._counter)
234                 Else
235                     CounterResult = loopFor._counter
236                 End If
237
238                 Return CheckContinueLoop(loopFor)
239             End Function
240
241             Public Shared Function ForNextCheckObj(ByVal Counter As Object, ByVal LoopObj As Object, ByRef CounterResult As Object) As Boolean
242
243                 Dim loopFor As ForLoopControl
244
245                 If LoopObj Is Nothing Then
246                     Throw VbMakeIllegalForException()
247                 End If
248
249                 If Counter Is Nothing Then
250                     Throw New NullReferenceException(SR.Format(SR.Argument_InvalidNullValue1, "Counter"))
251                 End If
252
253                 loopFor = CType(LoopObj, ForLoopControl)
254
255                 Dim needToChangeType As Boolean = False
256
257                 If Not loopFor._useUserDefinedOperators Then
258                     ' At this point, we know it's IConvertible
259                     Dim counterTypeCode As TypeCode = Counter.GetType.GetTypeCode
260
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)))
264                         Else
265                             Dim widestType As Type = GetWidestType(MapTypeCodeToType(counterTypeCode), loopFor._widestType)
266                             Dim widestTypeCode As TypeCode = GetTypeCode(widestType)
267
268                             If widestTypeCode = TypeCode.String Then
269                                 widestTypeCode = TypeCode.Double
270                             End If
271
272                             loopFor._widestTypeCode = widestTypeCode
273                             loopFor._widestType = MapTypeCodeToType(widestTypeCode)
274                             needToChangeType = True
275                         End If
276                     End If
277                 End If
278
279                 If needToChangeType OrElse loopFor._useUserDefinedOperators Then
280                     Counter = ConvertLoopElement("Start", Counter, Counter.GetType(), loopFor._widestType)
281
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)
285                     End If
286                 End If
287
288                 If Not loopFor._useUserDefinedOperators Then
289                     loopFor._counter = Operators.AddObject(Counter, loopFor._stepValue)
290
291                     Dim resultTypeCode As TypeCode = loopFor._counter.GetType.GetTypeCode ' CType(LoopFor.Counter, IConvertible).GetTypeCode()
292
293                     If Not loopFor._enumType Is Nothing Then
294                         counterResult = System.Enum.ToObject(loopFor._enumType, loopFor._counter)
295                     Else
296                         counterResult = loopFor._counter
297                     End If
298
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
304                         Return False
305                     End If
306                 Else
307                     ' Execute addition.
308                     loopFor._counter = Operators.InvokeUserDefinedOperator(
309                         loopFor._operatorPlus,
310                         True,
311                         Counter,
312                         loopFor._stepValue)
313
314                     If loopFor._counter.GetType() IsNot loopFor._widestType Then
315                         loopFor._counter = ConvertLoopElement("Start", loopFor._counter, loopFor._counter.GetType(), loopFor._widestType)
316                     End If
317
318                     counterResult = loopFor._counter
319                 End If
320
321                 Return CheckContinueLoop(loopFor)
322             End Function
323
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
328                 Else
329                     Return count >= limit
330                 End If
331             End Function
332
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
337                 Else
338                     Return count >= limit
339                 End If
340             End Function
341
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
346                 Else
347                     Return count >= limit
348                 End If
349             End Function
350
351             Private Shared Function CheckContinueLoop(ByVal loopFor As ForLoopControl) As Boolean
352
353                 If Not loopFor._useUserDefinedOperators Then
354                     Dim icompare As IComparable
355                     Dim compareResult As Integer
356
357                     Try
358                         icompare = CType(loopFor._counter, IComparable)
359                         compareResult = icompare.CompareTo(loopFor._limit)
360
361                         If loopFor._positiveStep Then
362                             Return compareResult <= 0
363                         Else
364                             Return compareResult >= 0
365                         End If
366
367                     Catch ex As InvalidCastException
368                         Throw New ArgumentException(SR.Format(SR.Argument_IComparable2, "loop control variable", VBFriendlyName(loopFor._counter)))
369                     End Try
370                 Else
371                     If loopFor._positiveStep Then
372                         Return CBool(Operators.InvokeUserDefinedOperator(
373                             loopFor._operatorLessEqual,
374                             True,
375                             loopFor._counter,
376                             loopFor._limit))
377                     Else
378                         Return CBool(Operators.InvokeUserDefinedOperator(
379                             loopFor._operatorGreaterEqual,
380                             True,
381                             loopFor._counter,
382                             loopFor._limit))
383                     End If
384                 End If
385             End Function
386
387         End Class
388     End Class
389 End Namespace